### Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook

### Author Topic: Convert a picture to Gray  (Read 6804 times)

#### winni

• Hero Member
• Posts: 3057
##### Re: Convert a picture to Gray
« Reply #15 on: December 23, 2019, 06:25:52 pm »
Hi!

Before you resize Image7 you have to initialize the internal bitmap.

The easiest way is to fill it white
Code: Pascal  [Select][+][-]
1. image7.canvas.brush.color := clWhite;
2. image7.Canvas.fillrect(0,0,image7.width,image7.height);
3.
Now you can resize the bitmap of image7 without crash.

Winni

#### fpauw

• New Member
• Posts: 11
##### Re: Convert a picture to Gray
« Reply #16 on: December 24, 2019, 08:35:21 am »
I add the code but now the same error is now on closing the program. The gray image is just a white surface...

Code: Pascal  [Select][+][-]
1. procedure TForm1.Button1Click(Sender: TObject);
2. Var
4.   ValR, ValG, ValB, monoByte : Byte;
5.   X, Y: Integer;
6. begin
7.   if openpicturedialog1.Execute then
8.      Begin
10.      end;
11.
12.   Image7.Picture.Bitmap.Width := Image1.Picture.Width;
13.   Image7.Picture.Bitmap.Height := Image1.Picture.Height;
14.   image7.canvas.brush.color := clWhite;
15.   image7.Canvas.fillrect(0,0,image7.width,image7.height);
16.
17.   For Y:=0 To Image1.Height-1 Do
18.   Begin
19.     ScanData := Image1.Picture.Bitmap.ScanLine[Y];
20.     ResultData := Image7.Picture.Bitmap.ScanLine[Y];
21.     For X:=0 To Image1.Width-1 Do
22.     Begin
23.       ValR := ScanData^.rgbRed;
24.       ValG := ScanData^.rgbGreen;
25.       ValB := ScanData^.rgbBlue;
26.
27.       MonoByte := round(0.2125 * ValR + 0.7154 * ValG + 0.0721 * ValB);
28.
29.       ResultData^.rgbRed:=MonoByte;
30.       ResultData^.rgbGreen:=MonoByte;
31.       ResultData^.rgbBlue:=MonoByte;
32.
33.       Inc(ScanData);
34.       Inc(ResultData);
35.     end;
36.   end;
37. end;
38.
« Last Edit: December 24, 2019, 09:19:36 am by fpauw »

#### martijnn

• New Member
• Posts: 16
##### Re: Convert a picture to Gray
« Reply #17 on: December 24, 2019, 09:16:20 am »
Have a look at comment on the ScanLine property:

Code: Pascal  [Select][+][-]
1. property ScanLine[Row: Integer]: Pointer read GetScanLine; platform; // Use only when wrpped by a begin/endupdate

Code: Pascal  [Select][+][-]
1. Image7.Picture.Bitmap.BeginUpdate;
2. // Double loop here
3. Image7.Picture.Bitmap.EndUpdate;
4.
... at least starts to show a grayscale picture (still with some issues left)

#### fpauw

• New Member
• Posts: 11
##### Re: Convert a picture to Gray
« Reply #18 on: December 24, 2019, 09:29:35 am »
Thanks I have output now!

And yes there are still errors now I have vertical white lines.

#### martijnn

• New Member
• Posts: 16
##### Re: Convert a picture to Gray
« Reply #19 on: December 24, 2019, 09:50:46 am »
As a next step it will probably help to match the number of bits per pixel of the input image. For example:

Code: Pascal  [Select][+][-]
1. image7.Picture.Bitmap.PixelFormat := image1.Picture.Bitmap.PixelFormat;

#### fpauw

• New Member
• Posts: 11
##### Re: Convert a picture to Gray
« Reply #20 on: December 24, 2019, 10:28:10 am »
Thanks a Lot!

Now it works without errors!

This is the result
Code: Pascal  [Select][+][-]
1. procedure TForm1.Button1Click(Sender: TObject);
2. Var
4.   ValR, ValG, ValB, monoByte : Byte;
5.   X, Y: Integer;
6. begin
7.   if openpicturedialog1.Execute then
8.      Begin
10.      end;
11.   Image7.Picture.Bitmap.Width := Image1.Picture.Width;
12.   Image7.Picture.Bitmap.Height := Image1.Picture.Height;
13.   image7.Picture.Bitmap.PixelFormat := image1.Picture.Bitmap.PixelFormat;
14.   Image7.Picture.Bitmap.BeginUpdate;
15.
16.   For Y:=0 To Image1.Height-1 Do
17.   Begin
18.     ScanData := Image1.Picture.Bitmap.ScanLine[Y];
19.     ResultData := Image7.Picture.Bitmap.ScanLine[Y];
20.     For X:=0 To Image1.Width-1 Do
21.     Begin
22.       //   X, Y, TotalTime, DistCurve: Integer;Point to the pixel location
23.       // Get RGB value of the pixel
24.       ValR := ScanData^.rgbRed;
25.       ValG := ScanData^.rgbGreen;
26.       ValB := ScanData^.rgbBlue;
27.
28.       MonoByte := round(0.2125 * ValR + 0.7154 * ValG + 0.0721 * ValB);
29.
30.       ResultData^.rgbRed:=MonoByte;
31.       ResultData^.rgbGreen:=MonoByte;
32.       ResultData^.rgbBlue:=MonoByte;
33.
34.       Inc(ScanData);
35.       Inc(ResultData);
36.     end;
37.   end;
38.   Image7.Picture.Bitmap.EndUpdate;
39. end;
40.
« Last Edit: December 24, 2019, 10:45:29 am by fpauw »

#### trheo

• Newbie
• Posts: 2
##### Re: Convert a picture to Gray
« Reply #21 on: October 21, 2021, 09:23:38 am »
Please, does anyone have an idea on how to convert directly Image1 to grey (without creating a new Image) ?

Thank you !!

#### Ally

• New Member
• Posts: 33
##### Re: Convert a picture to Gray
« Reply #22 on: October 21, 2021, 10:20:41 am »
Hallo trheo,

rhsBitmapGrayscale uses TLazIntfImage, which speeds up processing significantly.

Code: Pascal  [Select][+][-]
1. // rhsBitmapGrayscale
2. // ----------------------------------------------------------------------------
3.
4. unit rhsBitmapGrayscale;
5.
6. {\$mode objfpc}{\$H+}
7.
8. interface
9.
10. uses
11.   SysUtils, Classes, Graphics, IntfGraphics, FPImage;
12.
13.   procedure BitmapGrayscale(BM: TCustomBitmap; R, G, B: Single);
14.
15. implementation
16.
17. procedure BitmapGrayscale(BM: TCustomBitmap; R, G, B: Single);
18. var
19.   IntfImg: TLazIntfImage = nil;
20.   x, y: Integer;
21.   TempColor: TFPColor;
22.   Gray: Word;
23. begin
24.   try
25.     IntfImg := BM.CreateIntfImage;
26.
27.     IntfImg.BeginUpdate;
28.     for y := 0 to IntfImg.Height - 1 do
29.       for x := 0 to IntfImg.Width - 1 do
30.       begin
31.         TempColor := IntfImg.Colors[x, y];
32.         Gray := Round(TempColor.Red * R + TempColor.Green * G + TempColor.Blue * B);
33.         TempColor.Red := Gray;
34.         TempColor.Green := Gray;
35.         TempColor.Blue := Gray;
36.         IntfImg.Colors[x, y] := TempColor;
37.       end;
38.     IntfImg.EndUpdate;
39.
41.   finally
42.     IntfImg.Free;
43.   end;
44. end;
45.
46. end.

Here is another example for the use of rhsBitmapGrayscale.

Code: Pascal  [Select][+][-]
1. BitmapGrayscale(Image1.Picture.Bitmap, 0.30, 0.59, 0.11);  // Neutral filter
2. BitmapGrayscale(Image1.Picture.Bitmap, 1.00, 0.00, 0.00);  // Red filter
3. BitmapGrayscale(Image1.Picture.Bitmap, 0.00, 1.00, 0.00);  // Green filter
4. BitmapGrayscale(Image1.Picture.Bitmap, 0.00, 0.00, 1.00);  // Blue filter
5. BitmapGrayscale(Image1.Picture.Bitmap, 0.00, 0.50, 0.50);  // Cyan filter
6. BitmapGrayscale(Image1.Picture.Bitmap, 0.50, 0.00, 0.50);  // Magenta filter
7. BitmapGrayscale(Image1.Picture.Bitmap, 0.50, 0.50, 0.00);  // Yellow filter

« Last Edit: October 21, 2021, 10:33:43 am by Ally »

#### trheo

• Newbie
• Posts: 2
##### Re: Convert a picture to Gray
« Reply #23 on: October 21, 2021, 10:45:39 am »
Thank you very much Ally

#### wp

• Hero Member
• Posts: 9605
##### Re: Convert a picture to Gray
« Reply #24 on: October 21, 2021, 11:46:46 am »
If the input bitmap is in pf24Bit pixelformat every r,g,b component is stored as 1 byte. After converting to grayscale by Ally's code, the 24Bit pixelformat stays the same, but r,g,b have the same value. Even worse, if the input bitmap is pf32Bit. It would be more memory-efficient to store the gray value only once, like in pf8bit.
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

#### Ally

• New Member
• Posts: 33
##### Re: Convert a picture to Gray
« Reply #25 on: October 21, 2021, 12:28:17 pm »
Hello wp,

that's true, of course.
But in the case of images with an alpha channel, the transparency would be lost.

#### Slawek

• New Member
• Posts: 14
##### Re: Convert a picture to Gray
« Reply #26 on: May 23, 2022, 04:10:35 pm »
I refresh the topic because I was looking for a procedure for changing the image to grayscale. But here's a bug that causes if Image1.Stretch is: = True; only part of the image is converted to grayscale.
Code: Pascal  [Select][+][-]
1. procedure TForm1.Button1Click(Sender: TObject);
2. Var
4.   ValR, ValG, ValB, monoByte : Byte;
5.   X, Y: Integer;
6. begin
7.   if openpicturedialog1.Execute then
8.      Begin
10.      end;
11.   Image7.Picture.Bitmap.Width := Image1.Picture.Width;
12.   Image7.Picture.Bitmap.Height := Image1.Picture.Height;
13.   image7.Picture.Bitmap.PixelFormat := image1.Picture.Bitmap.PixelFormat;
14.   Image7.Picture.Bitmap.BeginUpdate;
15.
16.   For Y:=0 To Image1.Height-1 Do
17.   Begin
18.     ScanData := Image1.Picture.Bitmap.ScanLine[Y];
19.     ResultData := Image7.Picture.Bitmap.ScanLine[Y];
20.     For X:=0 To Image1.Width-1 Do
21.     Begin
22.       //   X, Y, TotalTime, DistCurve: Integer;Point to the pixel location
23.       // Get RGB value of the pixel
24.       ValR := ScanData^.rgbRed;
25.       ValG := ScanData^.rgbGreen;
26.       ValB := ScanData^.rgbBlue;
27.
28.       MonoByte := round(0.2125 * ValR + 0.7154 * ValG + 0.0721 * ValB);
29.
30.       ResultData^.rgbRed:=MonoByte;
31.       ResultData^.rgbGreen:=MonoByte;
32.       ResultData^.rgbBlue:=MonoByte;
33.
34.       Inc(ScanData);
35.       Inc(ResultData);
36.     end;
37.   end;
38.   Image7.Picture.Bitmap.EndUpdate;
39. end;
40.
To correct it:
Line 16 should read: For Y: = 0 To Image1.Picture.Height-1 Do
And in know 20: For X: = 0 To Image1.Picture.Width-1 Do
Please, does anyone have an idea on how to convert directly Image1 to grey (without creating a new Image) ?

Thank you !!
Just change Image 7 to Image1. Moreover, in my opinion, lines 11, 12, 13, 18, 31 will not be needed then and only one PRGBQuad type variable is enough.
Like this:
Code: Pascal  [Select][+][-]
1. procedure TForm1.Button1Click(Sender: TObject);
2. Var
4.   ValR, ValG, ValB, monoByte : Byte;
5.   X, Y: Integer;
6. begin
7.   if openpicturedialog1.Execute then
8.      Begin
10.      end;
11.   Image1.Picture.Bitmap.BeginUpdate;
12.
13.   For Y:=0 To Image1.Picture.Height-1 Do
14.   Begin
15.     LineData := Image1.Picture.Bitmap.ScanLine[Y];
16.     For X:=0 To Image1.Picture.Width-1 Do
17.     Begin
18.       //   X, Y, TotalTime, DistCurve: Integer;Point to the pixel location
19.       // Get RGB value of the pixel
20.       ValR := LineData^.rgbRed;
21.       ValG := LineData^.rgbGreen;
22.       ValB := LineData^.rgbBlue;
23.
24.       MonoByte := round(0.2125 * ValR + 0.7154 * ValG + 0.0721 * ValB);
25.
26.       LineData^.rgbRed:=MonoByte;
27.       LineData^.rgbGreen:=MonoByte;
28.       LineData^.rgbBlue:=MonoByte;
29.
30.       Inc(LineData);
31.     end;
32.   end;
33.   Image1.Picture.Bitmap.EndUpdate;
34. end;
35.

#### winni

• Hero Member
• Posts: 3057
##### Re: Convert a picture to Gray
« Reply #27 on: May 23, 2022, 04:31:11 pm »
Hi!

The default graphics of fpc/Lazarus are very restricted and uncomfortable.

Install the BGRAbitmapPack and you solve the problem with only some lines of code:

Code: Pascal  [Select][+][-]
1. uses ...BGRAbitmap;
2. ...
3. procedure TForm1.Button3Click(Sender: TObject);
4. var tmp: TBGRABitmap;
5. begin
6.   if openpicturedialog1.Execute then
7.        tmp := TBGRABitmap.Create ( openpicturedialog1.FileName) else exit;
8.   tmp.InplaceGrayscale;
9.   BGRAReplace (tmp,tmp.resample(Image1.width, Image1.height));
10.   tmp.Draw (Image1.Canvas,0,0,true);
11.   tmp.saveToFile('Gray.png');
12.   tmp.free;
13. end;
14.

Winni

#### Ally

• New Member
• Posts: 33
##### Re: Convert a picture to Gray
« Reply #28 on: May 23, 2022, 04:49:55 pm »
Hello,

in the meantime I have created a sample program for my unit rhsBitmapGrayscale.pas.
The program shows how to get different results with different values.

Many greetings Roland