I am trying to split image based on the number of contours formed. The below image i get 2 rectangular boxes, how to split this seperately. Please help.
Current Output:
Expected output:
Current output Code:
import cv2
image = cv2.imread('C:/sample_Contours.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (20,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(thresh, [c], -1, (255,255,255), thickness=15)
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,15))
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(thresh, [c], -1, (255,255,255), thickness=15)
cv2.imshow('thresh',thresh)
cv2.waitKey(0)
You can use cv function boundingRect to find the rectangle that contains the countour. Then from this rectangle it is a simple cropping.
Try something like:
m = 32 #crop margin
for c in cnts:
x,y,w,h = cv2.boundingRect(c)
crop = thresh[y-m:y+m+h,x-m:x+w+m]
Related
I have an image such as the following:
Original Image
I need to remove the line that the characters are written on, so I use the following code:
image = cv2.imread('im.png')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Remove horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (20,1))
remove_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(remove_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(result, [c], -1, (255,255,255), 5)
plt.imshow(result)
The resulting image looks like this:
Image With Line Removed
As you can see, the portions of the characters that were also touching the line were removed, which I need to preserve.
I attempted to solve this by using the following code:
image = cv2.imread('connected/im.png')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Remove horizontal
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40,1))
detected_lines = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detected_lines, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(image, [c], -1, (255,255,255), 2)
# Repair image
repair_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,6))
result = 255 - cv2.morphologyEx(255 - image, cv2.MORPH_CLOSE, repair_kernel, iterations=2)
plt.imshow(result)
This appears to solve the issue (sort of), but the image appears grainy, and you can still tell where the line is:
Repaired Image
Does anyone know of a better way of repairing the image / removing the line?
For what it is worth, you can erase the horizontal line with a purely vertical closing, and replace just that part in the original image. A little better than erasing.
I actually want to remove the comb below the dates with the help of opencv. Attaching the image for the referance .Till now I am able to remove the horizontal line of the comb with the code given below.`
import cv2
import os
image = cv2.imread(r'path to file')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (25,1))
detected_lines = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detected_lines, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(image, [c], -1, (255,255,255), 2)
repair_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,6))
ver =cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, repair_kernel, iterations=1)
result = 255 - cv2.morphologyEx(255 - image, cv2.MORPH_CLOSE, repair_kernel, iterations=1)
cv2.imshow('image', image)
cv2.imshow('result', result)
cv2.imwrite(r"path to image",image)
cv2.waitKey()`
Original Image
Image after removing horizontal lines
Vertical lines are probably the smallest contours (with the smallest heights) so I use it to detect them.
Using again findContours but with cv2.RETR_LIST instead of cv2.RETR_EXTERNAL I get all contours.
For every contour I calculate height = max(Y) - min(Y)
If height is between 23 and 25 then I have vertical rectangle which I can remove using filled drawContours
I had to check between 23 and 25 because I found 2 smaller contours which I had to skip. Because I had to choose values 23, 25 manually so this method is not so universal.
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cnts = cv2.findContours(gray, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
#X = c[:,0,0]
Y = c[:,0,1]
min_y = min(Y)
max_y = max(Y)
height = max_y - min_y
if 23 <= height <= 25:
print(f'{max_y:3} - {min_y:3} = {height} <---')
cv2.drawContours(image, [c], -1, (0,0,255), 2) # red border
cv2.drawContours(result, [c], -1, (255,255,255), -1) # filled
else:
print(f'{max_y:3} - {min_y:3} = {height}')
print('----')
Image:
Result:
Full code:
import cv2
import os
image = cv2.imread('Cfxvo.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (25,1))
detected_lines = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detected_lines, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(image, [c], -1, (255,255,255), 2)
repair_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,6))
ver = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, repair_kernel, iterations=1)
result = 255 - cv2.morphologyEx(255 - image, cv2.MORPH_CLOSE, repair_kernel, iterations=1)
# --- new code ---
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cnts = cv2.findContours(gray, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
X = c[:,0,0]
Y = c[:,0,1]
min_y = min(Y)
max_y = max(Y)
height = max_y - min_y
if 23 <= height <= 25:
print(f'{max_y:3} - {min_y:3} = {height} <---')
cv2.drawContours(image, [c], -1, (0,0,255), 2)
cv2.drawContours(result, [c], -1, (255,255,255), -1)
else:
print(f'{max_y:3} - {min_y:3} = {height}')
print('----')
# ---
cv2.imshow('image', image)
cv2.imshow('result', result)
cv2.imwrite("output-image.png", image)
cv2.imwrite("output-result.png", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
EDIT:
You can also check other values to make sure.
All rectangles have similar width 6-7 pixels.
And all have max_y = 61
I am trying to locate the X&Y of all horizontal lines in a PDF document.
I was using the code here:
code to detect horizontal lines
This code marks the horizontal lines verywell but I am not able to extract their coordinates in the document.
This is my code:
def DetectLine(pageNum):
# Convert to grayscale and adaptive threshold to obtain a binary image
img = cv2.imread(outpath + 'page_' + str(pageNum) + '.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Then we create a kernel and perform morphological transformations to isolate horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15,1))
detected_lines = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
# detect lines find contours and draw the result
cnts = cv2.findContours(detected_lines, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(img, [c], -1, (36,255,12), 3)
cv2.imshow('image_' + str(pageNum), img)
This function gets the pagenumber and reads a pre-prepared JPG of the specific page.
How can I return the Xs & Ys?
If only need the points:
you can extract it with:
Point 1: c[0][0] or cnts[num]c[0][0]
Point 2: c[1][0] or cnts[num]c[1][0]
where num is the index of the contour
Middle point
The solution will be:
(cnts[0][1][0][0]+cnts[0][0][0][0])//2,cnts[0][0][0][1]
Since each line or countour for get has two points, you can calculate the middle point with the average formula.
e.g:
x1=10 and x2=90, the middle point then is (10+90)/2
Here is the complete code:
import cv2
image = cv2.imread('2.png')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15,1))
detected_lines = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detected_lines, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
x,y=(c[1][0][0]+c[0][0][0])//2,c[0][0][1]
print(f'The middle point is: x: {x}, y: {y}')
cv2.drawContours(image, [c], -1, (36,255,12), 3)
cv2.circle(image, (x,y), radius=5, color=(0, 0, 255), thickness=-1)
cv2.imshow('thresh', thresh)
cv2.imshow('detected_lines', detected_lines)
cv2.imshow('image', image)
cv2.waitKey()
The result image is the following:
I am trying to remove all the lines present in the image.
I am able to detect the lines but when I am trying to remove the lines, I am still getting few small lines in the final image. I have used cv2.getStructuringElement to get both the horizontal and vertical lines. In some cases, the final image is getting completely distorted and I am not able to move forward
The image is taken from google
res = verticle_lines_img + horizontal_lines_img
res = cv2.bitwise_not(res)
fin=cv2.bitwise_or(img_bin, res,mask =cv2.bitwise_not(res))
fin= cv2.bitwise_not(fin)
exp =255-res
final = cv2.bitwise_and(exp,img_bin)
final = cv2.bitwise_not(final)
exp = ~exp
finalised = cv2.bitwise_and(img_bin,final)
finalised = cv2.bitwise_not(finalised)
Please help! Thanks
Here's an approach
Convert image to grayscale
Otsu's threshold to obtain binary image
Remove horizontal lines
Remove vertical lines
After converting to grayscale, we Otsu's threshold to get a binary image
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
From here we construct a special horizontal kernel to detect horizontal lines. Once the lines are detected, we fill in the lines to effectively remove the line
# Remove horizontal
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10,1))
detected_lines = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detected_lines, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(image, [c], -1, (255,255,255), 2)
Similarly, to remove vertical lines, we construct a special vertical kernel
# Remove vertical
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,10))
detected_lines = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(detected_lines, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(image, [c], -1, (255,255,255), 2)
Here's the detected lines in green
Result
You can fine tune the results by adjusting the kernel size. For instance, changing (10,1) to say (15,1) will tighten the line detection while lowering it to (5,1) will loosen the detection
Full Code
import cv2
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Remove horizontal
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10,1))
detected_lines = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detected_lines, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(image, [c], -1, (255,255,255), 2)
# Remove vertical
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,10))
detected_lines = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(detected_lines, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(image, [c], -1, (255,255,255), 2)
cv2.imshow('thresh', thresh)
cv2.imshow('image', image)
cv2.waitKey()
I'm trying to detect main structures of many floor plan pictures by detecting straight lines and edges, with reference from here.
The example above is one example I need to deal with, is it possible to get main structure by detecting lines with opencv HoughLinesP from it? Thanks for your help at advance.
import cv2
import numpy as np
def get_lines(lines_in):
if cv2.__version__ < '3.0':
return lines_in[0]
return [l[0] for l in lines]
img = cv2.imread('./test.jpg', 1)
img_gray = gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cannied = cv2.Canny(img_gray, threshold1=50, threshold2=200, apertureSize=3)
lines = cv2.HoughLinesP(cannied, rho=1, theta=np.pi / 180, threshold=80, minLineLength=30, maxLineGap=10)
for line in get_lines(lines):
leftx, boty, rightx, topy = line
cv2.line(img, (leftx, boty), (rightx,topy), (255, 255, 0), 2)
cv2.imwrite('./lines.png', img)
cv2.imwrite('./canniedHouse.png', cannied)
cv2.waitKey(0)
cv2.destroyAllWindows()
Results:
lines.png
canniedHouse.png
Other references:
How to get the external contour of a floorplan in python?
Floor Plan Edge Detection - Image Processing?
Here's an approach
Convert image to grayscale
Adaptive threshold to obtain binary image
Perform morphological transformations to smooth image
Create horizontal kernel and detect horizontal lines
Create vertical kernel and detect vertical lines
After converting to grayscale, we adaptive threshold to obtain a binary image
image = cv2.imread('1.jpg')
original = image.copy()
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
From here we perform morphological transformations to smooth the image
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)
Now we create a horizontal kernel with cv2.getStructuringElement() and detect horizontal lines
# Find horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (35,2))
detect_horizontal = cv2.morphologyEx(close, 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(original, [c], -1, (36,255,12), -1)
Similarly, we create a vertical kernel and detect vertical lines
# Find vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2,35))
detect_vertical = cv2.morphologyEx(close, 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(original, [c], -1, (36,255,12), -1)
Here's the result
import cv2
import numpy as np
image = cv2.imread('1.jpg')
original = image.copy()
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)
# Find horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (35,2))
detect_horizontal = cv2.morphologyEx(close, 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(original, [c], -1, (36,255,12), -1)
# Find vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2,35))
detect_vertical = cv2.morphologyEx(close, 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(original, [c], -1, (36,255,12), -1)
cv2.imshow('thresh', thresh)
cv2.imshow('close', close)
cv2.imshow('original', original)
cv2.waitKey()
If you wanted to just find the external contour you can find contours after the morphological close operation
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(original, [c], -1, (36,255,12), 3)