【问题标题】:Finding length of each line segment passing through centroid , and how to constrict the line till outer contour查找通过质心的每条线段的长度,以及如何将线限制到外轮廓
【发布时间】:2020-09-28 01:30:20
【问题描述】:

Input Image

Processed Image

import numpy as np
import cv2


img = cv2.imread('Image(i).png', 0)
ret, img =cv2.threshold(img, 128, 255, cv2.THRESH_BINARY)
img_bw = img<=120
img_bw =img_bw.astype('uint8') 
#Fit the ellipses
contours0, hierarchy = cv2.findContours( img.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
outer_ellipse = [cv2.approxPolyDP(contours0[0], 0.1, True)]
inner_ellipse = [cv2.approxPolyDP(contours0[0], 0.1, True)]
ref = np.zeros_like(img_bw)
out=img.copy()
h, w = img.shape[:2]
vis = np.zeros((h, w, 3), np.uint8)
cv2.drawContours( vis, outer_ellipse, -1, (255,0,0), 1)
cv2.drawContours( vis, inner_ellipse, -1, (0,0,255), 1)

##Extract contour of ellipses
cnt_outer = np.vstack(outer_ellipse).squeeze()
cnt_inner = np.vstack(inner_ellipse).squeeze()

#Determine centroid
M = cv2.moments(cnt_inner)
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
print (cx, cy)

#Draw full segment lines 
#cv2.line(vis,(cx,0),(cx,w),(150,0,0),1)

width = img.shape[1]
height = img.shape[0]
N = 20
for i in range(N):
 tmp = np.zeros_like(img_bw)
 theta = i*(360/N)
 theta *= np.pi/180.0
 cv2.line(tmp, (cx, cy),
       (int(cx-np.cos(theta)*w),
         int(cy+np.sin(theta)*h)), (150,0,0), 1)
    
(row,col) = np.nonzero(np.logical_and(tmp, ref))
     
#cv2.line(out, (cx, cy), (col,row),(255,0,0), 1)
    
    # Show the image
    
cv2.imshow('Output', out)
cv2.waitKey(0)
cv2.destroyAllWindows()
  

从处理后的图像中可以看出,穿过质心的线不会被限制到外轮廓并且正在穿过它。

我希望线条在外轮廓处停止,以便我可以测量从质心到外轮廓的距离。 第一个图像是输入图像,第二个图像是通过质心的线段。

【问题讨论】:

    标签: python opencv image-processing contour


    【解决方案1】:

    这是一种可能的方法:

    • 在白色背景上绘制用黑色填充的外部轮廓

    您现在有一个黑色椭圆。然后,无需实际绘制任何东西:

    • 使用skimage.draw.line 获取沿所有半径的点列表
    • 使用 Numpy argmax() 沿半径获取第一个白色像素

    代码如下:

    #!/usr/bin/env python3
    
    import cv2
    import math
    from skimage.draw import line
    import numpy as np
    
    
    # Load image as greyscale
    img = cv2.imread('ellipses.png', cv2.IMREAD_GRAYSCALE)
    _, img = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY)
    h, w = img.shape
    
    #Fit the ellipses
    contours, hierarchy = cv2.findContours( img, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
    outer_ellipse = [cv2.approxPolyDP(contours[0], 0.1, True)]
    
    # Draw outer contour filled with black on white background
    vis = np.zeros_like(img) + 255
    cnt = cv2.drawContours(vis, outer_ellipse, -1, 0, -1)
    
    # Centroid by existing method
    cx, cy = 365, 335
    
    maxThickness = 0
    # Take 10 points along top
    for x in range(0,w,int(w/10)):
        # ... and bottom
        for y in 0, h-1:
            # Get y and x of all pixels between centroid and top and bottom edge
            yy, xx = line(cy, cx, 0, x)
            firstWhiteIndex = np.argmax(vis[yy,xx])
            fx, fy = xx[firstWhiteIndex], yy[firstWhiteIndex]
            # Get length of this radial line
            length = np.sqrt((cx-fx)**2 + (cy-fy)**2)
            # Remember if longer than all others so far seen
            if length > maxThickness:
                maxThickness = length
                fxMax, fyMax = fx, fy
        
    # Take 10 points down left side
    for y in range(0,h,int(h/10)):
        # ... and right
        for x in 0, w-1:
            # Get y and x of all pixels between centroid and left and right edge
            yy, xx = line(cy, cx, 0, x)
            firstWhiteIndex = np.argmax(vis[yy,xx])
            fx, fy = xx[firstWhiteIndex], yy[firstWhiteIndex]
            # Get length of this radial line
            length = np.sqrt((cx-fx)**2 + (cy-fy)**2)
            # Remember if longer than all others so far seen
            if length > maxThickness:
                maxThickness = length
                fxMax, fyMax = fx, fy
    
    print(f'Max thickness: {maxThickness}')
    # Draw thickest radius in mid-grey
    cv2.line(img, (cx,cy), (fxMax, fyMax), 128, 5)
    cv2.imwrite('result.png', img)
    

    【讨论】:

      【解决方案2】:

      我的方法不是最好的,但这是我现在能想到的。

      在上图中画线的同时,修改代码并执行以下操作:

      • 在for循环之前,绘制一个大小相同的二值图像,只包含外轮廓圆。保存此图片以备后用。
      • 现在在 for 循环中,在单独的二进制空白图像中绘制每一行。因此,现在您将有两个图像,第一个图像只有外圈,第二个图像将只包含线条。
      • 现在对这 2 个图像执行 bitwise_and 操作。
      • 现在您将得到一个白色像素,它是直线和外圆的交点。
      • 现在在找到的图像中找到白色像素的坐标,因此您将获得相交点的坐标。

      显然这不是最有效的方式,但它是实时的。另外,请记住,图像中的外圆宽度应至少为 2,线条的宽度应为 1。在某些情况下,您可能会得到多个交点,取其中的任何一个。它们之间的差异只有 1-2 个像素,可以忽略不计。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-07-13
        • 2018-11-04
        • 2020-07-04
        • 2015-06-02
        • 1970-01-01
        • 1970-01-01
        • 2016-09-19
        • 2016-10-21
        相关资源
        最近更新 更多