两个图中四个顶点的对应坐标是一样,那么如何通过 opencv api 检测到以上两张图片内容的不同。
import cv2
# 读取图片
img1 = cv2.imread('image1.jpg')
img2 = cv2.imread('image2.jpg')
# 检测第一个矩形
rect1 = cv2.minAreaRect(cv2.findContours(img1, cv2.RETR_EXTERNAL))
box1 = cv2.boxPoints(rect1)
pt1 = tuple(box1[0])
pt2 = tuple(box1[1])
pt3 = tuple(box1[2])
pt4 = tuple(box1[3])
cv2.drawContours(img1, [pt1, pt2, pt3, pt4], -1, (0, 0, 255), 2)
# 检测第二个矩形
rect2 = cv2.minAreaRect(cv2.findContours(img2, cv2.RETR_EXTERNAL))
box2 = cv2.boxPoints(rect2)
pt5 = tuple(box2[0])
pt6 = tuple(box2[1])
pt7 = tuple(box2[2])
pt8 = tuple(box2[3])
cv2.drawContours(img2, [pt5, pt6, pt7, pt8], -1, (0, 0, 255), 2)
# 检查两个矩形是否闭合
if cv2.isClosed(box1, pt1) == True and cv2.isClosed(box1, pt2) == True and \
cv2.isClosed(box1, pt3) == True and cv2.isClosed(box1, pt4) == True and \
cv2.isClosed(box2, pt5) == True and cv2.isClosed(box2, pt6) == True and \
cv2.isClosed(box2, pt7) == True and cv2.isClosed(box2, pt8) == True:
print('The two rectangles are closed.')
else:
print('The two rectangles are not closed.')
# 显示图片
cv2.imshow('image1', img1)
cv2.imshow('image2', img2)
cv2.waitKey(0)
cv2.destroyAllWindows()
OpenCV 是一个软件工具包,用于处理实时图像和视频,并提供分析和机器学习功能。使用这些标准化的软件包可以极大提高我们的开发效率,并且这些工具包对算法运行速度有特别的优化,能够使得这些算法在拥有GPU或支持多线程的电脑上得到加速。掌握OpenCV中的基本数据类型和常用函数是视觉组迈出开发的第一步,同时也能学习大量的相关知识。
这是OpenCV的官方网站,可以在这里的社区和其他开发者交流或查阅说明文档和例程。
首先你需要安装OpenCV,可以参考Ubuntu下OpenCV+contirb模块完全安装指南-NeoZng。
基本数据类型
Mat:矩阵类型,能够保存图像。
Point:一个像素点,或者任何类型的“点”。
Scalar:一个四维点类,是许多函数的参数。
Size:同样是一对数据构成的组,一般表示一块区域或图像的宽高,有些时候可以和point互换。
Rect:rectangle,矩形类,拥有Point和Size成员,用于表示一块矩形的区域。
RotatedRect:同上,不过有额外的成员angle用于表示角度。注意这个类的角度系统有些独特,务必阅读:OpenCV中旋转矩形的角度。
具体的说明请参阅OpenCV的说明文档,或在IDE内选择switch to declaration,便能转到注释处。
imgproc 模块(image process)
是我们使用OpenCV时最重要的模块之一。主要是一些像素级的操作,通过图像滤波、形态学操作、阈值操作、通道处理、图像变换、轮廓查找等功能来凸显图像特征或滤除噪声。还可以通过一些简单的绘图函数在图片上作画、输出文本。下面列出一些常用函数:
画图
circle() //画出一个颜色、大小、粗细可调的圆,一般用于标记角点等特殊位置
line() //在两点之间画出一条直线,用于框出目标或作为参考。
//用于标记装甲板、能量机关的角点,框出候选的目标
颜色空间转换
cvtColor() //将图片从一个颜色空间转换到另一个颜色空间
split() //把图片的不同通道进行拆分,放入不同的Mat
subtract() //将两张矩阵的每个元素相减
//这在自瞄中将用于RGB到GRAY和HSV等空间的转换和颜色通道的分离。
阈值
threshold() //阈值操作,对一个特定的分量与阈值进行比较,大于阈值则全部设为某个值,小于阈值设为另一个值
inRange() //进阶版本,可以确定一个分量是否在一个区间内
//我们使用这两个函数来筛选特征,对拆分后的颜色空间进行操作以屏蔽不感兴趣的部分
滤波与平滑
blur() //加权模糊图像
GaussianBlur() //高斯加权模糊
medianBlur() //中值滤波
bilateralFilter() //双边滤波
//用于对图像进行降噪处理,或是抹去小光斑等
形态学操作
erode() //腐蚀操作,二值图的边缘或收缩
dilate() //膨胀操作,二值图的边缘会扩张
getStructuringElement() //获得结构元素(核)
morphologyEx() //更多的形态学操作,包括Opening,Closing,Morphological Gradient,Top Hat,Black Hat等
//用于增强图像的某些特征
其他图像算子
Sobel() //微分运算,检测边缘,微分会使得图像中像素强度(某个分量)变化最大的部分为极值
Laplacian() //二阶微分,检测边缘,二阶微分会使得图像中像素强度变化最剧烈的部分为零
//寻找图像中的边缘
滤波、平滑、形态学操作等都属于使用图像算子对图片进行卷积操作,学习过数字图像处理或信号与系统的同学应该对此熟悉。在OpenCV中,你可以使用 getStructuringElement() 来构建独特的卷积核,随后使用 filer2D() 来对图像进行卷积运算。
寻找/画出轮廓 + 矩形/椭圆拟合
floodFill() //漫水法,常用于寻找轮廓的预处理操作,和“画图”软件中“油漆桶”工具有相同的效果
findContour() //寻找二值图中的轮廓,并保存为一组点,算法类似于漫水法,遍历所有像素并查找相邻像素
drawContour() //根据一组点画出轮廓
//承接上面的各种预处理,用于找出图像中的轮廓并进行下一步操作
minAreaRect() //通过轮廓点,拟合出最小面积的RotatedRect
boundingRect() //通过轮廓点,找到其外接矩形Rect(水平)
fitEcllipse() //通过轮廓点,用最小二乘法拟合出一个外接椭圆,函数会返回椭圆的内接旋转矩形RotatedRect
minEnclosingCircle() //通过轮廓点,找到最小面积的包含圆(注意不是外接圆)
//将轮廓点转换为更容易处理的形状对象
仿射、投影变换
remap() //根据给定的映射(函数)改变图像中每个像素点的位置
warpPerspective() //进行透视变换
getPerspectiveTransform() //获得透视变换所需的矩阵(4个点)
warpAffine() //进行仿射变换
getAffineTransform() //获得仿射变换所需的矩阵(3个点,为什么比透视少一个点?)
//一般用于把图像根据变换关系转化成正视图以便进行模板匹配、SVM匹配等操作
要区分仿射变换和投影变换,请记住仿射变换是线性变换,而投影变换不单单是线性变换。仿射变换会保持对象的相似关系和平行关系,而投影变换可能会改变这种关系,添加了非线性的因素(仿射变换的维度比投影少1,投影是一个商空间)。
imgcodecs 模块(image reading and writing)
imread() //根据路径读取一张图片
imwrite() //向对应路径写入一张图像
imreadmulti() //一次读取多张图片
//读取、保存测试用的图片或者自己制作的卷积核、用作模板匹配的图片模板等
videoio 模块 (video input and output)
class VideoCapture()
//构建一个视频捕获类,捕获的视频可以以每一帧图像的形式保存到Mat中
//VideoCapture cap(0); Mat frame; cap>>frame;这样就把一帧图片保存到frame内部了
//这个类能够通过get(),set()方法获取和设置一些相机参数
class VideoWriter()
//构建一个视频保存类,能够方便地保存视频,并且提供各种格式
//在实验室时无法模拟赛场的光线环境,常常在比赛时录制相机第一视角的视频,以供之后测试使用
//也可以把检测完的每一帧图片连成视频,保存下来,之后根据这个视频来查找问题、改进算法
highgui模块(high level graphis user interface)
imshow()/*在指定名称的窗口中显示一张图片,注意和waitKey()配合使用否则可能导致异常,用于查看一些算法处理后的结果,waitKey()的参数为图片显示的时间*/
//以下这个组合可以极大地方便参数调试,在程序运行的过程中通过回调函数,可以实时修改参数值
nameWindow() //新建一个空窗口
createTrackBar() //创建一个拖条,传入相关的参数可以实现参数调节
getTrackBarPos() //返回拖条所在的位置
//这个组合能够通过键盘和鼠标向程序传递参数,改变程序的状态,调试的时候非常好用
setMouseCallback() //设置鼠标的回调函数
waitKeyEx() //从键盘读取输入
除了第一个 imshow(),在使用highgui模块时需要你了解一些响应式编程的方法(有些类似于中断编程),不同于以往的的控制流命令式(面向过程)编程,响应式的程序在运行的时候会监听并响应异步数据流(Event Stream),可以时时和用户交互。我们使用的操作系统图形界面几乎都采用了响应式编程。
其他模块
ML:machine learning模块,内有封装完全的多层感知机、基于Dtree决策树的boost集成算法、EM(expectation Maximization)、逻辑回归、朴素贝叶斯分类器、模拟退火优化、支持向量机等经典的机器学习算法和一个能够提供各种功能的内置的数据集包。对于自动步兵、哨兵和雷达的开发有很大的帮助,可能也能帮助我们构建其他需要这些算法的模块如:装甲板分类(Bayes或SVM)。
calib3d:Camera Calibration and 3D Reconstruction模块,包含了相机标定的算法和一些三维重建方法。我们在得到装甲板的位置后,需要解算装甲板到相机的距离,这就会用到这个模块内的 solvePnP()函数。在前面提到过,为了正确的反映物体在原本的位置关系,我们需要对相机进行标定,也是利用这个模块中的函数,幸好OpenCV官方已经为我们编写了一个标定例程,我们可以在OpenCV编译目录下的 /opencv/samples/cpp/tutorial_code/calib3d 处找到它,在修改 in_VID5.xml 和VID5.xml 内的参数后就可以开始标定了。
如果你想要更具体的实例和操作,请参考在OpenCV中用例程标定相机-NeoZng这篇文章。
video analysis:Kalman Filter,OpenCV提供了封装好的标准KF,我们可以通过修改状态转移矩阵、控制矩阵和测量矩阵从而将其升级为Extended版本(扩展卡尔曼滤波)。可以方便地调用此模块完成基础的运动轨迹预测功能,同时减少噪声。
extra_module
ccalib:Custom Calibration Pattern for 3D reconstruction,双目相机的标定和双目测距可以利用这里的函数进行。
tracking:目标追踪模块,内有几个经典的跟踪器如KCF、HAAR、HOG、GOTURN等。可以用于装甲板追踪和雷达站的目标跟踪。
videostab:视频稳定模块,提供了一些提升视频稳定性的工具,如防抖、插帧、消除模糊等。但由于处理速度稍慢,缺乏实时性,难以用于自动瞄准算法。在雷达站上可以运用,也可以用于分析制作测试视频。
cudaXXX:以cuda开头的这些模块都是可以利用英伟达的通用并行计算平台(CUDA)来加速(如果你的显卡是英伟达生产的)。
若要使用extra_module中的模块,需要和opencv-contrib交叉编译,前文的安装教程中提及了这一点。