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
Related
I've been using opencv to approximate an abnormal shape as a polygon. It runs successfully, but it would be nice to know what it is counting as a side. I would want to plot the vertices of this polygonal approximation. How would I go on about doing that?
Here's my code:
import cv2
import numpy as np
# load image
img = cv2.imread('tinytri.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# apply canny edge detection
edges = cv2.Canny(gray, 60, 160)
# 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.01 * peri, True)
print('number of sides:',len(approx))
Something like that should work:
for vertex in big_contour:
contour = cv2.circle(contour, vertex[0], 2, (225,0,0), 2)
I have the image, i have removed the noise (dots in the background) and, I want to draw a bounding box around the block of text In image how can I do it using python OpenCV
Input image:
Noise Removed Image:
Here is the code used to remove noise in background Where i can change to save images with bounding boxes around the text
import cv2
import matplotlib.pyplot as plt
import glob
import os
def remove_dots(image_path,outdir):
image = cv2.imread(image_path)
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,51,9)
# Create horizontal kernel then dilate to connect text contours
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
dilate = cv2.dilate(thresh, kernel, iterations=2)
# Find contours and filter out noise using contour approximation and area filtering
cnts = cv2.findContours(dilate, 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.04 * peri, True)
x,y,w,h = cv2.boundingRect(c)
area = w * h
ar = w / float(h)
if area > 1200 and area < 50000 and ar <8:
cv2.drawContours(mask, [c], -1, (255,255,255), -1)
# Bitwise-and input image and mask to get result
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
result = cv2.bitwise_and(image, image, mask=mask)
result[mask==0] = (255,255,255) # Color background white
cv2.imwrite(os.path.join(outdir,os.path.basename(image_path)),result)
for jpgfile in glob.glob(r'C:\custom\TableDetectionWork\text_detection_dataset/*'):
print(jpgfile)
remove_dots(jpgfile,r'C:\custom\TableDetectionWork\textdetect/')
You can do that by using a horizontal morphology filter to merge the letters in a mask image. Then find the contours. Then get the bounding boxes.
Input:
import cv2
import numpy as np
img = cv2.imread("john.jpg")
# convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# threshold
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
# invert
thresh = 255 - thresh
# apply horizontal morphology close
kernel = np.ones((5 ,191), np.uint8)
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
# get external contours
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
# draw contours
result = img.copy()
for cntr in contours:
# get bounding boxes
pad = 10
x,y,w,h = cv2.boundingRect(cntr)
cv2.rectangle(result, (x-pad, y-pad), (x+w+pad, y+h+pad), (0, 0, 255), 4)
# save result
cv2.imwrite("john_bbox.png",result)
# display result
cv2.imshow("thresh", thresh)
cv2.imshow("morph", morph)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Morphology Closed Image:
Bounding Boxes Image:
Here is the core of your code modified to do what you want in Python/OpenCV. It is just adding my code to the end of your code.
Input:
import cv2
import numpy as np
image = cv2.imread("john.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,51,9)
# Create horizontal kernel then dilate to connect text contours
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
dilate = cv2.dilate(thresh, kernel, iterations=2)
# Find contours and filter out noise using contour approximation and area filtering
cnts = cv2.findContours(dilate, 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.04 * peri, True)
x,y,w,h = cv2.boundingRect(c)
area = w * h
ar = w / float(h)
if area > 1200 and area < 50000 and ar <8:
cv2.drawContours(mask, [c], -1, (255,255,255), -1)
# Bitwise-and input image and mask to get result
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
result = cv2.bitwise_and(image, image, mask=mask)
result[mask==0] = (255,255,255) # Color background white
# NEW CODE HERE TO END _____________________________________________________________
gray2 = cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)
thresh2 = cv2.threshold(gray2, 128, 255, cv2.THRESH_BINARY)[1]
thresh2 = 255 - thresh2
kernel = np.ones((5 ,191), np.uint8)
close = cv2.morphologyEx(thresh2, cv2.MORPH_CLOSE, kernel)
# get external contours
contours = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
# draw contours
result2 = result.copy()
for cntr in contours:
# get bounding boxes
pad = 10
x,y,w,h = cv2.boundingRect(cntr)
cv2.rectangle(result2, (x-pad, y-pad), (x+w+pad, y+h+pad), (0, 0, 255), 4)
cv2.imwrite("john_bboxes.jpg", result2)
cv2.imshow("mask",mask)
cv2.imshow("thresh",thresh)
cv2.imshow("dilate",dilate)
cv2.imshow("result",result)
cv2.imshow("gray2",gray2)
cv2.imshow("thresh2",thresh2)
cv2.imshow("close",close)
cv2.imshow("result2",result2)
cv2.waitKey(0)
cv2.destroyAllWindows()
Bounding Boxes on Your Result:
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)
I have to find out automatically coordinates (only one point) where border (object) begin, I do not know how to handle function findContours.
testImage.jpg
image = cv2.imread("testImage.jpg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Threshold
ret, thresh = cv2.threshold(gray,225,255,0)
# Contours
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# Here are coordinates that I have to find out
coord = contours[0][1]
# Coordinates to point
point = (coord[0][0],coord[0][1])
# Draw circle on coordinates
cv2.circle(image,point,10,(0,255,0),1)
cv2.imshow("Image", image)
cv2.waitKey()
cv2.destroyAllWindows()
Output
And my goal is find out coordinates anywhere on the border (blue line) - see last image.
Goal
Thanks.
I tweaked your code a little. It seems to do the trick. Check out if it helps:
import cv2
import numpy as np
image = cv2.imread("test.jpg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
t = 230 # threshold: tune this number to your needs
# Threshold
ret, thresh = cv2.threshold(gray,t,255,cv2.THRESH_BINARY_INV)
#kernel = np.ones((9,9),np.uint8)
#thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
cv2.imshow("thresh", thresh)
cv2.waitKey(1)
# Contours
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# find the coordinate with the smallest x value
for contour in contours:
coord = min(contour[0], key=lambda c: c[0])
# Coordinates to point
point = (coord[0],coord[1])
#draw circles on coordinates
cv2.circle(image,point,10,(0,255,0),5)
cv2.imshow("Image", image)
cv2.waitKey()
cv2.destroyAllWindows()
Note: Increase parameter t to move your green circle 'farther outside' of the contour. Decrease it to move inside.
#Sparkofska
Thanks for your idea, I use it with another way to find out.
image = cv2.imread('testImage.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
t=230
ret, thresh = cv2.threshold(gray,t,255,cv2.THRESH_BINARY_INV)
cv2.imshow("filter", thresh)
# Canny Edge detection
canny = cv2.Canny(thresh, 0, 100)
cv2.imshow("canny", canny)
# Find contours
cnts = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2:]
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
x, y, w, h = cv2.boundingRect(c)
# My coordinates
cv2.circle(image,(x,y), 10, (0,255,0), 5)
cv2.imshow("output", image)
cv2.waitKey()
cv2.destroyAllWindows()
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()