PythonでDocumentScanner（斜めの四角を長方形に）

# 四角の形を補正したい

以下のような斜めになった四角を９０度の長方形に補正したい。
いわゆるドキュメントスキャナー。

![pexels-pixabay-163194.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/b20ac42f-821e-900d-69b0-b054aa8f50cf.jpeg)

![d977f744-8b51-464c-b307-dc9e5575cdd0.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/7e731545-09bf-3eb0-a373-40b0632a1a56.jpeg)
**↑補正された画像**

# Python単体で可能

以下のコードでできます。

```python
import cv2
import numpy as np


def biggest_contour(contours):
    biggest = np.array([])
    max_area = 0
    for i in contours:
        area = cv2.contourArea(i)
        if area > 1000:
            peri = cv2.arcLength(i, True)
            approx = cv2.approxPolyDP(i, 0.015 * peri, True)
            if area > max_area and len(approx) == 4:
                biggest = approx
                max_area = area
    return biggest


img = cv2.imread('input.jpg') # your image
img_original = img.copy()

# Image modification
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.bilateralFilter(gray, 20, 30, 30)
edged = cv2.Canny(gray, 10, 20)

# Contour detection
contours, hierarchy = cv2.findContours(edged.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours = sorted(contours, key=cv2.contourArea, reverse=True)[:10]

biggest = biggest_contour(contours)

cv2.drawContours(img, [biggest], -1, (0, 255, 0), 3)

# Pixel values in the original image
points = biggest.reshape(4, 2)
input_points = np.zeros((4, 2), dtype="float32")

points_sum = points.sum(axis=1)
input_points[0] = points[np.argmin(points_sum)]
input_points[3] = points[np.argmax(points_sum)]

points_diff = np.diff(points, axis=1)
input_points[1] = points[np.argmin(points_diff)]
input_points[2] = points[np.argmax(points_diff)]

(top_left, top_right, bottom_right, bottom_left) = input_points
bottom_width = np.sqrt(((bottom_right[0] - bottom_left[0]) ** 2) + ((bottom_right[1] - bottom_left[1]) ** 2))
top_width = np.sqrt(((top_right[0] - top_left[0]) ** 2) + ((top_right[1] - top_left[1]) ** 2))
right_height = np.sqrt(((top_right[0] - bottom_right[0]) ** 2) + ((top_right[1] - bottom_right[1]) ** 2))
left_height = np.sqrt(((top_left[0] - bottom_left[0]) ** 2) + ((top_left[1] - bottom_left[1]) ** 2))

# Output image size
max_width = max(int(bottom_width), int(top_width))
# max_height = max(int(right_height), int(left_height))
max_height = int(max_width * 1.414)  # for A4

# Desired points values in the output image
converted_points = np.float32([[0, 0], [max_width, 0], [0, max_height], [max_width, max_height]])

# Perspective transformation
matrix = cv2.getPerspectiveTransform(input_points, converted_points)
img_output = cv2.warpPerspective(img_original, matrix, (max_width, max_height))

# Image shape modification for hstack
gray = np.stack((gray,) * 3, axis=-1)
edged = np.stack((edged,) * 3, axis=-1)

img_hor = np.hstack((img_original, gray, edged, img))
cv2.imwrite('Contour.jpg', img_hor)
cv2.imwrite('document.jpg', img_output)
```

![d977f744-8b51-464c-b307-dc9e5575cdd0.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/7e731545-09bf-3eb0-a373-40b0632a1a56.jpeg)

![83e445e8-d023-477f-a644-96dd15ffd9e6.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/88757f7e-2338-9e17-57d8-d69afa0a3196.jpeg)

手順としては、画像をエッジ化だけにして四角形を検出し、四隅を計算してワープ変換しています。

引用元は以下の動画です。

https://www.youtube.com/watch?v=SQ3D1tlCtNg&t=27s

🐣
****
フリーランスエンジニアです。
お仕事のご相談こちらまで
rockyshikoku@gmail.com

機械学習、ARアプリ（Web/iOS）を作っています。
機械学習／AR関連の情報を発信しています。

[Twitter](https://twitter.com/JackdeS11)
[Medium](https://medium.com/@rockyshikoku)
[GitHub](https://github.com/john-rocky)