I am new to OpenCV and I would like to know how to find the coordinates of vertical lines in an image. I do have a sample image
(This image is a row of a table. Since the data is sensitive i cannot display the info inside these cells.)
There are 4 vertical lines and i like to have the coordinates of each lines.
What I am aiming to do is to identify the vertical lines and cut each cells between these vertical lines.
If i get the coordinates of the lines then it is easy to identify these cells
This is what I have tried:
file_path = 'unnamed.jpg'
img = Image.open(file_path)
width = img.width
height = img.height
# Get table list
document_img = cv2.imread(file_path)
table_list = [np.array(document_img, copy=True)]
# Identifying the Vertical lines in the image
img = cv2.cvtColor(table_list[0], cv2.COLOR_BGR2GRAY)
print("img \n")
cv2_imshow(img)
img_height, img_width = img.shape
thresh, img_bin = cv2.threshold(img, 180, 255, cv2.THRESH_BINARY)
print("img_bin \n")
cv2_imshow(img_bin)
img_bin_inv = 255 - img_bin
print("img_bin_inv \n")
cv2_imshow(img_bin_inv)
kernel_len_ver = max(10, img_height // 50)
ver_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, kernel_len_ver)) # shape (kernel_len, 1) inverted! xD
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
# Use vertical kernel to detect and save the vertical lines in a jpg
image_1 = cv2.erode(img_bin_inv, ver_kernel, iterations=3)
print("image_1 \n")
cv2_imshow(image_1)
vertical_lines = cv2.dilate(image_1, ver_kernel, iterations=4)
print("vertical_lines \n")
cv2_imshow(vertical_lines)
# print("\n\n\n")
Image.fromarray(vertical_lines, mode='L').save('vertical_lines.jpg')
file_path = 'unnamed.jpg'
img = Image.open(file_path)
width = img.width
height = img.height
# Get table list
document_img = cv2.imread(file_path)
table_list = [np.array(document_img, copy=True)]
# Identifying the Vertical lines in the image
img = cv2.cvtColor(table_list[0], cv2.COLOR_BGR2GRAY)
print("img \n")
cv2_imshow(img)
img_height, img_width = img.shape
thresh, img_bin = cv2.threshold(img, 180, 255, cv2.THRESH_BINARY)
print("img_bin \n")
cv2_imshow(img_bin)
img_bin_inv = 255 - img_bin
print("img_bin_inv \n")
cv2_imshow(img_bin_inv)
kernel_len_ver = max(10, img_height // 50)
ver_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, kernel_len_ver)) # shape (kernel_len, 1) inverted! xD
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
# Use vertical kernel to detect and save the vertical lines in a jpg
image_1 = cv2.erode(img_bin_inv, ver_kernel, iterations=3)
print("image_1 \n")
cv2_imshow(image_1)
vertical_lines = cv2.dilate(image_1, ver_kernel, iterations=4)
print("vertical_lines \n")
cv2_imshow(vertical_lines)
# print("\n\n\n")
Image.fromarray(vertical_lines, mode='L').save('vertical_lines.jpg')
img = cv2.imread('vertical_lines.jpg')
# Convert the image to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Apply a threshold to the grayscale image
ret, thresh = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY)
# Apply a morphological operation to close gaps in the lines
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
# Find the contours in the binary image
contours, hierarchy = cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Initialize a list to store the coordinates of the vertical lines
vertical_lines = []
# Iterate through each contour and extract the coordinates of the bounding rectangle if it's a vertical line
for contour in contours:
x,y,w,h = cv2.boundingRect(contour)
vertical_lines.append((x,y,x+w,y+h))
vertical_lines = sorted(vertical_lines)
img2 = cv2.imread('unnamed.jpg')
count = 0
for i in range(len(vertical_lines)):
if i < len(vertical_lines) - 1:
count = count + 1
roi = img2[0:width, vertical_lines[i][0]:vertical_lines[i+1][0]]
cv2.imwrite("/content/roi/roi_"+ str(count) +".jpg", roi)
Using this code i only get thick black vertical lines. Not the lines that i mentioned in the image.
If you know your image will look like that
For example, you may know that the lines will be vertical, black and of the same height, and there will be no other items in the image.
In this case you don't need OpenCV. You could do this:
D, the differences between each pixel and the pixel to the left (putting 0 for the leftmost pixel of each row)Dvertically, to obtain a single row of numbers. The 4 lowest values in D should be the X coordinates of your lines. (You may need to do a little extra work to prevent double-recognition of single lines if the brightness varies gradually)Dhorizontally, to obtain a single column of numbers. The result will have a long series of very low values in the middle, surrounded by very high values at the start and end. The start and end of the middle zone give you the Y1 and Y2 values (shared between all the line segments).If what you have drawn is only a simulation of what you will be reading
For example:
Then you should use OpenCV. Read up on the Hough transform, which will enable you to find the orientation and position of the major "lines" on an image. You can limit the Hough transform to look for lines that are close to vertical.
It will give you the horizontal position, but you will have to find the vertical ends of the lines yourself.
https://opencv24-python-tutorials.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_houghlines/py_houghlines.html
Example OpenCV code