Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

I found your problem quite interesting and decided to spend some time on it. Actually it took me well over 2 hours to work it out and write the answer!! My coding efficiency sucks :(

Generally the idea is simple: while copying masked out part of the image (cat with whiskers) do not copy blue parts as they are, but get rid of their blue channel and replace it by the average of the remaining two. Your blue background has a hue value of 120 in OpenCV. However, this clear hue is affected by the whiskers and the outline of the cat. So we want to get rid of the bluish hue for all the pixels of hue in the range of 120+-10. Once we have done that, we can copy the masked part of the image.

Here is the code. First we find the blue and bluish pixels.

Mat hsv, blue_mask;
cvtColor(src,hsv,CV_BGR2HSV);
Mat nlb(src.rows,src.cols,CV_8UC3,Scalar(110,70,70));
Mat nub(src.rows,src.cols,CV_8UC3,Scalar(130,255,255));
inRange(hsv,nlb,nub,blue_mask);
//you can dilate the mask here to make sure the edges are blue-free

The effect can be seen below. If you lower the upper bound of saturation or value just by 1 you will see some compression artifacts:

image description

But with values of 255 the mask is clear:

image description

As I hate to iterate pixel by pixel I was looking for some other solution to replace the blue channel under the mask above with the average value of the other two channels. This is probably not the simplest nor the fastest solution. If anybody knows a better way, please enlighten me.

Mat((spl[1]+spl[2])/2).copyTo(spl[0],mask); //overwrite bluish pixels of B with (G+R)/2
vector<Mat> channels;
channels.push_back(spl[0]);
channels.push_back(spl[1]);
channels.push_back(spl[2]);
merge(channels,new_src); //merge all channels to create new version of the input image

This new image looks like this:

image description

You don't even need to threshold your blue channel and mask it!

If you still see some blue tint, play with the lower and upper bounds for hue, saturation and value. They will be probably a little different for other images.

I found your problem quite interesting and decided to spend some time on it. Actually it took me well over 2 hours to work it out and write the answer!! My coding efficiency sucks :(

Generally the idea is simple: while copying the masked out part of the image (cat with whiskers) do not copy blue parts as they are, but get rid of their blue channel and replace it by the average of the remaining two. Your blue background has a hue value of 120 in OpenCV. However, this clear hue is affected by the whiskers and the outline of the cat. So we want to get rid of the bluish hue for all the pixels of hue in the range of 120+-10. Once we have done that, we can copy the masked part of the image.

Here is the code. First we find the blue and bluish pixels.

Mat hsv, blue_mask;
cvtColor(src,hsv,CV_BGR2HSV);
Mat nlb(src.rows,src.cols,CV_8UC3,Scalar(110,70,70));
Mat nub(src.rows,src.cols,CV_8UC3,Scalar(130,255,255));
inRange(hsv,nlb,nub,blue_mask);
//you can dilate the mask here to make sure the edges are blue-free

The effect can be seen below. If you lower the upper bound of saturation or value just by 1 you will see some compression artifacts:

image description

But with values of 255 the mask is clear:

image description

As I hate to iterate pixel by pixel I was looking for some other solution to replace the blue channel under the mask above with the average value of the other two channels. This is probably not the simplest nor the fastest solution. If anybody knows a better way, please enlighten me.

Mat((spl[1]+spl[2])/2).copyTo(spl[0],mask); //overwrite bluish pixels of B with (G+R)/2
vector<Mat> channels;
channels.push_back(spl[0]);
channels.push_back(spl[1]);
channels.push_back(spl[2]);
merge(channels,new_src); //merge all channels to create new version of the input image

This new image looks like this:

image description

You don't even need to threshold your blue channel and mask it!

If you still see some blue tint, play with the lower and upper bounds for hue, saturation and value. They will be probably a little different for other images.

I found your problem quite interesting and decided to spend some time on it. Actually it took me well over 2 hours to work it out and write the answer!! My coding efficiency sucks :(

Generally the idea is simple: while copying the masked out part of the image (cat with whiskers) do not copy blue parts as they are, but get rid of their blue channel and replace it by the average of the remaining two. Your blue background has a hue value of 120 in OpenCV. However, this clear hue is affected by the whiskers and the outline of the cat. So we want to get rid of the bluish hue for all the pixels of hue in the range of 120+-10. Once we have done that, we can copy the masked part of the image.

Here is the code. First we find the blue and bluish pixels.

Mat hsv, blue_mask;
cvtColor(src,hsv,CV_BGR2HSV);
Mat nlb(src.rows,src.cols,CV_8UC3,Scalar(110,70,70));
Mat nub(src.rows,src.cols,CV_8UC3,Scalar(130,255,255));
inRange(hsv,nlb,nub,blue_mask);
//you can dilate the mask here to make sure the edges are blue-free

The effect can be seen below. If you lower decrease the upper bound of saturation or value just by 1 you will see some compression artifacts:

image description

But However with values of 255 the mask is clear:

image description

As I hate to iterate pixel by pixel I was looking for some other solution to replace the blue channel under the mask above with the average value of the other two channels. This is probably not the simplest nor the fastest solution. If anybody knows a better way, please enlighten me.

Mat((spl[1]+spl[2])/2).copyTo(spl[0],mask); //overwrite bluish pixels of B with (G+R)/2
vector<Mat> channels;
channels.push_back(spl[0]);
channels.push_back(spl[1]);
channels.push_back(spl[2]);
merge(channels,new_src); //merge all channels to create new version of the input image

This new image looks like this:

image description

You don't even need to threshold your blue channel and mask it!

If you still see some blue tint, play with the lower and upper bounds for hue, saturation and value. They will be probably a little different for other images.

I found your problem quite interesting and decided to spend some time on it. Actually it took me well over 2 hours to work it out and write the answer!! My coding efficiency sucks :(

Generally the idea is simple: while copying the masked out part of the image (cat with whiskers) do not copy blue parts as they are, but get rid of their blue channel and replace it by the average of the remaining two. Your blue background has a hue value of 120 in OpenCV. However, this clear hue is affected by the whiskers and the outline of the cat. So we want to get rid of the bluish hue for all the pixels of hue in the range of 120+-10. Once we have done that, we can copy the masked part of the image.

Here is the code. First we find the blue and bluish pixels.

Mat hsv, blue_mask;
cvtColor(src,hsv,CV_BGR2HSV);
Mat nlb(src.rows,src.cols,CV_8UC3,Scalar(110,70,70));
Mat nub(src.rows,src.cols,CV_8UC3,Scalar(130,255,255));
inRange(hsv,nlb,nub,blue_mask);
//you can dilate the mask here to make sure the edges are blue-free

The effect can be seen below. If you decrease the upper bound of saturation or value just by 1 you will see some compression artifacts:

image description

However with values of 255 the mask is clear:

image description

As I hate to iterate pixel by pixel I was looking for some other solution to replace the blue channel under the mask above with the average value of the other two channels. This is probably not the simplest nor the fastest solution. If anybody knows a better way, please enlighten me.

Mat((spl[1]+spl[2])/2).copyTo(spl[0],mask); //overwrite bluish pixels of B with (G+R)/2
vector<Mat> channels;
channels.push_back(spl[0]);
channels.push_back(spl[1]);
channels.push_back(spl[2]);
merge(channels,new_src); //merge all channels to create new version of the input image

This new image looks like this:

image description

Actually this new version of the input image happens to be the desired output image! You don't even need to threshold your blue channel and mask it!it anymore!

If you still see some blue tint, play with the lower and upper bounds for hue, saturation and value. They will be probably a little different for other images.

I found your problem quite interesting and decided to spend some time on it. Actually it took me well over 2 hours to work it out and write the answer!! My coding efficiency sucks :(

Generally the idea is simple: while copying the masked out part of the image (cat with whiskers) do not copy blue parts as they are, but get rid of their blue channel and replace it by the average of the remaining two. Your blue background has a hue value of 120 in OpenCV. However, this clear hue is affected by the whiskers and the outline of the cat. So we want to get rid of the bluish hue for all the pixels of hue in the range of 120+-10. Once we have done that, we can copy the masked part of the image.

Here is the code. First we find the blue and bluish pixels.

Mat hsv, blue_mask;
cvtColor(src,hsv,CV_BGR2HSV);
Mat nlb(src.rows,src.cols,CV_8UC3,Scalar(110,70,70));
Mat nub(src.rows,src.cols,CV_8UC3,Scalar(130,255,255));
inRange(hsv,nlb,nub,blue_mask);
//you can dilate the mask here to make sure the edges are blue-free

The effect can be seen below. If you decrease the upper bound of saturation or value just by 1 you will see some compression artifacts:

image description

However with values of 255 the mask is clear:

image description

As I hate to iterate pixel by pixel I was looking for some other solution to replace the blue channel under the mask above with the average value of the other two channels. This is probably not the simplest nor the fastest solution. If anybody knows a better way, please enlighten me.

Mat((spl[1]+spl[2])/2).copyTo(spl[0],mask); //overwrite bluish pixels of B with (G+R)/2
vector<Mat> channels;
channels.push_back(spl[0]);
channels.push_back(spl[1]);
channels.push_back(spl[2]);
merge(channels,new_src); //merge all channels to create new version of the input image

This new image looks like this:

image description

Actually this new version of the input image happens to be the desired output image! You don't even need to threshold your blue channel and mask it anymore!

Unfortunately, some of the cat's fur is missing, but I think you can play more with matting in Photoshop to get better results. Let us know if you succeed.

If you still see some blue tint, play with the lower and upper bounds for hue, saturation and value. They will be probably a little different for other images.