0 前言
🔥 優質競賽項目系列,今天要分享的是
🚩 python+opencv+深度學習實現二維碼識別
🥇學長這里給一個題目綜合評分(每項滿分5分)
- 難度系數:3分
- 工作量:3分
- 創新點:3分
該項目較為新穎,適合作為競賽課題方向,學長非常推薦!
🧿 更多資料, 項目分享:
https://gitee.com/dancheng-senior/postgraduate
2 二維碼基礎概念
2.1 二維碼介紹
二維條碼/二維碼(2-dimensional bar
code)是用某種特定的幾何圖形按一定規律在平面(二維方向上)分布的、黑白相間的、記錄數據符號信息的圖形;在代碼編制上巧妙地利用構成計算機內部邏輯基礎的“0”、“1”比特流的概念,使用若干個與二進制相對應的幾何形體來表示文字數值信息,通過圖象輸入設備或光電掃描設備自動識讀以實現信息自動處理:它具有條碼技術的一些共性:每種碼制有其特定的字符集;每個字符占有一定的寬度;具有一定的校驗功能等。同時還具有對不同行的信息自動識別功能、及處理圖形旋轉變化點。
2.2 QRCode
常見的二維碼為QR Code,QR全稱Quick Response,是一個近幾年來移動設備上超流行的一種編碼方式,它比傳統的Bar
Code條形碼能存更多的信息,也能表示更多的數據類型。
2.3 QRCode 特點
1、符號規格從版本1(21×21模塊)到版本40(177×177 模塊),每提高一個版本,每邊增加4個模塊。
2、數據類型與容量(參照最大規格符號版本40-L級):
- 數字數據:7,089個字符
- 字母數據: 4,296個字符
- 8位字節數據: 2,953個字符
- 漢字數據:1,817個字符
3、數據表示方法:
- 深色模塊表示二進制"1",淺色模塊表示二進制"0"。
4、糾錯能力:
- L級:約可糾錯7%的數據碼字
- M級:約可糾錯15%的數據碼字
- Q級:約可糾錯25%的數據碼字
- H級:約可糾錯30%的數據碼字
5、結構鏈接(可選)
- 可用1-16個QR Code碼符號表示一組信息。每一符號表示100個字符的信息。
3 機器視覺二維碼識別技術
3.1 二維碼的識別流程
首先, 對采集的彩色圖像進行灰度化, 以提高后繼的運行速度。
其次, 去除噪聲。 采用十字形中值濾波去除噪音對二碼圖像的干擾主要是鹽粒噪聲。
利用灰度直方圖工具, 使用迭代法選取適當的閾值, 對二維碼進行二值化處理,灰度化 去噪 二值化 尋找探測圖形確定旋轉角度 定位 旋轉
獲得數據使其變為白底黑色條碼。
最后, 確定二維碼的位置探測圖形, 對條碼進行定位, 旋轉至水平后, 獲得條碼數據,
以便下一步進行解碼。
3.2 二維碼定位
QR 碼有三個形狀相同的位置探測圖形, 在沒有旋轉的情況下, 這三個位置探測圖形分別位于 QR 碼符號的左上角、 右上角和左下角。
三個位置探測圖形共同組成圖像圖形。
每個位置探測圖形可以看作是由 3 個重疊的同心的正方形組成, 它們分別為 7 7 個深色模塊、 5 5 個淺模塊和 3*3 個深色模塊。
位置探測圖形的模塊寬度比為 1: 1:3: 1: 1。
這種 1: 1: 3: 1: 1 的寬度比例特征在圖像的其他位置出現的可能性很小, 故可以將此作為位置探測圖形的掃描特征。 基于此特征,
當一條直線上(稱為掃描線) 被黑白相間地截為1: 1: 3:1: 1 時, 可以認為該直線穿過了位置探測圖形。
另外, 該掃描特征不受圖像傾斜的影響。 對比中的兩個 QR 碼符號可以發現, 無論 QR碼符號是否傾斜, 都符合 1: 1: 3:1: 1 的掃描特征。
3.3 常用的掃描方法
- 在 X 方向進行依次掃描。
(1) 固定 Y 坐標的取值, 在 X 方向上畫一條水平直線(稱為掃描線) 進行掃描。 當掃描線被黑白相間地截為 1: 1: 3: 1: 1 時,
可以認為該直線穿過了位置探測圖形。 在實際判定時, 比例系數允許 0. 5 的誤差, 即比例系數為1 的, 允許范圍為 0. 5~1. 5, 比例系數為 3
的, 允許范圍為 2. 5~3. 5。
(2) 當尋找到有直線穿過位置探測圖形時, 記錄下位置探測圖形的外邊緣相遇的第一點和最后一點 A 和 B。 由 A、 B
兩點為端點的線段稱為掃描線段。將掃描線段保存下來。
用相同的方法, 完成圖像中所有水平方向的掃描。
- 在 Y 方向, 使用相同的方法, 進行垂直掃描, 同樣保存掃描得到的掃描線段。
掃描線段分類掃描步驟獲得的掃描線段是沒有經過分類的, 也就是對于特定的一條掃描線段, 無法獲知其具體對應于三個位置探測圖形中的哪一個。
在計算位置探測圖形中心坐標之前, 要將所有的掃描線段按照位置進行歸類。 一般采用距離鄰域法進行掃描線段的分類。
距離鄰域法的思想是: 給定一個距離閾值 dT, 當兩條掃描線段的中點的距離小于 d T 時, 認為兩條掃描線段在同一個鄰域內, 將它們分為一類,
反之則歸為不同的類別。
距離鄰域法的具體步驟如下:
(1) 給定一個距離閾值 dT , d T要求滿足以下條件: 位于同一個位置探測圖形之中的任意兩點之間的距離小于 dT ,
位于不同位置探測圖形中的任意兩點之間的距離大于 d T
(2) 新建一個類別, 將第 1 條掃描線段歸入其中。
(3) 對于第 i 條掃描線段 l i (2≤i≤n), 做以下操作:
a) 求出 l i 的中點 C i 。
b) 分別計算C i與在已存在的每一個類別中的第一條掃描線段的中點的距離d,若 d<d T , 則直接將 l i 加入相應類別中。
c) 若無法找到 l i 可以加入的類別, 則新建一個類別, 將 l i 加入其中。
(4) 將所有類別按照包含掃描線段的數目進行從大到小排序, 保存前 3 個類別(即
包含掃描線段數目最多的 3 個類別), 其余的視為誤判得到的掃描線段(在位置探測圖形以外的位置得到的符合掃描特征的掃描線段),
直接舍去。距離鄰域法結束后得到的分好 3 個類別的掃描線段就分別對應了 3 個位置探測圖形。距離鄰域法的關鍵就是距離閾值的選取。 一般對于不同大小的 QR
碼圖像, 要使用不同的距離閾值。
(1) 在 X 方向的掃描線段中找出最外側的兩條, 分別取中點, 記為 A、 B。 由 A、 B兩點連一條直線。
(2) 在 Y 方向的掃描線段中找出最外側的兩條, 分別取中點, 記為 C、 D。 由 C、 D兩點連一條直線。
(3) 計算直線 AB 與直線 CD 的交點 O, 即為位置探測圖形中心點。
將 QR 碼符號的左上、 右上位置探測圖形的中心分別記為 A、 B。 連接 A、 B。 直線 AB 與水平線的夾角α 即為 QR 碼符號的旋轉角度。
對于該旋轉角度α , 求出其正弦值 sinα 與余弦值 cosα 即可。 具體計算公式如下:
位置探測圖形邊長的計算是基于無旋轉圖像的, 在無旋轉圖像中, 水平掃描線段的長度即為位置探測圖形的邊長。
水平掃描線段 AB 的長度即為位置探測圖形的邊長 X。
對于經過旋轉的 QR 碼圖像, 先通過插值算法生成旋正的 QR 碼圖像, 然后按照如上所述的方法進
4 深度學習二維碼識別
基于 CNN 的二維碼檢測,網絡結構如下
4.1 部分關鍵代碼
篇幅有限,學長在這只給出部分關鍵代碼
首先,定義一個 AlgoQrCode.h
?
#pragma once
? #include
? #include
? using namespace cv;
? using namespace std;
? class AlgoQRCode{private:Ptr<wechat_qrcode::WeChatQRCode> detector;public:bool initModel(string modelPath);string detectQRCode(string strPath);bool compression(string inputFileName, string outputFileName, int quality);void release();};?
該頭文件定義了一些方法,包含了加載模型、識別二維碼、釋放資源等方法,以及一個 detector 對象用于識別二維碼。
然后編寫對應的源文件 AlgoQrCode.cpp
? bool AlgoQRCode::initModel(string modelPath) {
? string detect_prototxt = modelPath + "detect.prototxt";
? string detect_caffe_model = modelPath + "detect.caffemodel";
? string sr_prototxt = modelPath + "sr.prototxt";
? string sr_caffe_model = modelPath + "sr.caffemodel";
? try
? {
? detector = makePtr<wechat_qrcode::WeChatQRCode>(detect_prototxt, detect_caffe_model, sr_prototxt, sr_caffe_model);
? }
? catch (const std::exception& e)
? {
? cout << e.what() << endl;
? return false;
? }
? return true;}string AlgoQRCode::detectQRCode(string strPath){if (detector == NULL) {return "-1";}vector<Mat> vPoints;vector<cv::String> vStrDecoded;Mat imgInput = imread(strPath, IMREAD_GRAYSCALE);// vStrDecoded = detector->detectAndDecode(imgInput, vPoints);....}bool AlgoQRCode::compression(string inputFileName, string outputFileName, int quality) {Mat srcImage = imread(inputFileName);if (srcImage.data != NULL){vector<int>compression_params;compression_params.push_back(IMWRITE_JPEG_QUALITY);compression_params.push_back(quality); //圖像壓縮參數,該參數取值范圍為0-100,數值越高,圖像質量越高bool bRet = imwrite(outputFileName, srcImage, compression_params);return bRet;}return false;}void AlgoQRCode::release() {detector = NULL;}?
5 測試結果
學長這里放到樹莓派中,調用外部攝像頭進行識別,可以看到,效果還是非常不錯的
6 最后
🧿 更多資料, 項目分享:
https://gitee.com/dancheng-senior/postgraduate