Python图像处理-如何删除特定轮廓并将值与周围像素混合?

Python图像处理-如何删除特定轮廓并将值与周围像素混合?,python,opencv,image-processing,computer-vision,Python,Opencv,Image Processing,Computer Vision,我正在做一个深度图像的项目。但我的深度相机存在噪音和像素读取失败的问题。有些点和轮廓(尤其是边)的值为零。如何忽略此零值并将其与周围的值混合? 我尝试了膨胀和侵蚀(变形图像处理),但仍然无法得到正确的组合。它确实消除了一些噪声,但我只需要在所有点上去掉零 图像示例: 零值是最深的蓝色(我使用的是colormap) 为了说明我想做什么,请参考这张拙劣的绘画图: 我想去除黑点(例如,黑点值为0或某个值),并将其与周围环境混合。 是的,我可以使用np.where或类似的函数来定位该点,但我不知道如

我正在做一个深度图像的项目。但我的深度相机存在噪音和像素读取失败的问题。有些点和轮廓(尤其是边)的值为零。如何忽略此零值并将其与周围的值混合? 我尝试了
膨胀
侵蚀
(变形图像处理),但仍然无法得到正确的组合。它确实消除了一些噪声,但我只需要在所有点上去掉零

图像示例:

零值是最深的蓝色(我使用的是colormap)

为了说明我想做什么,请参考这张拙劣的绘画图:

我想去除黑点(例如,黑点值为0或某个值),并将其与周围环境混合。 是的,我可以使用
np.where
或类似的函数来定位该点,但我不知道如何混合它。也许需要一个过滤器?我需要在一个流中这样做,所以我需要一个相当快的过程,也许10-20 fps就可以了。提前谢谢你

更新:

除了修复,还有别的方法吗?我已经找过各种各样的修补,但我不需要像雕刻那样复杂。我只需要将它与简单的直线、曲线或形状和1D混合。我认为修复是一种过分的做法。此外,我需要它们足够快,可以用于10-20 fps的视频流,甚至更好。

两种情况下的图像修复都太慢,这是一个已知的问题。 我不认为不深入研究算法就可以加快速度

如果您真的对“传统”(即没有深入学习)修复算法感兴趣,并准备实施一种修复算法,我强烈建议您看看。该算法在视觉上的表现与双谐波方法相当或优于双谐波方法,并且,一旦正确地转换为代码,即使对于大型图像,也可以非常快地执行

事实上,双谐波修复的实现在性能上远远不是最优的。当前版本非常简单,因为它是以nD输入支持为主要目标编写的

实施的可能改进包括但不限于:

  • 预生成双平面图(每个蒙版像素单独计算)

  • 将掩码划分为独立的连接区域(构建atm单个巨大矩阵)

  • Cythonization(不确定我是否能够在Cython atm中编写nD代码)

  • 更快的linsolve

  • 并行实现

  • 作为中间解决方案,您可以尝试为2D(+颜色)实现更快的Cythonized版本(同时考虑上面的其他要点),因为这是最常见的用例


    如果您正在寻找一种“足够快和好”的修复方法,请查看GitHub上众多的修复解决方案。

    也许使用NaN调整高斯滤波器是足够快和好的?当你把零点/黑点看作是NANS时,方法也适用于较大的黑色区域。

    #导入模块
    将matplotlib.pyplot作为plt导入
    将numpy作为np导入
    进口撇渣
    导入撇渣过滤器
    #播种
    np.随机种子(42)
    #创建虚拟图像
    #(光滑,外观更逼真)
    尺寸=50
    img=np.random.rand(大小,大小)
    img=撇除.过滤器.高斯(img,sigma=5)
    #创建虚拟缺失点/NaN点
    掩码=np.rand.rand(大小,大小)<0.02
    img[mask]=np.nan
    #定义并应用调整后的高斯滤波器
    # (https://stackoverflow.com/a/36307291/5350621)
    def(U,sigma=1,truncate=4.0):
    V=U.copy()
    V[np.isnan(U)]=0
    VV=撇除.过滤器.高斯(V,sigma=sigma,truncate=truncate)
    W=0*U.复制()+1
    W[np.isnan(U)]=0
    WW=skimage.filters.gaussian(W,sigma=sigma,truncate=truncate)
    返回VV/WW
    平滑=高斯(img,sigma=1,截断=4.0)
    #不平滑整个图像,但仅复制平滑的点
    fill=img.copy()
    填充[遮罩]=平滑[遮罩]
    #绘图结果
    vmin,vmax=np.nanmin(img),np.nanmax(img)
    方面='auto'
    小地块(121)
    plt.title('原始图像(白色=NaN)')
    plt.imshow(img,aspect=aspect,vmin=vmin,vmax=vmax)
    打印轴(“关闭”)
    小地块(122)
    打印标题(“填充图像”)
    plt.imshow(fill,aspect=aspect,vmin=vmin,vmax=vmax)
    打印轴(“关闭”)
    
    在Python/OpenCV中有一种方法可以做到这一点

    使用中值滤波填充孔。

    • 读取输入
    • 变灰
    • 制作遮罩的阈值(斑点为黑色)
    • 反转遮罩(斑点为白色)
    • 从反转遮罩中找到最大的光斑轮廓周长,并使用该值的一半作为中值滤波器大小
    • 对图像应用中值滤波
    • 将掩码应用于输入
    • 将反转遮罩应用于中值滤波图像
    • 将两者相加,形成结果
    • 保存结果
    输入:


    阈值图像作为遮罩:

    反向遮罩:

    中值滤波图像:

    遮罩图像:

    遮罩中值滤波图像:

    结果:


    看看修复算法。OpenCV实现了一些。在Skimage中也有修复算法。@GeorgeProfenza第一次听到这个术语,我会试试it@fmw42谢谢你,我会把它和opencv修补软件进行比较。谢谢你的推荐,我会试试的!除了修补,还有别的办法吗?我已经找过各种各样的修补,但我不需要像雕刻那样复杂。我只需要将它与简单的直线、曲线或形状和1D混合。我认为修复是一种过分的做法。此外,我需要他们足够快,可以用于视频流10-20 fps,甚至更好。聪明。比修补快得多。
    # import modules
    import matplotlib.pyplot as plt
    import numpy as np
    import skimage
    import skimage.filters
    
    # set seed
    np.random.seed(42)
    
    # create dummy image
    # (smooth for more realisitc appearance)
    size = 50
    img = np.random.rand(size, size)
    img = skimage.filters.gaussian(img, sigma=5)
    
    # create dummy missing/NaN spots
    mask = np.random.rand(size, size) < 0.02
    img[mask] = np.nan
    
    # define and apply NaN-adjusted Gaussian filter
    # (https://stackoverflow.com/a/36307291/5350621)
    def nangaussian(U, sigma=1, truncate=4.0):
        V = U.copy()
        V[np.isnan(U)] = 0
        VV = skimage.filters.gaussian(V, sigma=sigma, truncate=truncate)
        W = 0*U.copy()+1
        W[np.isnan(U)] = 0
        WW = skimage.filters.gaussian(W, sigma=sigma, truncate=truncate)
        return VV/WW
    smooth = nangaussian(img, sigma=1, truncate=4.0)
    
    # do not smooth full image but only copy smoothed NaN spots
    fill = img.copy()
    fill[mask] = smooth[mask]
    
    # plot results
    vmin, vmax = np.nanmin(img), np.nanmax(img)
    aspect = 'auto'
    plt.subplot(121)
    plt.title('original image (white = NaN)')
    plt.imshow(img, aspect=aspect, vmin=vmin, vmax=vmax)
    plt.axis('off')
    plt.subplot(122)
    plt.title('filled image')
    plt.imshow(fill, aspect=aspect, vmin=vmin, vmax=vmax)
    plt.axis('off')
    
    import cv2
    import numpy as np
    import math
    
    # read image
    img = cv2.imread('spots.png')
    
    # convert to gray
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # threshold 
    mask = cv2.threshold(gray,0,255,cv2.THRESH_BINARY)[1]
    
    # erode mask to make black regions slightly larger
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
    mask = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel)
    
    
    # make mask 3 channel
    mask = cv2.merge([mask,mask,mask])
    
    # invert mask
    mask_inv = 255 - mask
    
    # get area of largest contour
    contours = cv2.findContours(mask_inv[:,:,0], cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    contours = contours[0] if len(contours) == 2 else contours[1]
    perimeter_max = 0
    for c in contours:
        perimeter = cv2.arcLength(c, True)
        if perimeter > perimeter_max:
            perimeter_max = perimeter
    
    # approx radius from largest area
    radius = int(perimeter_max/2) + 1
    if radius % 2 == 0:
        radius = radius + 1
    print(radius)
    
    # median filter input image
    median = cv2.medianBlur(img, radius)
    
    # apply mask to image
    img_masked = cv2.bitwise_and(img, mask)
    
    # apply inverse mask to median
    median_masked = cv2.bitwise_and(median, mask_inv)
    
    # add together
    result = cv2.add(img_masked,median_masked)
    
    # save results
    cv2.imwrite('spots_mask.png', mask)
    cv2.imwrite('spots_mask_inv.png', mask_inv)
    cv2.imwrite('spots_median.png', median)
    cv2.imwrite('spots_masked.png', img_masked)
    cv2.imwrite('spots_median_masked.png', median_masked)
    cv2.imwrite('spots_removed.png', result)
    
    cv2.imshow('mask', mask)
    cv2.imshow('mask_inv', mask_inv )
    cv2.imshow('median', median)
    cv2.imshow('img_masked', img_masked)
    cv2.imshow('median_masked', median_masked)
    cv2.imshow('result', result)
    cv2.waitKey(0)
    cv2.destroyAllWindows()