1

I am not having difficulty transforming a found box, it is the fact that I am not able to detect the box in the first place when it is at an angle.

Here is a sample image I want the largest ~1230:123 rectangle in the image the problem is the rectangle can be rotated.

Here is a picture of a rotated barcode that I am unable to detect:
enter image description here

The function I have been using to process uses contour area just looks for the largest rectangle.

What methods should I use to look for a rotated rectangle so that even when rotated I can detect it?

    #PYTHON 3.6 Snippet for Image Processing

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# compute the Scharr gradient magnitude representation of the images
# in both the x and y direction using OpenCV 2.4
ddepth = cv2.cv.CV_32F if imutils.is_cv2() else cv2.CV_32F
gradX = cv2.Sobel(gray, ddepth=ddepth, dx=1, dy=0, ksize=-1)
gradY = cv2.Sobel(gray, ddepth=ddepth, dx=0, dy=1, ksize=-1)
# subtract the y-gradient from the x-gradient
gradient = cv2.subtract(gradX, gradY)
gradient = cv2.convertScaleAbs(gradient)
# blur and threshold the image
blurred = cv2.blur(gradient, (8, 8))
(_, thresh) = cv2.threshold(blurred, 225, 255, cv2.THRESH_BINARY)
# construct a closing kernel and apply it to the thresholded image
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (21, 7))
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
# perform a series of erosions and dilations
closed = cv2.erode(closed, None, iterations = 4)
closed = cv2.dilate(closed, None, iterations = 4)
# find the contours in the thresholded image, then sort the contours
# by their area, keeping only the largest one
cnts = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL,
        cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
c = sorted(cnts, key = cv2.contourArea, reverse = True)[0]
# compute the rotated bounding box of the largest contour

rect = cv2.minAreaRect(c)
Rotem
  • 30,366
  • 4
  • 32
  • 65

1 Answers1

1

You don't need all the preprocessing (like Sobel, erode, dilate) for finding before executing findContours.

findContours works better when contours are full (filled with white color) instead of having just the edges.
I suppose you can keep the code from cv2.findContours to the end, and get the result you are looking for.

You may use the following stages:

  • Apply binary threshold using Otsu's thresholding (just in case image is not a binary image).
  • Execute cv2.findContours, and Find the contour with the maximum area.
  • Use cv2.minAreaRect for finding the minimum area bounding rectangle.

Here is a code sample:

import numpy as np
import cv2

img = cv2.imread('img.png', cv2.IMREAD_GRAYSCALE)  # Read input image as gray-scale

ret, img = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)  # Apply threshold using Otsu's thresholding (just in case image is not a binary image).

# Find contours in img.
cnts = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2]  # [-2] indexing takes return value before last (due to OpenCV compatibility issues).

# Find the contour with the maximum area.
c = max(cnts, key=cv2.contourArea)

# Find the minimum area bounding rectangle
# https://stackoverflow.com/questions/18207181/opencv-python-draw-minarearect-rotatedrect-not-implemented
rect = cv2.minAreaRect(c)
box = cv2.boxPoints(rect)
box = np.int0(box)

# Convert image to BGR (just for drawing a green rectangle on it).
bgr_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)

cv2.drawContours(bgr_img, [box], 0, (0, 255, 0), 2)

# Show images for debugging
cv2.imshow('bgr_img', bgr_img)
cv2.waitKey()
cv2.destroyAllWindows()

Result:
enter image description here

Note: The largest contour seems to be a parallelogram and not a perfect rectangle.

Rotem
  • 30,366
  • 4
  • 32
  • 65
  • Will convert all my stuff over to using OTSU thanks for the help. The issue I was having as well was it recognizing the gradient top left as a rectangle. Iv began to solve it by first getting a perspective view of the full card removing the background. Converting it to 1920x1080 without an angle and then performing the barcode finding function. The issue with the image above was that even if a slight bit of the corner is cut off of the full card it will not recognize it as a rectangle. Thanks so much – Orion Nelson Apr 22 '21 at 22:54