Alpha Blending is a technique to merge/blend two images or ROI’s (Region Of Interest) together. It is done by taking two arrays and applying weights to each. With this, the AddWeighted() function calculates the new pixel values in the blended ROI. Here is a simple porgam I used to implement an alpha blend.

# include <cv.h> # include <highgui.h> int main( int argc, char** argv ) { IplImage *src1, *src2; if( argc == 9 && ((src1=cvLoadImage(argv[1],1)) != 0 )&&((src2=cvLoadImage(argv[2],1)) != 0)) { int x = atoi(argv[3]); int y = atoi(argv[4]); int width = atoi(argv[5]); int height = atoi(argv[6]); double alpha = (double)atof(argv[7]); double beta = (double)atof(argv[8]); cvSetImageROI(src1, cvRect(x,y,width,height)); cvSetImageROI(src2, cvRect(x,y,width,height)); cvAddWeighted(src1, alpha, src2, beta, 0.0, src1); cvResetImageROI(src1); cvNamedWindow( "Alpha_blend", 1 ); cvShowImage( "Alpha_blend", src1 ); cvWaitKey(); } return 0; }

Get the code from my Git Repository

If you need help with Git, follow this quick introduction – Getting Started With Git

This program takes in eight arguments…

argv[1] - The destination image. argv[2] - Source image to be blended onto the destination image. argv[3], argv[4] - The (x,y) coordinate of the ROI in both the destination image and the source image to be blended. argv[5], argv[6] - Width and Height from the point (x,y) of the ROI. argv[7] - Alpha, the weight of argv[1]. argv[8] - Beta, the weight of argv[2] to be blended onto argv[1].

For this simple example I didn’t have any applications in mind, I just wanted to see some images blended together. So the (x,y) coordinate and the width and height for the ROI are the same for both images.

The first new function that I used here was the cvSetImageROI…

void cvSetImageROI( IplImage* image, CvRect rect );

image - Image header. rect - ROI rectangle.

This function sets the ROI (Region Of Interest) for a particular image. This happens inside the IplImage structure. Recall there was a data member…

struct _IplROI *roi;

in the IplImage which is a pointer to the _IplROI struct and looks like this…

typedef struct _IplROI { int coi; int xOffset; int yOffset; int width; int height; } IplROI;

Notice this is just information for a subregion of the original image, where coi is channel of interest. To set that subregion I needed a rectangular region of the original image, and for this I used a CvRect structure…

typedef struct CvRect { int x; int y; int width; int height; } CvRect;

This is pretty self explanatory…

The way it is initialized is even easier!

CvRect cvRect( int x, int y, int width, int height ) { CvRect r; r.x = x; r.y = y; r.width = width; r.height = height; return r; }

There is kind of a lot going on here in this one line.

cvSetImageROI(src1, cvRect(x,y,width,height));

First calling cvRect as we can see returns a cvRect structure. The cvSetImageROI function expects a cvRect struct to be its second parameter, so it wouldn’t matter if I defined the cvRect in or out of the function call, as long as I’m sure to pass it in there. Then, cvSetImageROI takes the image passed in and sets its roi pointer to the region defined by the cvRect structure.

For example, say I create a pointer to an IplImage structure called img, then I set the roi pointer to some rectangular subregion of the image. Under these circumstances if I pass around the img pointer whenever I reference it’s picture, it will not refer to the original image, but instead it will reference the roi that was set.

The IplImage structure doesn’t loose the original image though, to set it so that it references the original again just call cvResetImageROI() which points the roi at NULL again.

void cvResetImageROI( IplImage* image );

image - Image header

Back to the program, once both of the roi’s are set for each image that is when the blending happens. I’ll have it noted that with the roi’s set to NULL you can still pass in both images and, provided they are the same size, both images in their entirety will be blended. It’s a pretty simple concept, the destination image is transformed following this simple calculation…

dst = src1*alpha + src2*beta + gamma

So I know alpha, beta, and gamma are doubles, what are src1, and src2? Well they are arrays, or matrices to be more precise. In each of their entries they store the RGB values of each pixel, and as our handy dandy linear algebra taught us, multiplying a matrix by a scalar will result in the multiplication of each entry with that scalar. This means I’m just changing the value of each pixel to be a mix (to the certain alpha and beta values) of the two images! Here is it’s declaration.

void cvAddWeighted( const CvArr* src1, double alpha, const CvArr* src2, double beta, double gamma, CvArr* dst );

src1 - The first source array. alpha - Weight of the first array elements. src2 - The second source array. beta - Weight of the second array elements. gamma - Scalar, added to each sum. dst - The destination array.

CvArr is a peculiar data type. I went rummaging through the source and this is what I found out about it.

typedef void CvArr; /* CvArr* is used to pass arbitrary * array-like data structures * into functions where the particular * array type is recognized at runtime: */

Ya, so take that however you’d like.

Alright so how did it work? Well I’ll show ya!

There it is! It’s no longer a secret…. Yes, I am Dr. Manhattan!

References

I’m studying from Gary Bradski’s Learning OpenCV