LoginSignup
32
20

More than 1 year has passed since last update.

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

Last updated at Posted at 2020-04-26

動機

  • 近所にあるショッピングモールには、外通路に多くの植物が植えられていて、一緒に植物の説明が記載されているプレートがあります。どんな種類(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

32
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
20