Image processing - eliminate arc-like smears

2019-08-07 09:49发布

I am dealing with this kind of image

(upper is post-processed) enter image description here

(lower is raw) So, first I converted the grayscale image into pure black and white binary image. I am interested in detecting the white blobs, and want to get rid of the arc-like smears in the corners. How can I do that?

I general, I know that my targets are almost circular in shape, not too big, but I want to encode something that automatically gets rid of everything else, like the lighter arcs in the upper left and right corners.

How would I do this in python, ideally skimage?

1条回答
仙女界的扛把子
2楼-- · 2019-08-07 10:36

You can just detect circle of the right size with skimage's methods hough_circle and hough_circle_peaks and cut it out.

Here I adapted my previous answer to your other question to do this:

# skimage version 0.14.0

import math
import numpy as np
import matplotlib.pyplot as plt

from skimage import color
from skimage.io import imread
from skimage.transform import hough_circle, hough_circle_peaks
from skimage.feature import canny
from skimage.draw import circle
from skimage.util import img_as_ubyte

INPUT_IMAGE = 'dish1.png' # input image name
BEST_COUNT = 1            # how many circles to detect (one dish)
MIN_RADIUS = 100          # min radius of the Petri dish
MAX_RADIUS = 122          # max radius of the Petri dish (in pixels)
LARGER_THRESH = 1.2       # circle is considered significantly larger than another one if its radius is at least so much bigger
OVERLAP_THRESH = 0.1      # circles are considered overlapping if this part of the smaller circle is overlapping

def circle_overlap_percent(centers_distance, radius1, radius2):
    '''
    Calculating the percentage area overlap between circles
    See Gist for comments:
        https://gist.github.com/amakukha/5019bfd4694304d85c617df0ca123854
    '''
    R, r = max(radius1, radius2), min(radius1, radius2)
    if centers_distance >= R + r:
        return 0.0
    elif R >= centers_distance + r:
        return 1.0
    R2, r2 = R**2, r**2
    x1 = (centers_distance**2 - R2 + r2 )/(2*centers_distance)
    x2 = abs(centers_distance - x1)
    y = math.sqrt(R2 - x1**2)
    a1 = R2 * math.atan2(y, x1) - x1*y
    if x1 <= centers_distance:
        a2 = r2 * math.atan2(y, x2) - x2*y
    else:
        a2 = math.pi * r2 - a2
    overlap_area = a1 + a2
    return overlap_area / (math.pi * r2)

def circle_overlap(c1, c2):
    d = math.sqrt((c1[0]-c2[0])**2 + (c1[1]-c2[1])**2)
    return circle_overlap_percent(d, c1[2], c2[2])

def inner_circle(cs, c, thresh):
    '''Is circle `c` is "inside" one of the `cs` circles?'''
    for dc in cs:
        # if new circle is larger than existing -> it's not inside
        if c[2] > dc[2]*LARGER_THRESH: continue
        # if new circle is smaller than existing one...
        if circle_overlap(dc, c)>thresh:
            # ...and there is a significant overlap -> it's inner circle
            return True
    return False

# Load picture and detect edges
image = imread(INPUT_IMAGE, 1)
image = img_as_ubyte(image)
edges = canny(image, sigma=3, low_threshold=10, high_threshold=50)

# Detect circles of specific radii
hough_radii = np.arange(MIN_RADIUS, MAX_RADIUS, 2)
hough_res = hough_circle(edges, hough_radii)

# Select the most prominent circles (in order from best to worst)
accums, cx, cy, radii = hough_circle_peaks(hough_res, hough_radii)

# Determine BEST_COUNT circles to be drawn
drawn_circles = []
for crcl in zip(cy, cx, radii):
    # Do not draw circles if they are mostly inside better fitting ones
    if not inner_circle(drawn_circles, crcl, OVERLAP_THRESH):
        # A good circle found: exclude smaller circles it covers
        i = 0
        while i<len(drawn_circles):
            if circle_overlap(crcl, drawn_circles[i]) > OVERLAP_THRESH:
                t = drawn_circles.pop(i)
            else:
                i += 1
        # Remember the new circle
        drawn_circles.append(crcl)
    # Stop after have found more circles than needed
    if len(drawn_circles)>BEST_COUNT:
        break

drawn_circles = drawn_circles[:BEST_COUNT]

# Draw circle and cut it out
colors  = [(250, 0, 0), (0, 250, 0), (0, 0, 250)]
fig, ax = plt.subplots(ncols=1, nrows=3, figsize=(10, 4))
color_image = color.gray2rgb(image)
black_image = np.zeros_like(image)
for center_y, center_x, radius in drawn_circles[:1]:
    circy, circx = circle(center_y, center_x, radius, image.shape)
    color = colors.pop(0)
    color_image[circy, circx] = color
    black_image[circy, circx] = image[circy, circx]
    colors.append(color)

# Output
ax[0].imshow(image, cmap=plt.cm.gray)        # original image
ax[1].imshow(color_image)                    # detected circle
ax[2].imshow(black_image, cmap=plt.cm.gray)  # cutout
plt.show()

Output:

Original Petri dish image, detected dish, cutout

Again, as in my previous answer, most of the code here is doing "hierarchy" computation to find the biggest best fitting circle.

查看更多
登录 后发表回答