一、应用背景
目前手上有个项目,在使用主相机去正对观察目标时会因为反光等问题导致采集到的图像存在较多影响图像处理的干扰。遂使用第二个相机做侧向观察。现设计程序建立主副相机的映射关系,统一主副相机坐标。
二、主要opencv函数使用(基于opencv4)
1,findHomography()函数详解点这里(或getPerspectiveTransform()函数详解点这里)
这是两个有关获取透视变换矩阵的函数。通过这两个函数就能大致得到两个相机中图像的对应关系,这里findHomography()和getPerspectiveTransform()应该都能满足需求。但后者只能接收4个点,而findHomography()可以根据棋盘图像获取任意数量的点,所以在项目中我只使用了findHomography()函数。
2,findChessboardCornersSB()函数详解点这里
对于相机相对位置的标定,我选择了棋盘标定。这个函数由整合了findChessboardCorners()+cornerSubPix()的功能。实际工作表现非常好,在画质较差的情况下依然能准确识别。
3,drawChessboardCorners(image: Mat, patternSize: Any, corners: Any, patternWasFound: Any)
这个函数用来把棋盘标定findChessboardCornersSB()得到的点按顺序画出来
参数 | 解释 |
---|---|
image | 棋盘格图像 |
patternSize | 棋盘格内部角点的行、列数 |
corners | findChessboardCorners()输出的角点 |
patternWasFound | findChessboardCorners()的返回值 |
4,warpPerspective(src,M, dsize, dst=None, flags=None, borderMode=None, borderValue=None)
获取透视变化后的图像
参数 | 解释 |
---|---|
src | 输入图像矩阵 |
M | 3*3的透视变换矩阵,可以通过getPerspectiveTransform/findHomography等函数获取 |
dsize | 结果图像大小,为宽和高的二元组 |
dst | 输出结果图像,可以省略,结果图像会作为函数处理结果输出 |
flags | 可选参数,插值方法的组合(int 类型) |
borderMode | 可选参数,边界像素模式(int 类型) |
borderValue | 可选参数,边界填充值 |
三、主要代码及步骤
1,画出棋盘格
屏幕上显示棋盘格可以按如下函数执行
def drawChessboard(self):
length=400
width=500
checkboard_img = np.zeros((length,width)) #这是正方形棋盘格整体大小 分辨率像素:行x列
block_size=100 #方块大小:边长
black_block = np.full((block_size,block_size),255) #做一个白色的且边长为block_size的矩阵方块
for row in range(length//block_size): #计算行有几个黑块
for col in range(width//block_size): #计算列有几个黑块
if (row+col)%2==0:
row_begin = row*block_size
row_end = row_begin+block_size
col_begin = col*block_size
col_end = col_begin+block_size
checkboard_img[row_begin:row_end,col_begin:col_end] = black_block #画白块
white_background = np.ones((800,1000))#cv2.findChessboardCornersSB函数需要更多的留白
white_background[200:600,200:700] = checkboard_img#因此将棋盘放入白色背景中
cv2.imshow("checker_board",white_background)
cv2.moveWindow("checker_board",0,0)
cv2.waitKey(0)
棋盘格我这边使用屏幕画出来的,也可以打印出来。
注意:1,棋盘格的长和宽需要设计成不一样的,这样算法能够分清图片的方向。
2,棋盘格周围需要一圈留白增加算法检测图像的成功率。
2,主相机的图像检测
def main_findCornersInChessboard(self):
img = cv2.imread("main_camera.jpg")
ret,corners = cv2.findChessboardCornersSB(img,(3,4))#findChessboardCorners+cornerSubPix
cv2.drawChessboardCorners(img,(3,4),corners,ret)
print(corners)
if ret:
cv2.imshow('img', img)
cv2.waitKey(1000)
cv2.destroyAllWindows()
return corners
3,副相机图像检测
#同理
def sub_findCornersInChessboard(self):
img = cv2.imread("sub_camera.jpg")
ret,corners = cv2.findChessboardCornersSB(img,(3,4))#findChessboardCorners+cornerSubPix
cv2.drawChessboardCorners(img,(3,4),corners,ret)
print(corners)
if ret:
cv2.imshow('img', img)
cv2.waitKey(1000)
cv2.destroyAllWindows()
return corners
4,获取副相机里透视变换后的图像
def getCalibrateInfo(self):
main_points= self.main_findCornersInChessboard()
sub_points= self.sub_findCornersInChessboard()
retval,mask = cv2.findHomography(main_points,sub_points,cv2.RANSAC,10)
im = cv2.imread(r"sub_camera.jpg")
dst = cv2.warpPerspective(im,retval,(1080,1280)) #1080*1280是我相机的分辨率
总结
这两张图片,左边这张为副相机经透视变换得到的图片。右边这张为主相机得到的图片。观察可以看出,两张图片特征点的像素坐标可以做到对应。这样从副相机中可以初步定位位于主相机中的坐标