以下のようなpdf内にあるカラーの写真を切り取る必要があったので、備忘録的にまとめておきます。
(なお、当方プログラミング初心者なので間違いがあれば優しく指摘していただけると助かります。。)
おおまかな流れとしては
- 写真の四隅の座標を把握する。
- 座標で切り取る。
の二つだけです。
まず、必要なモジュールをインポートし、画像を読み込みます。
import cv2
import numpy as np
from PIL import Image
image = cv2.imread('pdfが置いてあるパス')
↑(訂正)imreadはpdfに対応していないようなので、pngなどに変換します。
次に読み込んだ画像をBGRからHSV空間に変換(マスク)します。
写真はカラーであることから、色が付いているものを検出するためです。
今回は彩度(Satulation)の範囲を10〜255としました。
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
upper = np.array([250, 255, 250])
lower = np.array([0, 10, 5])
mask = cv2.inRange(hsv, lower, upper)
次に、写真の境界を見つけていきます。
また、検出された境界に緑色の線を引いていきます。
# マスクされた画像の中から境界を見つけ、一番大きいものを保持する
(mask, cnts, hierarcy) = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
c = max(cnts, key=cv2.contourArea)
c_sort = sorted(cnts, key=cv2.contourArea)
c2 = c_sort[-2]
# 境界を最適化する
peri = cv2.arcLength(c2, True)
approx = cv2.approxPolyDP(c2, 0.01 * peri, True)
epsilon = 0.05*cv2.arcLength(c,True)
approx = cv2.approxPolyDP(c, epsilon, True)
epsilon2 = 0.05*cv2.arcLength(c2,True)
approx2 = cv2.approxPolyDP(c2, epsilon, True)
# 境界を緑の線で描く
cv2.drawContours(image, [approx], -1, (0, 255, 0), 4)
cv2.drawContours(image, [approx2], -1, (0, 255, 0), 4)
ここで今の境界を見てみます。
cv2.imshow("Image_mask", image)
下の写真は四角になっていますが、上の写真は四角になっていませんでした。
そこで、四隅の座標だけ取得するため、boundingRectを使います。
x,y,w,h = cv2.boundingRect(c)
x_2 = x+w
y_2 = y+h
image = cv2.rectangle(image,(x,y),(x_2,y_2),(0,0,255),2)
x2,y2,w2,h2 = cv2.boundingRect(c2)
x2_2 = x2+w2
y2_2 = y2+h2
image = cv2.rectangle(image,(x2,y2),(x2_2,y2_2),(0,0,255),2)
これで、四隅の座標が取得できました。
あとは、これらの座標で切り取るだけです。
im = Image.open('pdfが置いてあるパス')
im_crop = im.crop((x, y, x_2, y_2))
im_crop.save('/保存する場所/保存名.png', quality=95)
im_crop = im.crop((x2, y2, x2_2, y2_2))
im_crop.save('/保存する場所/保存名.png', quality=95)
背景画像にマスクした画像を合成する、というものはあったのですが、
単純に切り取る方法が見つからなかったので、残しておこうと思いました。
OpenCVの導入にはちょうどいいのかもしれません。