Search code examples
c++opencviplimage

Translucent objects on IplImage


I draw objects on IplImage like this:

cvLine(image, point_1, point_2, color, thickness, CV_AA); // Line
cvCircle(mage, point, radius, color, thickness, CV_AA); // Circle
// and some others...

How can I draw them translucent? cv::Scalar does not support alpha channel, if I understand correctly. I found something similar, but not quite appropriate: link. Here we are talking about translucenty IplImage, not about the objects on it.


Solution

  • So, I tested it now with IplImage and cv::Mat, and both cvCircle and cv::circle don't support drawing semi-transparent objects. I used OpenCV 3.4.0, since this version still supports the old C API.

    Let's have a look at the following code:

    // IplImage - doesn't work
    IplImage* ipl = cvCreateImage(cvSize(201, 201), IPL_DEPTH_8U, 4);
    cvSet(ipl, CvScalar(255, 0, 0, 255));
    cvCircle(ipl, CvPoint(100, 100), 50, CvScalar(0, 0, 255, 128), CV_FILLED);
    
    // cv::Mat - doesn't work
    cv::Mat img = cv::Mat(201, 201, CV_8UC4, cv::Scalar(255, 0, 0, 255));
    cv::circle(img, cv::Point(100, 100), 50, cv::Scalar(0, 0, 255, 128), cv::FILLED);
    

    We create a blue 4-channel image with zero transparency, and draw a red circle with 0.5 transparency. In both cases, we get the following output:

    False output

    We see, that the part of red circle actually "replaces" the pixel values in the original blue image.

    So, for IplImage as well as for cv::Mat we need to use blending, e.g. using addWeighted. Let's have a look at this code:

    // IplImage - works
    IplImage* iplBG = cvCreateImage(cvSize(201, 201), IPL_DEPTH_8U, 3);
    cvSet(iplBG, CvScalar(255, 0, 0));
    IplImage* iplFG = cvCreateImage(cvSize(201, 201), IPL_DEPTH_8U, 3);
    cvSet(iplFG, CvScalar(0, 0, 0));
    cvCircle(iplFG, CvPoint(100, 100), 50, CvScalar(0, 0, 255), CV_FILLED);
    IplImage* iplOut = cvCreateImage(cvSize(201, 201), IPL_DEPTH_8U, 3);
    cvAddWeighted(iplBG, 1, iplFG, 0.5, 0, iplOut);
    
    // cv::Mat - works
    cv::Mat imgBG = cv::Mat(201, 201, CV_8UC3, cv::Scalar(255, 0, 0));
    cv::Mat imgFG = cv::Mat(201, 201, CV_8UC3, cv::Scalar(0, 0, 0));
    cv::circle(imgFG, cv::Point(100, 100), 50, cv::Scalar(0, 0, 255), cv::FILLED);
    cv::Mat imgOut;
    cv::addWeighted(imgBG, 1, imgFG, 0.5, 0, imgOut);
    

    In fact, we create a blue 3-channel background image like this:

    Background

    And, we create a black foreground 3-channel image of the same size with the red circle:

    Foreground

    Using addWeighted with alpha = 1 and beta = 0.5, we get the expected output for both versions:

    Correct output