勉強しないとな~blog

ちゃんと勉強せねば…な電気設計エンジニアです。

OpenCVやってみる- 29. 点数文字輪郭検出2

春のパン祭りシール点数集計の続きです。

今回の内容

前回、春のパン祭り画像から点数文字の輪郭を取得できましたが、まだ1画像でしか試していないので、 他の画像でもやってみます。

関数化

いつも通りの手順ですが、内容が増えてきたので、関数化しておきたいと思います。
関数の内容は、

  • 画像のリサイズ
  • 2値化
  • 輪郭検出

になります。

リサイズについて

画像のリサイズについては、今までは固定の縮小率にしていましたが、今回はリサイズ後の画像サイズの目標を設定して、それに向けたリサイズをするようにします。

前回も少し考えたように、

  • シール台紙は画像全体の縦横半分程度は写るようにする
  • シールが縦横に5個ずつ並ぶ

という前提条件を設定して、あとシール領域の解像度の目標を決めたいですが、
前回の画像を見返してみると、リサイズ後でだいたい縦横80pixelずつでした。
ということで、

  • 解像度は縦横それぞれ80x5x2=800pixel以上

にリサイズするようにしたいと思います。

2値化について

前回は2値化の後にClosing処理で細かい輪郭の除去をしていましたが、輪郭検出後の面積でのフィルタリングでも十分そうだったので、Closing処理は省いてみます。

import cv2
import numpy as np
%matplotlib inline
from matplotlib import pyplot as plt

# image: Input image, BGR format
def calculate_harupan(image, debug):
    h, w, chs = image.shape
    if h > 800 or w > 800:
        k = 800.0/h if w > h else 800.0/w
    else:
        k = 1.0
    img = cv2.resize(image, None, fx=k, fy=k, interpolation=cv2.INTER_AREA)
    if debug:
        print('Resized to ', img.shape)
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    ret, th_hue = cv2.threshold(hsv[:,:,0], 135, 255, cv2.THRESH_BINARY)
    contours, hierarchy = cv2.findContours(th_hue, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    indices0 = [i for i,hier in enumerate(hierarchy[0,:,:]) if hier[3] == -1]
    indices1 = [i for i,hier in enumerate(hierarchy[0,:,:]) if hier[3] in indices0]
    if debug:
        print('Number of contours: ', len(contours))
        print('Number of indices0: ', len(indices0), 'indices1: ', len(indices1))
    contours1 = [contours[i] for i in indices1]
    contours1_filtered = [ctr for ctr in contours1 if cv2.contourArea(ctr) > 800*800/4000]
    if debug:
        return contours1_filtered, img
    else:
        return contours1_filtered

実践

では、各画像に適用してみます。

img1 = cv2.imread('harupan_190428_1.jpg')
img2 = cv2.imread('harupan_190428_2.jpg')
img3 = cv2.imread('harupan_200317_1.jpg')
img4 = cv2.imread('harupan_210227_2.jpg')
img5 = cv2.imread('harupan_210402_1.jpg')
img6 = cv2.imread('harupan_210402_2.jpg')
img7 = cv2.imread('harupan_210414_1.jpg')

imgs = [img1, img2, img3, img4, img5, img6, img7]

plt.figure(figsize=(20,15), dpi=100)
for i,im in enumerate(imgs):
    ctrs, img_resize = calculate_harupan(im, True)
    img_ctrs = cv2.drawContours(img_resize, ctrs, -1, (0,255,0), 2)
    plt.subplot(241+i), plt.imshow(cv2.cvtColor(img_ctrs, cv2.COLOR_BGR2RGB)), plt.xticks([]), plt.yticks([])
    
plt.show()
Resized to  (1067, 800, 3)
Number of contours:  2534
Number of indices0:  1264 indices1:  1048
Resized to  (1067, 800, 3)
Number of contours:  2427
Number of indices0:  1217 indices1:  1049
Resized to  (1067, 800, 3)
Number of contours:  1836
Number of indices0:  434 indices1:  630
Resized to  (1067, 800, 3)
Number of contours:  1021
Number of indices0:  295 indices1:  683
Resized to  (1067, 800, 3)
Number of contours:  1689
Number of indices0:  480 indices1:  1063
Resized to  (1067, 800, 3)
Number of contours:  1320
Number of indices0:  367 indices1:  844
Resized to  (1067, 800, 3)
Number of contours:  873
Number of indices0:  424 indices1:  392

f:id:nokixa:20211213072314p:plain

いい感じです。

気になるのは、

  • 1番目の画像、シール部分での光の反射で'0'の文字が1つ検出できていない
  • 3番目の画像、点数文字以外の輪郭が残っている
    おそらく台紙が画像全体に写っていて、細かい文字の面積も大きくなってしまったため

というところ。

光の反射は対処が難しいなー...
どうしようか。

ここまで

今回はここまでにします。
一部課題はありますが、おおむね前回の方針でよさそうということで。

次回はテンプレートマッチングをやっていきたいと思います。