【问题标题】:Using a mask with an adaptive threshold?使用具有自适应阈值的掩码?
【发布时间】:2012-03-23 15:38:46
【问题描述】:

我正在使用 OpenCV-2.3 API 用 C++ 编写一个小程序。 我在使用非矩形蒙版处理自适应阈值时遇到问题。

到目前为止,我正在对整个图像执行自适应阈值并随后进行遮罩。我意识到,就我而言,这是一个错误,因为蒙版像素将用于计算我感兴趣的像素的阈值(而我只是想将前者排除在分析之外)...... 但是,与 cv::norm 等函数不同,cv::adaptiveThreshold 似乎并不明确支持掩码。

您知道任何明显的解决方案或解决方法吗? 非常感谢您的建议, 昆汀

【问题讨论】:

    标签: c++ opencv


    【解决方案1】:

    我已经编写了一些 Python(抱歉不是 c++)代码,这些代码将允许进行屏蔽自适应阈值处理。它不是很快,但它可以满足您的需求,并且您可以将它用作 C++ 代码的基础。它的工作原理如下:

    1. 将图像中的蒙版像素设置为零。
    2. 确定卷积块内每个像素的未屏蔽邻居的数量。
    3. 执行卷积,并通过块内未屏蔽的邻居数对其进行平均。这会产生像素邻域块内的平均值。
    4. 阈值,通过将图像与平均邻域值进行比较,mean_conv
    5. 重新添加图像的被屏蔽(非阈值)部分。

    图像显示,初始图像,蒙版,最终处理的图像。

    代码如下:

    import cv
    import numpy
    from scipy import signal
    
    def thresh(a, b, max_value, C):
        return max_value if a > b - C else 0
    
    def mask(a,b):
        return a if b > 100 else 0
    
    def unmask(a,b,c):
        return b if c > 100 else a
    
    v_unmask = numpy.vectorize(unmask)
    v_mask = numpy.vectorize(mask)
    v_thresh = numpy.vectorize(thresh)
    
    def block_size(size):
        block = numpy.ones((size, size), dtype='d')
        block[(size - 1 ) / 2, (size - 1 ) / 2] = 0
        return block
    
    def get_number_neighbours(mask,block):
        '''returns number of unmasked neighbours of every element within block'''
        mask = mask / 255.0
        return signal.convolve2d(mask, block, mode='same', boundary='symm')
    
    def masked_adaptive_threshold(image,mask,max_value,size,C):
        '''thresholds only using the unmasked elements'''
        block = block_size(size)
        conv = signal.convolve2d(image, block, mode='same', boundary='symm')
        mean_conv = conv / get_number_neighbours(mask,block)
        return v_thresh(image, mean_conv, max_value,C)
    
    image = cv.LoadImageM("image.png", cv.CV_LOAD_IMAGE_GRAYSCALE)
    mask = cv.LoadImageM("mask.png", cv.CV_LOAD_IMAGE_GRAYSCALE)
    
    #change the images to numpy arrays
    original_image = numpy.asarray(image)
    mask = numpy.asarray(mask)
    # Masks the image, by removing all masked pixels.
    # Elements for mask > 100, will be processed
    image = v_mask(original_image, mask)
    # convolution parameters, size and C are crucial. See discussion in link below.
    image = masked_adaptive_threshold(image,mask,max_value=255,size=7,C=5)
    # puts the original masked off region of the image back
    image = v_unmask(original_image, image, mask)
    #change to suitable type for opencv
    image = image.astype(numpy.uint8)
    #convert back to cvmat
    image = cv.fromarray(image)
    
    cv.ShowImage('image', image)
    #cv.SaveImage('final.png',image)
    cv.WaitKey(0)
    

    写完这篇文章后,我发现this great link 有很多图像示例,解释得很好,我在上面的示例中使用了他们的文本图像。

    注意。 scipy signal.convolve2d() 似乎不尊重 Numpy 掩码,因此上述解决方法是必要的。

    【讨论】:

    • 非常感谢您的回答。我正在调查你的建议。昆汀
    • @Quentin Geissmann - 你是否让它在你的 c++ 代码中工作?
    • 我想我知道如何在不让它变得很慢的情况下实现......但尚未实现。 :) 谢谢你
    • 您可以通过使用 signal.fftconvolve 来加快速度。或者,甚至更快,通过与 1-only 内核(它是可分离的,因此可以使用 scipy.ndimage.filters.correlate1d )进行卷积,然后从 conv 中减去 image 并从数字邻居中减去 mask 以返回原始内核。与 fftconvolve 相比,我得到了 3 倍的加速(15 秒对 45 秒)。您使用 convolve2d 的原始版本没有在合理的时间内完成。
    • 一些使用correlate1d的Python代码见这里:gist.github.com/neothemachine/9880622 还包括昆汀的回答中使用模糊的方法。
    【解决方案2】:

    根据您的建议,在阅读了您的链接后,我编写了这个小 C++ 函数: 这仅比自适应阈值慢 1.5,但我可能会改进它。

    void adaptiveThresholdMask(const cv::Mat src,cv::Mat &dst, double maxValue,      cv::Mat mask, int thresholdType, int blockSize, double C){
    cv::Mat img, invertMask, noN, conv,kernel(cv::Size(blockSize,blockSize),CV_32F);
    
    /* Makes a image copy of the source image*/
    src.copyTo(img);
    
    /* Negates the mask*/
    cv::bitwise_not(mask,invertMask);
    
    /* Sets to 0 all pixels out of the mask*/
    img = img-invertMask;
    /* The two following tasks are both intensive and
     * can be done in parallel (here with OpenMP)*/
    #pragma omp parallel sections
    {
        {
            /* Convolves "img" each pixels takes the average value of all the pixels in blocksize*/
            cv::blur(img,conv,cv::Size(blockSize,blockSize));
        }
        #pragma omp section
        {
            /* The result of bluring "mask" is proportional to the number of neighbours */
            cv::blur(mask,noN,cv::Size(blockSize,blockSize));
        }
    }
    
     /* Makes a ratio between the convolved image and the number of 
     * neighbours and subtracts from the original image*/
     if(thresholdType==cv::THRESH_BINARY_INV){
        img=255*(conv/noN)-img;
        }
    else{
        img=img-255*(conv/noN);
        }
    
    /* Thresholds by the user defined C*/
    cv::threshold(img,dst,C,maxValue,cv::THRESH_BINARY);
    
    /* We do not want to keep pixels outside of the mask*/
    cv::bitwise_and(mask,dst,dst);
    
    }
    

    再次感谢您

    【讨论】:

    • 不错的一个 :) 我想我可以大大加快我的 python 代码的速度,我会在某个时候尝试解决它..
    • 我不明白这个解决方案。你能详细说明这个加速版本是如何工作的吗?我试图将它移植到 opencv-python 但没有成功
    • 这是一个非常好的解决方案,而且速度非常快(在 Python 中测试),尽管当您的图像非常暗时它会变得有点不准确,因为由于模糊操作,您会在整数分辨率上划伤。
    猜你喜欢
    • 2013-09-08
    • 2016-01-07
    • 2019-10-25
    • 2013-01-23
    • 1970-01-01
    • 2014-04-03
    • 1970-01-01
    • 1970-01-01
    • 2021-04-29
    相关资源
    最近更新 更多