标签(空格分隔): 机器视觉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")#释放窗口