ImageEn for Delphi and C++ Builder ImageEn for Delphi and C++ Builder

 

ImageEn Forum
Profile    Join    Active Topics    Forum FAQ    Search this forumSearch
Forum membership is Free!  Click Join to sign-up
Username:
Password:
Save Password
Forgot your Password?

 All Forums
 ImageEn Library for Delphi, C++ and .Net
 ImageEn and IEvolution Support Forum
 Efficient MultiBitmap saving?
 New Topic  Reply to Topic
Next Page
Author Previous Topic Topic Next Topic
Page: of 2

PeterPanino

935 Posts

Posted - Feb 14 2024 :  09:44:38  Show Profile  Reply
I capture a window with CaptureFromScreen multiple times, appending each time the images to a TIEMultiBitmap, and then at the end, save the MultiBitmap as a GIF file:

FMultiBitmap.Write(ThisTempFile);


The saving of the GIF file takes more than 10 seconds (!) with the MultiBitMap containing appr. 100 images. Is there a way to make this more efficient? For example by:

1. CaptureFromScreen to a more efficient image format?

2. Converting the MultiBitmap images to a more efficient image format before saving?

3. Is it possible to handle a progress bar while saving the MultiBitmap to show the saving progress?

PeterPanino

935 Posts

Posted - Feb 14 2024 :  10:31:14  Show Profile  Reply
I have now set the MultiBitmap Params each time when appending an image:

// append the screenshot bitmap to the gif multi bitmap:
  var idx := FMultiBitmapGIF.AppendImage(FIOGIFScreenshot.IEBitmap);
  FMultiBitmapGIF.Params[idx].BitsPerSample := 8;
  FMultiBitmapGIF.Params[idx].SamplesPerPixel := 1;
  FMultiBitmapGIF.Params[idx].GIF_DelayTime := 10;


Unfortunately, this did not optimize the efficiency regarding file size and saving time.
Go to Top of Page

PeterPanino

935 Posts

Posted - Feb 14 2024 :  13:29:01  Show Profile  Reply
I have now implemented the progress bar:

FMultiBitmap := TIEMultiBitmap.Create; // for storing the animated GIF    
FMultiBitmap.OnProgress := MultiBitmapProgress;
	
procedure TForm1.MultiBitmapProgress(Sender: TObject; per: integer);
begin
  ProgressBarSave.Position := per;
end;


And it works:



However, saving to the GIF file when the MultiBitmap contains a few hundred images takes a VERY LONG time! Isn't there a way to make this more efficient?
Go to Top of Page

xequte

38690 Posts

Posted - Feb 14 2024 :  14:20:13  Show Profile  Reply
Hi Peter

I think the main issue will be the size of the images you are saving. I presume the resulting GIF file is huge (many MB).

What is the width and height of the GIF frames?

Nigel
Xequte Software
www.imageen.com
Go to Top of Page

PeterPanino

935 Posts

Posted - Feb 14 2024 :  14:58:24  Show Profile  Reply
Hi Nigel,

The image sizes can vary depending on the window from which the user chooses to make a GIF video. Is there a way to optimize the single images before saving them to the GIF file?

The documentation describes a feature IEOptimizeGIF that optimizes an EXISTING animated GIF file.

Is there such a similar feature for a GIF file already loaded in the TIEMultiBitmap in memory? That would be optimal for reducing the time to save the TIEMultiBitmap to a GIF file.
Go to Top of Page

PeterPanino

935 Posts

Posted - Feb 14 2024 :  16:38:19  Show Profile  Reply
Improved the UI:

Go to Top of Page

xequte

38690 Posts

Posted - Feb 14 2024 :  22:28:19  Show Profile  Reply
Hi Peter

IEOptimizeGIF() is a file only method at this time. It should not greatly improve save speed to optimize the GIF before the save because usually the optimization is not significant. That is true for standard animated GIFs, but in this case you may get a better result. Have you compared the file size before and after calling IEOptimizeGIF()?

Have you considered using a true video format such as AVI?

Nigel
Xequte Software
www.imageen.com
Go to Top of Page

PeterPanino

935 Posts

Posted - Feb 15 2024 :  02:02:19  Show Profile  Reply
To implement a procedure for optimizing GIFs within an ImageEn.TIEMultiBitmap object, where consecutive, largely identical images are combined into a single image, one can follow these steps. This approach leverages the TIOParams.GIF_DelayTime property to extend the display time of the first image in a series of identical or nearly identical images and then delete the subsequent similar images. This method can significantly reduce the size of the ImageEn.TIEMultiBitmap object without a noticeable loss in quality or functionality.

Steps for GIF Optimization:

1. Comparing Images: Determine whether two consecutive images are identical or nearly identical. This could be done through a direct pixel comparison or through a less computationally intensive method, such as comparing hash values of the images.

2. Combining Images: If two consecutive images are identified as identical, adjust the TIOParams.GIF_DelayTime of the first image by summing the delay times of the identical images.

3. Removing Subsequent Identical Images: Delete the subsequent images that have been combined with the first identical image.

Here's a pseudocode example illustrating these steps:

procedure OptimizeGIFImages(MultiBitmap: ImageEn.TIEMultiBitmap);
var
  i: Integer;
  Identical: Boolean;
begin
  i := 0;
  while i < MultiBitmap.Count - 1 do
  begin
    Identical := CompareImages(MultiBitmap.Bitmap[i], MultiBitmap.Bitmap[i + 1]);
    if Identical then
    begin
      // Increase the GIF_DelayTime of the current image
      MultiBitmap.Params[i].GIF_DelayTime := MultiBitmap.Params[i].GIF_DelayTime + MultiBitmap.Params[i + 1].GIF_DelayTime;
      
      // Delete the subsequent identical image
      MultiBitmap.Delete(i + 1);
    end
    else
      Inc(i);
  end;
end;

function CompareImages(Image1, Image2: TIEBitmap): Boolean;
begin
  // Implement a function to compare two images
  // Return True if the images are identical or nearly identical, otherwise False
end;
Go to Top of Page

xequte

38690 Posts

Posted - Feb 15 2024 :  15:10:06  Show Profile  Reply
Sorry Peter, I assumed the images in your TIEMultiBitmap were already unique.

Rather than filling the TIEMultiBitmap and then de-duping, you would be better to not append an image if it identical to the last one added.

i.e.

// Only add image if not identical to the last image
if ( mbmp.Count > 0 ) and ( newBitmap.GetHash = mbmp.ImageHash[ mbmp.Count - 1 ]) then
begin
  // Image is same as last one, increase display time
  mbmp.Params[ mbmp.Count - 1 ].GIF_DelayTime := mbmp.Params[ mbmp.Count - 1 ].GIF_DelayTime + newInterval;
end
else
begin
  // New image
  mbmp.AppendImage( newBitmap );
  mbmp.Params[ mbmp.Count - 1 ].GIF_DelayTime := newInterval;  
end;


This method uses an MD5 hash as a quick way to compare images, but you might prefer to exclude images that are "mostly" identical. Take a look at the options for CompareWith:

http://www.imageen.com/help/TImageEnProc.CompareWith.html

Nigel
Xequte Software
www.imageen.com
Go to Top of Page

PeterPanino

935 Posts

Posted - Feb 15 2024 :  16:53:18  Show Profile  Reply
Hi Nigel,

Let's discuss this later, as there is another problem:

When I save the TIEMultiBitmap to a GIF file:

FMultiBitmapGIF.Write(ThisTempFile);

...and then play it in an external viewer such as IrfanView, then it plays at its regular size and at the original speed. Everything OK!

But when I assign the TIEMultiBitmap to a TImageEnMView on another form (by copying the Params):

formPreview.ImageEnMView1.AssignEx(FMultiBitmapGIF, True);
formPreview.ImageEnMView1.Playing := True;

...then the GIF plays way too fast, and the displayed size of the GIF is too small!

I have not found any settings to make the GIF play
1. at its original size and
2. at the original speed set by Params.

The documentation says: "The default playback speed is set for each frame by its ImageDelayTime time." But this is not the case in this case.
Go to Top of Page

xequte

38690 Posts

Posted - Feb 15 2024 :  22:09:00  Show Profile  Reply
Hi Peter

Please see the documentation:

https://www.imageen.com/help/TImageEnMView.AssignEx.html

You need to pass TRUE to include the params when assigning.


Regarding the size, what is the value of Zoom for both controls?

Nigel
Xequte Software
www.imageen.com
Go to Top of Page

PeterPanino

935 Posts

Posted - Feb 16 2024 :  01:44:08  Show Profile  Reply
Hi Nigel,

Please read my posting - as I wrote, I DO pass TRUE when assigning:

formPreview.ImageEnMView1.AssignEx(FMultiBitmapGIF, True);
formPreview.ImageEnMView1.Playing := True;


Regarding the size: FMultiBitmapGIF is not a control but a TIEMultiBitmap Object. And the formPreview.ImageEnMView1.Zoom = 100.
Go to Top of Page

PeterPanino

935 Posts

Posted - Feb 16 2024 :  03:03:16  Show Profile  Reply
I have now tested it:

1. Saved the GIF from the TIEMultiBitmap object:

FMultiBitmapGIF.Write(ThisTempFile);


...and then loaded it in the \Demos\ImageEditing\AnimatedGIF demo: It plays at normal speed.

But when ASSIGNING the TIEMultiBitmap (by passing TRUE) to the TImageEnMView, it plays way too fast. This proves that ASSIGNING does in fact not pass the Params although I did pass True:

formPreview.ImageEnMView1.AssignEx(FMultiBitmapGIF, True);
formPreview.ImageEnMView1.Playing := True;


Go to Top of Page

PeterPanino

935 Posts

Posted - Feb 16 2024 :  04:00:12  Show Profile  Reply
I have now empirically found the bug - it is in TImageEnMView.AssignEx:

I have measured ImageEnMView1.ImageDelayTime[0] right after assigning:

formPreview.ImageEnMView1.AssignEx(FMultiBitmapGIF, TRUE);
CodeSite.Send('formPreview.ImageEnMView1.ImageDelayTime[0]', formPreview.ImageEnMView1.ImageDelayTime[0]);


... which gives this result:

TformCapture.SpeedButtonStopClick: ImageDelayTime[0] = 0.00

This proves that AssignEx, in fact, does not copy the Params, although CopyParams is passed as TRUE! TImageEnMView sets ImageDelayTime[n] correctly after loading a GIF from a file, but NOT when assigning with AssignEx!


Can you confirm this?

Go to Top of Page

PeterPanino

935 Posts

Posted - Feb 16 2024 :  06:08:37  Show Profile  Reply
I have now solved the SIZE problem:

As TImageEnMView during the animation incorrectly sets the animated image size to the TImageEnMView THUMBNAIL size (and not to each frame image's original size as it correctly should be!), I have to correct this manually:

formPreview.ImageEnMView1.AssignEx(FMultiBitmapGIF, True);
formPreview.ImageEnMView1.ThumbWidth  := FMultiBitmapGIF.ImageWidth[0];
formPreview.ImageEnMView1.ThumbHeight := FMultiBitmapGIF.ImageHeight[0];


Now, the animated image size has the exact original size.

BTW, there should be a separate TImageEnAnimationPlayer component that fixes all these issues and only plays animated GIFs, etc. (and can load them by both file loading and assigning).
Go to Top of Page

PeterPanino

935 Posts

Posted - Feb 16 2024 :  09:25:01  Show Profile  Reply
The animation speed issue is still not solved:

I capture the window in a Timer event (Interval = 100) from a TImageEnIO object:

procedure TformCapture.TimerCaptureTimer(Sender: TObject);
begin
  // create the screenshot from the target window:
  var h := FTargetHandle;
  if FIOGIFScreenshot.CaptureFromScreen(iecsSpecifiedWindow, FIncludeMouseCursor, h) = False then
    FIOGIFScreenshot.CaptureFromScreen(iecsSpecifiedWindow2, FIncludeMouseCursor, h);

  // append the screenshot bitmap to the gif multibitmap:
  var idx := FMultiBitmapGIF.AppendImage(FIOGIFScreenshot.IEBitmap);
  FMultiBitmapGIF.Params[idx].BitsPerSample := 8;
  FMultiBitmapGIF.Params[idx].SamplesPerPixel := 1;
  FMultiBitmapGIF.Params[idx].GIF_DelayTime := 10; // Display each frame for 1/10th of a second = 100 milliseconds
end;


After the Timer has been disabled, I assign the MultiBitmap to ImageEnMView1:

// Show Preview:
  if CheckBoxPreview.Checked then
  begin
    formPreview.ImageEnMView1.Clear;
    formPreview.ImageEnMView1.AssignEx(FMultiBitmapGIF, True); // CopyParams does not work!

    formPreview.ImageEnMView1.ThumbWidth  := FMultiBitmapGIF.ImageWidth[0];
    formPreview.ImageEnMView1.ThumbHeight := FMultiBitmapGIF.ImageHeight[0];
    for var i := 0 to formPreview.ImageEnMView1.ImageCount - 1 do
      formPreview.ImageEnMView1.ImageDelayTime[i] := FMultiBitmapGIF.Params[i].GIF_DelayTime / 10; // delay ratio

    formPreview.Show;
    SetForegroundWindow(formPreview.Handle);
    formPreview.ImageEnMView1.Playing := True;
    formPreview.ImageEnMView1.Zoom := 100;
  end;


However, the animation is still running too fast (the last two digits are SECONDS!):



What is wrong here?
Go to Top of Page

PeterPanino

935 Posts

Posted - Feb 16 2024 :  12:14:24  Show Profile  Reply
I found the error - the delay ratio was wrong. It should be:

for var i := 0 to formPreview.ImageEnMView1.ImageCount - 1 do
  formPreview.ImageEnMView1.ImageDelayTime[i] := FMultiBitmapGIF.Params[i].GIF_DelayTime * 10; // delay ratio


Now, it works perfectly:

Go to Top of Page

PeterPanino

935 Posts

Posted - Feb 16 2024 :  16:45:13  Show Profile  Reply
As for the optimizing, I decided to do the optimizing AFTER the capture because:
With large images, the process of getting the hash could potentially overlap two timer event cycles and thus lead to errors.

So, I implemented the optimization:

procedure OptimizeMultiBitmap(AMultiBitmap: TIEMultiBitmap);
var
  i: Integer;
begin
  if not Assigned(AMultiBitmap) then EXIT;
  if AMultiBitmap.Count < 2 then EXIT;
  // Iterate through all images of the AMultiBitmap from High to Low, excluding the first one
  // because there's no previous image to compare with for the first image:
  for i := AMultiBitmap.Count - 1 downto 1 do
  begin
    // Compare the current image hash with the previous image hash:
    if AMultiBitmap.ImageHash[i] = AMultiBitmap.ImageHash[i - 1] then
    begin
      // If the hashes are the same, sum the current image's GIF_DelayTime
      // to the previous image's GIF_DelayTime:
      AMultiBitmap.Params[i - 1].GIF_DelayTime := AMultiBitmap.Params[i - 1].GIF_DelayTime + AMultiBitmap.Params[i].GIF_DelayTime;
      // Delete the current image since it's a duplicate:
      AMultiBitmap.DeleteImage(i);
    end;
  end;
end;


It works perfectly: When capturing the clock test video, I could reduce the number of GIF frames from over 300 to 26 while the animation remains the same!
Go to Top of Page

xequte

38690 Posts

Posted - Feb 16 2024 :  18:02:33  Show Profile  Reply
Hi Peter

I'm sorry, i cannot reproduce that. I tested as follows:

var
  mbmp: TIEMultiBitmap;
begin
  mbmp := TIEMultiBitmap.Create();
  mbmp.Read( 'D:\Testing_Multimedia\GIF_Animated\ABUELAT.GIF' );
  ImageEnMView2.Clear;
  ImageEnMView2.AssignEx( mbmp, True );
  lblDebug.Caption := IntToStr( mbmp.Params[0].GIF_DelayTime ) + '/' + IntToStr( ImageEnMView2.MIO.Params[0].GIF_DelayTime );
  mbmp.Free;
end;


lblDebug.Caption was "25/25" as expected.

Nigel
Xequte Software
www.imageen.com
Go to Top of Page

PeterPanino

935 Posts

Posted - Feb 17 2024 :  04:10:42  Show Profile  Reply
Hi Nigel

The difference may be that you are READING the GIF from a FILE. I will try to make a test project and send it to you.
Go to Top of Page

PeterPanino

935 Posts

Posted - Feb 17 2024 :  04:17:43  Show Profile  Reply
BTW, I need an "AutoShrink" property for the TImageEnMView "Playing" mode. Is there a workaround for that?
Go to Top of Page
Page: of 2 Previous Topic Topic Next Topic  
Next Page
 New Topic  Reply to Topic
Jump To: