Lazarus

Programming => Graphics and Multimedia => Graphics => Topic started by: klausbits on July 07, 2016, 11:00:00 am

Title: merge two images
Post by: klausbits on July 07, 2016, 11:00:00 am
Hi.

I want to interpolate between two images like this

Code: Pascal  [Select][+][-]
  1. var picture1,picture2,picture3: Tpicture;
  2. picture1:= Tpicture.create;
  3. picture2:= Tpicture.create;
  4. picture3:= Tpicture.create;
  5. picture1.LoadFromFile(d:\image1.jpg);
  6. picture2.LoadFromFile(d:\image2.jpg);
  7.  
  8. picture3 = 0.7*picture1 + 0.3*picture2;
  9.  

how can I do this?
Title: Re: merge two images
Post by: Thaddy on July 07, 2016, 11:06:05 am
You need to set transparency levels for those pictures so the one shines through the other.
Title: Re: merge two images
Post by: klausbits on July 07, 2016, 11:26:56 am
to Thaddy,
thanks for the tip. I have to think about this if it serves my purpose.

Rather I thought to have
red3:=0.7*red1+0.3*red2;
green3:=...
blue3:=...
Title: Re: merge two images
Post by: Thaddy on July 07, 2016, 11:35:39 am
@Klausbits:
;) Yup, that is basically what transparency does <very big smile> so why re-invent wheels? I never got further than an octagonal one.... ;)

You got the algorithm almost spot on but think about weights of the values. Now let your drivers do the real work ;)
Title: Re: merge two images
Post by: klausbits on July 07, 2016, 11:55:12 am
to Thaddy,

I am not sure thsat this is the same (pardon me, I am a physicist;-)

How would you program your proposal?
Title: Re: merge two images
Post by: circular on July 07, 2016, 03:11:28 pm
How to do that with BGRABitmap:

Code: Pascal  [Select][+][-]
  1. uses BGRABitmap, BGRABitmapTypes;
  2. var bmp1,bmp2,bmp3: TBGRABitmap;
  3. begin
  4.   bmp1 := TBGRABitmap.Create('d:\image1.jpg');
  5.   bmp2 := TBGRABitmap.Create('d:\image2.jpg');
  6.   bmp3 := TBGRABitmap.Create(bmp1.Width,bmp1.Height); //note that size may be different between bmp1 and bmp2
  7.   bmp3.CrossFade(Rect(0,0,bmp3.Width,bmp3.Height), bmp1, bmp2, round(0.3*255), dmSet);
  8.   bmp1.Free;
  9.   bmp2.Free;
  10.   ...
  11. end;

It works with images that have transparent parts as well.
Title: Re: merge two images
Post by: klausbits on July 07, 2016, 04:27:50 pm
to circular:

thank you, I'll try it.

Meanwile I worked around with
colorm:=picm.bitmap.Canvas.Pixels[nw,nh];      //color of picm, but has only 256 colors :-(
or
fpcolorm1:=picm1.bitmap.Canvas.getColor(nw,nh);  //FPcolor of picm1 does not work :-(
Title: Re: merge two images
Post by: klausbits on July 07, 2016, 08:43:43 pm
many thanks for your tips. Here my own solution, I hope it works:
Code: Pascal  [Select][+][-]
  1.   begin                //see diary 6.July 2016
  2.     picm:= TPicture.Create;
  3.     picm1:= TPicture.Create;
  4.     for n:=0 to nwidth-1 do
  5.     begin
  6. //        picture2[n]:= TPicture.Create;   //already defined
  7.       nr:=n;                 //real of n
  8.       mr:=nr*nmax/nwidth;    //real of m
  9.       m:=floor(mr);
  10.       dmr:=mr-m;            //fade position
  11.       Str(m:4,nstring);
  12.       fastring:=files+'\b'+nstring+'.jpg';
  13.       Str(m+1:4,nstring);
  14.       fbstring:=files+'\b'+nstring+'.jpg';
  15.       picm.LoadFromFile(fastring);      //image 1
  16.       picm1.LoadFromFile(fbstring);     //image 2
  17.       for nh:=0 to nheight-1 do
  18.       begin
  19.         for nw:=0 to nwidth-1 do        //for every pixel do
  20.         begin
  21.           colorm:=picm.bitmap.Canvas.Pixels[nw,nh];      //color of picm
  22.           RedGreenBlue(colorm, mRed, mGreen, mBlue);     //bytes! -> only 512 different colors?
  23.           colorm1:=picm1.bitmap.Canvas.Pixels[nw,nh];      //color of picm1
  24.           RedGreenBlue(colorm1, m1Red, m1Green, m1Blue);
  25. //          fpcolorm1:=picm1.bitmap.Canvas.getColor(nw,nh);  //FPcolor of picm1 does not work
  26.           nred:=round((1-dmr)*mRed+dmr*m1Red);             //to be scaled by 256?  or 8?
  27.           ngreen:=round((1-dmr)*mGreen+dmr*m1Green);
  28.           nblue:=round((1-dmr)*mBlue+dmr*m1Blue);
  29.  //         function RGBToColor(R, G, B: Byte): TColor;
  30.           colorn:=RGBToColor(nred, ngreen, nblue);
  31.           picture2[n].bitmap.Canvas.Pixels[nw,nh]:=colorn;   //image 3
  32.         end;
  33.       end;
  34.     end;
  35.     Str(n:4,nstring);
  36.     fastring:=files+'\bb'+nstring+'.jpg';  // bb... different name to not overwrite b... files of user
  37.     picture2[n].SaveToFile(fastring); //
  38.     picture2[n].free; //
  39.   end;
  40.  
Title: Re: merge two images
Post by: circular on July 07, 2016, 09:44:51 pm
That's the idea.
Title: Re: merge two images
Post by: russellman on October 19, 2023, 08:18:55 pm
I use BGRABitmap.
Hear is a PDF file that show how to do this.
https://drive.google.com/file/d/1-HWs7X5YNKkbDUdx9WpcBZ_RByodXFKb/view?usp=share_link

uses
   BGRABitmap, BGRABitmapTypes;

procedure TForm1.FormDropFiles(Sender: TObject; const FileNames: array of string
  );
begin
  ///If first image is drop on form.
  if tic = False then begin
    ///Create bmp1 TBGRABitmap.
    bmp1 := TBGRABitmap.Create;
    ///Load the first image into bmp1.
    bmp1.LoadFromFile(FileNames[0]);
    ///Create (bmp2) a TBGRABitmap canvas. The size of bmp1.
    bmp2 := TBGRABitmap.Create(bmp1.Width,bmp1.Height);
    ///Puts bmp1 (image data) into bmp2. Using Linear Blend.
    bmp2.PutImage(0, 0, bmp1, dmLinearBlend );
    ///bmp1 Free memory.
    bmp1.Free;
    ///Togels to All other images.
    tic := True
  ///All other images are drop on form.
  end else begin
    ///Create bmp1 TBGRABitmap.
    bmp1 := TBGRABitmap.Create;
    ///Load the first image into bmp1.
    bmp1.LoadFromFile(FileNames[0]);
    ///Puts bmp1 (image data) into bmp2. Using Linear Blend.
    ///bmp2 was not freed.
    bmp2.PutImage(0, 0, bmp1, dmLinearBlend);
    ///bmp1 Free memory.
    bmp1.Free;
  end; ///end if else().
  ///Draw bmp2 to Image1.
  Image1.Picture.Assign(bmp2);
  Image1.Picture.SaveToFile(Saved.png');
end;
Title: Re: merge two images
Post by: r.lukasiak on October 25, 2023, 06:56:23 am
Hi everyone,
I see the post is quite old but since #russellman recently revived it, I'll give it a try since it's about something I need :-)

I was playing with CrossFade and it's somehow working but there is something I can't figure out.
I have 2 BGRABitmaps, lets name them img1 and img2. Img1 is non-transparent picture but img2 is partly transparent. I need to merge them but without any calculating partly transparent pixels whatsoever. Img2 consists of either 100% transparent pixels or 100% opaque color pixels. I need the 100% opaque pixels to just replace the pixels from img1 and the 100% transparent pixels just keep the original pixels from img1.
so my first shot was:
Code: Pascal  [Select][+][-]
  1. img.CrossFade(Rect(0,0,img1.Width,img1.Height), img1, img2, 255, dmSet);
but it resulted in img be the same as img2, it took just 1ms though.
Then I started playing with mode value but all of them resulted in the same, img = img1, just taking 5-8ms.
Next I changed 255 to 254 and this did the trick, I got expected result but it took 26-28ms, depending on the mode.
I just don't understand why 255 doesn't give the result I expected. It looks like 254 causes the function to calculate the img1 and img2 pixels, that's why it's taking so much more time.  Even though it visually looks good, img2 seems to cover img1 in "just" 254/255, am I wrong?
Is AFadePosition and equivalent of opacity? Setting a value between 0 and 254 gives results as if it were but everything changes with 255.

Searching for any answer led me to this post and #russellman's using PutImage instead of CrossFade. By these functions names I'd say that CrossFade really calculates transparency according to AFadePosition whereas PutImage just drops one image onto another. PutImage indeed gives expected result in just 7ms. How PutImage different to CrossFade?

Could someone shed more light on it?

Thanks in advance!

Title: Re: merge two images
Post by: circular on October 25, 2023, 04:05:42 pm
Hello @r.lukasiak

CrossFade is designed to create a transition between two images based on the fade position. When fade position is 0 to be equivalent of putting the first image (img1 in your case), and when fade position is 255 to be equivalent of putting the second image (img2). In between, it is supposed to be the intermediate value.

When using as the fade position different from 0 or 255, the function will perform some interpolation between the two images, which is why you noticed an increase in computation time. For example, if at one pixel, the first image is opaque and the second is transparent, with fade position 128 the resulting pixel should be half-transparent.

if your goal is to just overlay img2 on img1 without any transitional effect, using PutImage is the more appropriate and efficient choice. CrossFade is intended for scenarios where you want a smooth transition between two images.

Note that the mode supplied to CrossFade applies after the interpolation has been made between the two images. In most scenarios, you would use dmSet if you want to replace the content of the target or dmDrawWithTransparency if there is a background you want to preserve.

I hope this clarifies things! Let me know if you have any further questions or if the result you get does not seem consistent with the explanations.
Title: Re: merge two images
Post by: r.lukasiak on October 26, 2023, 07:09:35 am
Thank you very much for this explanation. Everthying makes perfect sense. However I'm still not sure why FadePosition=255 makes img1 disappear and the result=img2. 0-254 works exactly as you explanation indicates, giving a mix of both pics, depending on the FadePosition value.

And indeed, PutImage  does exactly what I needed and it's really fast.

thanks a lot!

Title: Re: merge two images
Post by: circular on October 26, 2023, 08:34:55 am
I am happy the explanation made sense.

Your assumption that there isn't a big difference between FadePosition=254 and FadePosition=255 is spot on. This is surprising indeed that there would be a significant different.

A couple of scenarios might explain this:
- if the draw mode is dmSetExceptTransparent: if the pixel has alpha 254 it won't be displayed but if it has alpha 255 it will.
- if you draw the final image with parameter Opaque=True on a control but in fact the final image has transparent pixels: if a pixel has an alpha of 1, it will still be fully displayed on the control.

Both cases can be addressed for example by first filling the target image with a background color. Were any of these scenarios applicable to your tests?
Title: Re: merge two images
Post by: Dzandaa on October 26, 2023, 11:50:00 am
Hi,

Is this that you want?

PictOrig, PictDest, NewPict: TBGRABitmap; // Must be of the same size
PBDest: TImage; 
PictOrig: Picture with transparency
PictDest: Picture without transparency
NewPict: Result


Code: Pascal  [Select][+][-]
  1. // **************************
  2. // ***** Filter Picture *****
  3. // **************************
  4. procedure TMergeColorsForm.BFxPictureClick(Sender: TObject);
  5. var
  6.  Pxo, Pxd, Pxn: PBGRAPixel;
  7.  i: integer;
  8. begin
  9.  if((PictOrig = nil) or (PictDest = nil)) then exit;
  10.  if((PictOrig.Width <> PictDest.Width) or (PictOrig.Height <> PictDest.Height)) then exit;
  11.  if(NewPict <> nil) then FreeAndNil(NewPict);
  12.  NewPict := TBGRABitmap.Create(PictOrig.Width, PictOrig.Height);
  13.  
  14.  Pxo := PictOrig.Data;
  15.  Pxd := PictDest.Data;
  16.  Pxn := NewPict.Data;
  17.  for i := Pred(PictOrig.NbPixels) downto 0 do
  18.  begin
  19.    if(Pxo^.alpha <> 0) then Pxn^:= Pxo^ else Pxn^ := Pxd^;
  20.    inc(Pxo);
  21.    inc(Pxd);
  22.    inc(Pxn);
  23.  end;
  24.  PBDest.Picture.Assign(NewPict) ;
  25.  PBDest.Repaint;
  26. end;                          
  27.  

B->
Title: Re: merge two images
Post by: r.lukasiak on October 27, 2023, 05:12:35 am
@circular, the first scenario is exactly what I experiences. FadePosition=254 is giving me expected results, but FadePosition=255 completely ignored img1 and just returned img2 (or blended img1 and img2 but with abolsute bias towards img2).  Anyway I'm glad to know it's normal that FadePosition=255 behaves this one, it was just weird to me but now it's all clear. Cheers!

@Dzandaa it seems to do exactly what I need but isn't it the same that PutImage does? Is there maybe some advantage of your solution over PutImage?

thank you all!
Title: Re: merge two images
Post by: Dzandaa on October 27, 2023, 11:18:10 am
@r.lukasiak

I really don't know :)

I just read your first post and tried to find a solution :)

Check the speed for both.

B->
Title: Re: merge two images
Post by: circular on October 27, 2023, 03:10:47 pm
@circular, the first scenario is exactly what I experiences. FadePosition=254 is giving me expected results, but FadePosition=255 completely ignored img1 and just returned img2 (or blended img1 and img2 but with abolsute bias towards img2).  Anyway I'm glad to know it's normal that FadePosition=255 behaves this one, it was just weird to me but now it's all clear. Cheers!
Hi! So if I understand, you are using dmSetExceptTransparent for the draw mode? In this case, yes, it is expected that when alpha is 255 the pixel is shown and otherwise not.

If you're happy with PutImage that's alright then. Otherwise you may want another Porter-Duff mode like "atop". I invite you to look at the table there to determine exactly what you are looking for: https://www.stat.auckland.ac.nz/~paul/Reports/GraphicsEngine/compositing/compositing.html

These modes are not all readily available in BGRABitmap, but it is implementable if we know exactly what you would like to do. The code proposed by @Dzanda can be adapted to one of the modes. For now, it is similar to PutImage without doing the alpha blending.
Title: Re: merge two images
Post by: r.lukasiak on October 30, 2023, 02:42:27 pm
@circular
Quote
Hi! So if I understand, you are using dmSetExceptTransparent for the draw mode? In this case, yes, it is expected that when alpha is 255 the pixel is shown and otherwise not.
With FadePosition=255 I actually get the same result regardless the draw mode. img2 always overlaps img1. I just took a look at the CrossFade code and it looks like with FadePosition = 0 and FadePosition = 255 the result is fixed, the draw mode doesn't matter, only values 1-254 make the difference.

@Dzandaa
Quote
I really don't know :)
It was silly asking you this question without actually testing your code. I finally got around to do it and it seems that your solution is ~3x faster than PutImage. Taking a look at PutImage code I see it's doing some blending and your solution doesn't. That's exactly what I was looking for, in my case alpha is either 0 or 255 so no need for any kind of blending.

Thank you very much!
Title: Re: merge two images
Post by: circular on October 30, 2023, 03:31:01 pm
Hi @r.lukasiak

With FadePosition=255 I actually get the same result regardless the draw mode. img2 always overlaps img1. I just took a look at the CrossFade code and it looks like with FadePosition = 0 and FadePosition = 255 the result is fixed, the draw mode doesn't matter, only values 1-254 make the difference.
As a matter of fact, yes, the mode parameter is used (passe as parameter to FillRect):
Code: Pascal  [Select][+][-]
  1. begin
  2.   if AFadePosition = 0 then
  3.     FillRect(ARect, Source1, mode) else
  4.   if AFadePosition = 255 then
  5.     FillRect(ARect, Source2, mode) else
  6.     InternalCrossFade(ARect, Source1,Source2, AFadePosition,nil, mode);
  7. end;

Well it seems you have what you need anyway.  :)
Title: Re: merge two images
Post by: r.lukasiak on October 30, 2023, 04:43:12 pm
ohh true... I overlooked it.

Anyway, yes, I got what I needed. I'd say, even more than I expected :-)

Thanks!
TinyPortal © 2005-2018