Search code examples
c++opencvimage-processingwatermarkblending

How to add a logo to an image as a watermark?


Recently, I'm interested in image processing using OpenCV, but I'm new to it.

I do some simple image processing on a lot of images, and finally I want to watermark each image with a logo which is a small png image.

There are a lot of codes which blend two images. Here is an example which I used to blend two images:

int main( int argc, char** argv )
{
        double alpha = 0.5; double beta; double input;
        Mat src1, src2, dst;

        // main image with real size.(Large)
        src1 = imread("a.jpg");

        // logo which will be used as a watermark.(small size)
        src2 = imread("logo.png");

        namedWindow("Linear Blend", 1);
        beta = ( 1.0 - alpha );

        addWeighted( src1, alpha, src2, beta, 0.0, dst);

        imshow( "Linear Blend", dst );

        waitKey(0);
        return 0;
}

Here, both images should be the same type and the same size, while my logo image is a small image which I want to blend to the main image in a corner (actually at an arbitrary point).

Can anyone help me to do that? (Maybe, one solution is to create a matrix from the logo which is the same size of the main image so every point outside of the logo should be zero and then finally blend two images which have equal size.)

my final code is like this:

int main( int argc, char** argv )
{
        double alpha = 0.5; double beta; double input;
        Mat src1, src2, src2_copy, dst;

        src1 = imread("a.jpg");
        src2 = imread("logo.png");


        resize(src2, src2_copy, src2.size() / 2, 0.5, 0.5);

        int x = 100;
        int y = 100;
        int w = src2_copy.size().width;
        int h = src2_copy.size().height;
        cv::Rect pos = cv::Rect(x, y, w, h);


        dst = src1.clone();

        namedWindow("Linear Blend", 1);
        beta = ( 1.0 - alpha );

        addWeighted(src1(pos), alpha, src2_copy, beta, 0.0, dst);

        imshow("Linear ", dst);


        waitKey(0);
        return 0;
}


Solution

  • You can access a (rectangular) region of interest (ROI) inside a cv::Mat using a cv::Rect (see the documentation on the base class), which is described by x, y, width, and height. This is a widely used technique, which becomes handy in a lot of use cases!

    So, now you just need to set up a proper ROI within your main image and blend your watermark there. Let's have a look at the following code snippet:

    // Artificial main image
    cv::Mat img = cv::Mat(300, 300, CV_8UC3, cv::Scalar(128, 128, 128));
    
    // Artificial watermark
    cv::Mat wtm = cv::Mat(25, 25, CV_8UC3, cv::Scalar(0, 0, 255));
    
    // Position of watermark in main image
    int x = 30;
    int y = 35;
    int w = wtm.size().width;
    int h = wtm.size().height;
    cv::Rect pos = cv::Rect(x, y, w, h);
    
    // Blending
    double alpha = 0.7;
    double beta = (1.0 - alpha);
    cv::addWeighted(img(pos), alpha, wtm, beta, 0.0, img(pos));
    

    The artifical main image looks like this:

    Main image

    The artificial watermark image looks like this:

    Watermark

    And, the final result looks like this:

    Result

    As you can see, in

    cv::addWeighted(img(pos), alpha, wtm, beta, 0.0, img(pos))
    

    the ROI img(pos) is used as source and destination of the operation, so you have in-place blending. If you want to have a separate output image while preserving your main image untouched, maybe clone your main image in the beginning, i.e.

    cv::Mat dst = img.clone()
    

    and then do the blending with dst(pos) instead of img(pos).

    Hope that helps!