Lazarus

Programming => Graphics => Graphics and Multimedia => BGRABitmap and LazPaint => Topic started by: TheBlackSheep on May 12, 2012, 10:03:48 pm

Title: [SOLVED] - "Tween" two images (Onionskin) & Chroma Key
Post by: TheBlackSheep on May 12, 2012, 10:03:48 pm
Hi

I'm looking for the "correct" way to tween two images - when I say "tween" I mean create an image with a ghost of another image (or images) sort of overlayed into it (with varying weights) - "onion skin" is another term for this.

I've sort of got it working with this code;

Code: [Select]

if chkbxOverlay.Checked then begin
    Overlay := TBGRALayeredBitmap.Create(Video.Width, Video.Height);
    OnionSkin := TBGRABitmap.Create('test20120511154948.png');

    Overlay.AddLayer(VideoImg, boLinearBlend, 10);
    Overlay.AddLayer(OnionSkin, boLinearBlend, 10);

    Composite := OverLay.ComputeFlatImage;
    Onionskin.Free;
    Composite.Draw( BGRAPanel1.Canvas , 0, 0 );
    Overlay.Free;
  end else begin
    VideoImg.Draw( BGRAPanel1.Canvas, 0, 0, True );
  end;

but it doesn't "feel" right at the minute and not sure how to vary the faded amount between the two images.  Not sure I need layered images (unless this is the best way to load several images and ghost them all).

there's an example of tweening on the efg2 website (http://www.efg2.com/Lab/ImageProcessing/Tween.htm (http://www.efg2.com/Lab/ImageProcessing/Tween.htm)) where a trackbar is used to control this fading from one image to another with a middle position where each image equally "ghosts" onto the resultant image.

In the above code the VideoImg is a frame from a webcam (from the 5dpo library).  The onionskin loaded image is a previous snapshot image (i.e. the last image grabbed from the webcam) - an onion skin operation is useful to show movement between the current position and the last position typically for animation ("scrubbing" allows other previous images to be loaded and it's easier then to see whether a set of animation frames are smooth or not).  The "Composite" above, is just a reference to a TBGRABitmap which isn't instantiated or free'd (do I need to do that here?).

When the overlay checkbox is unchecked the videoimg goes direct to the canvas - but when it's checked I want to load the previous image and ghost it over the live webcam video feed.

TheBlackSheep
Title: Re: "Tween" two images
Post by: circular on May 13, 2012, 12:09:40 pm
If I understand what "tween" means, the best way to do it is this way :

Code: [Select]
function MakeTween(img1,img2: TBGRABitmap; position: byte): TBGRABitmap;
begin
  if position = 0 then
    result := img1.Duplicate
  else if position = 255 then
    result := img2.Dupliacte
  else
  begin
    result := img1.Duplicate;
    result.PutImage(0,0,img2,dmSet,position);
  end;
end;

Then you can draw and then free the resulting image. By using this method, you can have a transition when there are transparent parts.

If there is no transparency, you can just do :
Code: [Select]
img1.PutImage(...)
img2.PutImage(...,dmSet,position);

Note : Composite needs to be freed, it is not a property.
Title: Re: "Tween" two images
Post by: TheBlackSheep on May 13, 2012, 01:21:15 pm
hi circular,

I must be missing something still - as a quick test (in Windows) I've put 3 TImages on a form (the two images from the Tween example on the efg2 page, a blank TImage for the result and a trackbar to control the tween amount.  I've loaded the images into Image1 and Image2 at design-time and put this in the trackbar change function;

Code: [Select]
procedure TForm1.TrackBar1Change(Sender: TObject);
var
   bmp1,bmp2, resultbmp: TBGRABitmap;

   function MakeTween(img1,img2: TBGRABitmap; position: byte): TBGRABitmap;
   begin
     if position = 0
        then result := TBGRABitmap(img1.Duplicate)
        else if position = 255
               then result := TBGRABitmap(img2.Duplicate)
     else
     begin
       result := TBGRABitmap(img1.Duplicate);
       result.PutImage(0,0,img2,dmSet,position);
     end;
   end;

begin
  bmp1 := TBGRABitmap.Create(Image1.Picture.Bitmap);
  bmp2 := TBGRABitmap.Create(Image2.Picture.Bitmap);
  resultbmp := MakeTween(bmp1,bmp2,trackbar1.position);
//  Image3.Picture.Bitmap.Assign(resultbmp.Bitmap);
  resultbmp.Draw(Image3.Canvas,0,0,true);
  resultbmp.Free;
end; 

When I move the trackbar and use the commented out line above it produces no image in Image3, the version as it stands above just shows a garbled image.  The sample images I'm using are here;

http://www.efg2.com/Lab/Bitmap/TulipSmall.ZIP (http://www.efg2.com/Lab/Bitmap/TulipSmall.ZIP)
http://www.efg2.com/Lab/Bitmap/SunflowerSmall.ZIP (http://www.efg2.com/Lab/Bitmap/SunflowerSmall.ZIP)

although any images in theory should do.

TheBlackSheep
Title: Re: "Tween" two images
Post by: TheBlackSheep on May 13, 2012, 02:46:26 pm
hi circular

after a bit of playing I seem to have it working...

Code: [Select]
procedure TForm1.TrackBar1Change(Sender: TObject);
var
   bmp1,bmp2, resultbmp: TBGRABitmap;

   function MakeTween(img1,img2: TBGRABitmap; position: byte): TBGRABitmap;
   begin
     if position = 0
        then result := TBGRABitmap(img1.Duplicate)
        else if position = 255
               then result := TBGRABitmap(img2.Duplicate)
     else
     begin
       result := TBGRABitmap(img1.Duplicate);
       result.PutImage(0,0,img2,dmDrawWithTransparency,position);
     end;
   end;

begin
  bmp1 := TBGRABitmap.Create('SunflowerSmall.bmp');
  bmp2 := TBGRABitmap.Create('TulipSmall.bmp');
  resultbmp := MakeTween(bmp1,bmp2,trackbar1.position);
  Image3.Picture.Bitmap.Assign(resultbmp.Bitmap);
  resultbmp.Draw(Image3.Canvas,0,0,true);
  resultbmp.Free;
end;
   

will try it in the 5dpo application - I guess next I need to work out how to do a Chromakey (any ideas?)...

thanks

TheBlackSheep
Title: Re: "Tween" two images
Post by: circular on May 13, 2012, 11:54:53 pm
Well, no you should not replace dmSet by dmDrawWithTransparency, because when the images are transparent, you don't get what you want. If the images are not transparent, there should be no difference, so you can leave dmSet anyway.

In order to draw the image, if using the Bitmap property does not work, you can use MakeBitmapCopy. This way you get a non transparent image with a background color already applied, assign it and then free it.
Title: Re: "Tween" two images
Post by: TheBlackSheep on May 14, 2012, 11:14:59 am
when I change it to dmSet it's just fades in the second image from no image to full opaque in the resultant window.  I did notice that in my previous code I was setting the resultant image twice (although it needed this to make it work for some reason). 

I've now changed it to this and this does seem to work;

Code: [Select]
procedure TForm1.TrackBar1Change(Sender: TObject);
var
   bmp1,bmp2, resultbmp: TBGRABitmap;

   function MakeTween(img1,img2: TBGRABitmap; position: byte): TBGRABitmap;
   begin
     if position = 0
        then result := TBGRABitmap(img1.Duplicate)
        else if position = 255
               then result := TBGRABitmap(img2.Duplicate)
     else
     begin
       result := TBGRABitmap(img1.Duplicate);
       result.PutImage(0,0,img2,dmSetWithTransparency,position);
     end;
   end;

begin
  bmp1 := TBGRABitmap.Create('000034.JPG');
  bmp2 := TBGRABitmap.Create('000014.JPG');
  resultbmp := MakeTween(bmp1,bmp2,trackbar1.position);
  Image3.Picture.Bitmap := resultbmp.MakeBitmapCopy(clNone);
  resultbmp.Free;
end; 

although you'll probably tell me that's still wrong  :(

I think the effect is better described as "translucency" as opposed to transparency - i.e. the second image is superimposed on top of the first image and with a 50:50 weighting each image is there in the output  - by adjusitng the trackbar you can increase or decrease the translucency of the image on top. 
Title: Re: "Tween" two images
Post by: circular on May 15, 2012, 12:21:20 pm
Oh I think there is a bug in the dmSet mode of PutImage. In fact, several bugs. I've just fixed them on subversion. Thanks for testing.

You wrote "dmSetWithTransparency" ? You mean "dmDrawWithTransparency" ?

You are right about translucency, but I did not find the right word at the time I defined it.

About memory usage, I suppose you should rather write something like :
Code: [Select]
var copy: TBitmap;
begin
  bmp1 := TBGRABitmap.Create('000034.JPG');
  bmp2 := TBGRABitmap.Create('000014.JPG');
  resultbmp := MakeTween(bmp1,bmp2,trackbar1.position);
  bmp1.Free;
  bmp2.Free;

  copy := resultbmp.MakeBitmapCopy(clBtnFace);
  resultbmp.Free;

  Image3.Picture.Bitmap := copy;
  copy.Free;
end; 

Try this with the last svn version, and see the difference between dmSet and dmDrawWithTransparency, so you'll understand why I was saying that you would rather want dmSet.
Title: Re: "Tween" two images
Post by: TheBlackSheep on May 16, 2012, 12:44:29 am
hi circular

yep, that seems to have worked now for the "translucent" image - after a bit of searching I think this is also known as "uniform transparency".

One side effect of the new bgrabitmap source appears that when I save a snapshot of the original 5dpo webcam video image as a png I appear to get a transparent mask applied across the whole image which I wasn't seeing before (this is not the same "tweened" image but the raw image prior to being blended).

If I save the same image as a bitmap it's fine - save it as a png and it's completely transparent - so it's definitely something to do with transparency within png's that's changed between this and the previous version (it could of course be that the current version is correct and the previous version was saving the png incorrectly but looked ok from the manner in which I was using it).

Code: [Select]

procedure TFMain.btnSnapshotClick(Sender: TObject);
begin
 if CBVideoActive.Checked and (VideoImg <> nil)
   then begin
     VideoImg.SaveToFile('test'+FormatDateTime('yyyymmddhhnnss',now)+'.bmp');  //this works
     VideoImg.SaveToFile('test'+FormatDateTime('yyyymmddhhnnss',now)+'.png');  //fully transparent
   end;
end;

procedure BGR24_to_TrueColor(src: PRGB24Pixel; dest: PLongWord; size: Integer);
var i: integer;
begin
 // move(src^, dest^, size*4);
  for i := 0 to size -1 do begin
    // this is the reason why often BGR instead of RGB
    // is used, we don't need shifting like above, we can
    // simply dump entire longwords into their new locations.
    dest[i] := PLongWord(@src[i])^;
  end;
end;

procedure TFMain.VideoFrame(Sender: TObject; FramePtr: PByte);
var Composite:TBGRABitmap;

  function MakeTween(img1,img2: TBGRABitmap; position: byte): TBGRABitmap;
   begin
     if position = 0
        then result := TBGRABitmap(img1.Duplicate)
        else if position = 255
               then result := TBGRABitmap(img2.Duplicate)
     else
     begin
       result := TBGRABitmap(img1.Duplicate);
       result.PutImage(0,0,img2,dmSet,position);
     end;
   end;

begin
  FrameRate:=round(1/((GetTickCount-FrameTime)/1000));
  FrameTime:=GetTickCount;
  StatusBar.SimpleText := format('(%d, %d) %d fps', [video.Width, Video.Height, FrameRate]);
  case Video.PixelFormat of
    uvcpf_YUYV: YUYV_to_Gray(PLongWord(FramePtr), PLongWord(VideoImg.Data), Video.Width * Video.Height);
    uvcpf_YUV420: YUV420_to_Gray(FramePtr, PLongWord(VideoImg.Data), Video.Width * Video.Height);
    uvcpf_RGB24: RGB24_to_TrueColor(PRGB24Pixel(FramePtr), PLongWord(VideoImg.Data), Video.Width * Video.Height);
    uvcpf_BGR24: BGR24_to_TrueColor(PRGB24Pixel(FramePtr), PLongWord(VideoImg.Data), Video.Width * Video.Height);
   end;

  if chkbxOverlay.Checked then begin
    OnionSkin := TBGRABitmap.Create('test20120511145457.png');
    Composite := MakeTween(videoimg,onionskin,(255 div trackbar1.max) * trackbar1.position);
    Composite.Draw( BGRAVirtualScreen1.Canvas, 0, 0, True );
    Composite.Free;
    OnionSkin.Free;
  end else begin
    VideoImg.Draw( BGRAVirtualScreen1.Canvas, 0, 0, True );
  end;
end;

this is basically the example from the SdpoVideo4L2 component - I've set the webcam to uvcpf_BGR24 pixelformat (the default I think) - the videoframe function is constantly running and I have a "snapshot" button which allows the user to take the current frame and save it - as you can see, VideoImg is a source not a target image for the "tween" operation and it looks ok on the BGRAVirtualScreen - we're copying the images to make the tween anyway so not sure how the image is now going fully transparent (unless I'm doing something really obviously stupid here  :o)  Any ideas?

TheBlackSheep
Title: Re: "Tween" two images
Post by: circular on May 16, 2012, 08:34:38 pm
Well the PNG writer is buggy sometimes, so it may just be that. Anyway, you can check this by applying AlphaFill(255) before saving to PNG. If there is no difference, then it's only PNG saving that is buggy.

It may also be compiling issue. To be sure it is not, go to bgrabitmap folder and delete the "lib" subfolder.

Oh and I'm sorry to tell you but the way you draw on the virtual screen is not the right way. Well it works under Windows and Linux to a certain extent, but if the form is redrawn for some reason, the image will disappear. And no MacOS, it won't appear at all.

The main idea is that you should not access with BGRAVirtualScreen1.Canvas, because it is only the visible result of the virtual screen. To draw on the virtual screen, you need to override the OnRedraw event. So for example, in the VideoFrame, you can keep the current bitmap (VideoImg) and call BGRAVirtualScreen1.Redraw. In the OnRedraw handler, put your "composite" code and then PutImage into the virtual screen (which is a parameter of the OnRedraw handler).

Is it clear or do you need more explanations about this ?
Title: Re: "Tween" two images
Post by: TheBlackSheep on May 16, 2012, 09:38:04 pm
thanks circular

it works!!!! sorted the "correct" way to draw on the bitmap (calling BGRAVirtualScreen1.redrawBitmap now) and in the OnRedraw event do putImage's instead of trying to draw directly to the canvas. 

the snapshot works with .bmp, .jpg and even .png using the alphafill (on a duplicated image) to produce the png (and the quality of the image being produced is much improved - it was a bit noisy before and I was thinking I might need to add in some kind of averaging across a few frames although I might still need to do this to avoid ambient light flicker which is fairly typical in animation software).

The onionskin also works much better so the canvas drawing was definitely wrong even though it looked like it was sort of working.

I'll tidy this up now to produce a simple animation grabber and publish the source on github.

Many thanks for your support on this, it's much appreciated.

TheBlackSheep
Title: Re: "Tween" two images
Post by: circular on May 17, 2012, 01:39:05 am
Cool.  :)

One final comment, you should avoid loading the OnionSkin image at each frame.
Code: [Select]
OnionSkin := TBGRABitmap.Create('test20120511145457.png');
Rather load it once at startup and free at the end, or something like that.

Title: Re: "Tween" two images
Post by: TheBlackSheep on May 17, 2012, 08:51:37 am
yep - it was just in there for testing at the minute, in practice it would normally be the previous snapshot image and loaded dynamically - I'll avoid loading it on every frame as it is currently.

Have you ever done anything with chromakey? (i.e. green screen transparency) - I'm thinking it's just a case of making a mask based on a pixel color bias towards green (or could be any color really) - there'd need to be a tolerance on the color as the greenscreens are never uniform to one color because of lighting/shading differences.

TheBlackSheep
Title: Re: "Tween" two images
Post by: circular on May 17, 2012, 12:02:49 pm
Never done chromakey but you can use BGRADiff or BGRAWordDiff functions to compare each pixel to some color.

So you can go through each scan line, compare each pixel and in the destination bitmap put either from one source or another.
Title: Re: "Tween" two images / ChromaKey
Post by: TheBlackSheep on May 21, 2012, 06:39:40 pm
hi circular

I thought I had the chromakey (greenscreen) working (it looks like it does on Win32) but on Linux it fails badly - any ideas?

I have 3 images on the form, the foreground has a green screen background - just a standard image found on a google image lookup for greenscreen images - the background can be any image and I've just used a random landscape picture. - the Composite one is the final "blended" image.  There are 4 trackbars - 1 for each color and a final one which is the BGRADiff threshold (named "tolerance");

All the trackbars are tied to this event to do the chromakey; 

Code: [Select]
procedure TfrmChromaKeyTest.TrackBarChange(Sender: TObject);
var
   BG,FG,Composite: TBGRABitmap;
   p1,p2,p3: PBGRAPixel;
   n : integer;
   diff : integer;
begin
  BG := TBGRABitmap.Create(imgBackground.Picture.Bitmap);//background source
  FG := TBGRABitmap.Create(imgForeground.Picture.Bitmap);//foreground source with greenscreen
  if (BG.width <> FG.width) or (BG.Height <> FG.Height)
    then BGRAReplace(BG,BG.Resample(FG.Width,FG.Height));
  Composite := TBGRABitmap.Create(FG.Width, FG.Height); //target

  p1 := BG.Data;
  p2 := FG.Data;
  p3 := Composite.Data;

  lblRedChromaKeyValue.Caption:=IntToStr(trkbrRedChromakey.Position);
  lblGreenChromaKeyValue.Caption:=IntToStr(trkbrGreenChromakey.Position);
  lblBlueChromaKeyValue.Caption:=IntToStr(trkbrBlueChromakey.Position);
  lblToleranceValue.Caption:=IntToStr(trkbrTolerance.Position);

  for n := FG.NbPixels-1 downto 0 do begin
    diff := BGRADiff(p2^, BGRA(trkbrRedChromakey.Position,trkbrGreenChromakey.Position,trkbrBlueChromakey.Position));
    if diff <= trkbrTolerance.Position
      then p3^ := p1^
      else p3^ := p2^;
    inc(p1);
    inc(p2);
    inc(p3);
  end;
  Composite.InvalidateBitmap;
  imgComposite.Picture.Bitmap := Composite.MakeBitmapCopy(clBlack);
  Composite.Free;
  FG.Free;
  BG.Free;
  DrawZoom;
end;   

I think this is what you'd suggested and the wiki example to get access to the pixels wthin the image and then compare using BGRADiff - it seems to work ok in Win32 but Linux the composite image is all messed up with the colors bleeding all over the image (moving the trackbars does have an effect but the "color bleed" is always there)

TheBlacksheep
Title: Re: "Tween" two images
Post by: circular on May 21, 2012, 10:15:46 pm
Maybe there is a bug in BGRADiff.

Your code seems good but :
- you could store BGRA(trkbrRedChromakey.Position,trkbrGreenChromakey.Position,trkbrBlueChromakey.Position) in a variable to avoid making it for each pixel
- Composite.MakeBitmapCopy(clBlack) returns an object that need to be freed, so you need to assign it a variable, assign it and then free it. When you write imgComposite.Picture.Bitmap := Composite.MakeBitmapCopy(clBlack), it is not a real assignment, it is a property that duplicates the bitmap.

Can you put a screenshot of the awful result on Linux, to see if it can be a bug of BGRADiff ? What if you try with BGRAWordDiff ?
Title: Re: "Tween" two images & Chroma Key
Post by: TheBlackSheep on May 21, 2012, 11:29:46 pm
ok fixed up the source to improve the efficiency (it does make it faster on Linux but this is still considerably slower than the Win32 version for some reason even in Linux64)

the function is now;

Code: [Select]
...
  r := trkbrRedChromakey.Position;
  g := trkbrGreenChromakey.Position;
  b := trkbrBlueChromakey.Position;
  t := trkbrTolerance.Position;

  for n := FG.NbPixels-1 downto 0 do begin
    diff := BGRADiff(p2^, BGRA(r,g,b));
    if diff <= t
      then p3^ := p1^
      else p3^ := p2^;
    inc(p1);
    inc(p2);
    inc(p3);
  end;

  Composite.InvalidateBitmap;
  Bitmap := Composite.MakeBitmapCopy(clBlack);
  imgComposite.Picture.Bitmap := Bitmap;
  Bitmap.Free;
  Composite.Free;
...

I've attached the two images showing it working ok in Win32 but not in Linux - if I change the threshold value then the foreground disappears (and just displays the background) or takes over the whole image but at a point when the green value and the threshold are as shown on the screenshots - the effect is displayed and it works well, just not in Linux  :(.

Note, I'm looking at a the "diff" being less than or equal to the threshold - it seems to work in win32 but I don't know if this is how it should be or if it actually should be within a range - I've tried it with a few greenscreen images and it appears to work ok although the tolerance and green values need adjusting to cope with the varying light/shadow levels for each particular image as would be expected (however it doesn't work anywhere near as well with blue-screen images).

I tried BGRAWordDiff and that doesn't produce any "merged" image at all but that could be because I'm using the BGRADiff and BGRAWordDiff incorrectly.

TheBlackSheep
Title: Re: "Tween" two images
Post by: circular on May 22, 2012, 01:59:26 am
That's right, you just need to compare the diff with some number. With WordDiff, the number is just bigger, so if you use numbers that are too small, you never get the color match with the chroma key.

I suppose the problem in Linux comes when you load the images :
Code: [Select]
  BG := TBGRABitmap.Create(imgBackground.Picture.Bitmap);//background source
  FG := TBGRABitmap.Create(imgForeground.Picture.Bitmap);//foreground source with greenscreen

This should work, but maybe there is a problem of encoding. Can you try to load directly from the files instead ?

Anyway congratulations ! I am very excited by that software. That's so cool.  8-)
Title: Re: "Tween" two images
Post by: TheBlackSheep on May 22, 2012, 09:05:40 am
yep that works ... so there's a difference between the two platforms when copying the bitmap from a TImage to a BGRABitmap.

I'd need to speed up the operation too as there's a significant drop in performance between Win32 and Linux - I was hoping to do this on a video image coming from a webcam but I just don't think it'll be fast enough.

TheBlackSheep
Title: Re: "Tween" two images
Post by: circular on May 22, 2012, 06:25:04 pm
What about preloading images ? It seems that you load the file, or import it from TImage each time you draw it, right ?
Title: Re: "Tween" two images
Post by: TheBlackSheep on May 22, 2012, 10:34:52 pm
hi circular

switched to using scanline - that appears to be faster in Linux and tidied up the source which I've attached together with the sample foreground and background image that are preloaded in the demo's timage's. 

If you press the shift key and click on the foreground image it will set the trackbar to the color under the mouse cursor.  Similarly on the composite image you can zoom into the image by pressing the shift key there too (again the zoom image shows the area under the mouse) - I just used these bits to try and work out the color differences to see if it was working.   clicking the zoom image shows the color under the mouse there too.  You can load in other images to see if it works on other images (the green chroma key - or other chroma color might need adjusting to suit other images) - a blue chroma key image I got from Google images works well after setting the key via the shift key setting rather than trying to do it manually - it looks like if the background chroma color is consistent then setting the color via the mouse "dropper" works fine if it's varied because of shadow/lighting differences then you need to play around with the threshold and chroma color.       

Pre-loading the images must be the right way to do this (you wouldn't want to do it on each trackbar change but in my defense it was only a test prog to prove the theory) - I'll try and integrate it into the 5dpo webcam code and see if there are problems with performance. 

Thanks for your continued support  :)

TheBlackSheep
Title: Re: "Tween" two images
Post by: circular on May 23, 2012, 01:55:14 am
You're welcome. I was very interesting and we made BGRABitmap better.  :)
Title: Re: "Tween" two images
Post by: circular on May 23, 2012, 02:03:16 am
I've tested your program. In fact I realised that maybe BGRADiff is not the best way to do this.

I suggest that you use BGRAToHSLA or BGRAToGSBA to get the a THSLAPixel, and then add the difference of both Hue and Saturation to get a color difference. BGRADiff also returns a difference between dark and light pixels, which is not what you want here I guess. Or maybe you can include the Lightness in the difference but divided by some value. See what I mean ?
Title: Re: "Tween" two images
Post by: Shebuka on May 23, 2012, 10:39:16 am
Hi, i'v tested your app and i have problems with mouse click... in intfgraphics.pas the TLazIntfImage.Create(ARawImage: TRawImage; ADataOwner: Boolean); receive the raw image, but when Create(Desc.Width, Desc.Height, []); is called the coordinates are negatives, instead of 389, 114 they are -388, -113 and then raise FPImageException.Create('Invalid Size'); is called inside TLazIntfImage.InternalSetSize
Title: Re: "Tween" two images
Post by: TheBlackSheep on May 23, 2012, 11:06:22 pm
hi circular

I've tried the following based on your suggestion (note the trackbars have max values of 65535 now);

Code: [Select]
...
  h := trkbrHueValue.Position;
  s := trkbrSaturationValue.Position;

  ht := trkbrHueTolerance.Position;


  for y := 0 to FG.Height - 1 do begin

    p1 := BG.ScanLine[y];
    p2 := FG.ScanLine[y];
    p3 := Composite.ScanLine[y];

    for x := 0 to FG.Width-1 do begin

      diff := (BGRAToHSLA(P2^).hue - h) + (BGRAToHSLA(P2^).saturation - s);
      if diff >= ht
        then p3^ := p1^
        else p3^ := p2^;

      inc(p1);
      inc(p2);
      inc(p3);
    end;
  end; 
...

but it doesn't seem to be as effective as the BGRADiff  method previously - unless I've misunderstood what you meant.

I've also tried comparing the HSL on an individual basis, i.e between ranges like this (ht,st being the tolerance +ve & -ve either side of the comparison values (h & s) on the basis that the test pixel from the fg image is between the relevant range of hue and saturation.

Code: [Select]


  h := trkbrHueValue.Position;
  s := trkbrSaturationValue.Position;
 
  ht := trkbrHueTolerance.Position;
  st := trkbrSatTolerance.Position;

  for y := 0 to FG.Height - 1 do begin

    p1 := BG.ScanLine[y];
    p2 := FG.ScanLine[y];
    p3 := Composite.ScanLine[y];

    for x := 0 to FG.Width-1 do begin

    h2 := BGRAToHSLA(P2^);
 
    if  (h2.hue > (h - ht)) and (h2.hue < (h + ht))
      and (h2.saturation > (s - st)) and (h2.saturation < (s + st))
        then p3^ := p1^
        else p3^ := p2^;   

      inc(p1);
      inc(p2);
      inc(p3);
    end;
  end; 
...
 

however it doesn't work particularly well either so I don't think this is correct either.

Shebuka - I haven't seen any issues with the mouse click so not sure how that's happening - I'm using CodeTyphon 2.60 as my platform but with the latest BGRABitmap code from Sourceforge although your issue sounds like it's a problem with the standard TImage and the mouseover - which image is it happening on? the foreground, the composite or the zoom?  Wherever I click on these images it works fine.

TheBlackSheep

Title: Re: "Tween" two images
Post by: Shebuka on May 24, 2012, 10:35:52 am
There is some sort of bug in CarbonCanvas GetPixel, and probably also in CarbonObject RawImage_FromCarbonBitmap

bug reported http://bugs.freepascal.org/view.php?id=22111
Title: Re: "Tween" two images
Post by: TheBlackSheep on May 24, 2012, 11:06:32 am
hi Shebuka,

ah you're on a Mac - that would explain why I'm not seeing that particular issue.

Have you checked the co-ordinates are positive in the example and are just negative when they're called internally by CarbonCanvas - i.e. it's not something to do with the fact that it's expected a word and getting an integer and deciding it's the wrong side of zero?  That would explain a 1 difference in the positive and negative values you're seeing - the Round function returns an int64 so it could be that in which case you might just be able to wrap a typecase to word around the call;

Pixel := imgForeground.Canvas.Pixels[word(Round(DropperX)),word(Round(DropperY))]; 
 
Or you could try kidding the result i.e. deliberately set it to a negative value by multiplying the GetPixel call by -1 as in;

imgZoom.Canvas.Pixels[X * -1, Y * -1];

depending on which mouseclick function is causing the issue (presume it happens on all of the images with this event)

Alternatively find the call where it's breaking in the CarbonCanvas and use abs() as in;

..GetPixel(abs(X), abs(Y));

it looks as if it's just negating the co-ordinates incorrectly (albeit off by one) and this should put them positive again although they may not be the actual pixel you're clicking (it's a bit of an estimate anyway on a stretched image - I couldn't readily find a function to work out the actual underlying pixel coordinates of a stretched image from the XY coords of a mouseclick event).

Iit might be worth checking to see if there are any updates in svn for the Carbon Canvas - I'd have thought GetPixel is a fairly common function to call and it should be causing issue elsewhere if it's not working correctly.

TheBlackSheep
 
Title: Re: "Tween" two images
Post by: circular on May 24, 2012, 03:07:35 pm
hi circular

I've tried the following based on your suggestion (note the trackbars have max values of 65535 now);

Code: [Select]
...
  h := trkbrHueValue.Position;
  s := trkbrSaturationValue.Position;

  ht := trkbrHueTolerance.Position;


  for y := 0 to FG.Height - 1 do begin

    p1 := BG.ScanLine[y];
    p2 := FG.ScanLine[y];
    p3 := Composite.ScanLine[y];

    for x := 0 to FG.Width-1 do begin

      diff := (BGRAToHSLA(P2^).hue - h) + (BGRAToHSLA(P2^).saturation - s);
      if diff >= ht
        then p3^ := p1^
        else p3^ := p2^;

      inc(p1);
      inc(p2);
      inc(p3);
    end;
  end; 
...

but it doesn't seem to be as effective as the BGRADiff  method previously - unless I've misunderstood what you meant.
You forgot to put an "abs". Try with :
Code: [Select]
diff := abs(BGRAToHSLA(P2^).hue - h) + abs(BGRAToHSLA(P2^).saturation - s);
Title: Re: "Tween" two images
Post by: Shebuka on May 24, 2012, 04:03:32 pm
hi Shebuka,

ah you're on a Mac - that would explain why I'm not seeing that particular issue.

Have you checked the co-ordinates are positive in the example and are just negative when they're called internally by CarbonCanvas - i.e. it's not something to do with the fact that it's expected a word and getting an integer and deciding it's the wrong side of zero?  That would explain a 1 difference in the positive and negative values you're seeing - the Round function returns an int64 so it could be that in which case you might just be able to wrap a typecase to word around the call;

Pixel := imgForeground.Canvas.Pixels[word(Round(DropperX)),word(Round(DropperY))]; 
 
Or you could try kidding the result i.e. deliberately set it to a negative value by multiplying the GetPixel call by -1 as in;

imgZoom.Canvas.Pixels[X * -1, Y * -1];

depending on which mouseclick function is causing the issue (presume it happens on all of the images with this event)

Alternatively find the call where it's breaking in the CarbonCanvas and use abs() as in;

..GetPixel(abs(X), abs(Y));

it looks as if it's just negating the co-ordinates incorrectly (albeit off by one) and this should put them positive again although they may not be the actual pixel you're clicking (it's a bit of an estimate anyway on a stretched image - I couldn't readily find a function to work out the actual underlying pixel coordinates of a stretched image from the XY coords of a mouseclick event).

Iit might be worth checking to see if there are any updates in svn for the Carbon Canvas - I'd have thought GetPixel is a fairly common function to call and it should be causing issue elsewhere if it's not working correctly.

TheBlackSheep

Hi, i'v reported on bugtraker what i'v digget out, the coords are always positive so it must be some internal problems

Quote from: me on bugtracker
In TCarbonBitmapContext.GetPixel the is an assignment

R := Classes.Rect(X, Y, 1, 1);

The R is the propagated to TCarbonWidgetSet.RawImage_FromCarbonBitmap and in this piece of code

Width := R.Right - R.Left;
Height := R.Bottom - R.Top;

Width and Height become negative... If i put S.X, S.Y instead of 1, 1 and remove the Right and Bottom it still not fixed because after returning back to GetPixel i then get "Invalid horizontal pixel index error" in

Result := IntfImage.TColors[X, Y];

Seems all ok after setting [X-1, Y-1], but sometimes instead of green color i get blue, brown or clBlack color.

Also sporadically it raise an access violation on the first assignment line (VBytes.Ah := B0;) of TLazIntfImage.GetColor_BPP32_A8R8G8B8_BIO_TTB
Title: Re: "Tween" two images
Post by: TheBlackSheep on May 24, 2012, 04:24:41 pm
hi circular

I've retried with abs() but can't seem to get the effect as good as using BGRADiff -  I've attached the amended source of Unit2 so you'd need to copy these over the source of the original if you want to play around with this - tbh I couldn't get my head around how this formula works with the colorspace in relation to the green background but you'd be better placed to understand that ...

Ok Shebuka - sorry, I couldn't help on the Mac side hopefully it'll get fixed shortly.

TheBlackSheep
Title: Re: "Tween" two images
Post by: circular on May 24, 2012, 10:35:28 pm
Okay, try with this adjustment :
Title: Re: "Tween" two images & Chroma Key
Post by: TheBlackSheep on May 24, 2012, 11:03:00 pm
Wow, that works great  :D - the shift+mouseclick on the foreground image green background (or blue background) to set the hue/saturation/light values almost nails the effect first time - it's just a little adjustment here or there and it's almost perfect.

Your library is amazing.  I just need to get this integrated into the 5dpo webcam stuff now and published on github.

Thanks again!

TheBlackSheep
Title: Re: [SOLVED] - "Tween" two images (Onionskin) & Chroma Key
Post by: circular on May 25, 2012, 01:55:09 am
You're welcome.  :)

Notice the HueDiff function, which is useful if the background color is red, because then it ranges for example from 0 to 100 and from 65535 to 65435.

You may also consider using BGRAToGSBA instead of BGRAToHSLA. You might get a better result, but it is slower, so probably not adapted to this.
Title: Re: "Tween" two images & Chroma Key
Post by: BigChimp on June 12, 2012, 08:46:37 pm
Your library is amazing.  I just need to get this integrated into the 5dpo webcam stuff now and published on github.
Hi Blacksheep,

Sorry for prodding you a bit, but any news on the integration/publication of the source code? I'd think such a project would be a great advertisement for Lazarus...

Note: I'm probably not going to use this code myself soon, and I understand if you need to polish some stuff etc, but I think having it out there would be very interesting for a lot of people...

Thanks,
BigChimp
Title: Re: [SOLVED] - "Tween" two images (Onionskin) & Chroma Key
Post by: TheBlackSheep on June 13, 2012, 12:58:43 pm
Hi BigChimp

no problem with the prodding - I must admit have been feeling rather guilty on this front - circular has done wonders with this library and it will be good to show off some of the more unusual features of functionality within it (especially as he found/fixed some issues whilst trying to help me). 

Unfortunately, I've been tied up with work commitments recently (I'm changing job and have trying to get certain requirements for that out of the way) but will get back to this particular piece of work as soon as I can. 

TheBlackSheep
Title: Re: [SOLVED] - "Tween" two images (Onionskin) & Chroma Key
Post by: BigChimp on June 13, 2012, 01:27:37 pm
I understand. Good luck on the job front and we'll see it when it's done ;)
Title: Re: [SOLVED] - "Tween" two images (Onionskin) & Chroma Key
Post by: circular on June 13, 2012, 03:33:18 pm
We all have things to do apparently. Well, it's still better to have something to do, right?  :)

I was thinking about guilt in this context. Guilt means that we do something that is negative. But you don't. You think that you haven't done something positive, that's neutral. It's neither positive neither negative. So it does not make sense to feel guilty.

If you can do things, it will be positive, and we would be grateful. If you can't, that's neutral, it's ok.

I suppose it's rather about choosing how to organize time.
Title: Re: [SOLVED] - "Tween" two images (Onionskin) & Chroma Key
Post by: lainz on June 13, 2012, 07:51:56 pm
We all have things to do apparently. Well, it's still better to have something to do, right?  :)

I was thinking about guilt in this context. Guilt means that we do something that is negative. But you don't. You think that you haven't done something positive, that's neutral. It's neither positive neither negative. So it does not make sense to feel guilty.

If you can do things, it will be positive, and we would be grateful. If you can't, that's neutral, it's ok.

I suppose it's rather about choosing how to organize time.

You're a great philosopher.
Title: Re: [SOLVED] - "Tween" two images (Onionskin) & Chroma Key
Post by: circular on June 13, 2012, 08:38:48 pm
Thanks.  :)
Title: Re: [SOLVED] - "Tween" two images (Onionskin) & Chroma Key
Post by: lainz on June 14, 2012, 12:07:02 am
Really, you must write those things in a blog, i think, those can be helpfull for some people.

I draw some art here http://lainzblog.blogspot.com/ nothing related but people like the designs.
Title: Re: [SOLVED] - "Tween" two images (Onionskin) & Chroma Key
Post by: circular on June 14, 2012, 10:04:42 am
Oh that's nice.

Well I do not have enough time now, but that's a good idea.
TinyPortal © 2005-2018