# How to alpha blend with transparency masks

I have figured out how to do alpha blend with transparency masks, but as with most things OpenCV, I suspect I'm not doing it as efficiently as could be.

I know how to copy an image with a mask onto another using cv::Mat::copyTo(). But when I do that, the 2nd image is copied 100% onto the destination. What I want is to copy it at 30% or some other value, like in the 3rd "blended" window in this example:

Note how the text "starry night blend test" is partially see-through in that final blended image?

The only way I could figure out how to do that is like this:

// compile with:  g++ test.cpp \$(pkg-config --libs opencv)
#include <opencv2/opencv.hpp>

int main()
{
const auto fontface         = cv::FONT_HERSHEY_PLAIN;
const auto fontscale        = 3.0;
const auto fontthickness    = 3;

const std::string text("starry night blend test");
const cv::Scalar purple(255, 0, 255);
const cv::Scalar white(255, 255, 255);

// Create the layers on which we'll write the text.  Layers start out as
// pure black.
cv::Mat font_layer  = cv::Mat::zeros(background.size(), CV_8UC3);
cv::Mat mask_layer  = cv::Mat::zeros(background.size(), CV_8UC1); // <- note the mask is 1 channel

// Write the text in colour on the "font" layer.  In my actual application
// there are many layers, and it is more complicated than just a line of
// text.
const cv::Point p(20, 100);
cv::putText(font_layer, text, p, fontface, fontscale, purple, fontthickness, cv::LINE_8);

// I also have a mask of the "font" layer.  In this example, the easiest
// way to produce that mask is to write out the same text, with the exact
// same options, but onto the single channel mat.
cv::putText(mask_layer, text, p, fontface, fontscale, white, fontthickness, cv::LINE_8);

// The function copyTo() has an optional "mask" parameter which we can use
// to copy parts of an image onto another.  In this case, we copy the text
// onto a clone of the image.
cv::Mat tmp = background.clone();

// This is the cool part of the code.  We do an alpha blend between the
// two mats.  One of which is just the background, and the other is the
// background with bright purple text.  When blended, it will appear as
// if the text layer is semi-transparent.  0.3 == 30%
const double alpha = 0.3;
const double beta = 1.0 - alpha;
cv::Mat blended;
cv::addWeighted(tmp, alpha, background, beta, 0, blended);

cv::imshow("tmp"        , tmp       );
cv::imshow("blended"    , blended   );
cv::imshow("background" , background);
cv::waitKey(0);

return 0;
}


1. First I copy the entire background image into "tmp"
2. Then I copy the font layer onto tmp using the mask
3. Then I "copy" again when I call into cv::addWeighted().

Is this the way I should be doing this, or is there a better solution?

edit retag close merge delete

Sort by » oldest newest most voted

your question is a bit unclear but maybe this answer will give you new ideas...

take a look at https://answers.opencv.org/question/7... and the code below

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>

using namespace cv;
using namespace std;

void overlayImage(Mat* src, Mat* overlay, const Point& location)
{
for (int y = max(location.y, 0); y < src->rows; ++y)
{
int fY = y - location.y;

if (fY >= overlay->rows)
break;

for (int x = max(location.x, 0); x < src->cols; ++x)
{
int fX = x - location.x;

if (fX >= overlay->cols)
break;

double opacity = ((double)overlay->data[fY * overlay->step + fX * overlay->channels() + 3]) / 255;

for (int c = 0; opacity > 0 && c < src->channels(); ++c)
{
unsigned char overlayPx = overlay->data[fY * overlay->step + fX * overlay->channels() + c];
unsigned char srcPx = src->data[y * src->step + x * src->channels() + c];
src->data[y * src->step + src->channels() * x + c] = srcPx * (1. - opacity) + overlayPx * opacity;
}
}
}
}

int main(int argc, char** argv)
{
Mat underlay1 = underlay0.clone();

if (underlay0.empty() || overlay.empty())
{
cout << "Could not read input image files " << endl;
return -1;
}

overlayImage(&underlay0, &overlay, Point());
imshow("result1", underlay0);

overlay = overlay - Scalar(0, 0, 0, 127);

overlayImage(&underlay1, &overlay, Point());
imshow("result2", underlay1);

waitKey();

return 0;
}

more

Official site

GitHub

Wiki

Documentation

## Stats

Seen: 4,943 times

Last updated: Sep 02 '19