媲美微信的二維碼識別庫

2021-02-15 鴻洋

作者:森碼

連結:

https://www.jianshu.com/p/e2866af44236

本文由作者授權發布。

1. 為什麼要做這個庫?

相信大家在平常的生活中,如果遇到掃碼的場景第一個想到的應該就是微信了,可以說微信使用二維碼打開了移動網際網路的另一扇大門,並且在掃碼體驗上及其優秀,本該有一定要求的掃碼過程,在經過微信的優化之後,讓用戶在使用時擁有了一種『隨意性』,像拍一張照片一樣簡單,像發一句消息隨意,像擺弄一件玩具一樣有趣。

有了這樣的『標杆』存在,大家在潛意識裡面也都有了標準,你們的掃一掃為什麼不好用?為什麼要識別這麼久?甚至我對準了也識別不出來?擺在我們面前的是各種用戶的不滿,解決這些問題就成了我們必須要面對的情形。

2. 選型

二維碼處理,繞不開的就是ZXing和ZBar了,ZXing作為老牌的識別庫已經"孵化"出了包括js、Python、C++、PHP等各個語言的lib,同時Android版本也一直在更新,但是ZBar作為C的處理者,上次的更新已經是7年前了。

為了讓二維碼的識別儘量的快,並且對圖像處理有更多的可能性,考量之後我們選用了更具活力的zxing-cpp,選用了它來作為我們的底層處理庫。

https://github.com/nu-book/zxing-cpp

3. 相機的處理

原始圖像的獲取至關重要,倘若這一步走不好,其他的處理再好也於事無補,對於從來沒有接觸這一領域的自己來說,踏遍Android相機的坑不知要花多少時間,好在已經有優秀的開源庫,這裡特別感謝BGAQRCode-Android的開源庫,操縱攝像頭的一些重要功能,比如自動對焦、觸摸對焦、放大縮小等都已經具備,自己也只是在其之上做了一些小改進,比如GroupView的改進、加入傳感器對焦、線程池處理等等。有了這些之後,我們就可以開始處理數據了。

https://github.com/bingoogolapple/BGAQRCode-Android

Android的相機獲取到的數據並非我們平常認為的RGB數據,而是視頻採集中的經常使用的NV21格式即YUV,所以在獲取到這些數據之後是無法直接使用的。

1. 格式轉換

要轉格式首先我們先要了解NV21在內存中是什麼樣子的。

YUV420


不同於我們平常的圖片格式,比如png的圖片,圖片由一個一個像素點構成,400 * 800的圖片就有320000個像素,每一個像素對應一個ARGB,即4個字節,分別表示(透明度,紅色色值,綠色色值,藍色色值),就是我們平常見到的(0,255,255,255)一個像素的內存是連在一起的,但是YUV不同於我們『認知』上的格式,這3個數值分別代表的是(明亮度,色度,濃度),一個很有意思的知識是:YUV的發明是由於彩色電視與黑白電視的過渡時期。

https://zh.wikipedia.org/wiki/YUV

YUV的組成


有了一個大概的了解之後,我們就可以把攝像頭的數據轉化為我們需要的數據,其實只要根據公式來推倒就可以了,但是了解原理能讓我們更好的理解。

YUV->RGB

2. 算法優化

對於二維碼來說,它是一個個黑白的點組成的,其實並不需要多麼色彩斑斕的裝飾,一張灰度圖或許是更好的選擇,一般的圖像處理,都是輪詢所有像素數據,對於一個或者一組數據進行處理。一個YUV轉化為RGB的算法就是要拿出所有像素,然後各種轉換,這無疑是一種浪費。一個更好的選擇是把原圖像直接轉化為灰度圖。

for (int i = top; i < height + top; ++i) {
    srcIndex += left;
    for (int j = left; j < left + width; ++j, ++desIndex, ++srcIndex) {
        p = data[srcIndex] & 0xFF;
        pixels[desIndex] = 0xff000000 | p << 16 | p << 8 | p;
    }
    srcIndex += margin;
}


3. 圖片裁剪


我們知道,通常二維碼識別的界面都有一個『框』,這個框並不是可有可無的,它不僅能告訴用戶我們正在掃描,二維碼應該放在這裡面,更能在我們處理時成倍的提高處理效率,在測試的過程中,裁剪和沒有經過裁剪的圖片處理一般要相差4-5倍的時間。

一張600 * 600的圖片識別要50-80ms;而一張完整照片,比如1920 * 1080的圖片,通常要經過200ms以上的時間處理,如果所有的圖片都不經過截取,那麼想要提升整體的識別效率是很困難的。

圖片裁剪是一個非常必要的操作,加上我們上面的灰度轉換,兩個操作合二為一,得到灰度圖的同時也裁剪了圖像。經過簡單的處理之後,這個圖像的「質量」是很高的。

截取後的圖像只有原圖像的1/5,更利於我們去處理數據,至此我們的圖像已經準備好了。

void ImageUtil::convertNV21ToGrayAndScale(int left, int top, int width, int height, int rowWidth,
                                          const jbyte *data, int *pixels) {
    int p;
    int desIndex = 0;
    int bottom = top + height;
    int right = left + width;
    int srcIndex = top * rowWidth;
    int marginRight = rowWidth - right;
    for (int i = top; i < bottom; ++i) {
        srcIndex += left;
        for (int j = left; j < right; ++j, ++desIndex, ++srcIndex) {
            p = data[srcIndex] & 0xFF;
            pixels[desIndex] = 0xff000000u | p << 16u | p << 8u | p;
        }
        srcIndex += marginRight;
    }
}

1. 解析二維碼

有了充足的準備,二維碼的識別已經是水到渠成的事情了,根據轉化好的數據,生成HybridBinarizer對象,通過MultiFormatReader即可解析。

2. 識別流程優化

在一些Demo中,二維碼處理流程通常是使用setOneShotPreviewCallback作為相機數據的處理,即一幀畫面處理完再處理下一幀(兩幀不一定是相連的),這樣的處理會造成兩個問題.

首先:相機獲取的畫面不一定是完全對焦好的,一般我們拿出手機都有一個對焦的動作,中間可能只有50%的畫面是可用的,這種情況下可能會丟失清晰的圖像而處理了模糊的圖像;

其次這種串行的處理也是對機能的浪費,現在的手機處理連續的圖像是綽綽有餘的;

最後,這樣的處理流程是不受我們控制的,只能來一張處理一張。


在流程改進中我使用了setPreviewCallback的回調,並統一加入線程池處理。這裡我可以控制一秒之內處理多少幀圖像,在測試中是300ms處理一幀(不同機型處理的速度不盡相同,為了避免線程池隊列過長,選擇了較低的處理速度,後期可以根據機型來動態設置處理間隔),為了加速處理,這4幀是識別框內的數據。

同時,為了能快速識別簡單的二維碼,每4幀處理完之後加入一幀全屏處理,這一幀可以作為識別圖像明亮度的主幀,也可以在二維碼超出識別框時,繼續識別數據。有了這個改動,就可以做到點擊掃一掃,抬手就能得到結果。

但是這個掃碼的距離實在不能讓人滿意,我們常用的掃一掃通常都會有一個放大的操作,而這個操作是掃碼優化中也是非常關鍵的一步。

3. 放大優化

想要進一步的優化我們就得更進一步的研究二維碼了,二維碼的生成細節和原理和二維碼(QR code)基本結構及生成原理有詳細的解釋,這裡我們發現左上、左下、右上三個位置探測圖形,在二維碼的解碼過程中,其實是分幾個步驟的,首先就是要定位這個二維碼確認其位置,然後才能取出裡面的數據,而這個定位的點就是這三個。

在距離二維碼較遠時,可能無法解析出完整的數據,但是卻能定位這個二維碼,通過定位點的信息,我們可以進行放大的操作,從而獲取到更加精確的圖像數據,也更有利於我們解析。

二維碼結構


void tryZoom(BarcodeReader.Result result) {
    int len = 0;
    float[] points = result.getPoints();
    if (points.length > 3) {
        float point1X = points[0];
        float point1Y = points[1];
        float point2X = points[2];
        float point2Y = points[3];
        float xLen = Math.abs(point1X - point2X);
        float yLen = Math.abs(point1Y - point2Y);
        len = (int) Math.sqrt(xLen * xLen + yLen * yLen);
    }
    handleAutoZoom(len);
}

4. 與微信的對比


微信的掃一掃可以說是秒級的處理,特別是在iOS的設備上,更不可思議的是它好像沒有距離的限制。經過我們的優化之後,我們的二維碼可以在50cm內解析出來,但是與微信相差的還是太遠,我們需要更好的處理圖像數據,來定位二維碼。


識別距離


二維碼的識別中,距離 是一個非常關鍵的制約條件,通常在30cm-40cm內是一定可以識別出來的,但是超過這個距離獲取到的圖像就會比較模糊,如果攝像頭的解析度不高識別率也會下降,如果超過這個閾值,識別算法就只能定位數據而無法解析數據,比如上圖中的B點,這裡我們加入自動放大就可以解決,但是超過這個距離呢?

我們就需要手機移動了。如果有一種方案,可以像在B點時一樣,雖然無法獲取到數據但是可以得到二維碼的位置、大小呢?要做到這個,OpenCV是一個不二之選。

說到圖像處理,我們大致有兩套方案,

方案一:處理圖像數據,獲取圖像輪廓,算法檢測二維碼位置。

方案二:機器學習,直接定位二維碼。

兩者其實都是可行的,只是在難易度方面的差異,我們首先嘗試了機器學習的方案,奈何自己學的還比較淺,收集到的樣本數據也不夠,訓練出來的模型也不太理想,比如一個沒有二維碼的畫面會檢查出好幾個,又比如有的時候又要離的特別近才能識別出來,這又違背了我們的本意。所以我選擇了方案一,雖然聽起來沒那麼高大上了,但是在實際的測試中也完全能達到預期水平。

當圖像即無法解析出數據也無法定位到二維碼時,我們採用OpenCV去處理圖像。因為之前已經進行過灰度處理了,這裡可以直接進行Canny化,然後執行findContours方法獲取輪廓信息,之後過濾輪廓信息,判斷點與點之間的距離,得到二維碼的位置信息。

(以上的過程看似簡單,其實進行了很多嘗試,包括二值化,毛邊去除、調節亮度、對比度處理,直接獲取點信息等等,這裡感謝

https://blog.csdn.net/jia20003/article/details/77348170

和https://blog.csdn.net/zwx1995zwx/article/details/79171979


的圖像過濾算法,作為一個圖像處理的門外漢真的學到很多)

canny化之後的圖像

識別二維碼輪廓

定位二維碼輪廓,紅色框框是自動生成的

拿到這些信息之後,我們就可以遵循在B點時的處理邏輯,直接放大圖像獲取數據。

你可能會想為什麼不直接截取圖像,這樣就不用費時費力再進行一輪識別,其實這裡也想到過,但是得到的數據精度丟失實在太多,我嘗試用微信去識別截取得到的二維碼,微信也無法檢測出來,這樣的處理對於簡單的二維碼或許可行,但是對於稍微複雜的二維碼或者我們所要解決的問題來說是遠遠不夠的.

在加入OpenCV之後,我們的識別距離擴大了一倍,得到的效果比預期的還要好。

識別效果展示

待完善的功能

aar過大,因為有OpenCV的加入,aar文件有7.7M。

不支持生成二維碼(將會在近期加入)。

擴展性、可定製性不夠,這個可以慢慢加入。

有時候會放的太大,試試縮小功能。

註:不同手機識別效率其實不盡相同,攝像頭越好,識別效率越高。

源碼地址

https://github.com/devilsen/CZXing

歡迎Star,歡迎Fork,歡迎和我一起開發。

在不到一個月的時間裡完成了主要功能(主要是自己在業餘時間完成的),感謝那些無私奉獻的博主之餘也感受到開源的便利和偉大,我能做的也只是用開源來回饋各位。

最後以牛頓的一句話來結尾吧,「我之所以站得高,是因為我站在巨人的肩膀上。」

             

推薦閱讀:

掃一掃 關注我的公眾號

如果你想要跟大家分享你的文章,歡迎投稿~

┏(^0^)┛明天見!

相關焦點

  • 【每周一坑】暴力計算圓周率 +【解答】生成/識別二維碼
    【解答】生成/識別二維碼上一次的題目 生成/識別二維碼,包括兩部分,生成和識別。這兩個步驟都可以通過第三方庫實現。生成部分比較簡單,使用 qrcode 庫即可:import qrcodeimg = qrcode.make('learn python with Crossin')img.show()img.save('qrcode.png')識別的部分,稍微有點麻煩,因為實際當中,拍攝到的二維碼是會有各種角度變形和幹擾的。
  • 微信怎麼製作二維碼錶白?微信小程序二維碼錶白教程
    其實,如今年輕人都流行二維碼錶白了。下面本文就來分享下微信怎麼製作二維碼錶白,只要藉助小程序二維碼就可以輕鬆製作掃碼識別「我愛你」個性表現,單身朋友趕快Get起來吧。微信小程序二維碼錶白教程微信小程序二維碼錶白教程:第一步:首先在手機中打開微信,然後進入小程序,如下圖所示。
  • SVG微信交互圖文|如何提高推送中二維碼識別率?
    推送中普通二維碼識別率普遍較低,為引導用戶掃描識別,利用再造設計、像素點的方式美化二維碼成為了品牌公眾號常用手法。然而各種美化後的二維碼已經司空見慣,尤其是受眾求新求變的心理,設計二維碼時更需要融入新鮮獨特的設計感,進而由「小編備用」研發設計出的交互二維碼模型裝置,運用在引流推送中進一步提高了二維碼識別率。下面就為大家介紹「小編備用」首發的交互二維碼模型案例:
  • 高唐時風中學微信識別二維碼查詢2020級初一新生臨時分班
    微信識別二維碼查詢2020級初一新生臨時分班
  • 微信如何掃描自己手機上的二維碼?
    我們每天都會通過微信掃描二維碼支付,但是這些都是掃描別人手機或者張貼的二維碼圖片的,如果二維碼是在自己的手機相冊內,又該如何通過微信掃描呢?  首先依照掃碼的基本流程,依次點擊「發現」-「掃一掃」,這樣就能跳轉到大家最熟悉不過的二維碼掃描界面了,如下圖所示......
  • 正式商用|VIN碼識別、護照識別、二維碼識別
    尊敬的百度大腦用戶: 感謝您長期以來對百度大腦OCR文字識別服務的支持,VIN碼識別、護照識別、二維碼識別功能已全面升級,已於12月5日上線計費功能,支持兩種計費方式:按量後付費、次數包預付費;次數包購買後一年內有效
  • 讓自己的微信二維碼更漂亮
    我們的微信二維碼往往都是這樣的:可選樣式很少,很一般如果是這樣的呢?首先我們將我們的原二維碼保存到電腦上,然後打開草料二維碼:https://cli.im/在右側,打開二維碼美化器:然後切換到高級模式,這樣才能獲得更多的美化功能:點擊「上傳取碼」,上傳我們的原二維碼,取出二維碼內容:
  • 微信「二維碼」為什麼是"QR code"?
    二維碼---QR code"二維碼"英文可譯為2-D barcodes (two-dimensional
  • 輕鬆識別文字,這款 Python OCR 庫支持超過 80 種語言
    細心觀察便可發現,身邊到處都是OCR的身影,文檔掃描、車牌識別、證件識別、銀行卡識別、票據識別等等。關於EasyOCRPython中有一個不錯的OCR庫-EasyOCR,在GitHub已有9700star。它可以在python中調用,用來識別圖像中的文字,並輸出為文本。
  • 微信520我愛你二維碼怎麼弄 製作自己的二維碼方法教程
    以前在上學的時候,表白都是寫情書來告白的,但是隨著網際網路的深度發展,現在都可以用二維碼錶白了,那麼怎麼用微信製作表白二維碼呢,下面就和小編一起來看看教程吧!  微信表白二維碼製作方法  1、首先我們需要擁有一個微信APP,這個大家肯定都會有,在發現裡打開小程序,在搜索框輸入關鍵詞「二維碼」,點擊進入你最感興趣的二維碼生成器小程序,在新建的二維碼裡點擊文本,如圖:
  • 拍發票:OCR自動識別+二維碼識別,能否改變「發票」報銷現狀?
    James說:「發票識別的速度,從5分鐘到1分鐘,從1分鐘到30秒,從30秒到5秒...」能不能用微信掃一下,你都不用列印,電子發票都在雲端,從我的卡裡扣完錢,然後兩分鐘之後錢又打回到我的卡裡。我也不用搞什麼假發票…..。」可見發票報銷對整個社會來說,痛點十足。
  • 肉眼識別二維碼?你也可以
    比如靠肉眼識別二維碼?雖然人類大腦的計算效率早已落後家用計算機好幾個量級,但這並不妨礙我們光靠人腦去完成一件「看似只有電子設備才能完成的事情」的嘗試。畢竟生活已經夠無聊了。以下方二維碼為例,三個分別位於二維碼的左上角、右上角、左下角的「回」字形符號,便是規定了尺寸、讀取方向的位置探測圖形。有這三個識別符號放在二維碼的三個角上後,解碼的時間響應就可以很快,比同時代的技術要快20倍。
  • 走進二維碼(QR Code)的世界之初體驗
    痞子衡每次學習新東西,總喜歡用Python,一是成熟的庫多,二是代碼簡潔寫起來快。今天我們來嘗試用Python做一個GUI工具,這個工具可以生成和識別二維碼,輸入文字即可轉換成二維碼圖片,待識別的二維碼既可以是本地圖片,也可以來自攝像頭。在做這個工具的過程中,我們可以對二維碼技術的實現有一個初步體驗。
  • 微信群二維碼將取消?自動設定有效期防亂加好友
    千龍-法晚聯合報導(記者 羅曉靜)「微信群二維碼將於3月13日失效,是不是新人就沒法進群了?」昨日至今日,關於微信將取消群二維碼的消息在各大微信群流傳。今天(7日)上午,微信官方回應記者稱,該傳言為假消息,每個微信群二維碼都會自動設定一個識別有效期,而這個有效期的時間是7天。
  • 微信群聊掃碼進二維碼大全
    掃碼可關注每日文章最後更新最新群聊(點擊識別)↓↓↓
  • 微信群大全,免費加群二維碼助手
    微信群大全,微群大全,群二維碼大全,微群助手,微群二維碼,微信群,加群,寶媽群,行業群,同城群,交友群,相親群,微商群,貨源群,推廣群
  • 二維碼識讀模塊嵌入自助售賣機的掃碼付款區域,讓感應識別更智能
    可以發現不管是商場還是地鐵站,整齊擺放的自助售賣機都可以直接刷手機支付寶(或微信)付款碼支付,而這些自助機的掃碼付款區域都有一個共同特性,即在機器內部安裝集成有模組類條碼識讀產品,也叫二維碼識讀模塊、二維碼掃描模塊、嵌入式掃描模組等。
  • 最新微信群聊二維碼10.29
    掃碼可關注每日文章最後更新最新群聊(點擊識別)↓↓↓
  • 微信新功能之廣州話也能識別了——微信語音輸入文字的方法
    我們知道,微信其實可以把語音識別成文字的。不過,過去的微信只能識別普通話。這樣很多說粵語,不方便在手機上寫字的同學只能望而興嘆。
  • 微信群 微信群大全 微信群二維碼分享
    今天各地群聊在哪裡能找到   附近的群聊二維碼 、今日最新群二維碼歡迎加入哪裡找本地微信群聊 怎麼加附近的微信群聊微信群二維碼(每天最新更新)服裝交流群就能找到附近的群本地附近的群大全信息(2020年最新群聊)聊天群群聊00後二維碼,聊天群群聊90後,團購群群聊二維碼,團購群群聊名稱,抖音群群聊聊,抖音群群聊二維碼,粉絲群群聊二維碼,粉絲群群聊聊,土豪群群聊二維碼,文章群群聊聊