Recent

Author Topic: Convert BMP image to grayscale using track bar  (Read 5731 times)

AjL0ra

  • Newbie
  • Posts: 5
Convert BMP image to grayscale using track bar
« on: February 25, 2022, 06:08:46 am »
hi a have a project with multiple tasks and the first step is to convert a bmp image using a trackbar form

here is the code I tried to use but its showing a lot of errors in the Trackbar1Change, it would be nice if some of you could help me

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls,
  9.   ExtDlgs, LCLintf, ComCtrls, Menus;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   MATRGB = array of array of array of byte;   //definir tips propios
  16.  
  17.   TForm1 = class(TForm)
  18.     Button1: TButton;
  19.     Image1: TImage;
  20.     MainMenu1: TMainMenu;
  21.     MenuItem1: TMenuItem;
  22.     MenuItem2: TMenuItem;
  23.     MenuItem3: TMenuItem;
  24.     MenuItem4: TMenuItem;
  25.     OpenPictureDialog1: TOpenPictureDialog;
  26.     ScrollBox1: TScrollBox;
  27.     StatusBar1: TStatusBar;
  28.     TrackBar1: TTrackBar;
  29.  
  30.     procedure Button1Click(Sender: TObject);
  31.     procedure FormCreate(Sender: TObject);
  32.     procedure Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer
  33.       );
  34.     procedure MenuItem3Click(Sender: TObject);
  35.     procedure MenuItem4Click(Sender: TObject);
  36.     procedure TrackBar1Change(Sender: TObject);
  37.   private
  38.  
  39.   public
  40.    procedure BtoM (Al,An:Integer; var M: MATRGB; B:Tbitmap); //copiar de Imagen .BMP a MAtriz usando Scanline
  41.    procedure MtoB (Al,An:Integer; M: MATRGB; var B:Tbitmap); // copiar MAtriz a Bitmap
  42.  
  43.  
  44.   end;
  45.  
  46. var
  47.   Form1: TForm1;
  48.   ALTO, ANCHO : Integer;
  49.  
  50.   MatIm : MATRGB;
  51.  
  52.   BMAP  : TBitmap;  //manipular imagenes BMP, se debe crear antes usar
  53.  
  54. implementation
  55.  
  56. {$R *.lfm}
  57.  
  58. { TForm1 }
  59.  
  60. procedure tform1.MtoB (Al,An:Integer; M: MATRGB; var B:Tbitmap);
  61. var
  62. i,j,k  : Integer;
  63. p      :Pbyte;
  64. begin
  65.  
  66.   //copiar contenido de MatIm al Bitmap --> escribir sobre la imagen
  67.  
  68.  
  69.   for i:=0 to Al-1 do begin
  70.      B.BeginUpdate;
  71.      p:=B.ScanLine[i];
  72.      B.EndUpdate;
  73.  
  74.    for j:=0 to An-1 do begin
  75.        k:=3*j;
  76.  
  77.        p[k+2]:= M[i,j,0];
  78.        p[k+1]:= M[i,j,1];
  79.        p[k]:= M[i,j,2];
  80.  
  81.     end;//j
  82.  
  83.   end;  //i
  84.  
  85.  
  86.  
  87. end;
  88.  
  89. procedure tform1.BtoM (Al,An:Integer; var M: MATRGB; B:Tbitmap);
  90. var
  91. i,j,k   :  Integer;
  92. p       :  Pbyte;  //arreglo dinámico de tipos byte
  93.  
  94. begin
  95.  
  96.   for i:=0 to Al-1 do begin
  97.  
  98.      B.BeginUpdate;
  99.      p:=B.ScanLine[i];
  100.      B.EndUpdate;
  101.  
  102.  
  103.      {
  104.        p alamacena todo el renglon y sus valores RGB
  105.  
  106.        NxN
  107.  
  108.       N´2 -   N
  109.  
  110.      }
  111.  
  112.     for j:=0 to An-1 do begin
  113.  
  114.      //copiar a MatIm
  115.  
  116.      k:=3*j;
  117.      M[i,j,0]:=p[k+2];
  118.      M[i,j,1]:=p[k+1];
  119.      M[i,j,2]:=p[k];
  120.  
  121.  
  122.     end;
  123.  
  124.   end; //i
  125.  
  126.  
  127.  
  128. end;
  129.  
  130. procedure TForm1.Button1Click(Sender: TObject);
  131. var
  132.  
  133. i,j  :  Integer;
  134. c1   : Tcolor;
  135.  
  136. begin
  137.  
  138.   {  sintaxis general de if
  139.     if (condicion) then
  140.      begin
  141.  
  142.  
  143.      end
  144.  
  145.      else
  146.        begin
  147.  
  148.        end
  149.   }
  150.  
  151.  
  152.  
  153.   If OpenPictureDialog1.Execute then   //abrir cuadro de dialogo/archivo
  154.  
  155.      begin
  156.          Image1.Enabled:=True;
  157.          Image1.Picture.LoadFromFile(OpenPictureDialog1.FileName);
  158.          ALTO:= Image1.Height;
  159.          ANCHO:= Image1.Width;
  160.  
  161.          SetLength(MatIm,ALTO,ANCHO,3);    //matriz en que copiamos contenido RGB de cada pixel
  162.            {
  163.               LA dimensión 3 de MatIm respecta a los canales RGB,  R=0, G=1 , B=2
  164.                 MatIm[fila,columna,canal]
  165.               }
  166.  
  167.  
  168.  
  169.       //leer toda la información de la imagen
  170.  
  171.           for i:=0 to ALTO-1 do
  172.           begin
  173.              for j:=0 to ANCHO-1 do
  174.              begin
  175.  
  176.               c1:=Image1.Canvas.Pixels[j,i];
  177.  
  178.  
  179.  
  180.               //extraer la composicion RGB del pixel i,j  y almacenar en MatIm
  181.               MatIm[i,j,0]:= GetRvalue(c1);
  182.               MatIm[i,j,1]:= GetGvalue(c1);
  183.               MatIm[i,j,2]:= GetBvalue(c1);
  184.  
  185.  
  186.  
  187.  
  188.  
  189.  
  190.              end; //j
  191.  
  192.           end; //i
  193.  
  194.  
  195.  
  196.  
  197.      end;
  198.  
  199. end;
  200.  
  201. procedure TForm1.FormCreate(Sender: TObject);
  202. begin
  203.    BMAP:=Tbitmap.Create;  //crear variabal para acceso a canvas
  204. end;
  205.  
  206. procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
  207.   Y: Integer);
  208. begin
  209.  
  210.   StatusBar1.Panels[1].Text:=IntToStr(X);
  211.   StatusBar1.Panels[2].Text:=IntToStr(Y);
  212.   StatusBar1.Panels[4].Text:=IntToStr(MatIm[y,x,0]);
  213.   StatusBar1.Panels[5].Text:=IntToStr(MatiM[y,x,1]);
  214.   StatusBar1.Panels[6].Text:=IntToStr(MatiM[y,x,2]);
  215.  
  216.  
  217. end;
  218.  
  219. procedure TForm1.MenuItem3Click(Sender: TObject);
  220. begin
  221.   //abrir imagen con Scanline
  222.   if OpenPictureDialog1.execute then begin
  223.  
  224.        Image1.Enabled:=True;
  225.        BMAP.LoadFromFile(OpenPictureDialog1.FileName); //cargar archivo
  226.  
  227.  
  228.        if BMAP.PixelFormat <> Pf24bit then  //si no es de 8 bits por canal
  229.           begin
  230.              BMAP.PixelFormat:=Pf24bit;
  231.           end;
  232.  
  233.  
  234.        ALTO:=BMAP.Height;
  235.        ANCHO:=BMAP.Width;
  236.  
  237.        Setlength(MatIm,ALTO,ANCHO,3);
  238.        BtoM(ALTO,ANCHO,MatIm,BMAP);   //copiar la informacion a MAtIm
  239.  
  240.        Image1.Picture.Assign(BMAP);
  241.  
  242.  
  243.  
  244.  
  245.  
  246.   end;
  247.  
  248.  
  249. end;
  250.  
  251. procedure TForm1.MenuItem4Click(Sender: TObject);
  252. var
  253. i,j       : Integer;
  254. k         : byte;
  255.  
  256. begin
  257.  
  258.   //aplicar el filtro negativo a la imagen
  259.  
  260.   for i:=0 to ALTO-1 do begin
  261.     for j:=0 to ANCHO-1 do begin
  262.  
  263.     for k:=0 to 2 do begin
  264.  
  265.           MatIm[i,j,k]:=  255 - MatIm[i,j,k];
  266.       end; //k
  267.  
  268.     end;  //j
  269.  
  270.   end;  //i
  271.  
  272.  MtoB(ALTO,ANCHO,MatIm,BMAP);  //copiar resultado a imagen
  273.  
  274.  Image1.Picture.Assign(BMAP);
  275.  
  276.  
  277.  
  278. end;
  279.  
  280. procedure TForm1.TrackBar1Change(Sender: TObject);
  281.   Var
  282.   ScanData, ResultData: PRGBQuad;
  283.   ValR, ValG, ValB, monoByte : Byte;
  284.   X, Y: Integer;
  285. begin
  286.   if openpicturedialog1.Execute then
  287.      Begin
  288.        Image1.Picture.LoadFromFile(openpicturedialog1.FileName);
  289.      end;
  290.   Image1.Picture.Bitmap.Width := Image1.Picture.Width;
  291.   Image1.Picture.Bitmap.Height := Image1.Picture.Height;
  292.   image1.Picture.Bitmap.PixelFormat := image1.Picture.Bitmap.PixelFormat;
  293.   Image1.Picture.Bitmap.BeginUpdate;
  294.  
  295.   For Y:=0 To Image1.Height-1 Do
  296.   Begin
  297.     ScanData := Image1.Picture.Bitmap.ScanLine[Y];
  298.     ResultData := Image1.Picture.Bitmap.ScanLine[Y];
  299.     For X:=0 To Image1.Width-1 Do
  300.     Begin
  301.       //   X, Y, TotalTime, DistCurve: Integer;Point to the pixel location
  302.       // Get RGB value of the pixel
  303.       ValR := ScanData^.rgbRed;
  304.       ValG := ScanData^.rgbGreen;
  305.       ValB := ScanData^.rgbBlue;
  306.  
  307.       MonoByte := round(0.2125 * ValR + 0.7154 * ValG + 0.0721 * ValB);
  308.  
  309.       ResultData^.rgbRed:=MonoByte;
  310.       ResultData^.rgbGreen:=MonoByte;
  311.       ResultData^.rgbBlue:=MonoByte;
  312.  
  313.       Inc(ScanData);
  314.       Inc(ResultData);
  315.     end;
  316.   end;
  317.   Image1.Picture.Bitmap.EndUpdate;
  318. end;
  319.  
  320. end.
  321.  
« Last Edit: February 25, 2022, 09:22:40 am by marcov »

Ally

  • Jr. Member
  • **
  • Posts: 53
Re: Convert BMP image to grayscale using track bar
« Reply #1 on: February 25, 2022, 08:58:27 am »
Hello AjL0ra,

your code is quite complex. Attached is an example that is a bit simpler.

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: Convert BMP image to grayscale using track bar
« Reply #2 on: February 25, 2022, 01:24:41 pm »
Hi!

To make it realy simple use BGRAbitmap.

Code: Pascal  [Select][+][-]
  1. uses .....,BGRAbitmap;
  2. ....
  3. procedure TForm1.Button2Click(Sender: TObject);
  4. var bmp: TBGRAbitmap;
  5. begin
  6.   bmp := TBGRAbitmap.create (Image1.Picture.Bitmap);
  7.   bmp.InplaceGrayscale;
  8.   bmp.Draw(Image1.Canvas,0,0);
  9.   bmp.free;
  10. end;          
  11.  
  12.  

Winni

ADMGNS

  • New Member
  • *
  • Posts: 30
  • keep it simple and smart
Re: Convert BMP image to grayscale using track bar
« Reply #3 on: February 25, 2022, 07:34:56 pm »
hello

you mean progressbar?

if so, yet another example.. pls see attachment.

regs

furious programming

  • Hero Member
  • *****
  • Posts: 858
Re: Convert BMP image to grayscale using track bar
« Reply #4 on: February 27, 2022, 03:01:46 pm »
And the example of using TBitmap.ScanLine:

Code: Pascal  [Select][+][-]
  1. procedure ConvertToGrayscale(ASource, AResult: TBitmap; ALevel: UInt8);
  2. type
  3.   PBitmapPixel = ^TBitmapPixel;
  4.   TBitmapPixel = packed record B, G, R: UInt8 end;
  5. type
  6.   PBitmapLine = ^TBitmapLine;
  7.   TBitmapLine = packed array [UInt16] of TBitmapPixel;
  8. var
  9.   LineSource, LineResult: PBitmapLine;
  10.   PixelSource, PixelResult: PBitmapPixel;
  11. var
  12.   LineIndex, PixelIndex: Integer;
  13.   GrayShade: UInt8;
  14. begin
  15.   AResult.BeginUpdate();
  16.   try
  17.     for LineIndex := 0 to ASource.Height - 1 do
  18.     begin
  19.       LineSource := ASource.ScanLine[LineIndex];
  20.       LineResult := AResult.ScanLine[LineIndex];
  21.  
  22.       for PixelIndex := 0 to ASource.Width - 1 do
  23.       begin
  24.         PixelSource := @LineSource^[PixelIndex];
  25.         PixelResult := @LineResult^[PixelIndex];
  26.  
  27.         GrayShade := Round(PixelSource^.R * 0.299 + PixelSource^.G * 0.587 + PixelSource^.B * 0.114);
  28.  
  29.         PixelResult^.R := Round(PixelSource^.R + (GrayShade - PixelSource^.R) / 255 * ALevel);
  30.         PixelResult^.G := Round(PixelSource^.G + (GrayShade - PixelSource^.G) / 255 * ALevel);
  31.         PixelResult^.B := Round(PixelSource^.B + (GrayShade - PixelSource^.B) / 255 * ALevel);
  32.       end;
  33.     end;
  34.   finally
  35.     AResult.EndUpdate();
  36.   end;
  37. end;

Test project in the attachment.
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

Ally

  • Jr. Member
  • **
  • Posts: 53
Re: Convert BMP image to grayscale using track bar
« Reply #5 on: February 28, 2022, 12:19:11 pm »
Hello ADMGNS and furious programming,

your examples don't seem to work with images that can contain transparent image parts (for example .png or .ico).

furious programming

  • Hero Member
  • *****
  • Posts: 858
Re: Convert BMP image to grayscale using track bar
« Reply #6 on: February 28, 2022, 06:49:27 pm »
@Ally: what did you expect? They will not work, because these are just simple examples operating on the TBitmap class containing a 24-bit image, as the author of this thread wanted. If you need an algorithm that will allow you to perform such operations on a 32-bit image, note that the pixel is represented by 32 bits, not 24 bits, so the structure describing the pixel must also contain a field for the alpha channel. The calculation is exactly the same, except that the alpha channel data should not be modified as it does not contain color information.

Example for 32-bit PNG image:

Code: Pascal  [Select][+][-]
  1. procedure TMainForm.ConvertToGrayscale(ASource, AResult: TPortableNetworkGraphic; ALevel: UInt8);
  2. type
  3.   PPNGPixel = ^TPNGPixel;
  4.   TPNGPixel = packed record B, G, R, A: UInt8 end;
  5. type
  6.   PPNGLine = ^TPNGLine;
  7.   TPNGLine = packed array [UInt16] of TPNGPixel;
  8. var
  9.   LineSource, LineResult: PPNGLine;
  10.   PixelSource, PixelResult: PPNGPixel;
  11. var
  12.   LineIndex, PixelIndex: Integer;
  13.   GrayShade: UInt8;
  14. begin
  15.   AResult.BeginUpdate();
  16.   try
  17.     for LineIndex := 0 to ASource.Height - 1 do
  18.     begin
  19.       LineSource := ASource.ScanLine[LineIndex];
  20.       LineResult := AResult.ScanLine[LineIndex];
  21.  
  22.       for PixelIndex := 0 to ASource.Width - 1 do
  23.       begin
  24.         PixelSource := @LineSource^[PixelIndex];
  25.         PixelResult := @LineResult^[PixelIndex];
  26.  
  27.         GrayShade := Round(PixelSource^.R * 0.299 + PixelSource^.G * 0.587 + PixelSource^.B * 0.114);
  28.  
  29.         PixelResult^.R := Round(PixelSource^.R + (GrayShade - PixelSource^.R) / 255 * ALevel);
  30.         PixelResult^.G := Round(PixelSource^.G + (GrayShade - PixelSource^.G) / 255 * ALevel);
  31.         PixelResult^.B := Round(PixelSource^.B + (GrayShade - PixelSource^.B) / 255 * ALevel);
  32.       end;
  33.     end;
  34.   finally
  35.     AResult.EndUpdate();
  36.   end;
  37. end;

Modified example in the attachment.
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

AjL0ra

  • Newbie
  • Posts: 5
Re: Convert BMP image to grayscale using track bar
« Reply #7 on: February 28, 2022, 08:26:19 pm »
furious programming, thank you with your examples, you my friend are life saver.
« Last Edit: February 28, 2022, 08:37:19 pm by AjL0ra »

furious programming

  • Hero Member
  • *****
  • Posts: 858
Re: Convert BMP image to grayscale using track bar
« Reply #8 on: February 28, 2022, 09:07:18 pm »
Keep in mind that you cannot create a 32-bit TBitmap and TPortableNetworkGraphic. I don't know why but setting PixelFormat to pf32bit do nothing — pixel format stays 24-bit still. That's why in the second example program, the 32-bit source PNG is loaded to the main object and then it is assigned to the other two objects. The Assign method changes the pixel format of the destination image, so it is possible to modify them as 32-bit images via ScanLine.

There are many solutions to avoid this problem, AFAIR such as using TFPImage and then copy it to the TPortableNetworkGraphic, but this is a waste of memory and CPU time (such a solutions are not very efficient).
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

AjL0ra

  • Newbie
  • Posts: 5
Re: Convert BMP image to grayscale using track bar
« Reply #9 on: February 28, 2022, 09:25:05 pm »
another doubt though how can change its color base using color modeling HSI and/or YUV

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: Convert BMP image to grayscale using track bar
« Reply #10 on: February 28, 2022, 09:51:47 pm »
Hi!

Use BGRAbitmap.

HSLA is one of the basics.
YUV is in the webp unit.

Winni

AjL0ra

  • Newbie
  • Posts: 5
Re: Convert BMP image to grayscale using track bar
« Reply #11 on: March 05, 2022, 03:10:24 am »
@furiousprogramming could you please show how to put the algorithm of hsi in the code Im tryin but failling tremendously
« Last Edit: March 05, 2022, 03:22:36 am by AjL0ra »

ADMGNS

  • New Member
  • *
  • Posts: 30
  • keep it simple and smart
Re: Convert BMP image to grayscale using track bar
« Reply #12 on: March 07, 2022, 08:36:16 am »
hello,
Hello ADMGNS and furious programming,

your examples don't seem to work with images that can contain transparent image parts (for example .png or .ico).

another example is here, see attchment.. it supports alpha channel i.e. it seperates alpha channel by direct access to BGRABitmap's bitmap data..

regs

furious programming

  • Hero Member
  • *****
  • Posts: 858
Re: Convert BMP image to grayscale using track bar
« Reply #13 on: March 07, 2022, 06:48:25 pm »
@AjL0ra: standard images uses RGB(A) pixels, so if you need to use HSI, you need functions to convert color to HSI and vice versa. Use Google and find examples or even code snippets with proper calculations. Then all you need to do is read pixel data, convert is to HSI color space, do whatever you want, convert color back to RGB format and store new values in the result image. So the template is as the same as previous:

Code: Pascal  [Select][+][-]
  1. procedure RGBToHSI(AR, AG, AB: UInt8; out AH, AS, AI: UInt16);
  2. begin
  3.   // implement calculations
  4. end;
  5.  
  6. procedure HSIToRGB(AH, AS, AI: UInt16; out AR, AG, AB: UInt8);
  7. begin
  8.   // implement calculations
  9. end;
  10.  
  11. procedure DoSomethingWith32BitImage(ASource, AResult: TPortableNetworkGraphic);
  12. type
  13.   PPNGPixel = ^TPNGPixel;
  14.   TPNGPixel = packed record B, G, R, A: UInt8 end;
  15. type
  16.   PPNGLine = ^TPNGLine;
  17.   TPNGLine = packed array [UInt16] of TPNGPixel;
  18. var
  19.   LineSource, LineResult: PPNGLine;
  20.   PixelSource, PixelResult: PPNGPixel;
  21. var
  22.   LineIndex, PixelIndex: Integer;
  23.   H, S, I: UInt16;
  24. begin
  25.   AResult.BeginUpdate();
  26.   try
  27.     for LineIndex := 0 to ASource.Height - 1 do
  28.     begin
  29.       LineSource := ASource.ScanLine[LineIndex];
  30.       LineResult := AResult.ScanLine[LineIndex];
  31.  
  32.       for PixelIndex := 0 to ASource.Width - 1 do
  33.       begin
  34.         PixelSource := @LineSource^[PixelIndex];
  35.         PixelResult := @LineResult^[PixelIndex];
  36.  
  37.         // translate RGB to HSI and store it in H, S and I variables
  38.         RGBToHSI(PixelSource^.R, PixelSource^.G, PixelSource^.B, H, S, I);
  39.        
  40.         // do whatever you want with H, S, and I variables
  41.        
  42.         // translate HSI to RGB and write result to the target image
  43.         HSIToRGB(H, S, I, PixelResult^.R, PixelResult^.G, PixelResult^.B);
  44.       end;
  45.     end;
  46.   finally
  47.     AResult.EndUpdate();
  48.   end;
  49. end;
« Last Edit: March 07, 2022, 06:50:20 pm by furious programming »
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

ADMGNS

  • New Member
  • *
  • Posts: 30
  • keep it simple and smart
Re: Convert BMP image to grayscale using track bar
« Reply #14 on: March 09, 2022, 09:20:15 am »
updated version, incremental progressing.. see attachment pls..

 

TinyPortal © 2005-2018