0

Image:

enter image description here

I have coordinates for the bounding polygon (not an exact rectangle). How can I rotate and extract the meter from image in an efficient way?

array([[337, 300],
       [574, 348],
       [567, 378],
       [329, 337]], dtype=int32)

I've been able to rotate the image by

(h, w) = im.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, angle, 1.0)
rotated = cv2.warpAffine(im, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)

but this changes the coordinates of box too.

Expected output :

enter image description here

Nazim Kerimbekov
  • 4,712
  • 8
  • 34
  • 58
rick_moody
  • 50
  • 9
  • Possible duplicate of [Crop Rectangle returned by minAreaRect OpenCV \[Python\]](https://stackoverflow.com/questions/37177811/crop-rectangle-returned-by-minarearect-opencv-python) – Kishore Kumar Singh Jul 25 '19 at 18:46
  • but the box is slanted @nathancy – rick_moody Jul 25 '19 at 19:50
  • 1
    Oh I see if its slanted, then there are two ways you can crop it. One is to use a mask and extract the ROI, the second is to use a perspective transform to obtain a birds eye view of the image. Here's a good [tutorial](https://www.pyimagesearch.com/2014/08/25/4-point-opencv-getperspective-transform-example/) – nathancy Jul 25 '19 at 19:57

1 Answers1

4

I think you want a Perspective Transform with 4 points to go from this:

enter image description here

to this:

enter image description here

#!/usr/bin/env python3

import cv2

# Set width and height of output image
W, H = 600, 200

# Load input image
img = cv2.imread('speedo.png')

# Define points in input image: top-left, top-right, bottom-right, bottom-left
pts0 = np.float32([[337,300],[574,348],[567,378],[329,337]])

# Define corresponding points in output image
pts1 = np.float32([[0,0],[W,0],[W,H],[0,H]])

# Get perspective transform and apply it
M = cv2.getPerspectiveTransform(pts0,pts1)
result = cv2.warpPerspective(img,M,(W,H))

# Save reult
cv2.imwrite('result.png', result)

You can do the same thing much more simply with ImageMagick which is installed on most Linux distros and is available for macOS and Windows. In the Terminal, or Command Prompt, you just run:

magick speedo.png -distort perspective '337,300 0,0 574,348 200,0 567,378 200,100 329,337 0,100' -crop 200x100+0+0 result.png

Keywords: Python, command-line, shell, ImageMagick, OpenCV, distort, perspective, warp, affine, 4-point, 4-pt, transform, image, image processing.

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • This is exactly what I was looking for and is surprisingly much easier and efficient than the rotate and crop techniques prevalent otherwise. Thanks man ! – rick_moody Jul 26 '19 at 11:14
  • Also if the box is an exact rectangle can we get a simpler solution? – rick_moody Jul 26 '19 at 11:16
  • If the box is an exact rectangle, aligned vertically or horizontally with the picture edges, you can just take a Numpy slice of the image, e.g. `speedo = image[10:100,80:200]` – Mark Setchell Jul 26 '19 at 11:25
  • Its not aligned with edges but in a slanted position. I have the 4 points of the rectangle how can I map it to a straight rectangle like **pts1** you specified i.e. I need the subimage to be of same size as the bounding box rectangle (Not distorted as your output above). – rick_moody Jul 26 '19 at 13:42
  • I don't understand what you mean. My output image would be exactly like your desired result in your question if the coordinates you gave were more accurate. If you want a different result, please update your question to show exactly what you want. Thank you. – Mark Setchell Jul 26 '19 at 14:02
  • I'm sorry I figured it out. Your answer was a great help, Thanks ! – rick_moody Jul 26 '19 at 15:22