Laex / Delphi-OpenCV

Project Delphi-OpenCV. Translation of OpenCV library header files in Delphi
507 stars 226 forks source link

Draw image over other image with transparency #73

Closed exilon closed 7 years ago

exilon commented 7 years ago

I'm trying to copy an small image with alpha over an big image but final image not respects small image transparency. I've tried with cvAddWeigthed, cvCopyImage, etc

Laex commented 7 years ago

Can you provide the original images?

exilon commented 7 years ago

Hi Laex, I'm using some test images like these: guacamayo logo2

exilon commented 7 years ago

I want to make a watermark. First, i'm tried this code, but transparency is not respected:

watermark := cvLoadImage('D:\logo2.png',CV_LOAD_IMAGE_UNCHANGED);
alpha := 0.7; 
beta := 1.0 - alpha;
image := cvLoadImage('D:\guacamayo.png',CV_LOAD_IMAGE_UNCHANGED);
cvSetImageROI(image,CvRect(x,y,watermark.Width,watermark.Height));
cvAddWeighted(watermark,alpha,image,beta,0,image);
cvResetImageROI(watermark));
cvReleaseImage(watermark);

Then i'm tried this code, but then transparency is not like png owns and images not blended:

watermark := cvLoadImage('D:\logo2.png',CV_LOAD_IMAGE_UNCHANGED);
image := cvLoadImage('D:\guacamayo.png',CV_LOAD_IMAGE_UNCHANGED);
cvSetImageROI(image,CvRect(x,y,watermark.Width,watermark.Height));
mask := cvCreateImage(CvSize(watermark.width,watermark.height),IPL_DEPTH_8U,1);
cvCvtColor(watermark,mask,CV_BGR2GRAY);
cvCopyImage(watermark,image,mask);
cvResetImageROI(image);
cvReleaseImage(watermark);
Laex commented 7 years ago
program Transparency1;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils,
  System.Math,
  ocv.highgui_c,
  ocv.core_c,
  ocv.core.types_c,
  ocv.imgproc_c,
  ocv.imgproc.types_c,
  uResourcePaths;

const
  batmanimg = cResourceMedia + 'batman.png';
  parrotimg = cResourceMedia + 'parrot.png';

const
  px = 200;
  py = 100;

var
  overlay: pIplImage = nil;
  underlay: pIplImage = nil;
  rgba: array [0 .. 3] of pIplImage;
  i, j, k: Integer;
  opacity: Double;
  overlayPx, srcPx: Byte;

begin
  try
    overlay := cvLoadImage(batmanimg, CV_LOAD_IMAGE_UNCHANGED);
    underlay := cvLoadImage(parrotimg, CV_LOAD_IMAGE_UNCHANGED);

    cvNamedWindow('overlay_source', CV_WINDOW_AUTOSIZE);
    cvShowImage('overlay_source', overlay);
    cvNamedWindow('underlay_source', CV_WINDOW_AUTOSIZE);
    cvShowImage('underlay_source', underlay);

    for i := 0 to 3 do
      rgba[i] := cvCreateImage(cvGetSize(overlay), IPL_DEPTH_8U, 1);
    cvSplit(overlay, rgba[2], rgba[1], rgba[0], rgba[3]);

    cvNamedWindow('alpha_overlay', CV_WINDOW_AUTOSIZE);
    cvShowImage('alpha_overlay', rgba[3]);

    for i := 0 to overlay^.width - 1 do
      for j := 0 to overlay^.height - 1 do
        for k := 0 to overlay^.nChannels - 1 do
        begin
          opacity := (overlay^.imageData[j * overlay^.widthStep + i * overlay^.nChannels + 3]) / 255;
          overlayPx := overlay^.imageData[j * overlay^.widthStep + i * overlay^.nChannels + k];
          srcPx := underlay^.imageData[(py + j) * underlay^.widthStep + (i + px) * overlay^.nChannels + k];
          underlay^.imageData[(py + j) * underlay^.widthStep + (i + px) * overlay^.nChannels + k] :=
            Trunc(srcPx * (1 - opacity) + overlayPx * opacity);
        end;

    cvNamedWindow('underlay', CV_WINDOW_AUTOSIZE);
    cvShowImage('underlay', underlay);

    cvWaitKey(0);

    cvReleaseImage(overlay);
    cvReleaseImage(underlay);
    for i := 0 to 3 do
      cvReleaseImage(rgba[i]);

    cvDestroyAllWindows;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

end.
exilon commented 7 years ago

Thank you Laex! You're great! Only two more qüestions... If underlay has only 3 channels and overlay has 4, result are not correct (shows as stripped bars). Must i convert to 4 channels first? how can i do?

If i want to blend these two images with transparency. I want same effect like AddWeighted, but respecting transparency. I changed this part of your code. Is correct?

alpha := 0.5
opacity := (overlay^.imageData[j * overlay^.widthStep + i * overlay^.nChannels + 3]) / 255;
opacity := opacity * alpha;
exilon commented 7 years ago

I've solved the alpha problem, and tried to solve blending overlay image (4 channels) over underlay image (3 channels) with this code:

 if srcHas4channels then underlay^.imageData[(j + y) * fOCVImage^.widthStep + (i + x) * overlay^.nChannels + k] := Trunc(srcPx * (1 - opacity) + overlayPx * opacity)
            else if opacity > 0 then underlay^.imagedata[(j + y) * underlay^.widthStep + underlay^.nchannels * (i + x) + k] := Trunc(srcPx * (1 - opacity) + overlayPx * opacity);

but result produces some kind of small vertical lines as you can see on batman logo. overlay

exilon commented 7 years ago

Finally i've solved the problem with these changes in code:

//overlay images two images
if underlay^.nChannels > 3 then srcHasAlpha := True
   else srcHasAlpha := False;
for i := 0 to overlay^.width - 1 do
begin
   for j := 0 to overlay^.height - 1 do
   begin
      for k := 0 to underlay^.nChannels - 1 do
      begin
         opacity := (overlay^.imageData[j * overlay^.widthStep + i * overlay^.nChannels + 3]) / 255;
         opacity := opacity * alpha;
         overlayPx := overlay^.imageData[j * overlay^.widthStep + i * overlay^.nChannels + k];
         if srcHasAlpha then
         begin
            srcPx := underlay^.imageData[(y + j) * underlay^.widthStep + (i + x) * overlay^.nChannels + k];
            underlay^.imageData[(j + y) * underlay^.widthStep + (i + x) * overlay^.nChannels + k] := Trunc(srcPx * (1 - opacity) + overlayPx * opacity);
         end
         else if opacity > 0 then
         begin
            srcPx := underlay^.imageData[(y + j) * underlay^.widthStep + (i + x) * underlay^.nChannels + k];
            underlay^.imagedata[(j + y) * underlay^.widthStep + (i + x) * underlay^.nchannels + k] := Trunc(srcPx * (1 - opacity) + overlayPx * opacity);
         end;
      end;
   end;
end;