前言

最近应学院的要求在做全景图,经费不足全都跪,申不了效果好的全景相机,只好租了Sony A7R2 + FE 12-24mm F4 G,外加Ronin SC自动环拍来拼接全景。

PS:SC的做工好nice,不过吐槽下SC app全景模式的计算有bug,会有位置不对的情况。而且还会有没有拍到的情况,设置了数字对焦更改了拍摄模式之后,拍不到的情况就改善多了。

拍完之后就是拼合了,全景图拼合软件,试遍了全网,也就PTGui的效果最好,然鹅售价感人。 Google的光流拼合算法看着也很nice,奈何找了一圈并没有软件或者api可以用。

小机灵

于是用python就抖一点小机灵了。PTGui版本是11.16。 以下是源代码,其实很简单,原理就是利用两张错位的图像来覆盖水印。

经测试在“全景编辑器”中将“偏航yaw”填为10°左右时效果最好。

另外因为水印边距是有移动的,所以每次拼合图片也要调节水印左边距的大小。

更新:边距移动是因为图片分辨率的问题,水印是相对与图片分辨率的,每个水印的宽度和间隔是固定的。 所以合成的图片分辨率越大水印越多。这样子就可以简化代码了,固定间距值就可以了,不过懒得改了。

不过鉴于此,下面的代码在图片保存时为17000*8500分辨率的效果最好。

嗯,看了一下60多张全景图图,残念(Φ皿Φ),还是选择改了下逻辑,现在基本都可以处理了。

17000*8500边距大概280

17500*8750边距大概550

18000*9000边距大概760

再更新下,直接自动算出左边距,果然偷懒是第一生产力。

import cv2
import os

#拼合图像
def combin(img_Pic1, img_Pic2):
    img_size = []
    col_num = 19  # 出现的完整水印列数
    WMark_width = 304  # 水印宽度
    interval_width = (int)(WMark_width/19*37)  # 空白区域宽度 (水印与间隔区域比例19:37)
    rang_error = 50  # 误差区间
    x_flag = 0 # 水印标志位
    x_left = 0 # 起始边距
    nor_w = interval_width-rang_error

    try:
        img_size = img_Pic1.shape
        addDist = (int)(img_size[1]*(10/360))  # 10°偏移量
        margin_width = (int)((img_size[1]-((col_num*WMark_width)+(col_num-1)*interval_width))/2)#左边距
        WM_Ratio = img_size[1]/((col_num*WMark_width)+(col_num+1)*interval_width)
        if(WM_Ratio > 1):  # 判断是否会出现残缺水印
            x_left = (int)((img_size[1]-((col_num*WMark_width)+(col_num+1)*interval_width))/2)

        for i in range(col_num+1):  # 拼合
            if(i == 0):  # 第一块区域
                w = (int)(margin_width)
                patch = img_Pic2[:, x_left : w]
                img_Pic1[:, (x_left + addDist):(w+addDist)] = patch
                x_flag += (int)(margin_width) + WMark_width

            elif(i == col_num):  # 最后一块区域
                w = img_size[1]-x_flag
                if(w > interval_width):
                    x_temp_1 = x_flag+rang_error+interval_width-addDist
                    x_temp_2 = x_flag+rang_error+interval_width  # 末尾水印开始的位置
                    x_temp_3 = img_size[1] - addDist  # temp2中对应的temp1末尾位置/起始位置

                    patch1 = img_Pic2[:, x_temp_1-rang_error: x_temp_3]
                    img_Pic1[:, x_temp_2-rang_error:] = patch1

                    patch2 = img_Pic2[:, x_temp_3:x_temp_2-rang_error]
                    img_Pic1[:, :x_temp_2-x_temp_3-rang_error] = patch2
                else:
                    x_temp_4 = img_size[1] - (x_flag + rang_error)
                    x_temp_5 = (x_flag+rang_error+addDist) % img_size[1]
                    patch = img_Pic2[:, x_flag+rang_error:]
                    img_Pic1[:, x_temp_5:x_temp_5 + x_temp_4] = patch
            else:
                x_temp_a = (x_flag+rang_error+addDist)
                x_temp_b = (x_flag+nor_w+addDist)
                x_temp_c = (x_flag+rang_error)
                x_temp_d = (x_flag+nor_w)
                img_Pic1[:, x_temp_a:x_temp_b] = img_Pic2[:, x_temp_c:x_temp_d]# 指定位置填充,大小要一样才能填充
                x_flag += (int)(interval_width) + WMark_width

            # cv2.namedWindow("Tracking", 0)
            # cv2.resizeWindow("Tracking", 1700, 850)
            # cv2.imshow("Tracking", img_Pic1)
            # cv2.waitKey(0)
    except Exception as e:
        print("error:\n" + (str)(e))
        return -1
    img_Pic1 = cv2.resize(img_Pic1, (17000, 8500), interpolation=cv2.INTER_AREA)
    return img_Pic1

#获取文件夹名
def file_name(dir_path):
    global f_namelist  # 文件或文件夹名称(图片)
    f_namelist = []
    for files in os.listdir(dir_path):
        f_namelist.append(files)
    return f_namelist

if __name__ == "__main__":
    print("-------请勿有中文路径,并将拼合图片分别命名为temp1.jpg与temp2.jpg-------")
    Pic_dir = input("请输入图片路径:")
    f_namelist = file_name(Pic_dir)
    for i in range(len(f_namelist)):
        img_Pic1 = cv2.imread(Pic_dir+"/"+f_namelist[i] + "/temp1.jpg")
        img_Pic2 = cv2.imread(Pic_dir+"/"+f_namelist[i] + "/temp2.jpg")

        img_out = combin(img_Pic1, img_Pic2)
        if(len((str)(img_out)) > 10):
            cv2.imwrite(Pic_dir+"/"+f_namelist[i]+"-final.jpg", img_out)
            print(f_namelist[i]+"-拼合完成!!!")
        else:
            print("error:" + f_namelist[i])

效果

如下图:

全景Temp 全景拼合

乌拉★,°:.☆( ̄▽ ̄)/$:.°★ 。当然有能力支持正版哈~


仅此而已的地方