Help us understand the problem. What is going on with this article?

画像の台形補正をしてみた

動機

  • 近所にあるショッピングモールには、外通路に多くの植物が植えられていて、一緒に植物の説明が記載されているプレートがあります。どんな種類(100種類以上はあると思われる)の植物があるか気になった為、プレートの情報を集計する事にしました。
  • しかしプレートの情報をスマホに打ち込んで情報収集するのは面倒くさい。。。ちょっとでも簡略化する為に写真を撮り、補正してからOCRする事にしました。

補正前画像

やった事

  1. プレート画像の取得
    1. プレート以外の箇所をマスク・二値化
    2. ノイズ除去
    3. 輪郭の取得・近似 近似
  2. プレート画像の補正
    1. 台形補正

1. プレート画像の取得

1. プレート以外の箇所をマスク・二値化

画像をHSV形式に変換し、Hue(色相)がプレートであろうの範囲に含まれているものを白、それ以外を黒と出力する事にし、マスクと2値化を行います。

preprocess.py
cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

良い感じにマスクできています。

補足

HSVとは色を3要素で表現する方式

  • 色相(Hue)
    • 色の種類(例えば赤、青、黄色)
  • 彩度(Saturation)
    • 色の鮮やかさ
  • 明度(Value・Brightness)
    • 色の明るさ

2. ノイズ除去

クロージングしてから、オープニング
プレートのピクセルが欠損する事がある為、先にクロージング

補足

  • 膨張(Dilation)
    • 注目画素の周辺に白色の画素が1つでも存在すれば、注目画素を白色に置き換える
  • 縮小(Erosion)
    • 注目画素の周辺に黒色の画素が1つでも存在すれば、注目画素を黒色に置き換える
  • クロージング
    • 同じ回数分だけ膨張して収縮する処理
  • オープニング
    • 同じ回数分だけ収縮して膨張する処理

3. 輪郭の取得・プレートの輪郭を近似

輪郭の取得

ノイズ除去した画像に含まれる物体の輪郭を検出し、描画します。

preprocess.py
img, contours, hierarchy = cv2.findContours(img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPL)
img = cv2.drawContours(img, contour, -1, (0, 0, 255), 30)
cv2.imwrite(output_path, img)

この用に輪郭が描写されました。

プレートの輪郭を取得

今回の画像ではプレート以外の輪郭が存在しないのですが、
プレート以外の輪郭が存在する時のため、最大の面積をもつ輪郭(プレート)を取得します。

preprocess.py
contour_areas = {}
for i, contour in enumerate(contours):
    area = cv2.contourArea(contour)
    contour_areas[i] = area

max_area = max(contour_areas.values())
max_area_idx = [i for i, v in contour_areas.items() if v == max_area][0]
max_contour = contours[max_area_idx]

プレート四隅の座標を取得

少数の点で領域の形を近似し、プレート四隅の座標を取得します。

preprocess.py
arc_len = cv2.arcLength(max_contour, True)
approx_contour = cv2.approxPolyDP(max_contour, epsilon=0.1 * arc_len, closed=True)
img = cv2.drawContours(img, approx_contour, -1, (0, 0, 255), 30)
cv2.imwrite(output_path, img)

良い感じに四隅の座標を取得する事ができました。

2. プレート画像の補正

1. 台形補正

4隅の座標を左上、左下、右上、右下に分け、台形補正し画像を描画します。

preprocess.py
approx = approx_contour.tolist()

left = sorted(approx, key=lambda x: x[0])[:2]
right = sorted(approx, key=lambda x: x[0])[2:]
left_down = sorted(left, key=lambda x: x[0][1])[0]
left_up = sorted(left, key=lambda x: x[0][1])[1]
right_down = sorted(right, key=lambda x: x[0][1])[0]
right_up = sorted(right, key=lambda x: x[0][1])[1]

perspective_base = np.float32([left_down, right_down, right_up, left_up])
perspective = np.float32([[0, 0], [700, 0], [700, 500], [0, 500]])

psp_matrix = cv2.getPerspectiveTransform(perspective_base, perspective)
plate_img = cv2.warpPerspective(org_img, psp_matrix, (700, 500))
cv2.imwrite(output_path, img)

台形補正の結果

まとめ

つまずいた事

  • 最初は補正前の画像に対し直線検出を行っていましたが、植物の線を取得したり、プレートの直線を取得しても切れてしまっていたりと、意図する結果にならなかったです。
  • その後に、RGBの値を元にマスクを試みたのですが、プレートの大部分が欠けたり、プレートと関係ない箇所がマスクできなかったりしたため、プレートの輪郭を上手く検出できなかったです。

良かった事

  • 完全に手探りで始めた為、どの手法を使えば良いのかわからず、結果的に色々な画像処理の手法を試せた事。(理論は追いついていませんが。)
    • 二値化(固定値、大津、適応的閾値処理)
    • 直線検出(ハフ変換、確率的ハフ変換)
    • エッジ検出(Canny法、LSD)
    • 平滑化(移動平均、ガウシアン)

課題

以下の画像の様にプレートに植物が重なり、プレートの領域が分断されると、台形補正が上手くいかない。(写真とる時に、植物がプレートに被らないようにするかな。。。)

オチ

ちなみに、Google Cloud Visionでは補正前と補正後ではOCR結果はほぼ変わりません、補正前でもほぼ完璧にOCRできています。Googleさん流石!!!

その他

ソースコードはこちら
https://github.com/ChihiroHozono/Plate-Text-Detector

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした