鱼眼图像的校正(Python实现)
問題描述
本文承接上文《魚眼圖像提取有效區域》,鏈接:https://blog.csdn.net/Megurine_Luka_/article/details/110563049
相對于常規鏡頭,魚眼鏡頭拍攝的圖像視角廣,能看到很大范圍的東西。但這會以所拍攝的圖像產生一定程度的扭曲為代價(畸變)。圖像校正就是修正畸變圖像的過程。
方案
本文的校正策略仍是參考論文《魚眼成像全景漫游系統的研究》,鏈接:https://xueshu.baidu.com/usercenter/paper/show?paperid=5a422997f595ef7b2d4dbe5f64b541ee&site=xueshu_se
一種傳統的魚眼校正方法是經緯度映射法,就是先將魚眼圖像映射在一個球面上。這樣做不無道理,因為魚眼鏡頭成像的時候,光線就是從一個球面經過折射,投射在成像平面上的(雖然事實并沒有那么簡單,但大體可以看作這樣),如下圖所示。而現在就是將成像過程反過來做。
之后,通過一系列坐標變換,將空間坐標轉換為經緯度坐標,再根據經緯度坐標直接投射到二維平面上,完成校正。
但這樣的效果并不理想,畸變仍然十分嚴重。舉個例子,格陵蘭島的面積不大,但它所跨的緯度很大,所以如果直接以經緯度為坐標投射在二維平面的話,它的面積會遠遠超過澳大利亞的面積。
首先,經度對畸變其實沒什么影響,直接映射即可,如下圖。我們需要處理的是緯度的問題。
對此,論文提出,將球面上的點經過一定的偏折,再映射到圖像平面上。下圖中(注意,此圖是從上向下看的,圖中的坐標軸與字母表示跟上圖是不對應的,從這里開始是緯度,是經度),是一個常數,它表示線段AC(點A就是要映射的點)與出射光線的夾角,最大為3/4Π,論文以及本文代碼中,將其設定為Π/2。實際上,如果光線不發生偏折,其映射結果是最精確的,但這需要一個無窮大的平面。偏折的意義在于將映射結果限定在一定范圍內。
經過一系列復雜的推導,可以得出如下結論:
推導過程詳見論文原文。其實是我沒看懂。
代碼
函數l
def ll(omiga,fai):x=math.sin(omiga)*math.sqrt(math.cos(fai)**2+(1-math.sin(fai))**2)y=math.sin(math.pi-omiga-math.atan((1-math.sin(fai))/abs(math.cos(fai))))return x/y插值處理
映射完畢后的圖片并不完整,因為算法是將球面上的點映射到一個平面上,但并不能保證平面上的每個點都被填滿,所以會存在一些縫隙,如下圖所示。
故此,需要對圖片進行插值處理。以下是我亂寫的插值算法,我也不知道它叫什么插值。
#data是二維數組,表示圖片,n,m表示大小 def inter(data,n,m):datat=np.copy(data)datas=np.copy(data)for j in range(m):sta=0if(data[0][j]==0):for i in range(n):if(data[i][j]!=0):sta=ibreakfor i in range(sta,0,-1):datat[i][j]=data[sta][j]end=0 if(data[n-1][j]==0):for i in range(n-1,0,-1):if(data[i][j]!=0):end=ibreakfor i in range(end,n,1):datat[i][j]=data[end][j]for i in range(n):if(data[i][j]!=0):x=data[sta][j]y=data[i][j]for k in range(sta,i,1):datat[k][j]=x+int((y-x)*(float(k-sta)/float(i-sta))) sta=ifor i in range(n):sta=0if(data[i][0]==0):for j in range(m):if(data[i][j]!=0):sta=jbreakfor j in range(sta,0,-1):datas[i][j]=data[i][sta]end=0 if(data[i][m-1]==0):for j in range(m-1,0,-1):if(data[i][j]!=0):end=jbreakfor j in range(end,m,1):datas[i][j]=data[i][end]for j in range(m):if(data[i][j]!=0):x=data[i][sta]y=data[i][j]for k in range(sta,j,1):datas[i][k]=x+int((y-x)*(float(k-sta)/float(j-sta))) sta=jfor i in range(n):for j in range(m):data[i][j]=int((datas[i][j]+datat[i][j])/2)return data主函數
再次說明,關于R,(u0,v0)的求解,詳見上一篇文章:https://blog.csdn.net/Megurine_Luka_/article/details/110563049
此代碼部分借鑒于https://blog.csdn.net/huoxingrenhdh/article/details/89057928,鳴謝。
#截取目標圖像,R為圓半徑,(u0,v0)為圓心img=cv2.imread(opt.path)img_valid=img[int(u0-R):int(u0+R+1),int(v0-R):int(v0+R+1)]#cv2.imwrite(opt.res_path,img_valid)m,n,k=img_valid.shape[:3]result=np.zeros((int(m*3),int(n*3),k))l=0w=0for i in range(m):for j in range(n):#經緯變換u=j-Rv=R-ir=math.sqrt(u*u+v*v)if(r==0):fi=0elif(u>=0):fi=math.asin(v/r)else:fi = math.pi - math.asin(v/r)f = R * 2 / math.pitheta = r / fx=f * math.sin(theta) * math.cos(fi)y=f * math.sin(theta) * math.sin(fi)z=f * math.cos(theta)#sita經度,fai緯度rr= math.sqrt(x * x + z * z)sita = math.pi / 2 - math.atan( y /rr)if(z>=0):fai = math.acos(x/rr)else:fai= math.pi - math.acos(x/rr)xx=round(f*sita)#opt.omiga取Π/2h=ll(opt.omiga,0)if fai<(math.pi/2):yy=round(f*(h-ll(opt.omiga,fai)))else:yy=round(f*(h+ll(opt.omiga,fai)))if ((xx < 1) | (yy < 1) | (xx > m) | (yy > n)):continuel=max(l,xx)w=max(w,yy)result[xx,yy,0] = img_valid[i, j, 0]result[xx,yy,1] = img_valid[i, j, 1]result[xx,yy,2] = img_valid[i, j, 2]result=result[0:l+1,0:w+1]#插值處理result=np.transpose(result,[2,0,1])result[0]=inter(result[0],l,w)result[1]=inter(result[1],l,w)result[2]=inter(result[2],l,w)result=np.transpose(result,[1,2,0])#輸出Undistortion = np.uint8(result)cv2.imwrite(opt.res_path,Undistortion)校正結果
?
總結
以上是生活随笔為你收集整理的鱼眼图像的校正(Python实现)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【2012 Semifinal 1】 Y
- 下一篇: ER实体关系图