it-swarm.com.ru

Прямоугольник кадрирования, возвращаемый minAreaRect OpenCV [Python]

minAreaRect в OpenCV возвращает повернутый прямоугольник. Как мне обрезать эту часть изображения, которая находится внутри прямоугольника? 

boxPoints возвращает координаты угловых точек повернутого прямоугольника, чтобы можно было получить доступ к пикселям, просматривая точки внутри поля, но есть ли более быстрый способ обрезки в Python? 

ПРАВКА

Смотрите code в моем ответе ниже.

7
Abdul Fatir

Вы не предоставили пример кода, поэтому я отвечаю и без кода. Вы можете сделать следующее:

  1. Из углов прямоугольника определите угол альфа вращения относительно горизонтальной оси.
  2. Поверните изображение по альфе так, чтобы обрезанный прямоугольник был параллелен границам изображения. Убедитесь, что временное изображение больше по размеру, чтобы информация не терялась (ср: поворот изображения без обрезки OpenCV )
  3. Обрезать изображение с помощью обрезки (см. Как обрезать изображение в OpenCV с помощью Python )
  4. Повернуть изображение обратно на альфа.
2
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

20
Oli Blum

Вот код для выполнения вышеуказанной задачи. Чтобы ускорить процесс, вместо того, чтобы сначала вращать все изображение и обрезать, часть изображения, которая имеет повернутый прямоугольник, сначала обрезается, затем поворачивается и снова обрезается, чтобы получить окончательный результат. 

# 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 1  isolated contour 2  isolated contour 3

И это также даст изображение результата как это:  results

7
mkrinblk