參考鏈接
[ 基於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());