2016-05-12 2 views
5

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

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

EDIT

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

+0

Вы можете: 1) создать маску для вращающегося прямоугольника (достаточно просто с 'fillConvexPoly' или' drawContours (... CV_FILLED) '). 2) Черные инициализируют матрицу того же размера, что и оригинал. 3) Скопируйте только содержимое маски в новое изображение ('new_image.setTo (old_image, mask)'), 4) Обрезайте новое изображение в ограничивающей рамке повернутого прямоугольника – Miki

ответ

1

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

  1. Из углов прямоугольника определите угол альфа вращения относительно горизонтальной оси.
  2. Поверните изображение по альфа, чтобы отрезанный прямоугольник был параллелен границам изображения. Убедитесь в том, что временное изображении больше по размеру, так что никакой информации не потеряется (сравните: Rotate image without cropping OpenCV)
  3. Обрезки изображения с помощью Numpy нарезки (сравните: How to crop an image in OpenCV using Python)
  4. Повернуть снимок обратно на -альфе.
+1

Для большого изображения не будет быть дорогим? –

+0

Я предполагаю, что встроенные функции всегда будут быстрее, чем выполнение вложенного цикла по пикселям.Но единственный способ узнать это - измерить его, это просто фей-строки кода, как описано выше. – tfv

+0

Если у меня много прямых, то это, вероятно, будет показано. Я буду писать код и вернуться к вам после попытки. –

8

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

# 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)) 
+0

Я пробовал этот код, но он не дает мне ROI. Любые улучшения с тех пор? –

+0

Это не дает вам правильную область. – epinal

+0

отлично работает для меня, спасибо. – razzak

8

здесь функция, которая выполняет эту задачу:

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

+0

Это именно то, что я хочу, и функция полностью ясна! Однако я не смог заставить его работать с этим изображением. https://i.imgur.com/4E8ILuI.jpg Он заканчивается слегка неправильным, а края обрезаны. Не могли бы вы взглянуть на него? – Hatshepsut

+0

Это не дает вам правильную область. – epinal

+0

Работает для меня хорошо, я много ищу и это лучший ответ для меня – Txeif

1

@AbdulF atir был готов к хорошему решению, но, как говорится в комментариях (@ 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

Смежные вопросы