It seems OpenCV does not provide very much support for transparent images. It has images with 4 channels, which are treated as BGRA images, with the fourth channel being the Alpha channel. It also supports saving these images using the PNG format, yet it does not support properly displaying these images in highgui, nor does it support combining these images.
I have looked a while for a way to combine a (BGR) background image with a (BGRA) foreground image, with semi-transparent images, but was unable to find one. So, I wrote my own and since I couldn't find a solution on the internet, I'll bet I can save some people some time by sharing my implementation, which is as follows:
void overlayImage(const cv::Mat &background, const cv::Mat &foreground,
cv::Mat &output, cv::Point2i location)
// start at the row indicated by location, or at row 0 if location.y is negative.
for(int y = std::max(location.y , 0); y < background.rows; ++y)
int fY = y - location.y; // because of the translation
// we are done of we have processed all rows of the foreground image.
if(fY >= foreground.rows)
// start at the column indicated by location,
// or at column 0 if location.x is negative.
for(int x = std::max(location.x, 0); x < background.cols; ++x)
int fX = x - location.x; // because of the translation.
// we are done with this row if the column is outside of the foreground image.
if(fX >= foreground.cols)
// determine the opacity of the foregrond pixel, using its fourth (alpha) channel.
double opacity =
((double)foreground.data[fY * foreground.step + fX * foreground.channels() + 3])
// and now combine the background and foreground pixel, using the opacity,
// but only if opacity > 0.
for(int c = 0; opacity > 0 && c < output.channels(); ++c)
unsigned char foregroundPx =
foreground.data[fY * foreground.step + fX * foreground.channels() + c];
unsigned char backgroundPx =
background.data[y * background.step + x * background.channels() + c];
output.data[y*output.step + output.channels()*x + c] =
backgroundPx * (1.-opacity) + foregroundPx * opacity;
This code only works if:
- The background is in BGR colour space.
- The foreground is in BGRA colour space.
The output image will always be of the same size as the background image, in BGR colour space. The position parameter determines how the foreground is placed on top of the background. A position of (100, -50) will move the foreground 100 pixels to the right and 50 pixels up.
It might not be the best solution as it was written in a hurry, so please feel free to improve.
Explanation of the code:
We first copy the background to the output, so we can continue with only the pixels where foreground and background overlap.
We start at the first scanline where they overlap, being either the top row of the background (y=0) or the top row of the foreground (y = position.y), so we take the maximum of these two. We do the same for the column value (x). We use fX and fY (foreground-x and foreground-y) as the current coordinates in the foreground image.
We determine the opacity of the foreground image using its fourth (alpha) channel. If it is > 0, we combine the pixels of the background and foreground for all channels.
We are done if we passed all overlapping pixels and output will now contain an image of the background, overlayed with the transparent foreground image.
int main(int argc, char *argv)
// add the second parameter "-1" as flag, to make sure the transparancy channel is read!
cv::Mat foreground = imread("D:/images/foreground.png", -1);
cv::Mat background = imread("D:/images/background.jpg");
overlayImage(background, foreground, result, cv::Point(0,0));