fitting a circle to a binary image

前端 未结 5 947
Happy的楠姐
Happy的楠姐 2020-12-15 12:48

I have been using skim age\'s thresholding algorithms to get some binary mask. For example, I obtain binary images like this:

相关标签:
5条回答
  • 2020-12-15 13:27

    Here is a solution that tries to make an optimal circle fit via minimization. It soon becomes apparent that the bubble isn't a circle :) Note the use of "regionprops" for easily determining area, centroid, etc. of regions.

    Circle fit to bubble

    from skimage import io, color, measure, draw, img_as_bool
    import numpy as np
    from scipy import optimize
    import matplotlib.pyplot as plt
    
    
    image = img_as_bool(color.rgb2gray(io.imread('bubble.jpg')))
    regions = measure.regionprops(image)
    bubble = regions[0]
    
    y0, x0 = bubble.centroid
    r = bubble.major_axis_length / 2.
    
    def cost(params):
        x0, y0, r = params
        coords = draw.circle(y0, x0, r, shape=image.shape)
        template = np.zeros_like(image)
        template[coords] = 1
        return -np.sum(template == image)
    
    x0, y0, r = optimize.fmin(cost, (x0, y0, r))
    
    import matplotlib.pyplot as plt
    
    f, ax = plt.subplots()
    circle = plt.Circle((x0, y0), r)
    ax.imshow(image, cmap='gray', interpolation='nearest')
    ax.add_artist(circle)
    plt.show()
    
    0 讨论(0)
  • 2020-12-15 13:39

    This should in general give very good and robust results:

    import numpy as np
    from skimage import measure, feature, io, color, draw
    
    img = color.rgb2gray(io.imread("circle.jpg"))
    img = feature.canny(img).astype(np.uint8)
    img[img > 0] = 255
    
    coords = np.column_stack(np.nonzero(img))
    
    model, inliers = measure.ransac(coords, measure.CircleModel,
                                    min_samples=3, residual_threshold=1,
                                    max_trials=500)
    
    print model.params
    
    rr, cc = draw.circle(model.params[0], model.params[1], model.params[2],
                         shape=img.shape)
    
    img[rr, cc] = 128
    
    0 讨论(0)
  • 2020-12-15 13:40

    This is actually a mostly solved problem in image processing. Looks like what you want is a Hough Transform, specifically the circular or elliptical kind. I believe the circular one is a bit less computationally intensive in general.

    Here are some code examples for scikit-image that show pretty much exactly what you're trying to do. And here is a link to the documentation.

    0 讨论(0)
  • 2020-12-15 13:40

    For someone looking to code Mark's suggestion in python, it is quite easy.

    collapsed = np.sum(binary_array, axis=0)
    # These indices will be already sorted
    indices = np.where(collapsed == collapsed.max())[0]
    c = indices[int(round((len(indices) - 1) / 2))]
    
    # Same for rows
    collapsed = np.sum(binary_array, axis=1)
    # These indices will be already sorted
    indices = np.where(collapsed == collapsed.max())[0]
    r = indices[int(round((len(indices) - 1) / 2))]
    
    # circle center is (r, c)
    

    This code takes care when your shape is not spherical and the collapsing along an axes can have multiple maxima. In that case, it takes the middle one (the one that can give you the largest radius when you fit the circle).

    0 讨论(0)
  • 2020-12-15 13:42

    Updated Answer

    Actually, if you use Connected Components Analysis, a.k.a Blob Analysis, you can do it with ImageMagick much more succinctly and accurately like this:

    convert 3J3qz.jpg                                  \
       -define connected-components:verbose=true       \
       -define connected-components:area-threshold=100 \
       -connected-components 8 null:
    

    Output:

    Objects (id: bounding-box centroid area mean-color):
      0: 720x576+0+0 370.6,322.1 213779 srgb(0,0,0)
      13: 488x513+104+0 347.7,250.7 200941 srgb(255,255,255)   <-- answer
    

    which shows your largest blob (the speech bubble) has its centroid at coordinates 347,250 from the top-left corner, and also gives you the bounding box which measures 488x513 pixels and its top-left corner is at 104,0 from which you can derive a radius.

    I can mark these with ImageMagick like this:

    convert 3J3qz.jpg \
       -fill red -draw "rectangle 342,245 352,255" 
       -stroke red -fill none -draw "rectangle 104,0 592,513" 
       out.png
    

    enter image description here

    Original Answer

    As you are curious... you can do what I was suggesting with ImageMagick in two lines:

    convert 3J3qz.jpg -resize 1x! -colorspace gray txt:
    
    # ImageMagick pixel enumeration: 1,576,255,gray
    0,0: (66,66,66)  #424242  gray(66)
    0,1: (70,70,70)  #464646  gray(70)
    0,2: (72,72,72)  #484848  gray(72)
    0,3: (76,76,76)  #4C4C4C  gray(76)
    ...
    0,152: (176,176,176)  #B0B0B0  gray(176)
    0,153: (176,176,176)  #B0B0B0  gray(176)
    0,154: (177,177,177)  #B1B1B1  gray(177)
    0,155: (177,177,177)  #B1B1B1  gray(177)
    0,156: (177,177,177)  #B1B1B1  gray(177)
    0,157: (177,177,177)  #B1B1B1  gray(177)
    0,158: (178,178,178)  #B2B2B2  gray(178)
    0,159: (178,178,178)  #B2B2B2  gray(178)
    0,160: (179,179,179)  #B3B3B3  gray(179)
    0,161: (179,179,179)  #B3B3B3  gray(179)
    0,162: (179,179,179)  #B3B3B3  gray(179)
    0,163: (179,179,179)  #B3B3B3  gray(179)
    0,164: (179,179,179)  #B3B3B3  gray(179)
    0,165: (179,179,179)  #B3B3B3  gray(179)
    0,166: (179,179,179)  #B3B3B3  gray(179)
    0,167: (179,179,179)  #B3B3B3  gray(179)
    0,168: (180,180,180)  #B4B4B4  gray(180)
    0,169: (180,180,180)  #B4B4B4  gray(180)
    0,170: (180,180,180)  #B4B4B4  gray(180)
    0,171: (180,180,180)  #B4B4B4  gray(180)
    0,172: (180,180,180)  #B4B4B4  gray(180)
    0,173: (180,180,180)  #B4B4B4  gray(180)
    0,174: (180,180,180)  #B4B4B4  gray(180)
    0,175: (180,180,180)  #B4B4B4  gray(180)
    0,176: (181,181,181)  #B5B5B5  gray(181)
    0,177: (181,181,181)  #B5B5B5  gray(181)
    0,178: (182,182,182)  #B6B6B6  gray(182)
    0,179: (182,182,182)  #B6B6B6  gray(182)
    0,180: (182,182,182)  #B6B6B6  gray(182)
    0,181: (182,182,182)  #B6B6B6  gray(182)
    0,182: (182,182,182)  #B6B6B6  gray(182)
    0,183: (182,182,182)  #B6B6B6  gray(182)
    0,184: (183,183,183)  #B7B7B7  gray(183)
    0,185: (183,183,183)  #B7B7B7  gray(183)
    0,186: (183,183,183)  #B7B7B7  gray(183)
    0,187: (183,183,183)  #B7B7B7  gray(183)
    0,188: (183,183,183)  #B7B7B7  gray(183)
    0,189: (183,183,183)  #B7B7B7  gray(183)
    0,190: (183,183,183)  #B7B7B7  gray(183)
    0,191: (183,183,183)  #B7B7B7  gray(183)
    0,192: (184,184,184)  #B8B8B8  gray(184)
    0,193: (184,184,184)  #B8B8B8  gray(184)
    0,194: (184,184,184)  #B8B8B8  gray(184)
    0,195: (184,184,184)  #B8B8B8  gray(184)
    0,196: (184,184,184)  #B8B8B8  gray(184)
    0,197: (184,184,184)  #B8B8B8  gray(184)
    0,198: (184,184,184)  #B8B8B8  gray(184)
    0,199: (184,184,184)  #B8B8B8  gray(184)
    0,200: (185,185,185)  #B9B9B9  gray(185)
    0,201: (185,185,185)  #B9B9B9  gray(185)
    0,202: (185,185,185)  #B9B9B9  gray(185)
    0,203: (185,185,185)  #B9B9B9  gray(185)
    0,204: (185,185,185)  #B9B9B9  gray(185)
    0,205: (185,185,185)  #B9B9B9  gray(185)
    0,206: (185,185,185)  #B9B9B9  gray(185)
    0,207: (185,185,185)  #B9B9B9  gray(185)
    0,208: (186,186,186)  #BABABA  gray(186)
    0,209: (186,186,186)  #BABABA  gray(186)
    0,210: (186,186,186)  #BABABA  gray(186)
    0,211: (186,186,186)  #BABABA  gray(186)
    0,212: (185,185,185)  #B9B9B9  gray(185)
    0,213: (186,186,186)  #BABABA  gray(186)
    0,214: (186,186,186)  #BABABA  gray(186)
    0,215: (186,186,186)  #BABABA  gray(186)
    0,216: (186,186,186)  #BABABA  gray(186)
    0,217: (186,186,186)  #BABABA  gray(186)
    0,218: (186,186,186)  #BABABA  gray(186)
    0,219: (186,186,186)  #BABABA  gray(186)
    0,220: (186,186,186)  #BABABA  gray(186)
    0,221: (186,186,186)  #BABABA  gray(186)
    0,222: (186,186,186)  #BABABA  gray(186)
    0,223: (186,186,186)  #BABABA  gray(186)
    0,224: (186,186,186)  #BABABA  gray(186)
    0,225: (186,186,186)  #BABABA  gray(186)
    0,226: (186,186,186)  #BABABA  gray(186)
    0,227: (186,186,186)  #BABABA  gray(186)
    0,228: (187,187,187)  #BBBBBB  gray(187)
    0,229: (187,187,187)  #BBBBBB  gray(187)
    0,230: (187,187,187)  #BBBBBB  gray(187)
    0,231: (187,187,187)  #BBBBBB  gray(187)
    0,232: (187,187,187)  #BBBBBB  gray(187)
    0,233: (187,187,187)  #BBBBBB  gray(187)
    0,234: (187,187,187)  #BBBBBB  gray(187) <---- max=234
    0,235: (187,187,187)  #BBBBBB  gray(187)
    0,236: (187,187,187)  #BBBBBB  gray(187)
    0,237: (187,187,187)  #BBBBBB  gray(187)
    0,238: (187,187,187)  #BBBBBB  gray(187)
    0,239: (187,187,187)  #BBBBBB  gray(187)
    0,240: (187,187,187)  #BBBBBB  gray(187)
    0,241: (187,187,187)  #BBBBBB  gray(187)
    0,242: (187,187,187)  #BBBBBB  gray(187)
    0,243: (187,187,187)  #BBBBBB  gray(187)
    0,244: (187,187,187)  #BBBBBB  gray(187)
    0,245: (187,187,187)  #BBBBBB  gray(187)
    0,246: (187,187,187)  #BBBBBB  gray(187)
    0,247: (187,187,187)  #BBBBBB  gray(187)
    0,248: (187,187,187)  #BBBBBB  gray(187)
    0,249: (187,187,187)  #BBBBBB  gray(187)
    0,250: (187,187,187)  #BBBBBB  gray(187)
    ...
    0,573: (0,0,0)  #000000  gray(0)
    0,574: (0,0,0)  #000000  gray(0)
    0,575: (0,0,0)  #000000  gray(0)
    

    And the other side

    convert 3J3qz.jpg -resize x1! -colorspace gray txt: 
    
    # ImageMagick pixel enumeration: 720,1,255,gray
    0,0: (0,0,0)  #000000  gray(0)
    1,0: (0,0,0)  #000000  gray(0)
    2,0: (0,0,0)  #000000  gray(0)
    3,0: (0,0,0)  #000000  gray(0)
    4,0: (0,0,0)  #000000  gray(0)
    ...
    241,0: (219,219,219)  #DBDBDB  gray(219)
    242,0: (220,220,220)  #DCDCDC  gray(220)
    243,0: (220,220,220)  #DCDCDC  gray(220)
    244,0: (221,221,221)  #DDDDDD  gray(221)
    245,0: (222,222,222)  #DEDEDE  gray(222)
    246,0: (223,223,223)  #DFDFDF  gray(223)
    247,0: (223,223,223)  #DFDFDF  gray(223)
    248,0: (224,224,224)  #E0E0E0  gray(224)
    249,0: (224,224,224)  #E0E0E0  gray(224)
    250,0: (225,225,225)  #E1E1E1  gray(225)
    251,0: (227,227,227)  #E3E3E3  gray(227)
    252,0: (229,229,229)  #E5E5E5  gray(229)
    253,0: (230,230,230)  #E6E6E6  gray(230)
    254,0: (231,231,231)  #E7E7E7  gray(231)
    255,0: (232,232,232)  #E8E8E8  gray(232)  <--- max=255
    256,0: (231,231,231)  #E7E7E7  gray(231)
    257,0: (231,231,231)  #E7E7E7  gray(231)
    258,0: (231,231,231)  #E7E7E7  gray(231)
    259,0: (231,231,231)  #E7E7E7  gray(231)
    260,0: (230,230,230)  #E6E6E6  gray(230)
    261,0: (230,230,230)  #E6E6E6  gray(230)
    262,0: (230,230,230)  #E6E6E6  gray(230)
    263,0: (230,230,230)  #E6E6E6  gray(230)
    264,0: (230,230,230)  #E6E6E6  gray(230)
    265,0: (230,230,230)  #E6E6E6  gray(230)
    266,0: (230,230,230)  #E6E6E6  gray(230)
    267,0: (230,230,230)  #E6E6E6  gray(230)
    268,0: (229,229,229)  #E5E5E5  gray(229)
    269,0: (230,230,230)  #E6E6E6  gray(230)
    270,0: (229,229,229)  #E5E5E5  gray(229)
    271,0: (229,229,229)  #E5E5E5  gray(229)
    272,0: (229,229,229)  #E5E5E5  gray(229)
    273,0: (229,229,229)  #E5E5E5  gray(229)
    274,0: (229,229,229)  #E5E5E5  gray(229)
    275,0: (229,229,229)  #E5E5E5  gray(229)
    276,0: (229,229,229)  #E5E5E5  gray(229)
    277,0: (229,229,229)  #E5E5E5  gray(229)
    278,0: (229,229,229)  #E5E5E5  gray(229)
    279,0: (229,229,229)  #E5E5E5  gray(229)
    280,0: (229,229,229)  #E5E5E5  gray(229)
    281,0: (229,229,229)  #E5E5E5  gray(229)
    282,0: (229,229,229)  #E5E5E5  gray(229)
    283,0: (229,229,229)  #E5E5E5  gray(229)
    284,0: (229,229,229)  #E5E5E5  gray(229)
    285,0: (229,229,229)  #E5E5E5  gray(229)
    286,0: (229,229,229)  #E5E5E5  gray(229)
    287,0: (230,230,230)  #E6E6E6  gray(230)
    288,0: (230,230,230)  #E6E6E6  gray(230)
    289,0: (230,230,230)  #E6E6E6  gray(230)
    290,0: (230,230,230)  #E6E6E6  gray(230)
    291,0: (230,230,230)  #E6E6E6  gray(230)
    292,0: (230,230,230)  #E6E6E6  gray(230)
    293,0: (230,230,230)  #E6E6E6  gray(230)
    294,0: (230,230,230)  #E6E6E6  gray(230)
    295,0: (231,231,231)  #E7E7E7  gray(231)
    296,0: (231,231,231)  #E7E7E7  gray(231)
    297,0: (231,231,231)  #E7E7E7  gray(231)
    298,0: (231,231,231)  #E7E7E7  gray(231)
    299,0: (231,231,231)  #E7E7E7  gray(231)
    300,0: (231,231,231)  #E7E7E7  gray(231)
    301,0: (231,231,231)  #E7E7E7  gray(231)
    302,0: (231,231,231)  #E7E7E7  gray(231)
    303,0: (231,231,231)  #E7E7E7  gray(231)
    304,0: (232,232,232)  #E8E8E8  gray(232)
    305,0: (231,231,231)  #E7E7E7  gray(231)
    306,0: (231,231,231)  #E7E7E7  gray(231)
    307,0: (231,231,231)  #E7E7E7  gray(231)
    308,0: (231,231,231)  #E7E7E7  gray(231)
    309,0: (232,232,232)  #E8E8E8  gray(232)
    310,0: (232,232,232)  #E8E8E8  gray(232)
    311,0: (232,232,232)  #E8E8E8  gray(232)
    312,0: (233,233,233)  #E9E9E9  gray(233)
    313,0: (232,232,232)  #E8E8E8  gray(232)
    314,0: (232,232,232)  #E8E8E8  gray(232)
    315,0: (232,232,232)  #E8E8E8  gray(232)
    316,0: (232,232,232)  #E8E8E8  gray(232)
    317,0: (232,232,232)  #E8E8E8  gray(232)
    318,0: (232,232,232)  #E8E8E8  gray(232)
    319,0: (232,232,232)  #E8E8E8  gray(232)
    320,0: (232,232,232)  #E8E8E8  gray(232)
    321,0: (233,233,233)  #E9E9E9  gray(233)
    322,0: (233,233,233)  #E9E9E9  gray(233)
    323,0: (233,233,233)  #E9E9E9  gray(233)
    324,0: (233,233,233)  #E9E9E9  gray(233)
    325,0: (233,233,233)  #E9E9E9  gray(233)
    326,0: (233,233,233)  #E9E9E9  gray(233)
    327,0: (233,233,233)  #E9E9E9  gray(233)
    328,0: (233,233,233)  #E9E9E9  gray(233)
    329,0: (233,233,233)  #E9E9E9  gray(233)
    330,0: (233,233,233)  #E9E9E9  gray(233)
    331,0: (233,233,233)  #E9E9E9  gray(233)
    332,0: (233,233,233)  #E9E9E9  gray(233)
    333,0: (233,233,233)  #E9E9E9  gray(233)
    334,0: (233,233,233)  #E9E9E9  gray(233)
    335,0: (233,233,233)  #E9E9E9  gray(233)
    336,0: (233,233,233)  #E9E9E9  gray(233)
    337,0: (233,233,233)  #E9E9E9  gray(233)
    338,0: (233,233,233)  #E9E9E9  gray(233)
    339,0: (233,233,233)  #E9E9E9  gray(233)
    340,0: (233,233,233)  #E9E9E9  gray(233)
    341,0: (233,233,233)  #E9E9E9  gray(233)
    342,0: (233,233,233)  #E9E9E9  gray(233)
    343,0: (233,233,233)  #E9E9E9  gray(233)
    344,0: (233,233,233)  #E9E9E9  gray(233)
    345,0: (233,233,233)  #E9E9E9  gray(233)
    346,0: (233,233,233)  #E9E9E9  gray(233)
    347,0: (233,233,233)  #E9E9E9  gray(233)
    348,0: (233,233,233)  #E9E9E9  gray(233)
    349,0: (233,233,233)  #E9E9E9  gray(233)
    350,0: (233,233,233)  #E9E9E9  gray(233)
    351,0: (233,233,233)  #E9E9E9  gray(233)
    352,0: (233,233,233)  #E9E9E9  gray(233)
    353,0: (233,233,233)  #E9E9E9  gray(233)
    354,0: (233,233,233)  #E9E9E9  gray(233)
    ...
    717,0: (0,0,0)  #000000  gray(0)
    718,0: (0,0,0)  #000000  gray(0)
    719,0: (0,0,0)  #000000  gray(0)
    
    0 讨论(0)
提交回复
热议问题