Rounded rectangle perspective transform - python

I have an image where I'm trying to export and do transform as rectangle (no matter in base photo). I have done with finding largest contour in image using mask (trained in pytorch). In variable pr_mask I have stored mask from network. After that from that mask I can get extract contours of my image. So from this image:
I can extract contours like this:
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray,(5,5),5)
canny = cv2.Canny(blur, 30, 80, 3)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
flag, thresh = cv2.threshold(canny, 80, 255, cv2.THRESH_BINARY)
kernel = np.ones((10,10),np.uint8)
dilation = cv2.dilate(canny,kernel,iterations = 1)
ret,thresh = cv2.threshold(dilation, 200, 255, 0)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
After that I fetch largest contour and then trying to extract contours:
approx = np.ones((10,10),np.uint8)
d = 0
while (len(approx)>4):
d=d+1
approx = cv2.approxPolyDP(largestcontour,d, True)
cv2.drawContours(imagebase, [largestcontour], -1, (0,255,0), 3)
hull = []
hull.append(cv2.convexHull(largestcontour, False))
cv2.drawContours(imagebase, hull, -1, (0,0,255), 3)
After that I get tthis result:
Is it possible to extract square (rectangle) which will transform my image to exact square without rounded corners and to fit whole image?
I'm trying to find a way how to solve this "perspective" issue and align element to specific position. My image is taken by cameras, so it can be in different positions, where boundingRectangle will not work ... any suggestions?

Related

Improve contour detection

The bubbles are the target of contour detection
I would like to detect the contour of the bubble in the photo. However, no matter what kind of contour detection I used, my program would not detect the whole contour lines. I tried with erosion, blur and morphologyEx methods to make the contour continuous, but it seems the bubble shape has changed completely.
May I ask is there other method that can help me correctly draw the bubble contour similar as original photo?
My code is as following:
cv2.imshow(self.window_name, clone)
canvas = cv2.imshow('canvas', canvas)
# waiting for the user to press any key
imageThresh = cv2.adaptiveThreshold(cloneG, 200, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 3, 11)
bubble_only = cv2.bitwise_and(imageThresh, imageThresh, mask = VA_mask)
thresh = cv2.adaptiveThreshold(bubble_only, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 3, 9)
kernel1 = np.ones((2,2), np.uint8)
# Perform morphological hit-or-miss for
erosion = cv2.morphologyEx(thresh, cv2.MORPH_ERODE, kernel1, iterations = 2)
# Inverted this image and blurred it with a kernel size of 4
ret, thresh1 = cv2.threshold(erosion, 127, 255, 1)
blur = cv2.blur(thresh1, (4,4))
# Again perform another threshold on this image to get the central portion of the edge
ret, thresh2 = cv2.threshold(blur, 145, 255, 0)
# Perform morphological dilation to thin the edge. Use an ellipse structuring element of kernel size of 2
kernel2 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (1,1))
final = cv2.morphologyEx(thresh2, cv2.MORPH_ERODE, kernel2, iterations = 2)
# find the contour then fill the contour
mask = np.zeros(clone.shape[:2], dtype = 'uint8')
contours, hier = cv2.findContours(final, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
bubble_shade = cv2.drawContours(mask, contours, -1, color = (255,255,255), thickness=cv2.FILLED)
area = []
for contour in contours:
cnt_Area = cv2.contourArea(contour)
area.append(cnt_Area)
print(sorted(area))
cv2.imwrite('bubble_shade.jpg', bubble_shade)
cv2.waitKey()
cv2.destroyAllWindows()
return clone
Detected bubble contour

Using OpenCV to find a rotated rectangle of a certain aspect ratio?

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:
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)
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:
Note: The largest contour seems to be a parallelogram and not a perfect rectangle.

Extract polygons by color from mask image

I would like to extract the Polygon Shape by color, if it's possible the corner coordinates,
Currently i'm trying with open-cv find contourns, but it's very precise i'm getting a lot of coordinates. what i want it's just the corner points to make the polygon by color.
img = cv2.imread('mask_ZT76_39_A_2_8.png', 0)
img = cv2.medianBlur(img, 5)
thresh = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
cv2.THRESH_BINARY,11,2)
image, contours, hierarchy = cv2.findContours(thresh.copy(),cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(image, contours, -1, (0,255,0), 3)
approx_=[]
for i in range(len(contours[0])):
epsilon = cv2.arcLength(contours[0][i],True)
approx = cv2.approxPolyDP(contours[0][i],epsilon,True)
Thanks.

Find contour of rectangle in object

I would like to find the contour of the rectangular photograph inside of the object. I've tried using the corner detection feature of OpenCV, but to no avail. I also tried to find all the contours using findContours, and filter out the contours with more (or less) than 4 edges, but this also didn't lead anywhere.
I have a sample scan here.
I have a solution for you, but it involves a lot of steps. Also, it may not generalize that well. It does work pretty good for your image though.
First a grayscale and threshold is made and findContours is used to create a mask of the paper area. That mask is inverted and combined with the original image, which makes the black edges white. A new grayscale and threshold is made on the resulting image, which is then inverted so findContours can find the dark pixels of the photo. A rotated box around the largest contours is selected, which is the area you seek.
I added a little extra, which you may not need, but could be convenient: perspectivewarp is applied to the box, so the area you want is made into a straight rectangle.
There is quite a lot happening, so I advise you to take some time a look at the intermediate steps, to understand what happens.
Result:
Code:
import numpy as np
import cv2
# load image
image = cv2.imread('photo.jpg')
# resize to easily view on screen, remove for final processing
image = cv2.resize(image,None,fx=0.2, fy=0.2, interpolation = cv2.INTER_CUBIC)
### remove outer black edge
# create grayscale
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# perform threshold
retr , mask = cv2.threshold(gray_image, 190, 255, cv2.THRESH_BINARY)
# remove noise
kernel = np.ones((5,5),np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
# create emtpy mask
mask_2 = np.zeros(image.shape[:3], dtype=image.dtype)
# find contours
ret, contours, hier = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# draw the found shapes (white, filled in ) on the empty mask
for cnt in contours:
cv2.drawContours(mask_2, [cnt], 0, (255,255,255), -1)
# invert mask and combine with original image - this makes the black outer edge white
mask_inv_2 = cv2.bitwise_not(mask_2)
tmp = cv2.bitwise_or(image, mask_inv_2)
### Select photo - not inner edge
# create grayscale
gray_image2 = cv2.cvtColor(tmp, cv2.COLOR_BGR2GRAY)
# perform threshold
retr, mask3 = cv2.threshold(gray_image2, 190, 255, cv2.THRESH_BINARY)
# remove noise
maskX = cv2.morphologyEx(mask3, cv2.MORPH_CLOSE, kernel)
# invert mask, so photo area can be found with findcontours
maskX = cv2.bitwise_not(maskX)
# findcontours
ret, contours2, hier = cv2.findContours(maskX, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# select the largest contour
largest_area = 0
for cnt in contours2:
if cv2.contourArea(cnt) > largest_area:
cont = cnt
largest_area = cv2.contourArea(cnt)
# find the rectangle (and the cornerpoints of that rectangle) that surrounds the contours / photo
rect = cv2.minAreaRect(cont)
box = cv2.boxPoints(rect)
box = np.int0(box)
print(rect)
#### Warp image to square
# assign cornerpoints of the region of interest
pts1 = np.float32([box[1],box[0],box[2],box[3]])
# provide new coordinates of cornerpoints
pts2 = np.float32([[0,0],[0,450],[630,0],[630,450]])
# determine and apply transformationmatrix
M = cv2.getPerspectiveTransform(pts1,pts2)
result = cv2.warpPerspective(image,M,(630,450))
#draw rectangle on original image
cv2.drawContours(image, [box], 0, (255,0,0), 2)
#show image
cv2.imshow("Result", result)
cv2.imshow("Image", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

Highlight all possible circles ( Bubble sheet choices ) in opencv

I am working on automatically correcting a bubble-sheet tests that are scanned.
Currently, I can extract the solutions part of the sheet and fix its rotation.
So I have this image.
The output image with detected contours
Running the following code yields in the output image
def get_answers(image):
display_normal("Just image",image)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurry = cv2.GaussianBlur(gray, (3, 3), 1)
thresh = cv2.threshold(blurry, 225, 255,
cv2.THRESH_BINARY_INV)[1]
display_normal("Binary", thresh)
# find contours in the thresholded image, then initialize
# the list of contours that correspond to questions
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[1]
questionCnts = []
# loop over the contours
for c in cnts:
# compute the bounding box of the contour, then use the
# bounding box to derive the aspect ratio
(x, y, w, h) = cv2.boundingRect(c)
ar = w / float(h)
# in order to label the contour as a question, region
# should be sufficiently wide, sufficiently tall, and
# have an aspect ratio approximately equal to 1
if w >= 18 and h >= 18 and 0.9 <= ar and ar <= 1.2:
questionCnts.append(c)
cv2.drawContours(image, questionCnts, -1, (255, 0, 0), 1)
display_normal("Image with contours",image.copy())
if(questionCnts < 45*4):
raise Exception("Didn't found all possible answers")
Here is the problem : I convert the input image to binary and try to find contours that looks like a circle, but I can't find the whole possible 45*4 choices.. I fail to detect some of these circles..
So is there any better idea/algorithm to do this specific task ?
You could have tried using adaptive threshold:
adapt_thresh = cv2.adaptiveThreshold(equ, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)
cv2.imshow('adapt_thresh.jpg', adapt_thresh)
(I resized the original image to keep it smaller)
UPDATE:
Another approach that I just performed.......
I equalized the gray scale image using histogram equalization:
equalized_img = cv2.equalizeHist(gray)
cv2.imshow('Equalized Image.jpg', equalized_img )
I then obtained the median of the equalized image using np.median(equalized_img) and applied a binary threshold by selecting all pixel values below [0.6 * median]
ret, thresh = cv2.threshold(equalized_img, lower, 255, 1)
cv2.imwrite("Final Image.jpg", thresh)
Now you can go ahead and find your desired contours on this image.
Hope it helps .. :)

Categories