I have (low res) images like the following:
and the ultimate goal is to have defined line segment coordinates for each of the lines in the image:
The strategy that I've tried is dilate -> find contours -> blur -> erode -> canny edge detection:
import numpy as np
import cv2
from skimage.feature import canny
image = cv2.imread('image.png')
kernel = np.ones((1,1),np.uint8)
dilated_img = cv2.dilate(gray, kernel, iterations = 1)
canvas = cv2.cvtColor(dilated_img, cv2.COLOR_GRAY2RGB)
contours, hierarchy = cv2.findContours(dilated_img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
for i,cont in enumerate(contours):
if ( hierarchy[0][i][3] != -1 ):
cv2.fillPoly(canvas, pts =[cont], color=(0, 180, 0))
else:
cv2.drawContours(canvas, cont, -1, (255, 0, 0), 1)
canvas = cv2.GaussianBlur(canvas,(1, 1),39)
edges = canny(image, 3, 1, 25)
The output (edges
) looks like the images below, except for the green, which I've added to denote what I hope to achieve with this strategy: if I can find the middle of the tubes (green), then I can construct the line segments from them. Maybe this is a needlessly complicated way of achieving the goal...
The edges mostly come out to include these tube-like structures, which could be completely enclosed or not.
The green lines in the images denote what I want to find -- basically just the approximate middle of the tube.
What I've tried to do with the edge
object is go row by row and (and column by column) pixel by pixel to find the middle of the tubes based on how the white and black pixels appear, but the results are messy and don't work well with many tube orientations.
(image 1 edges, color inversion)
So assuming that this strategy is viable for achieving the goal stated above, how can I find the mid-points of the tubes? If the strategy isn't good, what would be better?
Thanks!
I hope this method will help you.
your green line is a skeletonized image of threshold image. check here for more about skeletonized image.
import cv2
import numpy as np
from skimage.morphology import skeletonize
def read_image(image_path):
image = cv2.imread(image_path)
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(image, 200, 255, cv2.THRESH_BINARY_INV)
return thresh
def get_skeleton_iamge(threshold_image):
skeleton = skeletonize(threshold_image / 255)
skeleton = skeleton.astype(np.uint8)
skeleton *= 255
return skeleton
if __name__ == "__main__":
threshold_image = read_image("image.png")
cv2.imshow("threshold_image", threshold_image)
skeleton_iamge = get_skeleton_iamge(threshold_image)
cv2.imshow("skeleton_iamge", skeleton_iamge)
canny_edges = cv2.Canny(threshold_image, 100, 200)
cv2.imshow("canny_edges", canny_edges)
# for displaying image only
colour_skeleton_iamge = cv2.cvtColor(skeleton_iamge, cv2.COLOR_GRAY2BGR)
colour_canny_edges = cv2.cvtColor(canny_edges, cv2.COLOR_GRAY2BGR)
colour_skeleton_iamge[skeleton_iamge == 255] = [0, 255, 0]
combined_image = cv2.scaleAdd(colour_skeleton_iamge, 0.5, colour_canny_edges, 0.5)
cv2.imshow("combined_image", combined_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
Output:
image | threshold_image | skeleton_iamge | canny_edges | combined_image |
---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |