Search code examples
c++imageopencvimage-processingflood-fill

Get floodfill outputs (connected pixels) copied into a new mat


Is there a convenient way to create a new Mat from the outputs of a floodfill operation? I want to get a mat of only the pixels which are detected as connected to the seed pixel and are flood filled technically.

Guess that I executed floodFill method to a certain seed point and only 1/4 of the total pixels were filled as they were connected. I want to copy those pixels only to a new image, which represent only those 1/4 number of pixels and most probably smaller than the original input image.

I did this anyway via a very long,higher time+cpu consuming approach. In brief my approach was giving different colors for different floodfill calls and keep records of pixels of same color in a separate data structure, and etc.

I want to know if there is a direct and easier approach using the Mask created by floodfill or using any other approach.


Solution

  • It's not completely clear what you need exactly. Please take a look at this code, and check if croppedResult is what you want.

    #include <opencv2\opencv.hpp>
    using namespace cv;
    
    int main()
    {
        // Create a test image
        Mat1b img(300, 200, uchar(0));
        circle(img, Point(150, 200), 30, Scalar(255));
        rectangle(img, Rect(30, 50, 40, 20), Scalar(255));
        rectangle(img, Rect(100, 80, 30, 40), Scalar(255));
    
        // Seed inside the circle
        Point seed(160, 220);
    
        // Setting up a mask with correct dimensions
        Mat1b mask;
        copyMakeBorder(img, mask, 1, 1, 1, 1, BORDER_CONSTANT, Scalar(0));
    
        Rect roi;
        uchar seedColor = 200;
        floodFill(img, mask,
            seed + Point(1,1),  // Since the mask is larger than the filled image, a pixel (x,y) in image corresponds to the pixel (x+1,y+1) in the mask
            Scalar(0),          // If FLOODFILL_MASK_ONLY is set, the function does not change the image ( newVal is ignored),
            &roi,               // Minimum bounding rectangle of the repainted domain.
            Scalar(5),          // loDiff
            Scalar(5),          // upDiff 
            4 | (int(seedColor) << 8) | FLOODFILL_MASK_ONLY);
            // 4-connected | with defined seedColor | use only the mask 
    
        // B/W image, where white pixels are the one set to seedColor by floodFill
        Mat1b result = (mask == seedColor);
    
        // Cropped image
        roi += Point(1,1);
        Mat1b croppedResult = result(roi);
    
        return 0;
    }
    

    Test image img:

    enter image description here

    Mask mask after floodFill:

    enter image description here

    Mask result with only seedColor pixels:

    enter image description here

    Cropped mask croppedResult:

    enter image description here


    UPDATE

        // B/W image, where white pixels are the one set to seedColor by floodFill
        Mat1b resultMask = (mask == seedColor);
        Mat1b resultMaskWithoutBorder = resultMask(Rect(1,1,img.cols,img.rows));
    
        Mat3b originalImage;
        cvtColor(img, originalImage, COLOR_GRAY2BGR); // Probably your original image is already 3 channel
    
        Mat3b imgMasked(img.size(), Vec3b(0,0,0));
        originalImage.copyTo(imgMasked, resultMaskWithoutBorder);
    
        Mat3b croppedResult = imgMasked(roi);
        return 0;