Recent

Author Topic: What's wrong with my code?  (Read 5712 times)

justnewbie

  • Sr. Member
  • ****
  • Posts: 273
What's wrong with my code?
« on: March 06, 2021, 01:05:51 pm »
Hi guys,
I'm working on a blur effect code, but it freezes. Why?
Code: Pascal  [Select][+][-]
  1. procedure TForm1.MenuBlurClick(Sender: TObject);
  2. var i,j: word;
  3.     origBitmap: TBitmap;
  4.     resultBitmap: TBitmap;
  5.     rgbColor: longint;
  6.     red, green, blue: byte;
  7.     red1, green1, blue1: byte;
  8.     red2, green2, blue2: byte;
  9.     red3, green3, blue3: byte;
  10.     red4, green4, blue4: byte;
  11.     avgRed, avgGreen, avgBlue: byte;
  12. begin
  13.  
  14.      resultBitmap := TBitmap.Create;
  15.      resultBitmap.Width := Image1.Width;
  16.      resultBitmap.Height := Image1.Height;
  17.  
  18.      origBitmap := TBitmap.Create;
  19.      origBitmap.Assign(Image1.Picture.Graphic);
  20.  
  21.      for i := 1 to Image1.Width - 2 do
  22.        for j := 1 to Image1.Height - 2 do
  23.        begin
  24.          rgbColor := ColorToRGB(origBitmap.Canvas.Pixels[i,j]);
  25.          blue := GetBValue(rgbColor);
  26.          green := GetGValue(rgbColor);
  27.          red := GetRValue(rgbColor);
  28.  
  29.          rgbColor := ColorToRGB(origBitmap.Canvas.Pixels[i - 1, j]);
  30.          blue1 := GetBValue(rgbColor);
  31.          green1 := GetGValue(rgbColor);
  32.          red1 := GetRValue(rgbColor);
  33.  
  34.          rgbColor := ColorToRGB(origBitmap.Canvas.Pixels[i, j - 1]);
  35.          blue2 := GetBValue(rgbColor);
  36.          green2 := GetGValue(rgbColor);
  37.          red2 := GetRValue(rgbColor);
  38.  
  39.          rgbColor := ColorToRGB(origBitmap.Canvas.Pixels[i, j + 1]);
  40.          blue3 := GetBValue(rgbColor);
  41.          green3 := GetGValue(rgbColor);
  42.          red3 := GetRValue(rgbColor);
  43.  
  44.          rgbColor := ColorToRGB(origBitmap.Canvas.Pixels[i + 1, j]);
  45.          blue4 := GetBValue(rgbColor);
  46.          green4 := GetGValue(rgbColor);
  47.          red4 := GetRValue(rgbColor);
  48.  
  49.          avgRed := (red1+red2+red3+red4) div 4;
  50.          avgGreen := (green1+green2+green3+green4) div 4;
  51.          avgBlue := (blue1+blue2+blue3+blue4) div 4;
  52.  
  53.          rgbColor := ((red+avgRed) div 2) + (((green+avgGreen) div 2)*256) + (((blue+avgBlue) div 2)*65536);
  54.  
  55.          resultBitmap.Canvas.Pixels[i, j] := rgbColor;        
  56.        end;
  57.      Image1.Picture.Bitmap := resultBitmap;
  58. end;

Thaddy

  • Hero Member
  • *****
  • Posts: 10787
Re: What's wrong with my code?
« Reply #1 on: March 06, 2021, 01:27:14 pm »
real programmers count from zero, not one.

justnewbie

  • Sr. Member
  • ****
  • Posts: 273
Re: What's wrong with my code?
« Reply #2 on: March 06, 2021, 01:33:37 pm »
real programmers count from zero, not one.
Thanks.
1./ I am not a real programmer.
2./ There is i-1 and j-1 in the cycle, real programmers should notice it  :)
BTW, any answer to my original question?

RayoGlauco

  • Full Member
  • ***
  • Posts: 109
  • Beers: 1567
Re: What's wrong with my code?
« Reply #3 on: March 06, 2021, 01:36:59 pm »
Your code works for me.
Maybe your image is too big, and it takes a very big number of iterations.

To err is human, but to really mess things up, you need a computer.

justnewbie

  • Sr. Member
  • ****
  • Posts: 273
Re: What's wrong with my code?
« Reply #4 on: March 06, 2021, 01:38:27 pm »
Your code works for me.
Maybe your image is too big, and it takes a very big number of iterations.
Strange.
I tried with small image too with no success.

wp

  • Hero Member
  • *****
  • Posts: 8410
Re: What's wrong with my code?
« Reply #5 on: March 06, 2021, 01:56:14 pm »
You are mixing up the size of the Image component and the size of the bitmap - the size of the image may be different from the size of contained bitmap. Therefore, your code may run out of the allocated memory.

Better for the first lines of your code:
Code: Pascal  [Select][+][-]
  1.      resultBitmap := TBitmap.Create;
  2.      resultBitmap.Width := Image1.Picture.Width;  // Image1.Width;
  3.      resultBitmap.Height := Image1.Picture.Height;  //Image1.Height;
  4.  
  5.      origBitmap := TBitmap.Create;
  6.      origBitmap.Assign(Image1.Picture.Graphic);
  7.  
  8.      for i := 1 to origBitmap.Width do // Image1.Width - 2 do   /// why -2?
  9.        for j := 1 to origBitmap.Height do //Image1.Height - 2 do

When you have a "large" image you may not be happy with the speed of this routine. This is because you use the Pixels[] property which has a huge overhead and is very slow. Better to use ScanLine or intermediate FCL images. But this is another topic - solve the current one first.
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

winni

  • Hero Member
  • *****
  • Posts: 2357
Re: What's wrong with my code?
« Reply #6 on: March 06, 2021, 02:10:04 pm »
Hi!

Two tips:

a) use BGRAbitmap (from the Online Package Manager) because TBitmap is an "All-OS-Agreement" that is very slow

b) Using TBitmap you can do in the mid of the loop:
   
   
Code: Pascal  [Select][+][-]
  1.  if j = 1 then
  2.      begin
  3.      label1.Caption := 'I='+IntToStr(i)+'  J='+IntToStr(J);
  4.      application.ProcessMessages:
  5.      end;
  6.  
   
With this code you can see if your code is still working but is only slow.

Winni   

PS.: Another advantage of BGRAbitmap:

if you try to get the color of a pixel outside the boundaries your app wont crash like with  TBitmap but just returns a transparent pixel (0,0,0,0).   


« Last Edit: March 06, 2021, 02:16:43 pm by winni »

Handoko

  • Hero Member
  • *****
  • Posts: 4233
  • My goal: build my own game engine using Lazarus
Re: What's wrong with my code?
« Reply #7 on: March 06, 2021, 02:25:12 pm »
Don't use TCanvas for any serious project.

I ever tried to write a 2D side scrolling space shooting game using Lazarus - TCanvas only, without any third party graphics library. It works, a bit slow but good enough to run on my old Dual Core laptop. That because I heavily optimized the code. You can search the forum for Deep Platformer, a simple game to prove that you can write games using TCanvas only. It works because it used only simple graphics and optimized the code.

TCanvas is okay for showing some dots, lines, circles and texts. It is okay for GUI application but it does not optimize for performance, even you run it on an Intel i9 RTX 2080, it's still slow because it is not optimized to fully use the power your machine can offer. For beginners, TCanvas is easy to learn and work with. But soon you will realize, it is not suitable for any serious projects. Scanline command is faster but still not hardware optimized. Try other graphics libraries, invest some time on it, you will be glad for doing it.

You may interest to know:

Platformer
https://forum.lazarus.freepascal.org/index.php/topic,44908.0.html

Graphics libraries available for Lazarus/FPC
https://wiki.freepascal.org/Graphics_libraries
« Last Edit: March 06, 2021, 02:28:34 pm by Handoko »

engkin

  • Hero Member
  • *****
  • Posts: 2722
Re: What's wrong with my code?
« Reply #8 on: March 06, 2021, 03:17:33 pm »
Instead of Word, use a type that includes negative numbers like Integer for the loop variables.

jamie

  • Hero Member
  • *****
  • Posts: 4472
Re: What's wrong with my code?
« Reply #9 on: March 06, 2021, 04:07:24 pm »
Use scan line instead to obtain a pointer to a row of known pixel formats..

It most likely is working but slow on your end..

Also, if you want to use a WORD (2 byte type) , try using SmallInt instead..

Laz does not fully support the windows way of working with Tbitmaps so it becomes a little ringgit get things to work with examples found  abroad.

 If you still have issues I'll ding into my Delphi archives and see if I can get something that works in Laz for you.

Btw, you don't indicate the Target, Laz and compiler version ?
The only true wisdom is knowing you know nothing

justnewbie

  • Sr. Member
  • ****
  • Posts: 273
Re: What's wrong with my code?
« Reply #10 on: March 06, 2021, 04:35:18 pm »
Thanks for the answers guys, especially the good advices for optimization.
Finally I found that the code works but extremely slow.
BUT! I wrote this code many years ago with Delphi (Windows) and worked well.
The Delphi version makes the blur like a shot (some milliseconds) on a 300x200 picture.
How is it possible that Lazarus does it in 53 seconds on the same 300x200 picture?!




jamie

  • Hero Member
  • *****
  • Posts: 4472
Re: What's wrong with my code?
« Reply #11 on: March 06, 2021, 04:43:27 pm »
Lazarus does not handle a Bitmap in the same way and thus suffers the raft of Lard when used that way..

Use the ScanLine to obtain an array of raw memory to the image.

make sure you use the same data type as that of the image... 24, 32 bit etc..
The only true wisdom is knowing you know nothing

wp

  • Hero Member
  • *****
  • Posts: 8410
Re: What's wrong with my code?
« Reply #12 on: March 06, 2021, 05:26:53 pm »
The Delphi version makes the blur like a shot (some milliseconds) on a 300x200 picture.
How is it possible that Lazarus does it in 53 seconds on the same 300x200 picture?!
Then your code must be doing something else during the calculation, such as repainting the image after changing the pixels.

My attached demo is based on your code (i.e. accessing the Pixels property) and it does it in less than 0.5 seconds.
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

justnewbie

  • Sr. Member
  • ****
  • Posts: 273
Re: What's wrong with my code?
« Reply #13 on: March 06, 2021, 05:45:06 pm »
Then your code must be doing something else during the calculation, such as repainting the image after changing the pixels.

My attached demo is based on your code (i.e. accessing the Pixels property) and it does it in less than 0.5 seconds.
Very strange, your code ran in 37 seconds for me.
There are no any special thing on my form, just some dialogs, see the full code:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, Menus, ExtDlgs,
  9.   ComCtrls, ExtCtrls, LCLIntf;
  10.  
  11.  
  12.  
  13. type
  14.  
  15.   { TForm1 }
  16.  
  17.   TForm1 = class(TForm)
  18.     Image1: TImage;
  19.     MainMenu1: TMainMenu;
  20.     MenuFile: TMenuItem;
  21.     MenuAdjustments: TMenuItem;
  22.     MenuExit: TMenuItem;
  23.     MenuAbout: TMenuItem;
  24.     N1: TMenuItem;
  25.     MenuSave: TMenuItem;
  26.     MenuNew: TMenuItem;
  27.     MenuOpen: TMenuItem;
  28.     MenuBlur: TMenuItem;
  29.     MenuEffects: TMenuItem;
  30.     MenuDrawing: TMenuItem;
  31.     OpenPictureDialog1: TOpenPictureDialog;
  32.     ProgressBar1: TProgressBar;
  33.     SavePictureDialog1: TSavePictureDialog;
  34.     ToolBar1: TToolBar;
  35.     procedure MenuBlurClick(Sender: TObject);
  36.     procedure MenuExitClick(Sender: TObject);
  37.     procedure MenuOpenClick(Sender: TObject);
  38.   private
  39.  
  40.   public
  41.  
  42.   end;
  43.  
  44. var
  45.   Form1: TForm1;
  46.  
  47. implementation
  48.  
  49. {$R *.lfm}
  50.  
  51. { TForm1 }
  52.  
  53.  
  54.  
  55. procedure TForm1.MenuOpenClick(Sender: TObject);
  56. begin
  57.   if OpenPictureDialog1.Execute then
  58.   begin
  59.     Image1.Picture.LoadFromFile(OpenPictureDialog1.FileName);
  60.   end;
  61. end;
  62.  
  63. procedure TForm1.MenuExitClick(Sender: TObject);
  64. begin
  65.   Close;
  66. end;
  67.  
  68. procedure TForm1.MenuBlurClick(Sender: TObject);
  69. var i,j: word;
  70.     origBitmap: TBitmap;
  71.     resultBitmap: TBitmap;
  72.     rgbColor: longint;
  73.     red, green, blue: byte;
  74.     red1, green1, blue1: byte;
  75.     red2, green2, blue2: byte;
  76.     red3, green3, blue3: byte;
  77.     red4, green4, blue4: byte;
  78.     avgRed, avgGreen, avgBlue: byte;
  79. begin
  80.  
  81.      resultBitmap := TBitmap.Create;
  82.      resultBitmap.Width := Image1.Picture.Width;
  83.      resultBitmap.Height := Image1.Picture.Height;
  84.  
  85.      origBitmap := TBitmap.Create;
  86.      origBitmap.Assign(Image1.Picture.Graphic);
  87.  
  88.      ProgressBar1.Min:=1;
  89.      ProgressBar1.Max:=Image1.Picture.Width-2;
  90.  
  91.      for i := 1 to Image1.Picture.Width-2 do
  92.        for j := 1 to Image1.Picture.Height-2 do
  93.        begin
  94.          rgbColor := ColorToRGB(origBitmap.Canvas.Pixels[i,j]);
  95.          blue := GetBValue(rgbColor);
  96.          green := GetGValue(rgbColor);
  97.          red := GetRValue(rgbColor);
  98.  
  99.          rgbColor := ColorToRGB(origBitmap.Canvas.Pixels[i - 1, j]);
  100.          blue1 := GetBValue(rgbColor);
  101.          green1 := GetGValue(rgbColor);
  102.          red1 := GetRValue(rgbColor);
  103.  
  104.          rgbColor := ColorToRGB(origBitmap.Canvas.Pixels[i, j - 1]);
  105.          blue2 := GetBValue(rgbColor);
  106.          green2 := GetGValue(rgbColor);
  107.          red2 := GetRValue(rgbColor);
  108.  
  109.          rgbColor := ColorToRGB(origBitmap.Canvas.Pixels[i, j + 1]);
  110.          blue3 := GetBValue(rgbColor);
  111.          green3 := GetGValue(rgbColor);
  112.          red3 := GetRValue(rgbColor);
  113.  
  114.          rgbColor := ColorToRGB(origBitmap.Canvas.Pixels[i + 1, j]);
  115.          blue4 := GetBValue(rgbColor);
  116.          green4 := GetGValue(rgbColor);
  117.          red4 := GetRValue(rgbColor);
  118.  
  119.          avgRed := (red1+red2+red3+red4) div 4;
  120.          avgGreen := (green1+green2+green3+green4) div 4;
  121.          avgBlue := (blue1+blue2+blue3+blue4) div 4;
  122.  
  123.          rgbColor := ((red+avgRed) div 2) + (((green+avgGreen) div 2)*256) + (((blue+avgBlue) div 2)*65536);
  124.  
  125.          resultBitmap.Canvas.Pixels[i, j] := rgbColor;
  126.          ProgressBar1.Position:=i;
  127.          Application.ProcessMessages;
  128.        end;
  129.      Image1.Picture.Bitmap := resultBitmap;
  130. end;
  131.  
  132. end.

Handoko

  • Hero Member
  • *****
  • Posts: 4233
  • My goal: build my own game engine using Lazarus
Re: What's wrong with my code?
« Reply #14 on: March 06, 2021, 06:28:35 pm »
My guess is you made the Application.ProcessMessages too responsive.

For example if you call it in X-Y loops of 300 x 300 counts then it will be called 90 thousands times. If each call takes about 1 milliseconds, then try to do the calculation then you will know it wastes a lot of time. You should reduce it's responsiveness.

If that really is the case, there are many things you can do. This is one of the tricks:
https://forum.lazarus.freepascal.org/index.php/topic,43806.msg307101.html#msg307101
« Last Edit: March 06, 2021, 06:34:52 pm by Handoko »

 

TinyPortal © 2005-2018