用OpenCV和Python識別二維碼和條形碼

本文首發在集智專欄python


導讀:計算機視覺專家Adrian Rosebrock近日分享瞭如何藉助OpenCV和Zbar,編寫出可以實時識別二維碼和條形碼的掃描程序,最後部署在樹莓派上,成功製做一款實用的條形碼&二維碼掃描設備。數據庫

最近有朋友問我(做者Adrian Rosebrock——譯者注)OpenCV裏有沒有什麼模塊能直接識別條形碼和二維碼,很遺憾,答案是沒有。可是OpenCV可以加快讀取條形碼和二維碼的過程,包括從硬盤加載圖像,從視頻流中抓取新的幀,並進行處理。 等咱們獲取圖像或視頻幀後,就能夠將其傳入Python中專用的條形碼解碼庫,好比Zbar。 而後Zbar會對條形碼或二維碼進行解碼。OpenCV能夠接着執行進一步的圖像處理工做以及展現結果。 聽起來有些複雜,其實整個處理過程至關簡單明瞭。程序庫Zbar也衍生了不少變體,其中pyzbar是個人最愛。編程

在本文,我會教你怎樣用OpenCV和Zbar讀取條形碼和二維碼。並且,我還會展現怎樣將咱們製做的這個條形碼&二維碼掃描儀部署到樹莓派上!!json

使用OpenCV和ZBar打造一款條形碼及二維碼掃描儀數組

本文主要分爲四部分。bash

  • 在第一部分,我會教你如何安裝Zbar庫(Python綁定)。
  • Zbar庫會連同OpenCV一塊兒用於掃描條形碼和二維碼。
  • 等正確配置好Zbar和OpenCV之後,我會展現如何用它們掃描一張圖像上的條形碼和二維碼。
  • 先識別一張圖像上的條形碼和二維碼練練手後,咱們就進入下一階段:用OpenCV和Zbar實時讀取二維碼和條形碼。
  • 最後,我會展現如何將製做好的實時二維碼&條形碼掃描儀部署到樹莓派上。

安裝Zbar(帶Python綁定)用於解碼條形碼&二維碼

前段時間Staya Mallick在LearnOpenCV博客上發表了一篇實用教程,講解如何用Zbar掃描條形碼。服務器

本文關於Zbar安裝部分基本上是根據這篇博文的指導,可是作了一點改進,主要是圍繞安裝Python Zbar綁定部分,目的是確保咱們能:網絡

使用Python3(官方Zbar Python綁定只支持Python 2.7) 準確地檢測和定位圖像中二維碼及條形碼ide

安裝所需的軟件,只需簡單三步。函數

第一步:從apt或brew庫中安裝Zbar

在Ubuntu或樹莓派上安裝Zbar

$ sudo apt-get install libzbar0
複製代碼

在MacOS系統中安裝Zbar

使用brew在macOS系統中安裝Zbar也很容易(假定你已經安裝了Homebrew):

$ brew install zbar
複製代碼

第二步:建立一個虛擬環境,安裝OpenCV。

這裏你有倆個選擇: 使用現成的已經安裝好了OpenCV的虛擬環境(跳過這一步,看第三步)。 或者建立一個新的獨立的虛擬環境,安裝OpenCV。

虛擬環境對於Python開發來講是很是實用的作法,我很是鼓勵使用虛擬環境。

我選擇建立一個新的獨立的Python 3 虛擬環境,而後安裝了OpenCV,並將環境命名爲barcode:

$ mkvirtualenv barcode -p python3
複製代碼

注:若是你已經安裝好了OpenCV,就能夠跳過OpenCV編譯過程,只需將你的cv2.so綁定符號連接(sym-link)入你的新Python虛擬環境中的site-pakages目錄。

第三步:安裝Pyzbar 如今我已經安裝了Python 3

虛擬環境,命名爲barcode,而後激活了barcode環境,安裝pyzbar:

$ workon barcode
$ pip install pyzbar
複製代碼

如你不是用的Python 虛擬環境,只需:

$ pip install pyzbar
複製代碼

若是想將pyzbar安裝到Python版系統中,確保你也使用sudo命令。

用OpenCV解碼單張圖像上的條形碼和二維碼

在咱們實現能實時讀取條形碼和二維碼以前,咱們首先建立一個單張圖像掃描儀練練手。

打開一個新文件,命名爲barcode_scanner_image.py,插入以下代碼:

# 導入所需工具包
from pyzbar import pyzbar
import argparse
import cv2

# 構建參數解析器並解析參數
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
 help="path to input image")
args = vars(ap.parse_args())
複製代碼

在第2-4行代碼,咱們導入了所需的工具包。

須要按照上一部分的指令安裝pyzbar和cv2(OpenCV)。不過,在Python安裝中包含了argparse,負責解析命令行參數。

對於該腳本咱們有一個必需的命令行參數(--image),在7-10行進行解析。

你會在這部分結尾處看到在傳入包含輸入圖像路徑的命令行參數時,如何運行這裏的腳本。

如今,咱們獲取輸入圖像,運行pyzbar:

# 加載輸入圖像
image = cv2.imread(args["image"])

# 找到圖像中的條形碼並進行解碼
barcodes = pyzbar.decode(image)
複製代碼

在第13行,咱們經過圖像的路徑(包含在咱們很方便的args目錄中)加載圖像。

從這裏,咱們調取pyzbar.decode來發現和解碼圖像中的條形碼(第16行)。

咱們還沒完成——如今咱們須要解析包含在barcode變量中的信息:

# 循環檢測到的條形碼
for barcode in barcodes:
 # 提取條形碼的邊界框的位置
 # 畫出圖像中條形碼的邊界框
 (x, y, w, h) = barcode.rect
 cv2.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2)

 # 條形碼數據爲字節對象,因此若是咱們想在輸出圖像上
 # 畫出來,就須要先將它轉換成字符串
 barcodeData = barcode.data.decode("utf-8")
 barcodeType = barcode.type

 # 繪出圖像上條形碼的數據和條形碼類型
 text = "{} ({})".format(barcodeData, barcodeType)
 cv2.putText(image, text, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX,
  0.5, (0, 0, 255), 2)

 # 向終端打印條形碼數據和條形碼類型
 print("[INFO] Found {} barcode: {}".format(barcodeType, barcodeData))

# 展現輸出圖像
cv2.imshow("Image", image)
cv2.waitKey(0)
複製代碼

從19行開始,咱們循環檢測到的barcodes。 在這裏的循環中,咱們繼續:

從barcode.rect對象(22行)提取邊界框(x,y)座標,這樣能讓咱們定位和肯定當前條形碼在輸入圖像的位置。

圍繞着檢測到的barcode(第23行),在圖像上畫出邊界框。

將barcode解碼爲「utf-8」字符串,提取barcode的類型(第27行和28行)。調取.decode(「utf-8」)函數將對象從字節數組轉換爲字符串,很是關鍵。你能夠經過刪除或添加註釋,試驗一下結果。

在圖像上格式化和繪製barcodeData和barcodeType(第31-33行)。

最後,輸出一樣的數據,朝終端輸入信息以進行調試(第36行)。

咱們測試一下搭建的OpenCV條形碼掃描儀。 從這裏,打開你的終端,執行以下命令:

$ python barcode_scanner_image.py --image barcode_example.png
[INFO] Found QRCODE barcode: {"author": "Adrian", "site": "PyImageSearch"}
[INFO] Found QRCODE barcode: https://www.pyimagesearch.com/
[INFO] Found QRCODE barcode: PyImageSearch
[INFO] Found CODE128 barcode: AdrianRosebrock
複製代碼

能夠在終端中看到,所有4個條形碼均被正確的發現和解碼!

如圖所示,識別出了圖像中的條形碼和二維碼,以紅框標出,並顯示出了它們包含的信息。

用OpenCV實時讀取條形碼和二維碼

在前面部分中,咱們學習瞭如何爲單張圖像建立一個Python+OpenCV條形碼掃描儀。

咱們的條形碼和二維碼掃描儀效果很好——可是問題來了,咱們能實時檢測和解碼條形碼+二維碼嗎?

咱們試試看。打開一個新文件,命名爲barcode_scanner_video.py,插入以下代碼:

# 導入所需工具包
from imutils.video import VideoStream
from pyzbar import pyzbar
import argparse
import datetime
import imutils
import time
import cv2

# 建立參數解析器,解析參數
ap = argparse.ArgumentParser()
ap.add_argument("-o", "--output", type=str, default="barcodes.csv",
 help="path to output CSV file containing barcodes")
args = vars(ap.parse_args())
複製代碼

在第2-8行,咱們導入了所需的工具包。 這裏回想一下上面的解釋,你應該識別pyzbar,argparse和cv2. 咱們會使用VideoStream以高效和單線程的方式處理獲取的視頻幀。若是你的系統中沒有安裝imutils,只需使用以下命令:

$ pip install imutils
複製代碼

咱們接着解析一個可選的命令行參數--output,其包含了指向輸出結果CSV文件的路徑。該文件會包含從視頻流中檢測到和解析出的條形碼的時間戳及載荷。若是該參數沒有指定,那麼CSV文件就會被咱們當前名爲「barcodes.csv」的工做目錄所替換(第11-14行)。

在這裏,咱們初始化視頻流,打開CSV文件:

# 初始化視頻流,讓攝像頭熱熱身
print("[INFO] starting video stream...")
# vs = VideoStream(src=0).start()
vs = VideoStream(usePiCamera=True).start()
time.sleep(2.0)

# 打開輸出CSV文件,用來寫入和初始化迄今發現的全部條形碼
csv = open(args["output"], "w")
found = set()
複製代碼

在第18-19行,咱們初始化和啓動了視頻流,你能夠: 使用本身的USB網絡攝像頭(無註釋行第18行及註釋行第19行) 或者若是你是使用樹莓派的話(和我同樣),能夠用PiCamera(無註釋行第19行及註釋行第18行)。

我選擇使用個人樹莓派 PiCamera,下部分會說到。

而後咱們等上幾秒鐘,讓攝像頭熱熱身(第20行)。

咱們會將發現的全部條形碼和二維碼以CSV文件寫入硬盤(確保不要寫重複)。這裏只是一種記錄條形碼的例子,固然你能夠按照本身的喜愛來,好比檢測到條形碼後,將它們讀取爲:

  • 將其保存在SQL數據庫中
  • 將其發送到服務器
  • 將其上傳至雲端
  • 發送郵件或文本信息

實際操做隨意,咱們只是用CSV文件做爲示例。

咱們在第24行代碼打開CSV文件以寫入硬盤。若是你修改了代碼以添加到文件,你能夠只需將第二個參數從「w」改成「a」(可是你後面只能換種方式搜索重複文件)。

咱們也初始化一個set用於found條形碼。這個set會包含獨一無二的條形碼,防止出現重複。

咱們開始獲取和處理視頻幀:

# 循環來自視頻流的幀
while True:
 # 抓取來自單線程視頻流的幀, 
 # 將大小從新調整爲最大寬度400像素
 frame = vs.read()
 frame = imutils.resize(frame, width=400)

 # 找到視頻中的條形碼,並解析全部條形碼
 barcodes = pyzbar.decode(frame)
複製代碼

在第28行,咱們開始循環,繼續抓取來自視頻流中的frame,並調整大小(第31和32行)。

在這裏,咱們調取pyzbar.decode以檢測和解碼frame中的所有條形碼和二維碼。

咱們接着循環檢測到的barcodes:

# 循環檢測到的條形碼
 for barcode in barcodes:
  # 提取條形碼的邊界框位置
  # 繪出圍繞圖像上條形碼的邊界框
  (x, y, w, h) = barcode.rect
  cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2)

  # 條形碼數據爲字節對象,因此若是咱們想把它畫出來
  # 須要先把它轉換成字符串
  barcodeData = barcode.data.decode("utf-8")
  barcodeType = barcode.type

  # 繪出圖像上的條形碼數據和類型
  text = "{} ({})".format(barcodeData, barcodeType)
  cv2.putText(frame, text, (x, y - 10),
   cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)

  # 若是條形碼文本目前不在CSV文件中, write
  # 就將時間戳+條形碼 to disk and update the set
  if barcodeData not in found:
   csv.write("{},{}\n".format(datetime.datetime.now(),
    barcodeData))
   csv.flush()
   found.add(barcodeData)
複製代碼

若是看看前面的循環部分,你會發現這裏循環和以前的很像。

實際上,第38-52行和前面識別單張圖像的腳本是同樣的。這部分代碼詳情解釋,參見單張圖像條形碼檢測和掃描部分。

第56-60行代碼比較新。在這些行代碼中,咱們是檢查是否發現了獨有的(此前沒有發現)條形碼(第56行)。

若是是這種狀況,咱們將時間戳和數據寫成CSV文件(第57-59行)。此外還能夠將barcodeData添加到found集,做爲一種處理重複文件的簡單方法。

在實時條形碼掃描腳本的剩餘代碼行中,咱們展現視頻幀,檢查是否按退出鍵,並進行清除:

# 展現輸出幀
 cv2.imshow("Barcode Scanner", frame)
 key = cv2.waitKey(1) & 0xFF

 # 若是按下」q」鍵就中止循環
 if key == ord("q"):
  break

# 關閉輸出CSV文件進行清除
print("[INFO] cleaning up...")
csv.close()
cv2.destroyAllWindows()
vs.stop()
複製代碼

在第63行,咱們展現輸出幀。

而後在第64-68行,咱們檢查是否按了「q」,執行主循環。

最後,咱們在第72-74行執行清除。

在樹莓派上部署條形碼和二維碼掃描儀

我決定使用樹莓派、觸摸屏和一個充電寶打造一款本身的實時條形碼掃描儀。

下圖顯示的是個人組裝成果。若是你也想本身作一個,如下是須要的部件:

  • 樹莓派3(你也能夠用最新的 3 B+)
  • 樹莓派攝像頭模塊
  • Pi Foundation 7英寸觸摸屏
  • RAVPower 22000mAh充電寶

很容易就能組裝好。

在這裏,打開你樹莓派上的終端,用以下命令啓動應用(這一步須要一個鍵盤/鼠標,可是後面就用不着了):

$ python barcode_scanner_video.py
[INFO] starting video stream...
複製代碼

等一切準備就緒後,就能夠將條形碼展現給攝像頭了,能夠打開barcode.csv文件(或者若是你願意,也能夠在另外一個終端上執行tail -f barcodes.csv,查看打開CSV文件時的數據)。

我首先向攝像頭展現了一個黑色背景上的二維碼,Zbar很輕鬆的檢測到了它:

而後又在我家廚房裏用這套裝置發現了另外一個二維碼:

成功了!並且能多角度掃描識別二維碼。

如今,咱們試試一個包含了json-blob數據的二維碼:

最後,我試了試傳統的1維條形碼:

1維的條形碼對於咱們這套系統來講略微難些,由於攝像頭不支持自動對焦。可是最後仍是成功的檢測和解碼了條形碼。

若是你用有自動對焦功能的USB網絡攝像頭的話,效果要好得多。

結語

在本文,咱們討論了怎樣用OpenCV和Python庫Zbar打造一款條形碼和二維碼掃描儀。

將Zbar和OpenCV安裝後,咱們建立了兩個Python腳本:

  • 第一個用於掃描單張圖像上的條形碼和二維碼。
  • 第二個用於實時讀取條形碼和二維碼的信息。 在這兩種狀況中,咱們都使用了OpenCV來加快進程。

最終,咱們將建立好的程序部署到樹莓派上,而且能成功實時識別條形碼和二維碼。

能夠本身試着去作一條這樣的條形碼&二維碼掃描儀,項目源代碼下載

參考資料:www.pyimagesearch.com


0806期《人工智能-從零開始到精通》限時折扣中!

戳這裏看詳情

談笑風生 在線編程 瞭解一下?

(前25位同窗還可領取¥200優惠券哦)

相關文章
相關標籤/搜索