Transparent pixels in OpenCV C++ (when using GrabCut) - c++

I am using OpenCV 's implementation of GrabCut in order to remove the background from images. But for now the new background is black. Is there any way to make it transparent?
For now this part of the code looks like this:
Mat binMask( image.size(), CV_8UC1 );
binMask = mask & 1;
image.copyTo( result, binMask );
Can I somehow fill the binMask with transparent pixels?
I've read some tutorials for overlaying images, but I don't need a transparent image in front of my picture but behind.
I hope someone could help.
Many thanks!

Since you are using a 8UC1 image type, it's not possible to have transparent pixels.
These are allowed only if you have an alpha channel, with alpha set to 0: you must use a 4-channel image (3 for colors, 1 for alpha channel). The alpha channel is supported in file formats such as PNG, but not in JPG.
In case of masking, you don't need by the way the usage of transparent pixels, since the black ones actually correspond to 0 and they don't influence the result when you're blending two images (addWeighted for example, or also in case of bitwise_or operation).

Related

OpenCV function to merge Mat's except for black/transparent pixels

I have a Mat containing text with a black background and a Mat containing a background. I want to merge the 2 Mat's so the text sits ontop of the background. Both Mats are BGR (CV_8UC3) and the same size (cols and rows).
Whats the OpenCV way of doing this?
I'm aware of a couple of functions but |= and += are changing the text colour and addWeighted() only works if I want one image to be slightly transparent. I just want to do a complete merge of the two (except not the black pixels in the text image). I'm looking for an OpenCV function rather than pixel editting myself, well a LUT would be good if thats an option.
Try this:
sourceText = yourTextImage;
sourceBackground = yourBackgroundImage;
cv::Mat textTransparent;
cv::inRange(sourceText, cv::Scalar(0,0,0), cv::Scalar(0,0,0), textTransparent);
cv::imshow("transparent pixel", textTransparent); // this should show all the transparent pixel as being white. If that's not the case, adjust the range.
std::cout << "is the transparency mask ok? Press any key with openCV window focus." << std::endl;
cv::Mat result = sourceBackground.clone();
sourceText.copyTo(sourceBackground, 255-textTransparent); // copy pixels, using the created mask.
instead you could copy the transparent pixels from the sourceBackground to the sourceText which is a bit faster because you don't need the 255-textTransparent...
Could not test the code, please tell me if there are bugs.

Cannot load and overlay transparent image in OpenCV

I have transparentGUI.png image with transparent background - in GIMP it looks like that:
When I load it in OpenCV by Mat imgColorPanel = imread("transparentGUI.png", -1); and then display it by imshow("widok", imgColorPanel);, i can see this image with white background (which is wrong, because it should be transparent):
Eventually I want to overlay this image (with it's transparent background) on camera captured image. If I do it using other image, which has background (so it's not transparent) - then it's correctly overlayed.
However, when I want to overlay transparentGUI.png image on captured camera image by addWeighted(imgColorPanel, 0.8, imgOriginal, 0.6, 0, imgOriginal);, program crashes with console error:
as well as Debugging error:
Everything works unless I want to load and display image with transparent background. I assume it may be a conflict with imgOriginal, because it's in HSV - I am building program for object detection by HSV colors, so i cannot change it. Is it ok with overlaing HSV image with BGRA image (the transparent one)? Default color range of OpenCV is BGR, so even camera capture image is converted to HSV, then I can overlay it by other image which is BGR - so, I think that something else must be a problem.
imgColorPanel and imgOriginal should have the same size. From your statement, imgColorPanel has 4 channels (RGBA). To make "addWeighted" work, imgOriginal should also have 4 channels, instead of 3 HSV channels.
For "imshow", I think it is just display issue, and you may print out the size of imgColorPanel to check if it has 4 channels.
You are having two different problems:
i can see this image with white background (which is wrong, because it
should be transparent)
well, it's not wrong because you can't see transparency anyway and there's no standard consensus on how it should be rendered independent of context. In the case of GIMP you do see something - the background of the context-window containing the image. In the case of HighGui you probably get a white-background simply because that's the nominal background in this case. The only way around this is to tell the program what exactly seeing transparency means in the context of your program. This might require using another GUI-system, depends on what you want to do.
program crashes with console error
as the error message says, the problem is that the images you are trying to overlay have a different size. A solution would be to first create a transparent base image of the desired size, e.g. like this:
cv::Mat imgColorPanel {imgOriginal.rows, imgOriginal.cols, CV_8UC4, cv::Scalar{0,0,0,0}};
and then load the panel from your image in the desired part of the base using a ROI (the image has to be smaller than the base of course..) e.g.:
cv::Mat panel {cv::imread("PATH/TO/FILE")};
cv::Rect roi {0,0,panel.rows,panel.cols}; // x,y,width,height
cv::Mat imRoi {imgColorPanel(roi)};
imRoi = panel.clone();

Is there a way to have both grayscale and rgb pixels on the same image opencv C++?

I need to be able to work with images where some regions are grayscale while others are kept on the RGB format. I don't want to convert an image into a grayscale since it will lose the channels and will become simply one channeled, is there a way to keep the RGB channels of some pixels on the picture and turn the others into a grayscale?
NO.
I see two solutions to this:
Have both a gray (Mat1b) and a rgb (Mat3b) image, and work on the image you need.
Have a single rgb (Mat3b) image, and set r,g,b channels to the same gray value where you need. In this way you can mimic to have a mixed gray/rgb image.

Combine two images based on a black and white mask

I want to create a mask operation...
I have two input images, of the same size (do they have to have the same depth/number of channels ? I'd like to be anything, likely 3 channels, CV_32FC3 or gray...) and I created a mask, of the same size (rows and cols)
cv::Mat mask = cv::Mat(image1.rows, image1.cols, CV_8UC1);
The mask is created with areas of black and white.
I would like to create a new cv::Mat, that will have image1 where mask has 1, and image2 where mask has 0.
I looked into cv::filter2D and copyTo... Also looked at addWeighted, but I don't want to blend them - the areas for each image should be completely separate. A roi would not help - the mask is likely to not contain a rectangle, but one or more polygons.
I can't find something that does what I want.
Is there any OpenCV function that combines my images based on the mask ? Or do I have to create my own, looping through rows and cols ?
Thank you.
Just use the bitwise_and() function and you're set. The references below include a full working example.
References:
How to "zero" everything within a masked part of an image in OpenCV
OpenCV bitwise_and + mask

transparent colour being shown some of the time

I am using a LPDIRECT3DTEXTURE9 to hold my image.
This is the function used to display my picture.
int drawcharacter(SPRITE& person, LPDIRECT3DTEXTURE9& image)
{
position.x = (float)person.x;
position.y = (float)person.y;
sprite_handler->Draw(
image,
&srcRect,
NULL,
&position,
D3DCOLOR_XRGB(255,255,255));
return 0;
}
According to the book I have the RGB colour shown as the last parameter will not be displayed on screen, this is how you create transparency.
This works for the most part but leaves a pink line around my image and the edge of the picture. After trial and error I have found that if I go back into photoshop I can eliminate the pink box by drawing over it with the pink colour. This can be see with the ships on the left.
I am starting to think that photoshop is blending the edges of the image so that background is not all the same shade of pink though I have no proof.
Can anyone help fix this by programming or is the error in the image?
If anyone is good at photoshop can they tell me how to fix the image, I use png mostly but am willing to change if necessary.
edit: texture creation code as requested
character_image = LoadTexture("character.bmp", D3DCOLOR_XRGB(255,0,255));
if (character_image == NULL)
return 0;
You are loading a BMP image, which does not support transparency natively - the last parameter D3DCOLOR_XRGB(255,0,255) is being used to add transparency to an image which doesn't have any. The problem is that the color must match exactly, if it is off even by only one it will not be converted to transparent and you will see the near-magenta showing through.
Save your images as 24-bit PNG with transparency, and if you load them correctly there will be no problems. Also don't add the magenta background before you save them.
As you already use PNG, you can just store the alpha value there directly from Photoshop. PNG supports transparency out of the box, and it can give better appearance than what you get with transparent colour.
It's described in http://www.toymaker.info/Games/html/textures.html (for example).
Photoshop is anti-aliasing the edge of the image. If it determines that 30% of a pixel is inside the image and 70% is outside, it sets the alpha value for that pixel to 70%. This gives a much smoother result than using a pixel-based transparency mask. You seem to be throwing these alpha values away, is that right? The pink presumably comes from the way that Photoshop displays partially transparent pixels.