remove small dashes with connected edge in binary threshold image

Viewed 166

I have a binary threshold image and I want to remove small dashes from an image. which is connected to the circle. My main focus is to extract an arc of the circle.

  • original image :

enter image description here

  • output image as :

enter image description here

2 Answers

I took the lazy approach to this since we have a hollow shape. This is a smoothing approach to the problem which is relatively simple (check the distance from inner to outer), but it loses details since it runs under the assumption of a simple closed shape. If this isn't good enough, let me know; there are more complicated methods for doing what you want that can give cleaner results.

So the basic steps are this: First, use findContours to get the inner and outer layer of the shape (dilate until you get two, we didn't have to for this case since it already does that).

Then, calculate the distance from the each point to the closest point on the other contour. From the graph you can get a pretty good idea of what we're going for here. The dashes are clear outliers to a relatively uniform graph. Here I manually set the cutoff to 10, but we could use averages and standard deviation to automatically set a cutoff.

enter image description here

Once we've removed the outlier points, we can just redraw the shape using the contours.

enter image description here

import cv2
import numpy as np

# returns a smoothed contour
def smoothed(contour, dists, cutoff):
    smooth_con = [];
    for a in range(len(dists)):
        if dists[a] < cutoff:
            smooth_con.append(contour[a]);
    return np.asarray(smooth_con);

# get the distance list for an array of points
def distList(src, other):
    dists = [];
    for point in src:
        point = point[0]; # drop extra brackets
        _, dist = closestPoint(point, other);
        dists.append(dist);
    return dists;

# returns squared distance of two points
def squaredDist(one, two):
    dx = one[0] - two[0];
    dy = one[1] - two[1];
    return dx*dx + dy*dy;

# find closest point (just do a linear search)
def closestPoint(point, arr):
    # init tracker vars
    closest = None;
    best_dist = 999999999;

    # linear search
    for other in arr:
        other = other[0]; # remove extra brackets
        dist = squaredDist(point, other);
        if dist < best_dist:
            closest = other;
            best_dist = dist;
    return closest, best_dist;

# load image
img = cv2.imread("circle_dashed.png");

# make a mask
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY);
mask = cv2.inRange(gray, 0, 100);

# get contours # OpenCV 3.4, if you're using OpenCV 2 or 4, it returns (contours, _)
_, contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE);
print(len(contours)); # we have two, inner and outer, no need to dilate first

# split
one = contours[0];
two = contours[1];

# get distances
one_dists = distList(one, two);
two_dists = distList(two, one);

# dump values greater than 10
smooth_one = smoothed(one, one_dists, 10);
smooth_two = smoothed(two, two_dists, 10);

# draw new contour
blank = np.zeros_like(mask);
cv2.drawContours(blank, [smooth_one], -1, (255), -1);
cv2.drawContours(blank, [smooth_two], -1, (0), -1);

# show
cv2.imshow("Image", img);
cv2.imshow("Smooth", blank);
cv2.waitKey(0);
  • I want to remove small dashes from an image

We remove the small dashes but we will also lose some features. We need to:


Binary Mask

  • enter image description here

  • We need to determine the lower and upper bound of the range.

  • As we can see the dashes are removed, but the circle turn into dots.

Dilate the mask

  • enter image description here

  • The dots are visible, still some features are missing

Inverse the mask

  • enter image description here

Code:

# Load libraries
import cv2
import numpy as np

# Load the image
img = cv2.imread("GOETr.png")

# Convert to hsv
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# Get the binary-mask
msk = cv2.inRange(hsv, np.array([0, 0, 214]), np.array([179, 255, 217]))

# Dilate the mask
krn = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
dlt = cv2.dilate(msk, krn, iterations=8)

# Inverse the mask
res = cv2.bitwise_not(dlt)

# Show result
cv2.imshow("res", res)
cv2.waitKey(0)

You need to change the parameters to get different results.

Related