pythonとOpenCVで歪み補正

書類をスキャンしたときは、画像が傾いているのみならず、歪んでいる場合がある。

この歪みをpythonで直すためのプログラム。ただし、大きな枠があり、歪みが小さい場合に限る。 

 

大まかな流れ :

イ)四角い枠の角の座標を取得する。(一番外側の線は紙そのものの輪郭)

ロ)台形補正で歪みを直す。

f:id:haitenaipants:20180817005843p:plain          f:id:haitenaipants:20180817005837p:plain          f:id:haitenaipants:20180817005855p:plain

 

以下のサイトを参考にした。(と言うかほとんどそのままです。本当ごめんなさい。)

opencv で マーカー付き用紙の向きを直してみる - 機械学習備忘録

opencv で カードの向きを直してみる - 機械学習備忘録

 

1)角の座標の取得

(1−1)輪郭検出

適当な画像を用意し(ここでは、test.png)、以下のコードを実行する。

import cv2
import numpy as np

img = cv2.imread("test.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

ret,thresh = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)

ret, contours, hierarchy = cv2.findContours(thresh , cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE )

 

ここでは、画像を読み込んだ後、白黒に二値化し、輪郭(contours)やら何やらを検出している。

 

(1−2)面積の大きい順に輪郭を並べ替える

上に続いて以下のコードを実行する。

menseki=[ ]

for i in range(0, len(contours)):
    menseki.append([contours[i],cv2.contourArea(contours[i])])

menseki.sort(key=lambda x: x[1], reverse=True)

 

ここでは、

menseki=[[輪郭1, 輪郭1の面積], [輪郭2, 輪郭2の面積], …]

を作り、これを面積の大きい順に並べ替えている。

 

(1−3)角の座標の取得

上に続いて以下のコードを実行する。 

epsilon = 0.1*cv2.arcLength(menseki[1][0],True)
approx = cv2.approxPolyDP(menseki[1][0],epsilon,True)

cv2.drawContours(img, approx, -1,(0, 0, 255),10)
cv2.imwrite("result.png",img)

 

epsilon, approxの説明はここの「輪郭の近似」を参照のこと。二値化した時に枠線がギザギザしてしまい、それを点として認識するのを避けている。

ここではapproxに角の4点が格納されている。

 

また、一番目に大きな輪郭(menseki[0][0])は紙そのものの輪郭なので、二番目に大きな輪郭(menseki[1][0])の点を取得している。

 

生成された画像(result.png)で角を取得できていることを確認。

 

以下参照

【OpenCV; Python】findcontours関数のまとめ 

https://imagingsolution.net/program/opencv/cvfindcontours-labelling/

 

(1−4)角の点の並び替え

approxには角の点が格納されているものの、その順番はバラバラである

そこで以下のコードで左下、左上、右下、右上に対応させる。

approx=approx.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]

 

コードの概略としては以下の通りである。

イ)numpy配列をただのlistに変換する。

ロ)x座標の小さい順に並び替えることにより角の4点を左右に分ける。

ハ)それらをy座標の小さい順に並び替えることにより、上下に分ける。

 

全ての四角形に対応するわけでは無いが、スキャンならこれで十分のはず。

 

2)台形補正

perspective1 = np.float32([left_down,right_down,right_up,left_up])
perspective2 = np.float32([[0, 0],[1654, 0],[1654, 2340],[0, 2340]])

 

psp_matrix = cv2.getPerspectiveTransform(perspective1,perspective2)
img_psp = cv2.warpPerspective(img, psp_matrix,(1654,2340))

 

cv2.imwrite("image_modified.png",img_psp)

 

ここで、perspective1は補正前の角の座標を、perspective2は補正後の角の座標をそれぞれ示す。ただし、perspective2としてA4の解像度(2340×1640)を用いている。

 

また、psp_matrixは台形補正に必要な変換行列を示す。

 

生成された画像(image_modified.png)で、歪み補正されていることを確認できる。 

 

以下参照 (と言うかそのまま使ってごめんなさい)

Python OpenCV3で透視変換 | from umentu import stupid

 

全コードは以下の通り。

###modify image file because of distortion by scanning###

### obtatin edge###

import cv2
import numpy as np

img = cv2.imread("test.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

ret,thresh = cv2.threshold(gray,100,255,cv2.THRESH_BINARY)

ret, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE )

 

menseki=[ ]
for i in range(0, len(contours)):
    menseki.append([contours[i],cv2.contourArea(contours[i])])

menseki.sort(key=lambda x: x[1], reverse=True)

 

epsilon = 0.1*cv2.arcLength(menseki[1][0],True)
approx = cv2.approxPolyDP(menseki[1][0],epsilon,True)

cv2.drawContours(img, approx, -1,(0, 0, 255),10)
cv2.imwrite("result.png",img)

 

approx=approx.tolist()

left = 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 = sorted(approx,key=lambda x:x[0]) [2:]
right_down= sorted(right,key=lambda x:x[0][1]) [0]
right_up= sorted(right,key=lambda x:x[0][1]) [1]

 

### modify distortion ###
perspective1 = np.float32([left_down,right_down,right_up,left_up])
perspective2 = np.float32([[0, 0],[1654, 0],[1654, 2340],[0, 2340]])
psp_matrix = cv2.getPerspectiveTransform(perspective1,perspective2)
img_psp = cv2.warpPerspective(img, psp_matrix,(1654,2340))
cv2.imwrite("image_modified.png",img_psp)

 

関連  

haitenaipants.hatenablog.com

  

haitenaipants.hatenablog.com