参考链接
[ 基于opencv 识别、定位二维码 (c++版)
](https://www.cnblogs.com/yuanchenhui/p/opencv_qr.html)
OpenCV4.0.0二维码识别代码简析
1.使用Qrdetector实现二维码检测
opencv中的QRCodeDetector类可以实现二维码的定位,识别功能,由于本项目使用的是自己设计的二维码,因此暂时只使用到QRCodeDetector的检测功能
函数接口
bool detect (InputArray img, OutputArray points) const
使用示例
QRCodeDetector dec;
vector<Point2f> list;
dec.detect(src, list);
原图
将list画出后,得到
优缺点
优点: 检测快速方便,速度较快,代码量少
缺点:准确率不高,有的时候标准的二维码也会检测失效
错误示例
2.使用轮廓检测算法实现二维码检测
思路
1.将彩图转换为灰度图
cvtColor(image, dstGray, COLOR_BGR2GRAY);
- 将灰度图使用二值化方法,转换为黑白
也可以直接使用Canny算法进行边缘提取
OTSU算法更适合色偏较大的二维码,能够实现准确的二值化
threshold(dstGray, dstGray, 100, 255, THRESH_BINARY);
或 threshold(srcGray, srcGray, 188, 255, THRESH_BINARY|THRESH_OTUS );
- 轮廓查找
进行轮廓层级查找,并进行层级判断,当嵌套层级大于2时,有可能为定位点
关于轮廓查找的参考文档
轮廓层级>2的条件不够强,如果环境光较为自然的话,可以把条件改成>5。某处光源过强,会照成提取出来的轮廓撕裂,这时要额外增加判断条件。具体做法参考开头的文档一
contours, hierarchy = cv2.findContours(srcGray, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
hierarchy = hierarchy[0]
found = []
for i in range(len(contours)):
k = i
c = 0
while hierarchy[k][2] != -1:
k = hierarchy[k][2]
c = c + 1
if c >= 2:
found.append(i)
foundContours = [contours[i] for i in found]
效果
增强判断条件后,只会检测到周围三个定位点
3.使用透视变换矫正二维码
1. 使用QRdetector得到的Point进行透视变换
透视变换参考
假设正常二维码的大小为(ROW,COL)
vector<Point2f> dstTri;
dstTri.push_back(Point2f( 0,0 ));
dstTri.push_back(Point2f( 0,COL-1 ));
dstTri.push_back(Point2f( ROW-1,COL-1));
dstTri.push_back(Point2f( ROW-1,0 ));//创建目的点
Mat warp_mat = getPerspectiveTransform(list, dstTri);//得到旋转矩阵
warpPerspective(src, output, warp_mat, srcImg.size());//对src进行透视变幻
经过透视变换结果
2. 通过二维码定位点的中点进行透视变换
由于qrcodetect类经常出错,为了解决此问题,只能使用轮廓查找定位二维码,但因为轮廓查找只找出三个定位点,无法透视变换,因此项目中二维码需要额外增加一个定位点(标准的二维码也不只3个定位点
- 通过minAreaRect方法生成轮廓的最小外接矩形
- 计算四个矩形中点,与对应的原图四个定位点中点进行透视变换
代码如下
RotatedRect rect = minAreaRect((contour2[i]));//生成某一定位点轮廓的最小外接矩形
M = cv2.moments(cnt) # 计算第一条轮廓的各阶矩,字典形式
center_x = int(M["m10"] / M["m00"])
center_y = int(M["m01"] / M["m00"])
center_point.append((center_x,center_y))
# 使用矩方法来计算中点
对四个中点按指定顺序进行排序,并做透视变换
效果与使用Qrdetector的point进行透视变换类似
warpPerspective(srcGray, output, warp_mat, srcGray.size());