web-dev-qa-db-ja.com

minAreaRect OpenCVによって返される四角形のトリミング[Python]

OpenCVのminAreaRectは、回転した長方形を返します。四角形の内側にある画像のこの部分をトリミングするにはどうすればよいですか?

boxPointsは、回転した長方形のコーナーポイントの座標を返すため、ボックス内のポイントをループすることでピクセルにアクセスできますが、Pythonでトリミングするより速い方法はありますか?

[〜#〜] edit [〜#〜]

以下の私の回答のcodeをご覧ください。

10
Abdul Fatir

サンプルコードが提供されていないため、コードなしでも回答しています。次のように進めることができます。

  1. 長方形の角から、水平軸に対する回転の角度アルファを決定します。
  2. トリミングされた長方形が画像の境界に平行になるように、画像をアルファで回転させます。情報が失われないように、一時画像のサイズが大きいことを確認します(cf: OpenCVをトリミングせずに画像を回転
  3. Numpyスライスを使用して画像をトリミング(cf: Pythonを使用してOpenCVで画像をトリミングする方法
  4. 画像を-alphaだけ回転させて戻します。
3
tfv

ここで、このタスクを実行する関数:

import cv2
import numpy as np

def crop_minAreaRect(img, rect):

    # rotate img
    angle = rect[2]
    rows,cols = img.shape[0], img.shape[1]
    M = cv2.getRotationMatrix2D((cols/2,rows/2),angle,1)
    img_rot = cv2.warpAffine(img,M,(cols,rows))

    # rotate bounding box
    rect0 = (rect[0], rect[1], 0.0) 
    box = cv2.boxPoints(rect0)
    pts = np.int0(cv2.transform(np.array([box]), M))[0]    
    pts[pts < 0] = 0

    # crop
    img_crop = img_rot[pts[1][1]:pts[0][1], 
                       pts[1][0]:pts[2][0]]

    return img_crop

ここで使用例

# generate image
img = np.zeros((1000, 1000), dtype=np.uint8)
img = cv2.line(img,(400,400),(511,511),1,120)
img = cv2.line(img,(300,300),(700,500),1,120)

# find contours / rectangle
_,contours,_ = cv2.findContours(img, 1, 1)
rect = cv2.minAreaRect(contours[0])

# crop
img_croped = crop_minAreaRect(img, rect)

# show
import matplotlib.pylab as plt
plt.figure()
plt.subplot(1,2,1)
plt.imshow(img)
plt.subplot(1,2,2)
plt.imshow(img_croped)
plt.show()

これは出力です

original and croped image

24
Oliver Wilken

上記のタスクを実行するコードは次のとおりです。プロセス全体を高速化するために、最初に画像全体を回転してトリミングする代わりに、回転した長方形を持つ画像の一部が最初にトリミングされ、次に回転され、最終結果を得るために再度トリミングされます。

# Let cnt be the contour and img be the input

rect = cv2.minAreaRect(cnt)  
box = cv2.boxPoints(rect) 
box = np.int0(box)

W = rect[1][0]
H = rect[1][1]

Xs = [i[0] for i in box]
Ys = [i[1] for i in box]
x1 = min(Xs)
x2 = max(Xs)
y1 = min(Ys)
y2 = max(Ys)

angle = rect[2]
if angle < -45:
    angle += 90

# Center of rectangle in source image
center = ((x1+x2)/2,(y1+y2)/2)
# Size of the upright rectangle bounding the rotated rectangle
size = (x2-x1, y2-y1)
M = cv2.getRotationMatrix2D((size[0]/2, size[1]/2), angle, 1.0)
# Cropped upright rectangle
cropped = cv2.getRectSubPix(img, size, center)
cropped = cv2.warpAffine(cropped, M, size)
croppedW = H if H > W else W
croppedH = H if H < W else W
# Final cropped & rotated rectangle
croppedRotated = cv2.getRectSubPix(cropped, (int(croppedW),int(croppedH)), (size[0]/2, size[1]/2))
9
Abdul Fatir

@AbdulFatirは良い解決策を講じていましたが、コメントに書かれているように(@Randika @epinal)、それも私にとってはまったく機能していなかったので、少し修正して、私の場合は機能しているようです。こちらが私が使用している画像です。 mask_of_image

im, contours, hierarchy = cv2.findContours(open_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print("num of contours: {}".format(len(contours)))


mult = 1.2   # I wanted to show an area slightly larger than my min rectangle set this to one if you don't
img_box = cv2.cvtColor(img.copy(), cv2.COLOR_GRAY2BGR)
for cnt in contours:
    rect = cv2.minAreaRect(cnt)
    box = cv2.boxPoints(rect)
    box = np.int0(box)
    cv2.drawContours(img_box, [box], 0, (0,255,0), 2) # this was mostly for debugging you may omit

    W = rect[1][0]
    H = rect[1][1]

    Xs = [i[0] for i in box]
    Ys = [i[1] for i in box]
    x1 = min(Xs)
    x2 = max(Xs)
    y1 = min(Ys)
    y2 = max(Ys)

    rotated = False
    angle = rect[2]

    if angle < -45:
        angle+=90
        rotated = True

    center = (int((x1+x2)/2), int((y1+y2)/2))
    size = (int(mult*(x2-x1)),int(mult*(y2-y1)))
    cv2.circle(img_box, center, 10, (0,255,0), -1) #again this was mostly for debugging purposes

    M = cv2.getRotationMatrix2D((size[0]/2, size[1]/2), angle, 1.0)

    cropped = cv2.getRectSubPix(img_box, size, center)    
    cropped = cv2.warpAffine(cropped, M, size)

    croppedW = W if not rotated else H 
    croppedH = H if not rotated else W

    croppedRotated = cv2.getRectSubPix(cropped, (int(croppedW*mult), int(croppedH*mult)), (size[0]/2, size[1]/2))

    plt.imshow(croppedRotated)
    plt.show()

plt.imshow(img_box)
plt.show()

これにより、次のような一連の画像が生成されます。 isolated contour 1isolated contour 2isolated contour 3

また、次のような結果イメージも表示されます。 results

7
mkrinblk