
Here's a image extraction widget that allows you to rotate the image and select a ROI by clicking and dragging the mouse. The idea is to use the mouse to select the bounding box window where we can use Numpy slicing to crop the image. Since OpenCV does not let you draw an angled rectangle, you can bypass that by first rotating the image.
Once you have selected the ROI, you can then crop the image using the bounding box coordinates. If we consider (0,0)
as the top left corner of the image with left-to-right as the x-direction and top-to-bottom as the y-direction and we have (x1, y1)
as the top-left vertex and (x2,y2)
as the bottom-right vertex of a ROI, we can crop the image by:
ROI = image[y1:y2, x1:x2]
We are able to do this since images are stored as a Numpy array in OpenCV. Here is a great resource for Numpy array indexing and slicing.
To use the widget:
left mouse click + drag
- select ROI
right mouse click
- reset image
r
- rotate image clockwise 5 degrees
e
- rotate image counter-clockwise 5 degrees
c
- crop selected ROI
q
- quit program
import cv2
import numpy as np
class ExtractImageWidget(object):
def __init__(self):
self.original_image = cv2.imread('plane.PNG')
self.original_image = cv2.resize(self.original_image, (640, 556))
self.clone = self.original_image.copy()
cv2.namedWindow('image')
cv2.setMouseCallback('image', self.extract_coordinates)
self.image_coordinates = []
self.angle = 0
self.extract = False
self.selected_ROI = False
def extract_coordinates(self, event, x, y, flags, parameters):
if event == cv2.EVENT_LBUTTONDOWN:
self.image_coordinates = [(x,y)]
self.extract = True
elif event == cv2.EVENT_LBUTTONUP:
self.image_coordinates.append((x,y))
self.extract = False
self.selected_ROI = True
self.crop_ROI()
cv2.rectangle(self.clone, self.image_coordinates[0], self.image_coordinates[1], (0,255,0), 2)
cv2.imshow("image", self.clone)
elif event == cv2.EVENT_RBUTTONDOWN:
self.clone = self.original_image.copy()
self.angle = 0
self.selected_ROI = False
def show_image(self):
return self.clone
def rotate_image(self, angle):
(h, w) = self.original_image.shape[:2]
(cX, cY) = (w / 2, h / 2)
self.angle += angle
M = cv2.getRotationMatrix2D((cX, cY), -self.angle, 1.0)
cos = np.abs(M[0, 0])
sin = np.abs(M[0, 1])
nW = int((h * sin) + (w * cos))
nH = int((h * cos) + (w * sin))
M[0, 2] += (nW / 2) - cX
M[1, 2] += (nH / 2) - cY
self.clone = cv2.warpAffine(self.original_image, M, (nW, nH))
self.selected_ROI = False
def crop_ROI(self):
if self.selected_ROI:
self.cropped_image = self.clone.copy()
x1 = self.image_coordinates[0][0]
y1 = self.image_coordinates[0][1]
x2 = self.image_coordinates[1][0]
y2 = self.image_coordinates[1][1]
self.cropped_image = self.cropped_image[y1:y2, x1:x2]
print('Cropped image: {} {}'.format(self.image_coordinates[0], self.image_coordinates[1]))
else:
print('Select ROI to crop before cropping')
def show_cropped_ROI(self):
cv2.imshow('cropped image', self.cropped_image)
if __name__ == '__main__':
extract_image_widget = ExtractImageWidget()
while True:
cv2.imshow('image', extract_image_widget.show_image())
key = cv2.waitKey(1)
if key == ord('r'):
extract_image_widget.rotate_image(5)
if key == ord('e'):
extract_image_widget.rotate_image(-5)
if key == ord('q'):
cv2.destroyAllWindows()
exit(1)
if key == ord('c'):
extract_image_widget.show_cropped_ROI()