SemtexB
SemtexB

Reputation: 660

Create mask from color Image in C++ (Superimposing a colored image mask)

I've wrote a code which detects squares (white) in realtime and draws a frame around it. Each side of length l of the squares is divided in 7 parts. Then I draw a line of length h=l/7 at each of the six points evolving from the deviation perpendicular to the side of the triangle (blue). The corners are marked in red. It then looks something like this:

enter image description here

For the drawing of the blue lines and circles I have a 3 Channel (CV_8UC3) matrix drawing, which is zero everywhere except at the positions of the red, blue and white lines. Then what I do to lay this matrix over my webcam image is using the addWeighted function of opencv. addWeighted( drawing, 1, webcam_img, 1, 0.0, dst); (Description for addWeighted here). But then, as you can see I get the effect that the colors for my dashes and circles are wrong outside the black area (probably also not correct inside the black area, but better there). It makes totally sense why it happens, as it just adds the matrices with a weight.

I'd like to have the matrix drawing with the correct colors over my image. Problem is, I don't no how to fix it. I somehow need a mask drawing_mask where my dashes are, sort of, superimposed to my camera image. In Matlab something like dst=webcam_img; dst(drawing>0)=drawing(drawing>0);

Anyone an idea how to do this in C++?

Upvotes: 2

Views: 1849

Answers (1)

Antonio
Antonio

Reputation: 20256

1. Custom version

I would write it explicitly:

const int cols = drawing.cols;
const int rows = drawing.rows;

for (int j = 0; j < rows; j++) {
    const uint8_t* p_draw = drawing.ptr(j); //Take a pointer to j-th row of the image to be drawn
    uint8_t* p_dest = webcam_img.ptr(j);  //Take a pointer to j-th row of the destination image
    for (int i = 0; i < cols; i++) {
        //Check all three channels BGR
        if(p_draw[0] | p_draw[1] | p_draw[2]) { //Using binary OR should ease the optimization work for the compiler
            p_dest[0] = p_draw[0]; //If the pixel is not zero, 
            p_dest[1] = p_draw[1]; //copy it (overwrite) in the destination image
            p_dest[2] = p_draw[2];
        }
        p_dest += 3; //Move to the next pixel
        p_draw += 3; 
    }
}

Of course you can move this code in a function with arguments (const cv::Mat& drawing, cv::Mat& webcam_img).

2. OpenCV "purist" version

But the pure OpenCV way would be the following:

cv::Mat mask;
//Create a single channel image where each pixel != 0 if it is colored in your "drawing" image
cv::cvtColor(drawing, mask, CV_BGR2GRAY);
 //Copy to destination image only pixels that are != 0 in the mask
drawing.copyTo(webcam_img, mask);

Less efficient (the color conversion to create the mask is somehow expensive), but certainly more compact. Small note: It won't work if you have one very dark color, like (0,0,1) that in grayscale will be converted to 0.


Also note that it might be less expensive to redraw the same overlays (lines, circles) in your destination image, basically calling the same draw operations that you made to create your drawing image.

Upvotes: 2

Related Questions