wake-up-neo.com

Von minAreaRect OpenCV [Python] zurückgegebenes Ernte-Rechteck

minAreaRect in OpenCV gibt ein gedrehtes Rechteck zurück. Wie schneide ich diesen Teil des Bildes, der sich innerhalb des Rechtecks ​​befindet? 

boxPoints gibt die Koordinaten der Eckpunkte des gedrehten Rechtecks ​​zurück, sodass auf die Pixel zugegriffen werden kann, indem die Punkte innerhalb der Box durchlaufen werden. Gibt es eine schnellere Methode zum Beschneiden in Python? 

EDIT

Siehe code in meiner Antwort unten.

7
Abdul Fatir

Sie haben keinen Beispielcode angegeben, daher antworte ich auch ohne Code. Sie könnten wie folgt vorgehen:

  1. Bestimmen Sie von den Ecken des Rechtecks ​​den Winkel Alpha der Drehung gegen die horizontale Achse.
  2. Bild um Alpha drehen, sodass das abgeschnittene Rechteck parallel zu den Bildrändern verläuft. Stellen Sie sicher, dass das temporäre Bild größer ist, damit keine Informationen verloren gehen (cf: Bild drehen, ohne OpenCV zu beschneiden )
  3. Bild mit numpy schneiden (cf: So schneiden Sie ein Bild in OpenCV mit Python )
  4. Bild um -alpha zurückdrehen.
2
tfv

hier eine Funktion, die diese Aufgabe erfüllt:

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

hier ein beispielgebrauch

# 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()

das ist die Ausgabe

 original and croped image

20
Oli Blum

Hier ist der Code zum Ausführen der obigen Aufgabe. Um den Vorgang zu beschleunigen, wird nicht zuerst das gesamte Bild gedreht und zugeschnitten, sondern ein Teil des Bildes mit dem gedrehten Rechteck wird zuerst beschnitten, dann gedreht und erneut zugeschnitten, um das Endergebnis zu erhalten. 

# 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 hatte eine gute Lösung, aber wie in den Kommentaren (@Randika @epinal) angegeben, funktionierte es auch nicht für mich, also habe ich es leicht modifiziert und es scheint für meinen Fall zu funktionieren. Hier ist das Bild, das ich verwende.  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()

Dies sollte eine Reihe von Bildern wie diese erzeugen:  isolated contour 1  isolated contour 2  isolated contour 3

Und es wird auch ein Ergebnisbild wie folgt aussehen:  results

7
mkrinblk