Hadi GhahremanNezhad
Hadi GhahremanNezhad

Reputation: 2455

OpenCV - How to copy part of an image to another with a mask?

I am trying to copy part of an image to another one based on a mask. But the result becomes white in all the mask pixels.

Mat img = imread("a CV_8UC3 RGB image");
Mat mask = imread("the mask image which is a CV_8UC1");

Mat img_masked;
img.copyTo(img_masked, mask);

imshow("img_masked", img_masked);

cvWaitKey(1);

Here are examples of the images:

enter image description here

enter image description here

enter image description here

I want the original pixels of the img in the result but only in the location of mask pixels.

How can I do this properly?

Upvotes: 2

Views: 4798

Answers (1)

stateMachine
stateMachine

Reputation: 5815

Two ways of doing this, depending on the data type of your mats.

The first one involves converting your images to grayscale. Since you didn't provide your inputs, I saved your screenshots as RGB png images and processed them this way:

//Read input image and mask (as RGB images):
cv::Mat imageInput = cv::imread( "C://opencvImages//road01.png", cv::IMREAD_COLOR );
cv::Mat imageMask = cv::imread( "C://opencvImages//roadMask.png", cv::IMREAD_COLOR );

//Convert images to grayscale:
cv::cvtColor( imageInput, imageInput, CV_RGB2GRAY );
cv::cvtColor( imageMask, imageMask, CV_RGB2GRAY );

//Prepare the masked image:
cv::Mat maskedImage;

//Use an AND operation to mask the original image:
cv::bitwise_and( imageInput, imageMask, maskedImage );

cv::imshow( "maskedImage [gray]", maskedImage );

As you see, I'm using a bitwise and to mask everything with a value of 0 in your original mask.

Here's another approach, assuming your input is an BGR (24-bit) image and your mask a binary (8-bit) image. You just basically split the BGR mat into three individual channels, mask them, and merge them back into a BGR matrix:

//BGR spliting:
std::vector<cv::Mat> bgrChannels(3);
cv::split( colorInput, bgrChannels );

//Mask every channel:
cv::bitwise_and( bgrChannels[0], imageMask, bgrChannels[0] ); //B
cv::bitwise_and( bgrChannels[1], imageMask, bgrChannels[1] ); //G
cv::bitwise_and( bgrChannels[2], imageMask, bgrChannels[2] ); //R

//Merge back the channels
cv::merge( bgrChannels, maskedImage );

cv::imshow( "maskedImage [color]", maskedImage );

Both solutions produce the same result:

Upvotes: 2

Related Questions