【问题标题】:Filling in a single colour background in OpenCV在 OpenCV 中填充单色背景
【发布时间】:2013-11-15 06:49:32
【问题描述】:

我正在用 OpenCV 编写一个程序,该程序拍摄寄生虫卵的照片并尝试识别其中的至少大部分。我的问题是我获得最佳效果的输入图像背景很大。我试过填充背景和裁剪它,但是当我这样做时,我得到的鸡蛋选择更差。

我目前考虑的解决方案是使用带有背景的图像然后填充它。感觉这很容易,因为我只想用黑色填充那个圆圈之外的任何东西,但我不确定如何实际执行该操作。如果有人可以指出一种使用方法,或者任何很棒的建议。

这是图片外观的链接:

谢谢!

【问题讨论】:

    标签: python opencv


    【解决方案1】:

    您似乎需要用黑色填充图像的外部,因为这样更容易识别鸡蛋,因为它们将被隔离为白色。

    但是,如果寄生虫卵神奇地显示为蓝色怎么办?我稍后会解释这一点,但这种方法可以让您摆脱每次需要新样本时单击图像的负担待分析。

    我用 C++ 编写了答案,但如果你按照代码的内容进行操作,我相信你可以快速将其翻译成 Python。

    #include <iostream>
    #include <vector>
    
    #include <opencv2/highgui.hpp>
    #include <opencv2/imgproc.hpp>
    
    
    int main(int argc, char* argv[])
    {
        // Load input image (3-channel)
        cv::Mat input = cv::imread(argv[1]);
        if (input.empty())
        {
            std::cout << "!!! failed imread()" << std::endl;
            return -1;
        }   
    
        // Convert the input to grayscale (1-channel)
        cv::Mat grayscale = input.clone();
        cv::cvtColor(input, grayscale, cv::COLOR_BGR2GRAY);
    

    灰度此时的样子:

        // Locate the black circular shape in the grayscale image
        std::vector<std::vector<cv::Point> > contours;
        cv::findContours(grayscale, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);
    
        // Fill the interior of the largest circular shape found with BLUE
        cv::Mat circular_shape = input.clone();
        for (size_t i = 0; i < contours.size(); i++)
        {
            std::vector<cv::Point> cnt = contours[i];
            double area = cv::contourArea(cv::Mat(cnt));        
    
            if (area > 500000 && area < 1000000) // magic numbers to detect the right circular shape
            {
                std::cout << "* Area: " << area << std::endl;
                cv::drawContours(circular_shape, contours, i, cv::Scalar(255, 0, 0), 
                                 cv::FILLED, 8, std::vector<cv::Vec4i>(), 0, cv::Point() );
            }           
        }   
    

    circular_shape此时的样子:

        // Create the output image with the same attributes of the original, i.e. dimensions & 3-channel, so we have a colored result at the end
        cv::Mat output = cv::Mat::zeros(input.size(), input.type());
    
        // copyTo() uses circular_shape as a mask and copies that exact portion of the input to the output
        input.copyTo(output, circular_shape);
    
        cv::namedWindow("Eggs", cv::WINDOW_NORMAL | cv::WINDOW_KEEPRATIO);  
        cv::imshow("Eggs", output);
        cv::resizeWindow("Eggs", 800, 600);
        cv::waitKey(0);
    
        return 0;
    }
    

    窗口上显示的输出是:

    这种解决方案的优点是用户不需要与应用程序交互来促进检测鸡蛋,因为它们已经被涂成蓝色。

    在此之后,可以对输出图像进行其他操作,例如图像其余部分的cv::inRange()isolate colored objects

    因此,为了完整起见,我将添加几行文本/代码来演示从现在开始您可以做些什么来将鸡蛋与图像的其余部分完全隔离:

    // Isolate blue pixels on the output image
    cv::Mat blue_pixels_only;
    cv::inRange(output, cv::Scalar(255, 0, 0), cv::Scalar(255, 0, 0), blue_pixels_only);
    

    blue_pixels_only 在这个阶段的样子:

    // Get rid of pixels on the edges of the shape 
    int erosion_type = cv::MORPH_RECT; // MORPH_RECT, MORPH_CROSS, MORPH_ELLIPSE
    int erosion_size = 3;
    cv::Mat element = cv::getStructuringElement(erosion_type, 
                                                cv::Size(2 * erosion_size + 1, 2 * erosion_size + 1), 
                                                cv::Point(erosion_size, erosion_size));
    cv::erode(blue_pixels_only, blue_pixels_only, element);
    cv::dilate(blue_pixels_only, blue_pixels_only, element);
    
    cv::imshow("Eggs", blue_pixels_only);
    cv::imwrite("blue_pixels_only.png", blue_pixels_only);
    

    blue_pixels_only 在这个阶段的样子:

    【讨论】:

    • 哦哇非常感谢你,这太棒了!不过我仍然有一个问题,那就是我需要忽略中心的大块以获得最佳效果(它是用于收集鸡蛋的设备的一部分,所以最好不要选择它)。它始终位于中心并且相比之下非常大,但并不总是在它的中间有洞。你能推荐一种处理这个问题的方法吗?不需要代码,只需大致了解您将如何处理它。谢谢!
    • 我用额外的注释更新了我的答案,我会写另一条评论告诉你如何在几分钟内摆脱中心部分(我实际上是在编写代码时这样做的)。
    • 嗯,删除中心部分的技术实际上与我们在此处添加最大的圆形形状相同。你只需要弄清楚area 的值是多少。代码中有一个循环,执行以下代码if (area &gt; 500000 &amp;&amp; area &lt; 1000000),对吗?您需要执行相同的逻辑,但使用不同的硬编码值将中心部分涂成绿色。这将让您稍后将绿色区域重新绘制为黑色。随意点击我的答案附近的复选框,将其选为官方问题解决者。
    【解决方案2】:

    解决了我的问题,我创建了一个鼠标事件回调,它用黑色填充我点击的任何内容。下面是我在回调中使用的代码:

    def paint(event, x, y, flags, param):
        global opening                                                                                                                         
    
        if event == cv2.EVENT_LBUTTONDOWN:
            h, w = opening.shape[:2]
            mask = np.zeros((h+2, w+2), np.uint8)
            cv2.floodFill(opening, mask, (x,y), (0, 0, 0)) 
            cv2.imshow("open", opening)
    

    【讨论】:

    • 您可以接受这个作为您问题的答案以关闭它。
    • 是的,我只需要再等一天就可以了。
    猜你喜欢
    • 1970-01-01
    • 2018-07-02
    • 2014-12-13
    • 1970-01-01
    • 1970-01-01
    • 2020-08-02
    • 2016-10-05
    • 2011-01-19
    • 2015-01-15
    相关资源
    最近更新 更多