今からでも間に合う

技術を学ぶのは今からでも遅くない

opencvを使ってバーコードを検出

GEEKY TRAVELLER - Detecting Barcodes in Images with Python and OpenCV

opencvにも手を出してみようと使い方・考え方を知るために上記サイトで紹介してあるコードを自分で試しながら書いてみます。

行列とか画像処理とか知らないので、雰囲気で理解していっています。

環境

  • VSCode
  • python 3.11.0

使用ライブラリ

  • numpy
  • opencv-python

バーコード検出

画像を読み込む

import numpy as np
import cv2
  
file = "./barcode.jpg"
      
#画像を読み込む
img = cv2.imread(file)
#画像を表示。画面は表示されるけど何かを待っている
cv2.imshow("original", img)
#これを呼び出すと画像が表示された
cv2.waitKey()
#キー押下しても破棄を明示しないと閉じない
cv2.destroyAllWindows()

original

画像表示部はあとでたくさん使うので関数にしておきます

def showImg(title, img):
    cv2.imshow(title, img)
    cv2.waitKey()
    cv2.destroyAllWindows()

グレースケールにする

cv2.cvtColorで簡単にできました。
第2引数に膨大なパターンを渡せるようです。

#グレースケール
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
showImg("gray scale", gray)

gray scale

ゾーベルフィルタをかける

ここからすでに何がしたいのかわかりません。
X方向、Y方向にゾーベルフィルタ*1とやらをかけているようです。

gradX = cv2.Sobel(gray, ddepth = cv2.CV_32F, dx = 1, dy = 0, ksize = -1)
gradY = cv2.Sobel(gray, ddepth = cv2.CV_32F, dx = 0, dy = 1, ksize = -1)
showImg("gradX", gradX)
showImg("gradY", gradY)

X方向ゾーベルフィルタ
Y方向ゾーベルフィルタ

結果の差をとる

こうすることで、水平方向のグラデーションが高く、垂直方向のグラデーションが低い画像となるようです。
(言っている意味は分からないけど)画像が変わっているのはわかります。

gradient = cv2.subtract(gradX, gradY) 
showImg("gradient", gradient)
gradient = cv2.convertScaleAbs(gradient)
showImg("gradient", gradient)

X-Y

ぼかして閾値を設定、2値化する

ぼかすことでノイズを滑らかにし、それに対して閾値を指定して2値化しています。

blurred = cv2.blur(gradient, (9, 9))
showImg("blurred", blurred)
(_, thresh) = cv2.threshold(blurred, 225, 255, cv2.THRESH_BINARY)
showImg("thresh", thresh)

ぼかし
閾値で2値化

なんだか胡散臭くなってきました。

矩形カーネルでバー間のギャップを埋める

もはや日本語に訳しても何言ってるかわかりません。

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (21, 7))
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
showImg("closed", closed)
closed = cv2.erode(closed, None, iterations = 4)
showImg("closed", closed)
closed = cv2.dilate(closed, None, iterations = 4)
showImg("closed", closed)

morphology

erode

dilate

バーコードの輪郭を見つける

もはやラストまで一気にコピペです。

(cnts, _) = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
c = sorted(cnts, key = cv2.contourArea, reverse = True)[0]
  
rect = cv2.minAreaRect(c)
box = np.int0(cv2.boxPoints(rect))
  
cv2.drawContours(img, [box], -1, (0, 255, 0), 3)
showImg("last", img)

結果

見つけられませんでした。

検出結果

おしまい

人間が見て認識するのと近しい精度を出すのって簡単ではないということはよくわかりました。
いつか検出できるようにリベンジしたいところ。

追記

ちょっとパラメータいじったらこんなのになった。 python入門Top

*1:ゾーベルフィルタ:1次微分により画像のエッジ検出をするための演算の一つ

プライバシーポリシー


d払いポイントGETモール