How to extract bottom part from engineering drawing image using python? - python

My input image
To extract highlighted part
My desired output
Please someone help and give me a suggestion. My images looks like this. This is just sample one. I need to crop the bottom template part and do OCR. I have attached my desire output picture. Please have a look. How to implement it using python?
PS: The sheet size will differ and there may the chance of template to dislocate. but mostly it will be in lower left corner

Here's a potential approach:
Obtain binary image. We convert to grayscale, Gaussian blur, then Otsu's threshold
Fill in potential contours. We iterate through contours and filter using contour approximation to determine if they are rectangular.
Perform morphological operations. We morph open to remove non-rectangular contours using a rectangular kernel.
Filter and extract desired contour. Find contours and filter using contour approximation, aspect ratio, and contour area to isolate the desired contour. Then extract using Numpy slicing.
Binary image
Filled in contours
Morphological operation to remove non-rectangular contours
Desired contour highlighted in green
Extracted ROI
Code
import cv2
# Grayscale, blur, and threshold
image = cv2.imread('1.png')
original = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Fill in potential contours
cnts = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.05 * peri, True)
if len(approx) == 4:
cv2.drawContours(thresh, [c], -1, (255,255,255), -1)
# Remove non rectangular contours
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40,10))
close = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)
# Filtered for desired contour
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.05 * peri, True)
x,y,w,h = cv2.boundingRect(approx)
aspect_ratio = w / float(h)
area = cv2.contourArea(approx)
if len(approx) == 4 and w > h and aspect_ratio > 2.75 and area > 45000:
cv2.drawContours(image, [c], -1, (36,255,12), -1)
ROI = original[y:y+h, x:x+w]
cv2.imwrite('image.png', image)
cv2.imwrite('ROI.png', ROI)
cv2.waitKey()

Related

can't find right numbers of contours count

i'm trying to find specific contours having red outlines. Below is the code, I'm trying on this image :
import numpy as np
import cv2
image = cv2.imread('C:/Users/htc/Desktop/image.png')
original = image.copy()
image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lower = np.array([0,50,50], dtype="uint8")
upper = np.array([10, 255,255], dtype="uint8")
mask = cv2.inRange(image, lower, upper)
# Find contours
cnts = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Extract contours depending on OpenCV version
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
print(len(cnts))
# Iterate through contours and filter by the number of vertices
for c in cnts:
perimeter = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.04 * perimeter, True)
if len(approx) > 5:
cv2.drawContours(original, [c], -1, (36, 255, 12), -1)
cv2.imshow('mask', mask)
cv2.imshow('original', original)
cv2.waitKey()
Output
the length of contour i'm getting is 14 which is not correct. The correct output will be 3. Where i'm doing wrong?
If you can notice, there are breaks in your mask image due to which many contours are being detected. To correct this (if you only want the count), you can dilate the mask image obtained before finding the contours as shown below.
mask = cv2.inRange(image, lower, upper)
# Dilating the mask
kernel = np.ones((3, 3), dtype=np.uint8)
mask = cv2.dilate(mask, kernel, iterations=2)
# Find contours
cnts = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
Just add a step at the beginning where you blur the picture a little bit.
image = cv2.GaussianBlur(image, (3, 3), 0, 0, cv2.BORDER_DEFAULT)

find number of sides python

I have used below code to find number of sides in an image but it's not giving appropriate result
import cv2
image = cv2.imread('sheet.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Find contours and perform contour approximation
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.03 * peri, True)
print('number of sides:',len(approx))
Further i want to find exact number of side in an image and length of each side in the image .
Below is the image for reference
Along with this can we get the length of each side also like here we have 4 sides so the length of the respective side and the number of corners .
What if we have circular shape or arc in any shape, then how to find number of edges and it's length
Here is one way to do that in Python/OpenCV using Canny edges.
Input:
import cv2
import numpy as np
# load image
img = cv2.imread('quadrilateral.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# apply canny edge detection
edges = cv2.Canny(gray, 90, 130)
# apply morphology close
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
morph = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel)
# get contours and keep largest
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)
# draw contour
contour = img.copy()
cv2.drawContours(contour, [big_contour], 0, (0,0,255), 1)
# get number of vertices (sides)
peri = cv2.arcLength(big_contour, True)
approx = cv2.approxPolyDP(big_contour, 0.03 * peri, True)
print('number of sides:',len(approx))
# save results
cv2.imwrite("quadrilateral_edges.jpg", edges)
cv2.imwrite("quadrilateral_morphology.jpg", morph)
cv2.imwrite("quadrilateral_contour.jpg", contour)
# show result
cv2.imshow("edges", edges)
cv2.imshow("morph", morph)
cv2.imshow("contour", contour)
cv2.waitKey(0)
cv2.destroyAllWindows()
Edge image:
Morphology image (to close boundary):
Contour:
Textual Results:
number of sides: 4

Opencv Python: How to detect filled, rectangular shapes on picture

I have the picture below.
I want to find the the black colored rectangles on the left using opencv. Thanks for help=)
Here's a simple approach using thresholding + morphological operations.
Obtain binary image. Load image, convert to grayscale, then adaptive threshold
Fill rectangular contours. Find contours and fill the contours to create filled rectangular blocks.
Perform morph open. We create a rectangular structuring element and morph open to remove the lines
Draw rectangles around largest rectangles Find contours and draw bounding rectangles around rectangles with an area above a certain treshold.
Here's each step visualized:
Obtain binary image
Adaptive treshold
Filled rectangular contours
Perform morph open
Draw rectangles around largest rectangles
In code:
import numpy as np
import cv2
#load the image
image = cv2.imread("mtF6y.jpg")
# grayscale
result = image.copy()
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
# adaptive threshold
thresh = cv2.adaptiveThreshold(gray,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,51,9)
# Fill rectangular contours
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(thresh, [c], -1, (255,255,255), -1)
# Morph open
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=4)
# Draw rectangles, the 'area_treshold' value was determined empirically
cnts = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
area_treshold = 4000
for c in cnts:
if cv2.contourArea(c) > area_treshold :
x,y,w,h = cv2.boundingRect(c)
cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 3)
cv2.imshow('thresh', thresh)
cv2.imshow('opening', opening)
cv2.imshow('image', image)
cv2.waitKey()

How to find the contour of a completed form scanned image?

I would like to detect the contour of the completed form in this scan.
Ideally I would want to find the corners of the table painted with red.
My final goal is to detect that the whole document was scanned and that the four corners are within the boundaries of the scan.
I used OpenCV from python - but it was not able to find the contour of the big container.
Any ideas?
With the observation that the form can be identified using the table grid, here's a simple approach:
Obtain binary image. Load the image, grayscale, Gaussian blur, then Otsu's threshold to get a binary image
Find horizontal sections. We create a horizontal shaped kernel and find horizontal table lines and draw onto a mask
Find vertical sections. We create a vertical shaped kernel and find vertical table lines and draw onto a mask
Fill text document body and morph open. We perform morph operations to close the table then find contours and fill the mask to obtain a contour of the shape. This step fulfills your needs since you can just find contours on the mask but we can go further and extract only the desired sections.
Perform four-point perspective transform. We find contours, sort for the largest contour, sort using contour approximation then perform a four-point perspective transform to obtain a birds eye view of the image.
Here's the results:
Input image
Detected contour to extract highlighted in green
Output after 4-point perspective transform
Code
import cv2
import numpy as np
from imutils.perspective import four_point_transform
# Load image, create mask, grayscale, and Otsu's threshold
image = cv2.imread('1.jpg')
mask = np.zeros(image.shape, dtype=np.uint8)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,11,3)
# Find horizontal sections and draw on mask
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (80,1))
detect_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detect_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(mask, [c], -1, (255,255,255), -1)
# Find vertical sections and draw on mask
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,50))
detect_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(detect_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(mask, [c], -1, (255,255,255), -1)
# Fill text document body
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
close_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
close = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, close_kernel, iterations=3)
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(mask, [c], -1, 255, -1)
# Perform morph operations to remove noise
# Find contours and sort for largest contour
opening = cv2.morphologyEx(mask, cv2.MORPH_OPEN, close_kernel, iterations=5)
cnts = cv2.findContours(opening, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
displayCnt = None
for c in cnts:
# Perform contour approximation
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
if len(approx) == 4:
displayCnt = approx
break
# Obtain birds' eye view of image
warped = four_point_transform(image, displayCnt.reshape(4, 2))
cv2.imwrite('mask.png', mask)
cv2.imwrite('thresh.png', thresh)
cv2.imwrite('warped.png', warped)
cv2.imwrite('opening.png', opening)
What about using the Hough transform with a narrow direction range, to find the verticals and horizontals ? If you are lucky, those that you need will be the longest, and after selecting them you can reconstruct the rectangle.

How to invert the background of square headers from black to white in an image using python?

I am trying to make the background of the square headers (The black bar that contains TERMS PONUMBER PROJECT) white and the text within black.
I have tried using the findContours method to find the contours and then crop and invert them so that I get them in the black text and white background form. But the problem is I am not having any idea on how to proceed ahead or is there any better approach to this
image =cv2.imread("default.jpg")
gray=cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
th, thresh = cv2.threshold(gray,1, 255, cv2.THRESH_BINARY_INV)
kernel = cv2.getStructuringElemnt(cv2.MORPH_ELLIPSE,(7,7))
morp_image = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
contours = cv2.findContours(morp_image,
cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2]
cnts = sorted(contours,key=cv2.contourArea)[-1]
The code above does find each such contour on an individual basis like if I change the [-1] in the last line of the code to [-2], it will find the next contour but I want to find all such areas in the image in a single go and make the background of such areas white while changing the text to black.
Thanks
Here's a simple approach
Convert image to grayscale and Gaussian blur
Otsu's threshold to obtain binary image
Find contours
Filter using the number of corners and contour area
Extract ROI, invert ROI, and replace into original image
The idea is that if the contour has 4 corners, it must be a square/rectangle. In addition, we filter using a minimum contour area to ignore noise. If the contour passes our filter then we have a desired ROI to invert. The detected ROIs
Now we extract each ROI using Numpy slicing. Here's each ROI before and after inverting
Now we simply replace each inverted ROI back into the original image to get our result
import cv2
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
cnts = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.015 * peri, True)
area = cv2.contourArea(c)
if len(approx) == 4 and area > 1000:
x,y,w,h = cv2.boundingRect(c)
ROI = 255 - image[y:y+h,x:x+w]
image[y:y+h, x:x+w] = ROI
cv2.imshow('image', image)
cv2.imwrite('image.png', image)
cv2.waitKey()

Categories