JPG fast loading lazarus / 39-Graphics ---------------------------------------------------------------------- user5 · 2015-08-05 17:59 Hi folks. I've searched the web and past postings of this forum looking for the fastest way to load graphics and the best that I've come up with is the code below which loads 450 240x180 jpgs in slightly less than 3 sec, which is pretty good. Does anyone know of something even better?  var AJpg: TJpegImage;  AJpg := TJpegImage.Create;  AJpg.LoadFromFile('c:\dir1\pix1.jpg');  Image1.picture.Bitmap.Assign(AJpg); Thanks ---------------------------------------------------------------------- user5 · 2015-08-06 04:26 I did some simple math on the stuff in the previous message and it turns out that the code shown loads a 6KB .jpg in .006 sec! That's pretty darn fast. All praise to Lazarus... ---------------------------------------------------------------------- derek.john.evans · 2015-08-06 05:20 EDIT: Updated the code to include a standard TBitmap loader. This reminds me of a task Ive been putting off for ages. Years ago, VisualCLX patched FreeCLX to use OleLoadPicture to load JPEG's, which was a lot faster back then. The patch can be found here: (Search for OleLoadPicture) http://unvclx.sourceforge.net/archive/index.php?version=v3.10&show=25& hl=1 I was unable to get OleLoadPicture working in Lazarus, but I have got OleLoadPictureFile working. Here is the unit file. You can switch between a Lazarus picture load, OleAut32 picture load via a boolean, soo.... I'd be interested in what the difference is, and should the Win32 LCL contain a similar patch? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit OleAut32PictureLoad; {$MODE DELPHI} interface uses Graphics, SysUtils; procedure BitmapLoadFromFile(const ABitmap: TBitmap; const AFileName: TFileName;  const AUseOle: Boolean);procedure PictureLoadFromFile(const APicture: TPicture; const AFileName: TFileName;  const AUseOle: Boolean); implementation uses  ActiveX, Classes, Forms, Types; function OleLoadPictureFile(varFileName: olevariant; var lpdispPicture: IDispatch): HResult;  stdcall external oleaut32dll Name 'OleLoadPictureFile'; const  HIMETRIC_INCH = 2540; procedure BitmapLoadFromFile(const ABitmap: TBitmap; const AFileName: TFileName;  const AUseOle: Boolean);var  LDispatch: IDispatch;  LPicture: IPicture;  LMetricX: OLE_XSIZE_HIMETRIC;  LMetricY: OLE_YSIZE_HIMETRIC;begin  LDispatch := nil;  if AUseOle and (OleLoadPictureFile(AFileName, LDispatch) = S_OK) then  begin    LPicture := LDispatch as IPicture;    LPicture.get_Width(LMetricX);  LPicture.get_Height(LMetricY);    ABitmap.SetSize(LMetricX * Screen.PixelsPerInch div HIMETRIC_INCH,      LMetricY * Screen.PixelsPerInch div HIMETRIC_INCH);    LPicture.Render(wireHDC(ABitmap.Canvas.Handle), 0, 0, ABitmap.Width,    ABitmap.Height, 0, LMetricY, LMetricX, -LMetricY, PRect(nil)^);  end else begin    ABitmap.LoadFromFile(AFileName);  end;end; procedure PictureLoadFromFile(const APicture: TPicture; const AFileName: TFileName;  const AUseOle: Boolean);begin  if AUseOle then  begin    BitmapLoadFromFile(APicture.Bitmap, AFileName, True);  end else begin    APicture.LoadFromFile(AFileName);  end;end; end. ---------------------------------------------------------------------- derek.john.evans · 2015-08-06 06:17 I just did some testing: (Tests are on a Intel Core 2 Duo 3.0 Windows XP. So, it might be different on Windows 7/8/Vista) 179 JPEG images. Total size 104 MB. OleAut32 (IPicture) = 41 seconds Lazarus TPicture = 55 Seconds EDIT: TBitmap.Assign(TJpegImage) also came in at 55 seconds (actually 56 seconds) TPicture.Bitmap.Assign(TJpegImage) also 55 seconds. Which isn't a big difference, but, if you are loading 100 pixel width thumbs, it takes 33 seconds. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure BitmapThumbLoadFromFile(const ABitmap: TBitmap; const AFileName: TFileName;  const AWidth: Integer);var  LDispatch: IDispatch;  LPicture: IPicture;  LMetricX: OLE_XSIZE_HIMETRIC;  LMetricY: OLE_YSIZE_HIMETRIC;begin  LDispatch := nil;  if OleLoadPictureFile(AFileName, LDispatch) = S_OK then  begin    LPicture := LDispatch as IPicture;    LPicture.get_Width(LMetricX);  LPicture.get_Height(LMetricY);    ABitmap.SetSize(AWidth, AWidth * LMetricY div LMetricX);    LPicture.Render(wireHDC(ABitmap.Canvas.Handle), 0, 0, ABitmap.Width,    ABitmap.Height, 0, LMetricY, LMetricX, -LMetricY, PRect(nil)^);  end else begin    raise Exception.Create('Unable to load thumb.');  end;end; An alternative is to inherit from TJPEGImage to get access to the TJPEGScale (Defined in FPReadJPEG) Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TJPEGScale = (jsFullSize, jsHalf, jsQuarter, jsEighth);  Which is fast, but the resulting quality can be unpredictable depending on the JPEG. What I've done in the past for a thumbnail view, is to use the fastest possible method for a draft view, so either TJPEGScale=jsEighth or maybe the OleAut32 stuff, then run a timer/thread to load improved thumbs. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type   TJPEGImageEx = class(TJPEGImage)  strict private    FScale: TJPEGScale;  protected    procedure InitializeReader(AImage: TLazIntfImage; AReader: TFPCustomImageReader); override;  public    property Scale: TJPEGScale read FScale write FScale;  end; procedure TJPEGImageEx.InitializeReader(AImage: TLazIntfImage; AReader: TFPCustomImageReader);begin  (AReader as TFPReaderJpeg).Scale := FScale;  inherited InitializeReader(AImage, AReader);end; Hope all that helps. Thats about as far as I went with JPEG loading. I know INTEL has a JPEG library, which I haven't tried: (Here is a doc that compares it to Microsoft's IPicture. http://www.drdobbs.com/inside-intels-jpeg-library/184405097 *** EDIT **** Curiosity got the better of me. So I looked for some Delphi INTEL JPEG Library code. After hacking some code from here, it seems the Intel Jpeg Lib loads the same images in 12 seconds! http://bouchez.info/myjpeg.html Here is my hackup version of MyJpeg. There are graphics libraries that support IJL which seems like it would be a good thing to investigate. www.wascal.net/bin/LazMyJpeg.zip ---------------------------------------------------------------------- user5 · 2015-08-06 20:24 derek.john.evan, thank you for your extremely interesting, extensive and potentially very exciting message on loading .jpgs quickly. I'm a little tired at the moment but later I'll certainly study what you posted and yes, it could be very helpful to me. It looks like you went to some effort and I'm sure that I and some other folks appreciate it.     I say your note is exciting because you may have given me the key I needed to be able to reduce the size of the program that I'm working on and to raise its playback performance level. Maybe, maybe not. I don't know yet. To explain as briefly as possible, my program currently has several methods of playing a project that will be made into a custom .mpg or .avi. One method uses MPlayer and while audio synchronization and loading speed are good, it can't display transparencies (as far as I know), it adds 30 Mb to the program size and exhibits some slight color shifts.     The other method loads the individual frames one after another from up to seven imported videos or animated .gifs into seven or less TImages for display while the program captures them into the project movie being made. The great advantage of this method is that transparencies are allowed, there are no color shifts, the self- adjusting sync is good and you basically see what you'll get in the finished movie.     The only drawback (up to now) is the fact that a project with more than two or three imported movies plays a bit slow because the program can't load all those images quite fast enough.     The thing is though, it almost is fast enough and if I can just increase the loading speed overall by a few hundredths of a second then everything will be peachy keen and I can do without MPlayer and its 30 Mb.     When I get a chance I may try out your suggested code and let ya know what the results are.     This program makes clips up to five minutes long which are then joined to any longer movie that may be under construction.     From the info that you gave, it appears that you're using uncompressed .jpgs that are quite large. The .jpgs that I'll be using will never be larger than 8 Kb each, with a maximum size of 528x386. That's pretty small storage space for a large picture and it helps with loading speeds.     Thanks again for your input. I enjoy sharing information too when I think I've got something useful to others, which isn't often for me because this forum has some sharp folks who may already know what I might say. ---------------------------------------------------------------------- user5 · 2015-08-07 22:07 I haven't seen anything that is faster than TJpegImage. derek.john.evans, I couldn't get any of the stuff that you posted to work correctly, which is probably because I'm doing something wrong. I downloaded Vampyre but I haven't used it yet.     For now, I'm going to have two methods of playback in my program; One using MPlayer (MPlayerControl) and the other one using TJpegImage. The second method works pretty well though it's load-sensitive and plays slightly slower than it should if there are a lot of videos (up to seven) in the project or the computer being used is old or if other programs are running at the same time.      I may try to use multithreading in conjuntion with TJpegImage; When it's time to display say, seven frames for capturing, the program (if possible) could start some threads to load six of the TImages at relatively the same time, followed by one more loading of the seventh TImage (To ensure that the code execution goes from 'parallel' execution back to linear execution).     If any of you guys are aware of anything that you know is faster than TJpegImage then I'd sure like to hear about it, but thanks in any event. ---------------------------------------------------------------------- derek.john.evans · 2015-08-08 05:38 Just to clarify. Are you saying you are pre converting selected movie frames to JPEG's which are then used to display in a movie frame navigation tool? ie: a preview Or, are are you converting the entire movie to JPEG's for editing, then joining them back together once editing is done? Are you using something like FFmpeg to extract all the JPEG's? Im unsure what your program actually is ---------------------------------------------------------------------- derek.john.evans · 2015-08-08 10:08 Ok, if you are doing what I think you are doing (and I might be completely wrong), the only solution that works here (2.8 P4 WinXP) is adjusting TJPEGScale before loading the JPEG. I used FFmpeg to convert a 7 minute movie to JPEG's at 24 FPS. The result is 10101 files which is about 7 * 60 * 24 = 10080. The frame size is 852 x 480, which is larger than what you say your max is (528x386). I then wrote a JPEG cache (50 slots), and a simple threaded buffer which reads 24 frames ahead of the frame position. The code chugs at Scale := jsFullSize but runs smoothly at jsHalf. I can get 4 movies running at jsQuarter and 8 movies using jsEighth. Performance := jpBestSpeed may help, but in my tests I cant see any significant speed increase. Here is the JPEGImageEx unit which gives you access to Scale. (EDIT: Performance is already avaliable) Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit JPEGImageEx; {$MODE DELPHI} interface uses  FPimage, FPReadJPEG, Graphics, IntfGraphics; type  TJPEGImageEx = class(TJPEGImage)  strict private    FScale: TJPEGScale;  protected    procedure InitializeReader(AImage: TLazIntfImage; AReader: TFPCustomImageReader); override;  public    property Scale: TJPEGScale read FScale write FScale;  end; implementation procedure TJPEGImageEx.InitializeReader(AImage: TLazIntfImage; AReader: TFPCustomImageReader);begin  (AReader as TFPReaderJpeg).Scale := FScale;  inherited InitializeReader(AImage, AReader);end; end. This is the code I used to test a cache/buffer idea. You may find ideas in there, but I wouldn't use the code. Not thread safe and not fully tested. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit JpegMovie; {$MODE DELPHI} interface uses  Classes, Contnrs, FPReadJPEG, Graphics,  JPEGImageEx, Math, SysUtils; type   TJPEGMovieFrame = class(TJPEGImageEx)  private    FFileName: TFileName;    procedure SetFileName(const AFileName: TFileName);  end;   TJPEGMovieCache = class(TObjectList)  private    FMaxSize: Integer;  public    constructor Create(const AMaxSize: Integer);    function LoadFromFile(const AFileName: TFileName): TJPEGMovieFrame;  end;   TJPEGMovieBuffer = class(TThread)  strict private    FMoviePath: String;    FFrameCache: TJPEGMovieCache;    FFrameCount: Integer;    FFrames: array of TJPEGMovieFrame;    FPosition: Integer;  protected    procedure Execute; override;    function GetFrameFileName(const AIndex: Integer): TFileName;  public    constructor Create(const AMoviePath: String);    destructor Destroy; override;  public    function GetFrame(const APosition: Integer): TJPEGImageEx;  end; implementation procedure TJPEGMovieFrame.SetFileName(const AFileName: TFileName);begin  FFileName := AFileName;  try    Scale := jsQuarter;    Performance := jpBestSpeed;    LoadFromFile(AFileName);  except  end;end; constructor TJPEGMovieCache.Create(const AMaxSize: Integer);begin  inherited Create(True);  FMaxSize := AMaxSize;end; function TJPEGMovieCache.LoadFromFile(const AFileName: TFileName): TJPEGMovieFrame;var  LIndex: Integer;begin  for LIndex := 0 to Count - 1 do  begin    Result := Items[LIndex] as TJPEGMovieFrame;    if SameFileName(Result.FFileName, AFileName) then    begin      Move(LIndex, 0);      Exit;    end;  end;  Result := TJPEGMovieFrame.Create;  Insert(0, Result);  if Count > FMaxSize then  begin    Delete(Count - 1);  end;  Result.SetFileName(AFileName);end; constructor TJPEGMovieBuffer.Create(const AMoviePath: String);begin  FMoviePath := AMoviePath;  FFrameCache := TJPEGMovieCache.Create(50);  FFrameCount := 0;  while FileExists(GetFrameFileName(FFrameCount + 1)) do  begin  Inc(FFrameCount);  end;  SetLength(FFrames, FFrameCount);  inherited Create(True);end; destructor TJPEGMovieBuffer.Destroy;begin  Terminate;  WaitFor;  inherited Destroy;  FreeAndNil(FFrameCache);end; function TJPEGMovieBuffer.GetFrameFileName(const AIndex: Integer): TFileName;begin  Result := FMoviePath + IntToStr(AIndex) + '.jpg';end; procedure TJPEGMovieBuffer.Execute;var  LIndex: Integer;begin  while not Terminated do  begin    for LIndex := Max(FPosition, 0) to Min(FPosition + 24, High(FFrames)) do    begin    FFrames[LIndex] := FFrameCache.LoadFromFile(GetFrameFileName(LIndex));    end;    Suspend;  end;end; function TJPEGMovieBuffer.GetFrame(const APosition: Integer): TJPEGImageEx;begin  FPosition := APosition;  if InRange(APosition, 0, High(FFrames)) then  begin    Resume;    Result := FFrames[APosition];  end else begin    Result := nil;  end;end; end. ---------------------------------------------------------------------- user5 · 2015-08-09 14:32 Sorry I didn't respond sooner. I've been a bit tied up. The attached zip file contains a picture, a text file and movie file that pretty much show what the program does. That's my voice in the movie.     The program is about 85% finished. Yes, it plays and records from intact videos as well as individual frames.     You seem like you really know your beans and I'm studying the stuff you posted.     Say, are you familiar with multithreading and would you look at some multithreading code that I need help on? If not, then I can post my multithreading query in this forum's General section. I know that this forum's archive has a lot of stuff on multithreading but a lot of it is over my head. I'm pretty new to multithreading but perhaps it's not what I need. Thanks again. ---------------------------------------------------------------------- derek.john.evans · 2015-08-09 15:50 Mmmm. That sounds like a lot of movie/image compositing. Its hard to say what I'd do without knowing what has been done. I'd implement a couple of object resource caches for TBitmaps and TJPEGImages. I'd then have a thread for each movie which renders frames ahead of the movie frame position and stores the results in an array of TBitmap (which is the size of the number of frames in the movie). The idea is to set the movie position in the main thread, and the threads will run in the background filling in the TBitmap array. You grab the TBitmap for the current movie position and render. I guess using caches removes the need to allocate/free objects, which simplifies the relationship between the main thread and the processing threads. Hope that helps. It might be completely wrong for what you want. Also, I think if your code is working fine, but just cant handle more projects, then quality reduction and frame skipping are solutions. ---------------------------------------------------------------------- user5 · 2015-08-09 17:36 When a movie is being made by capturing a specific area of the screen in my program, the user at that point can choose the rate at which the frames are processed, as well as whether that processing is done from disk or memory but the idividual frames are in fact stored on disk. I'm amazed that loading jpgs from disk is actually very fast, almost as fast as memory! You are quite correct that all those frames can take up a lot of disk space, too much to be stored in memory. A five minute clip (The maximum length that may be joined to a longer movie that's under construction) at 30 fps = (9000 frames * 8 Kb) = 72 Mg. If the project has the maximum number of imported videos (7) then that's 504 Mb temporary storage space in addition to an occasional full lenght converted copy of the original import. I'll probably lower the maximum clip size to 3 minutes. Each clip is like a scene in a movie. Most computers can temporarily spare 302 Mb of space but the program also checks each step of the way to make sure that there's enough disk space and the amount of needed space won't always be the maximum. The program also does some tricks to keep that space as small as possible. This frame stuff is pretty recent and I may make it be simply another option for the user. MPlayerControl is NOT load sensitive while this new frame stuff is, but the frame method is a more perfect playback as long as the computer processor isn't over burdened. You sound like you know about multithreading so I'll post the best functioning multithreading code that I currently have and describe what I'm trying to do with it and what I need from it. Sorry that the movie I attached was so short but I had a hard time keeping within this forum's maximum file attachment size limits. All praise to Lazarus of course and thanks so much for helping me. All the rest of the folks here have been helping me for a long time. There are some really sharp folks here. Jeez ---------------------------------------------------------------------- user5 · 2015-08-10 12:53 derek.john.evan, your suggestion about threading is what I planned to do and your ideas about processing the captured bitmaps is similar to what the program already currently does, if the user decides to use memory instead of disk for processing. We are thinking along the same lines. The program uses a ram disk instead of an array but both methods use live memory. The program gives the user a wide and convenient range of options to get the most speed as possible. Of course, one downside to using a ram disk is that its reserved memory is not available for computer processing and it's also not very dynamic but I'm going to leave it the way it is. I need to move on here if I'm going to have the prototype ready in five months. I might post my message about multithreading in this forum's General section because it's about more than graphics and I might reach more folks there. If I do, then I'll mention this thread. Since you have tried so much to help me, as a courtesy to you I've attached to this message some screenshots of the most important windows of this program. The Custom page is the main activity page. The other screenshots are self-explanatory. The screenshots were made while testing the program on an older 512 Mb Dell computer. It runs well on my faster computer too. I'm designing the program so that it will run on any Windows computer. Might adapt it for other OS systems later. ---------------------------------------------------------------------- user5 · 2015-08-11 12:49 Help! I got all excited about possibly using dspack for Lazarus from github.com/TheBlackSheep/DSPack-Lazarus but when I try to install the component I get an error message directing me to the Package Graph window which tells me that I need the package pl_win_directx, which led me to CodeTyphoon. Never heard of it. Do I download and install CodeTyphoon and then try again? I'm sorry that I'm always needing help but I would sure appreciate some here. derek.john.evan, I know that I've already presumed upon you greatly but are you familiar with dspack for Lazarus? Thanx. ---------------------------------------------------------------------- user5 · 2015-08-11 13:00 Well, I just found out that the zip file for CodeTyphoon is almost 600 Mb so unless there's something I don't know then I guess this won't work out. ---------------------------------------------------------------------- derek.john.evans · 2015-08-11 13:31 avra posted information about these links. https://bitbucket.org/avra/ct2laz http://forum.lazarus.freepascal.org/index.php?topic=28897.0 http://www.pilotlogic.com/sitejoom/index.php/forum/installation- setup/2963-sourcefilemanager-pas Direct links to Typhon components: http://www.pilotlogic.com/codetyphon/current/src/typhon_src.7z http://www.pilotlogic.com/codetyphon/current/src/CodeOcean.7z ---------------------------------------------------------------------- derek.john.evans · 2015-08-11 13:37 Btw, thats some interesting GUI designing you have going on there. Reminds me of some old Amiga video editing software I used. ---------------------------------------------------------------------- user5 · 2015-08-11 14:21 I found and successfully installed the package pl_win_directx into a Lazarus project and then tried to install the dspack package again. Then I got a message saying that I need directshow9. Following error messages, I downloaded and installed directshow9.pas, directdraw.pas and dxtypes.pas but I kept getting error messages so I gave up. I've heard that directx9 includes directshow9 but I don't know if installing directx9 will help me. Sure is a big hassle to install DSpack. I've been searching this forum's archives and trying to do my homework but at the moment I'm stuck. I really wanted to try dspack because it looked pretty cool. derek.john.evan, I will check out the links you posted and thanks. ---------------------------------------------------------------------- briantokyo · 2020-08-12 03:33 In Delphi this is ultra FAST: https://blog.synopse.info/?post/2010/03/14/Fast-JPEG-decoder-using- SSE/SSE2 but if you are on a W10 machine, GDI+ is probably faster: https://github.com/synopse/mORMot/blob/master/SynGdiPlus.pas SynGDIPlus.TJpegImage.Create -> load -> .ToBitmap ---------------------------------------------------------------------- avra · 2020-08-12 08:25 Quote from: user5 on August 11, 2015, 04:21:00 pmSure is a big hassle to install DSpack. I've been searching this forum's archives and trying to do my homework but at the moment I'm stuck.DSPack is 8 years old. At the time, it used to work with CodeTyphon fork of Lazarus and it's pl_win_directx which also worked directly in Lazarus. Since then CodeTyphon has changed quite a bit, and decided to change the names of many classes, components and even files, breaking Lazarus compatibility at many levels. I didn't like such change so I made ct2laz tool first, and then later ct4laz which can be downloaded via OPM. This means that DSPack should be more close to Lazarus then new CodeTyphon. Enough for introduction. You should show me the exact error message. If I have to guess it may be 2 things: 1) If you can not start IDE then you need to put d3dx81ab.dll next to your Lazarus EXE. You need to have 32bit IDE version for this. 2) I have just checked and it seams that OPM does not deliver my original pl_0_libs and many DLLs were deleted. OPM version has 1MB and my version has 23MB. Therefore I advise you to try to download all needed pl_* packages from directly from my site (https://bitbucket.org/avra/ct4laz/downloads/), uninstall OPM version and install my original version. When DLLs are missing, pl_* packages install fine, but lots of examples simply do not work. You should also read about this DirectX problem on Win10: https://forum.lazarus.freepascal.org/index.php/topic,39508.msg280694.h tml#msg280694