標籤(空格分隔): 機器視覺html
雙目視覺定位是咱們的一個課程設計,最近剛作完,拿出來與你們分享一下,實驗的目的是在拍攝的照片中識別球體,並求出該球體到相機的實際距離嗎,咱們要求須要用matlab,可是matlab調用雙目攝像頭(一個USB口)卻總是隻能調用雙目攝像頭中的一個,可是利用Python的OpenCV庫卻能夠同時調用兩個,所以咱們選用了Python用於拍攝圖片。python
備註:由於根據得出的視差圖識別出圓球略困難,咱們沒有采用視差圖深度圖去得出球體的深度,而是直接利用校訂後的圖片像素差,代入公式直接求出距離。git
首先咱們要知道在攝像機模型中,通常要涉及四種座標系:世界座標系、攝像機座標系、圖像座標系、像素座標系。相機標定的做用能夠理解爲找到一種將世界座標系和圖像座標系之間的變換關係。
具體標定能夠參考這篇文章:雙目視覺相機標定github
- 1.像素座標系(u,v):
顧名思義,每一點都是一個像素,原點一般設置在圖像的左上角處,點的橫縱座標分別表明相應的像素在圖像中位於第幾行第幾列;- 2.圖像座標系(x,y):
將原點設置在相機光軸與圖像平面的交點處,橫軸、縱軸與像素座標系的橫軸、縱軸平行; 圖像平面的中心爲座標原點,爲了描述成像過程當中物體從相機座標系到圖像座標系的投影透射關係而引入,方便進一步獲得像素座標系下的座標。圖像座標系是用物理單位(例如毫米)表示像素在圖像中的位置。- 3.相機座標系(x,y,z):
攝像機站在本身角度上衡量的物體的座標系。攝像機座標系的原點在攝像機的光心上,z軸與攝像機光軸平行。它是與拍攝物體發生聯繫的橋頭堡,世界座標系下的物體需先經歷剛體變化轉到攝像機座標系,而後在和圖像座標系發生關係。它是圖像座標與世界座標之間發生關係的紐帶,溝通了世界上最遠的距離。單位爲長度單位如mm。- 4.世界座標系 (Xw,Yw,Zw):
物體在真實世界中的座標。
具體標定利用matlab的工具箱,步驟以下:
1.打印標定板:
ide
2.拿着標定板拍照(拍照代碼在文末),效果圖如圖:
函數
3.將上面的照片按left,right分爲兩組,放到兩個文件夾中,打開matlab進行標定。在命令行窗口輸入stereoCameraCalibrator,出現以下界面:
工具
4.而後將上面的「Skew」、「Tangential Distortion」以及「2 Coefficients」等選項選上,將「3 Coefficients」選項去掉,以下:
spa
5.載入圖像:
備註:標定板打印的標準一點,圖中的25是小方格邊長,方格!邊長!根據你的實際狀況修改。命令行
6.點擊按鈕進行標定:
設計
7.選中直方圖中偏差較大的一些組,(與此同時左側的圖片也會被選中),將這些刪除,而後他會自動從新標定。
8.以爲沒問題了,就把標定的數據導出(那個綠色的對勾符號)。以後關掉這個工具箱,會彈出一個框,而後選擇yes保存生成的.mat文件。記住本身保存的路徑,下次用的時候提早在matlab中打開這個文件。至此,標定完成。
標定的參數包括不少,包括相機的內參,外參等,有旋轉矩陣,平移矩陣等。能夠本身去查查。
圖像校訂是根據標定獲取的畸變矩陣,來校訂相機拍攝時形成的偏差,包括徑向畸變和切向畸變。二者合起來是一個15的矩陣,不過matlab標定的時候卻只顯示兩個12的矩陣,查了知道缺失的一個參數能夠設置爲0,.若是你利用python雙目測距這篇文章作的雙目視覺定位,這句話確定會給予你幫助。
這裏採用了霍夫變換去識別小球,霍夫變換找圓的基本思想是,對於一個圓
咱們能夠把它放入以a,b,r爲座標軸的三維座標系系中,而不是以x,y爲座標軸的二維座標系中,又由於同一平面不在一條直線的三個點就能夠肯定一個圓,因此圖中對應的A點(a,b,r)就是一個圓心,可是若是這樣找的話,圖中能夠找到不少這樣的三個點,因此也確定能找到不少圓,所以須要進行‘投票’,即若是有不少點交於這一點,那麼這個點極有多是圓心:
具體代碼以下,(github上找的):
function [Hough_space,Hough_circle_result,Para] = Hough_circle(BW,Step_r,Step_angle,r_min,r_max,p) %--------------------------------------------------------------------------------------------------------------------------- % input: % BW:二值圖像; % Step_r:檢測的圓半徑步長; % Step_angle:角度步長,單位爲弧度; % r_min:最小圓半徑; % r_max:最大圓半徑; % p:以p*Hough_space的最大值爲閾值,p取0,1之間的數. % a = x-r*cos(angle); b = y-r*sin(angle); %--------------------------------------------------------------------------------------------------------------------------- % output: % Hough_space:參數空間,h(a,b,r)表示圓心在(a,b)半徑爲r的圓上的點數; % Hough_circle:二值圖像,檢測到的圓; % Para:檢測到的圓的圓心、半徑. %--------------------------------------------------------------------------------------------------------------------------- circleParaXYR=[]; Para=[]; %獲得二值圖像大小 [m,n] = size(BW); %計算檢測半徑和角度的步數、循環次數 並取整,四捨五入 size_r = round((r_max-r_min)/Step_r)+1; size_angle = round(2*pi/Step_angle); %創建參數空間 Hough_space = zeros(m,n,size_r); %查找非零元素的行列座標 [rows,cols] = find(BW); %非零座標的個數 ecount = size(rows); % Hough變換 % 將圖像空間(x,y)對應到參數空間(a,b,r) % a = x-r*cos(angle) % b = y-r*sin(angle) i = 1; ecount = ecount(1); for i=1:ecount for r=1:size_r %半徑步長數按必定弧度把圓幾等分 for k=1:size_angle a = round(rows(i)-(r_min+(r-1)*Step_r)*cos(k*Step_angle)); b = round(cols(i)-(r_min+(r-1)*Step_r)*sin(k*Step_angle)); if (a>0&&a<=m&&b>0&&b<=n) Hough_space(a,b,r)=Hough_space(a,b,r)+1;%h(a,b,r)的座標,圓心和半徑 end end end end % 搜索超過閾值的匯集點,對於多個圓的檢測,閾值要設的小一點!經過調此值,能夠求出全部圓的圓心和半徑返回值就是這個矩陣的最大值 max_para = max(max(max(Hough_space))); %一個矩陣中,想找到其中大於max_para*p數的位置 index = find(Hough_space>=max_para*p); length = size(index);%符合閾值的個數 Hough_circle_result=zeros(m,n); %經過位置求半徑和圓心。 length = length(1); k = 1; par = 1; for i=1:ecount for k=1:length par3 = floor(index(k)/(m*n))+1; par2 = floor((index(k)-(par3-1)*(m*n))/m)+1; par1 = index(k)-(par3-1)*(m*n)-(par2-1)*m; if((rows(i)-par1)^2+(cols(i)-par2)^2<(r_min+(par3-1)*Step_r)^2+5&&... (rows(i)-par1)^2+(cols(i)-par2)^2>(r_min+(par3-1)*Step_r)^2-5) Hough_circle_result(rows(i),cols(i)) = 1;%檢測的圓 end end end % 從超過峯值閾值中獲得 for k=1:length par3 = floor(index(k)/(m*n))+1;%取整 par2 = floor((index(k)-(par3-1)*(m*n))/m)+1; par1 = index(k)-(par3-1)*(m*n)-(par2-1)*m; circleParaXYR = [circleParaXYR;par1,par2,par3]; Hough_circle_result(par1,par2)= 1; %這時獲得好多圓心和半徑,不一樣的圓的圓心處彙集好多點,這是由於所給的圓不是標準的圓 end %集中在各個圓的圓心處的點取平均,獲得針對每一個圓的精確圓心和半徑; while size(circleParaXYR,1) >= 1 num=1; XYR=[]; temp1=circleParaXYR(1,1); temp2=circleParaXYR(1,2); temp3=circleParaXYR(1,3); c1=temp1; c2=temp2; c3=temp3; temp3= r_min+(temp3-1)*Step_r; if size(circleParaXYR,1)>1 for k=2:size(circleParaXYR,1) if (circleParaXYR(k,1)-temp1)^2+(circleParaXYR(k,2)-temp2)^2 > temp3^2 XYR=[XYR;circleParaXYR(k,1),circleParaXYR(k,2),circleParaXYR(k,3)]; %保存剩下圓的圓心和半徑位置 else c1=c1+circleParaXYR(k,1); c2=c2+circleParaXYR(k,2); c3=c3+circleParaXYR(k,3); num=num+1; end end end c1=round(c1/num); c2=round(c2/num); c3=round(c3/num); c3=r_min+(c3-1)*Step_r; Para=[Para;c1,c2,c3]; %保存各個圓的圓心和半徑的值 circleParaXYR=XYR; End
計算距離採用了一個公式:,其中f爲焦距,T爲兩個相機之間的距離,分母爲兩個圖片的像素差,例如校訂以後,球心在第一張圖片的像素座標爲(u,v1),在第二章圖片的像素座標爲(u,v2),則像素差爲|v1-v2|。
注意這個參數,rectifyStereoImages()這個函數要用:
代碼以下,讀入兩幅圖,可是隻在一張圖上進行了距離標註,另外一張圖只負責提供圓心座標
注意若是不能準確識別出圓球,須要修改最小,最大圓半徑這兩個參數:
I1 = imread('E:\image\left_0.jpg');%讀取左右圖片 I2 = imread('E:\image\right_0.jpg'); %%%注意校訂的第三個參數,他的名字由你獲得的標定文件獲得,看上圖%%%% [J1, J2] = rectifyStereoImages(I1,I2,calibrationSession.CameraParameters); I1=rgb2gray(J1) I=rgb2gray(J2) %------------------------------------------ BW1=edge(I1,'sobel') BW=edge(I2,'sobel') %---------- Step_r = 0.5; %角度步長0.1,單位爲弧度 Step_angle = 0.1; %最小圓半徑2 minr =5; %最大圓半徑30 maxr = 8; %以thresh*hough_space的最大值爲閾值,thresh取0-1之間的數 thresh = 1; %-----------這個只負責提供其中一張圖片的圓心座標,這個函數是上一段代碼 [Hough_space,Hough_circle_result,Para] = Hough_circle(BW1,Step_r,Step_angle,minr,maxr,thresh); %開始檢測另外一個圓 [Hough_space,Hough_circle_result,Para1] = Hough_circle(BW,Step_r,Step_angle,minr,maxr,thresh); %兩幅圖像素差 sub=(Para(2)-Para1(2)) %*100/67%若是這裏有67%縮放比例 %焦距 jiaoju=3.6 %相機距離 jixian=12.2 depth=(jiaoju*jixian)/abs(sub) %距離,轉成字符串才能放上去 depth=num2str(depth) axis equal figure(1); imshow(BW,[]),title('邊緣(圖一)'); axis equal figure(2); imshow(Hough_circle_result,[]),title('檢測結果(圖一)'); axis equal figure(3),imshow(I,[]),title('檢測出圖中的圓(圖一)') hold on; %-------------------------------------------------------------- %以紅色線標記出的檢測圓心與圓 plot(circleParaXYR(:,2), circleParaXYR(:,1), 'r+'); %打上距離值 text(circleParaXYR(:,2)+circleParaXYR(3), circleParaXYR(:,1)-circleParaXYR(3),depth,'color','red') for k = 1 : size(circleParaXYR, 1) t=0:0.01*pi:2*pi; x=cos(t).*circleParaXYR(k,3)+circleParaXYR(k,2); y=sin(t).*circleParaXYR(k,3)+circleParaXYR(k,1); plot(x,y,'r-'); End
獲得的效果圖:
隨便改改代碼甚至還能夠皮一下:
視差圖還好說,深度圖卻至關詭異,因此咱們把深度圖註釋掉了。
clear all clc I1 = imread('E:\image\left_0.jpg');%讀取左右圖片 I2 = imread('E:\image\right_0.jpg'); %I1=imcrop(I1,[0,118,320,320])%去除黑邊 %I2=imcrop(I2,[0,118,320,320]) figure imshowpair(I1, I2, 'montage'); title('Original Images'); %--------------------------------------------------------------- load('E:\image\calibrationSession.mat');%加載你保存的相機標定的mat %校訂圖片 [J1, J2] = rectifyStereoImages(I1,I2,calibrationSession.CameraParameters); % figure % imshow(J1) figure imshowpair(J1, J2, 'montage'); title('Undistorted Images'); %----------------------------------------- figure; imshow(cat(3, J1(:,:,1), J2(:,:,2:3)), 'InitialMagnification', 100) %---------------------------------------------------------------------------- %disparityRange = [-6 10];如此果想生成彩色視差圖,將此註釋解除 disparityMap1 = disparity(rgb2gray(J1),rgb2gray(J2),'DisparityRange',[0,16],'BlockSize',5,'ContrastThreshold',0.3,'UniquenessThreshold',5); figure imshow(disparityMap1)%%%,disparityRange);如此果想生成彩色視差圖,將此註釋解除 % title('Disparity Map');%生成彩色視差圖,將此註釋解除 % colormap(gca,jet) %生成彩色視差圖,將此註釋解除 % colorbar%生成彩色視差圖,將此註釋解除 %---深度圖註釋掉 %pointCloud3D = reconstructScene(disparityMap1, %calibrationSession.CameraParameters);%深度圖 %figure; %imshow(pointCloud3D);
效果圖:
import cv2 import time AUTO = True # 自動拍照,或手動按s鍵拍照 INTERVAL = 2 # 自動拍照間隔 cv2.namedWindow("shuangmu") cv2.moveWindow("shuangmu", 400, 0) shuangmu_camera = cv2.VideoCapture(1)#打開雙目攝像頭 counter = 0 utc = time.time() ###pattern = (12, 8) # 棋盤格尺寸 folder = "e:/keshe/" # 拍照文件目錄 def shot(pos, frame): global counter path = folder + pos + "_" + str(counter) + ".jpg" cv2.imwrite(path, frame)#保存圖像 print("snapshot saved into: " + path) while True: #ret是布爾值,若是讀取幀是正確的則返回True,frame就是每一幀的圖像 ret,double_frame = shuangmu_camera.read() heigh=len(double_frame) width=len(double_frame[0]) width2=int(width/2) left1=double_frame[:heigh,0:width2] right1=double_frame[:heigh,width2:width] cv2.imshow("shuangmu", double_frame) now = time.time() if AUTO and now - utc >= INTERVAL: shot("left", left1) shot("right", right1) counter += 1 utc = now key = cv2.waitKey(1)#等待鍵盤輸入,1表示延時1ms切換到下一幀圖像,對於視頻而言 if key == ord("q"): break elif key == ord("s"): shot("right", right_frame) counter += 1 cv2.destroyWindow("shuangmu")#釋放窗口