Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Separate lines and circles in an image using OpenCV in python

I'm trying to separate the lines and circles, instead of lines some circles are connected with curves.

I tried to use contours to find circles but it is however including the lines inside the contour, so I also tried skeletoning the image so that to see if the connection between the circles and lines might break, but it is unsuccessful.

Hough_circles is not detecting circles in all cases, so the only option I've to find the circles using contours once the lines around it are eliminated.

Input Image

  • EDIT

Example 2

Input

Input Image

Output :  Not desired output

Output image

In the output image, I got circles weren't got separated and lines got merged with circles and the contour gave a different shape.

Please find some way to split the circles and lines. Please try to answer it in Python instead of C++. C++ answers are allowed too.

Thanks in advance!

like image 675
Strange Avatar asked Mar 03 '23 20:03

Strange


1 Answers

Here's a simple approach using morphological operations. The idea is to fill the contours, create an elliptical shaped structuring element, then morph open to remove the lines. From here we simply find the external contours and draw the circles. Here's the process visualized:

Filled thresholded image

enter image description here

Morph open

enter image description here

Result

enter image description here

Code

import cv2

# Load iamge, grayscale, Otsu's threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# Fill contours
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:
    cv2.drawContours(thresh, [c], -1, (255,255,255), -1)

# Morph open
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=3)

# Draw circles
cnts = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    cv2.drawContours(image, [c], -1, (36,255,12), 3)

cv2.imshow('thresh', thresh)
cv2.imshow('opening', opening)
cv2.imshow('image', image)
cv2.waitKey()

For the other image using contour hierarchy

enter image description here enter image description here enter image description here enter image description here

import cv2
import numpy as np

# Load image, grayscale, Otsu's threshold
image = cv2.imread('1.png')
mask = np.zeros(image.shape, dtype=np.uint8)
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# Filter using contour hierarchy
cnts, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
hierarchy = hierarchy[0]
for component in zip(cnts, hierarchy):
    currentContour = component[0]
    currentHierarchy = component[1]
    x,y,w,h = cv2.boundingRect(currentContour)
    # Only select inner contours
    if currentHierarchy[3] > 0:
        cv2.drawContours(mask, [currentContour], -1, (255,255,255), -1)

# Filter contours on mask using contour approximation
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
cnts = cv2.findContours(mask, 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)
    if len(approx) > 5:
        cv2.drawContours(mask, [c], -1, (0,0,0), -1)
    else:
        cv2.drawContours(image, [c], -1, (36,255,12), 2)

cv2.imshow('thresh', thresh)
cv2.imshow('image', image)
cv2.imshow('mask', mask)
cv2.waitKey()

Note: For a through explanation on contour hierarchy, take a look at understanding contour hierarchy and retrieval modes

like image 91
nathancy Avatar answered Mar 05 '23 18:03

nathancy