Python 完成“的等高线”;“破碎的”;形象

Python 完成“的等高线”;“破碎的”;形象,python,opencv,image-processing,Python,Opencv,Image Processing,我有一个嘈杂的灰度图像,我想分割/遮罩从其余部分跨越图像的大圆弧。我打算遮罩圆弧和圆弧上方的所有像素 为此,我对图像设置了阈值以创建二值图像,并使用cv2.findContours()跟踪圆弧的轮廓 原始图像: 大津阈值后的图像: 阈值+关闭: 闭合图像的轮廓: 如您所见,闭合图像不会创建实心圆弧。闭合会进一步导致圆弧失去形状。绿线是闭合图像的轮廓。蓝线是用approxpolyDP()创建的,但我无法让它工作。有没有更好的方法来掩盖图像中的弧线 这是我的密码: import cv2, m

我有一个嘈杂的灰度图像,我想分割/遮罩从其余部分跨越图像的大圆弧。我打算遮罩圆弧和圆弧上方的所有像素

为此,我对图像设置了阈值以创建二值图像,并使用
cv2.findContours()
跟踪圆弧的轮廓

原始图像:

大津阈值后的图像:

阈值+关闭:

闭合图像的轮廓:

如您所见,闭合图像不会创建实心圆弧。闭合会进一步导致圆弧失去形状。绿线是闭合图像的轮廓。蓝线是用
approxpolyDP()
创建的,但我无法让它工作。有没有更好的方法来掩盖图像中的弧线

这是我的密码:

import cv2, matplotlib
import numpy as np
import matplotlib.pyplot as plt

# read an image
img = cv2.imread('oct.png')

# get gray image and apply Gaussian blur
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5, 5), 0)

# get binary image
ret, thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

# close image to "solidify" it
kernel = np.ones((3,3),np.uint8)
closing = cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,kernel, iterations = 3)


# find contours
(_, contours, _) = cv2.findContours(closing, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

cnt = contours[0]
max_area = cv2.contourArea(cnt)

for cont in contours:
    if cv2.contourArea(cont) > max_area:
        cnt = cont
        max_area = cv2.contourArea(cont)

# define main arc contour approx. and hull
perimeter = cv2.arcLength(cnt, True)
epsilon = 0.1 * cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)

# hull = cv2.convexHull(cnt)

# cv2.isContourConvex(cnt)
imgcopy = np.copy(img)
cv2.drawContours(imgcopy, [cnt], -1, (0, 255, 0), 3)
cv2.drawContours(imgcopy, [approx], -1, (0, 0, 255), 3)

# plot figures
plt.figure(1)
plt.imshow(imgcopy, cmap="gray")
plt.figure(2)
plt.imshow(thresh, cmap="gray")
plt.figure(3)
plt.imshow(closing, cmap="gray")

你在正确的道路上。如果你先稍微平滑一下图像,你的收尾效果可能会更好。我喜欢在形态学操作结束后应用阈值。在这种情况下,关闭和阈值设置的顺序实际上并不重要,但在最后保持阈值设置有助于以后细化预处理。一旦设置阈值,就会丢失大量信息,需要确保保留所有需要的信息,因此在设置阈值之前正确过滤图像非常重要

下面是一个快速尝试,我相信它可以改进:

import matplotlib.pyplot as pp
import PyDIP as dip

img = pp.imread('/Users/cris/Downloads/MipBB.jpg')
img = img[:,:,0]
smooth = dip.Gauss(img, [3])        # Gaussian smoothing with sigma=3
smooth = dip.Closing(smooth, 25)    # Note! This uses a disk SE with diameter 25 pixels
out = dip.Threshold(smooth, 'triangle')[0]
pp.imsave('/Users/cris/Downloads/MipBB_out.png', out)

我使用了三角形阈值法(也称为弦方法,或斜双模阈值,见p.L.Rosin,“单模阈值”,模式识别34(11):2083-20962001),因为它在这种情况下效果更好


代码使用,但我确信您可以使用OpenCV重新创建相同的过程。

我建议使用RANSAC方法,使用弧的边缘信息拟合2个椭圆。只需使用canny或任何其他您认为合适的方法即可获得边缘。当然,这种方法只能在圆弧为椭圆时使用。如果是直线,则可以使用直线拟合零件替换椭圆拟合零件

结果如下:

代码如下:

import numpy as np
import cv2
import random as rp


def ransac_ellipse(iter, srcimg, x, y):

    x_size = np.size(x)
    best_count = x_size
    for i in range(iter):

        base = srcimg.copy()

        # get 5 random points
        r1 = int(rp.random() * x_size)
        r2 = int(rp.random() * x_size)  
        r3 = int(rp.random() * x_size)
        r4 = int(rp.random() * x_size)
        r5 = int(rp.random() * x_size)  

        p1 = (x[r1],y[r1])
        p2 = (x[r2],y[r2])
        p3 = (x[r3],y[r3])
        p4 = (x[r4],y[r4])
        p5 = (x[r5],y[r5])

        p_set = np.array((p1,p2,p3,p4,p5))

        # fit ellipse
        ellipse = cv2.fitEllipse(p_set)

        # remove intersected ellipse
        cv2.ellipse(base,ellipse,(0),1)

        # count remain
        local_count = cv2.countNonZero(base)

        # if count is smaller than best, update
        if local_count < best_count:
            best_count = local_count
            best_ellipse = ellipse


    return best_ellipse

img = cv2.imread('arc.jpg',0)

# Speed up and remove noise
small = cv2.resize(img,(0,0),fx = 0.25,fy = 0.25)

# remove remaining noise
median = cv2.medianBlur(small,21)

# get canny edge
edge = cv2.Canny(median,180,20)


cv2.imshow("Edge",edge)


# obtain the non zero locations
y, x = np.where(edge > 0)
# ransac ellipse to get the outter circle
ellipse1 = ransac_ellipse(10000,edge,x,y)


# remove the outter circle
cv2.ellipse(edge,ellipse1,(0),2)

# ransac ellipse to get the inner circle
y, x = np.where(edge > 0)
ellipse2 = ransac_ellipse(10000,edge,x,y)

disp = cv2.cvtColor(small,cv2.COLOR_GRAY2BGR)
cv2.ellipse(disp,ellipse1,(0,0,255),1)
cv2.ellipse(disp,ellipse2,(0,0,255),1)


cv2.imshow("result",disp)
cv2.waitKey(0)
将numpy导入为np
进口cv2
将随机导入为rp
def ransac_椭圆(iter,srcimg,x,y):
x_尺寸=np.尺寸(x)
最佳数量=x尺寸
对于范围内的i(iter):
base=srcimg.copy()
#获得5个随机点
r1=int(rp.random()*x_大小)
r2=int(rp.random()*x_大小)
r3=int(rp.random()*x_大小)
r4=int(rp.random()*x_大小)
r5=int(rp.random()*x_大小)
p1=(x[r1],y[r1])
p2=(x[r2],y[r2])
p3=(x[r3],y[r3])
p4=(x[r4],y[r4])
p5=(x[r5],y[r5])
p_集=np.数组((p1,p2,p3,p4,p5))
#拟合椭圆
椭圆=cv2.fitEllipse(p_集)
#删除相交椭圆
椭圆(底面,椭圆,(0),1)
#余数
本地计数=cv2。计数非零(基)
#如果计数小于最佳值,则更新
如果本地计数<最佳计数:
最佳计数=本地计数
最佳椭圆=椭圆
返回最佳椭圆
img=cv2.imread('arc.jpg',0)
#加速并消除噪音
小=cv2。调整大小(img,(0,0),fx=0.25,fy=0.25)
#消除剩余噪音
中位数=cv2。中位数(小,21)
#机灵
边缘=cv2.Canny(中值,180,20)
cv2.imshow(“边缘”,边缘)
#获取非零位置
y、 x=np.其中(边>0)
#拉萨克椭圆得到外圆
椭圆E1=ransac_椭圆(10000,边,x,y)
#拆下外圆
cv2.椭圆(边,椭圆1,(0),2)
#用拉萨克椭圆得到内圈
y、 x=np.其中(边>0)
椭圆E2=ransac_椭圆(10000,边,x,y)
disp=cv2.CVT颜色(小,cv2.COLOR_GRAY2BGR)
cv2.椭圆(显示,椭圆1,(0,0255),1)
cv2.椭圆(显示,椭圆2,(0,0255),1)
cv2.imshow(“结果”,显示)
cv2.等待键(0)

一个技巧是在变形闭合的灰色图像上找到精明的边缘,然后在最左边|最右边的点上划线,再次变形,然后在上面找到轮廓@消音器金 , 感谢您花时间回复。您是否使用cv2.Canny()处理Canny边?如果是,您使用了什么阈值?当我尝试同样的方法时,我会得到一系列代表边缘的虚线。另外,在我的代码中,变形是通过相同的过程关闭的吗?@消音器金 另外,在进行第一次变形关闭时,如何消除噪波,但仍然没有模糊的圆弧轮廓?无论何时应用“变形闭合”,它都会扭曲圆弧的轮廓?谢谢我先调整0.5x的大小,然后使用内核5进行medianBlur以减少噪音,然后使用椭圆内核5x5迭代=2进行变形关闭,然后使用canny(50200)获得边缘。这只是一个玩具尝试,太复杂,不太稳定。