Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
603 views
in Technique[技术] by (71.8m points)

java - opencv blend (overlay) two image with different channels

I want to overlay two images (maybe different format (channels) with opencv. Originally I use addWeighted, however, it fails on two images with different channels. Is there any way in opencv that can handle this case? Thanks

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

No, there is no API in OpenCV that offers this feature nativelly. On the other hand, there's nothing stopping you from writing your own code to do that.

A few weeks ago a made some changes to a function I saw somewhere else on the Internet to be able to:

  • Pass two input images: the background as BGR and the foreground as BGRA;
  • Blend them together according to a simple transparency rule;

(I don't remember where most of this code came from, sorry... but thank you whoever you are!)

void overlayImage(const cv::Mat &background, const cv::Mat &foreground, cv::Mat &output, cv::Point2i location, double opacity = 1.0)
{
    background.copyTo(output);

    // 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)
            break;

        // 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)
                break;

            // determine the opacity of the foregrond pixel, using its fourth (alpha) channel.
            double opacity_level = ((double)foreground.data[fY * foreground.step + fX * foreground.channels() + 3]) / 255.;
            if (opacity >= 0.0 && opacity < 1.0)
                opacity_level *= opacity;

            // and now combine the background and foreground pixel, using the opacity, but only if opacity > 0.
            for (int c = 0; opacity_level > 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_level) + foregroundPx * opacity_level;
            }
        }
    }
}

Below you will find the input images used for testing: the left is the background and the image on the right is the foreground.

image image

To copy the foreground completely over the background, just do:

cv::Mat background = cv::imread("road.png");         // 3-chan BGR
cv::Mat foreground= cv::imread("tulip.png", -1);     // 4-chan BGRA
cv::Point location(0, 0);  

cv::Mat output;
overlayImage(input_bkg, input_target, output, location, 1.0);
cv::imwrite("output_alpha1.0.png", output);

and to perform a copy with 50% transparency:

overlayImage(input_bkg, input_target, output, location, 0.5);
cv::imwrite("output_alpha0.5.png", output);

Here are the results:

image image

This implementation is not bulletproof for production purposes, so use it at your own risk.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

56.9k users

...