#Attempts at skeletonization are failing.

17 messages · Page 1 of 1 (latest)

scarlet lantern
#

So im currently working on a computer vision pipeline in openCV and skimage to process and interpret IQ puzzles.
Right now Im at the contour detection phase and ive been trying to further simplify the contours of the image. I found that the skeletonization function of skimage looks promising for its ability to turn multi pixel thick lines into single lines. Which I was planning on pairing with my contour detection which would avoid near duplicate/overlapping contours.

However upon testing the skeletonization function ive found it to be very limited and sensitive to noise. It works about 50% of the time however there are lots of images that cause edge cases to occur and lead to big gaps in the skeleton. My expectation was something that would basically function identically to a human just tracing the lines. But ofc its not that easy.

Im wondering is there potential preprocessing I can do? Better skeletonization algorithms? Or a new approach altogether?

my current pipeline is as follows img->grayscale->quantize pixels->adaptive_thresh->skeletonize(skipped)->find contours->extract primitives(later)

heres a snippet of my code:
https://pastebin.com/tKA02dXs

deft pendantBOT
#

-# Python @haughty idol @sudden kite @verbal maple @versed void

little bluff
#

I have no clue whst this is but it looks awesome man

scarlet lantern
#

I got some minor improvements by tweaking the hyper parameters of the threshold function which was a bit aggressive with the 11 neighbor pixel blending.
Ill probably keep tweaking and potentially look into applying more filters to clean

versed void
#

I don't think you even need the skeletonization

#

the binary thresh + skeleton images look almost identical so the lines are already 1-2px thick -> skeletonizer has nothing to do really?

#

the gaps are mostly a threshold failure ig

#

adaptive threshold seems like it might not be optimal either because your inputs have very even illumination?

#

Maybe try doing like Otsu + some tiny close after

#

you probably also want to split it up into the different panels

scarlet lantern
# versed void the binary thresh + skeleton images look almost identical so the lines are alrea...

Yea that seems like the best line of approach the only issue im encountering is that when i reach the contour stage I have to deal with these near duplicate contours. Because it calculates one per line edge. I spent like an hour trying to create a function to delete the inner contour. Which was proven to be kind of difficult for me to handcraft. Thats why i wanted to use the skeleton because single pixel lines by default only create a single contour. Do you know of any premade functions or functionality for this so i dont have to reinvent the wheel.

The above image is an example of what im talking about. the red triangle is the inner piece and the green is the outer. I want to filter out the contours that are like overlaps which would give us just the outer most or the green triangle and removing the inner red triangle due to near overlap

versed void
#

cv2.RETR_EXTERNAL is something you should take a look at

#

I'd focus on the panel split first though

little bluff
#

Awesome christmas tree segmentation

scarlet lantern
#

`#Making a function to obtain contours recursively while prevent dupes
def recursive_contours(gray,depth=0):
if depth == 20:
print("WARNING MAX DEPTH REACHED!")
return []
all_contours = []
, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
outermost_contours,
= cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
enclosed_contours = []
inner_contours = []
for i, contour in enumerate(outermost_contours):
all_contours.append(contour)
if is_enclosed_shape(contour,min_area=10):
enclosed_contours.append(contour)
#Get inside contours
for i, contour in enumerate(enclosed_contours):
inner_content,mask = get_inside_contour(gray,contour)

    inner_gray = np.ones_like(gray) * 255
    
    #Remove outer lines from previous contour
    inner_gray[mask == 255] = inner_content
    inner_gray = remove_outer_lines(inner_gray)
    #Recursively get contents
    contours = recursive_contours(inner_gray,depth+1)
    for j, con in enumerate(contours):
        inner_contours.append(con)
for i, contour in enumerate(inner_contours):
    all_contours.append(contour)

return all_contours`
#

custom function I wrote to get all the contours of the shape without near overlaps works very well. just needs some slight tweaks and a solid check. Also Ill want to do some line normalization and gap prefilling to prevent gaps when thresholding.

But its very very close to what I wanted