Convert a picture to Gray lazarus / 39-Graphics ---------------------------------------------------------------------- fpauw · 2019-12-20 11:39 I am trying to convert a color bitmap to a gray image. The code works partly but The image is deformed. This only happens with BMP with a JPG it is ok What do I miss. 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 TForm1.Button1Click(Sender: TObject);Var  ScanData: PRGBQuad;  ValR, ValG, ValB: Byte;  X, Y : Integer;  Mono: Double;begin  if openpicturedialog1.Execute then     Begin       Image1.Picture.LoadFromFile(openpicturedialog1.FileName);     end;   Image7.Picture.Bitmap.Height := Image1.Height;  Image7.Picture.Bitmap.Width := Image1.Width;   For Y:=0 To Image1.Height-1 Do  Begin    ScanData := Image1.Picture.Bitmap.ScanLine[Y];    For X:=0 To Image1.Width-1 Do  Begin      //   X, Y, TotalTime, DistCurve: Integer;Point to the pixel location      // Get RGB value of the pixel      ValR := ScanData^.rgbRed;      ValG := ScanData^.rgbGreen;      ValB := ScanData^.rgbBlue;       //START Convert to gray      mono := (0.2125 * ValR) + (0.7154 * ValG) + (0.0721 * ValB);      Image7.Canvas.pixels [X,Y]:=StringToColor(IntToStr(Round(mono)*256*256+Round(mono)*256+Roun d(mono)));       Inc(ScanData);    end;  end;end; ---------------------------------------------------------------------- marcov · 2019-12-20 11:52 Make sure your BMP is the good format, 24 or 32-bit RGB(A)? ---------------------------------------------------------------------- wp · 2019-12-20 11:55 The code in lines 14 and 15 (of the posted code snippet) is not necessary and in my opinion the reason for the issue because it forces the bitmap size to be equal to the size of the Image control. Suppose you loaded a photo of 6000x4000 (24 MPixel) into a TImage of 100x100 then this code would truncate the photo to this tiny size - certainly not what you want. ---------------------------------------------------------------------- winni · 2019-12-20 12:53 Hi! What the hell do you code complicated stuff in line 30? Simplify it 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 Var monoByte : byte; …....MonoByte := round(0.2125 * ValR + 0.7154 * ValG + 0.0721 * ValB);Image7.Canvas.pixels[X,Y]:= RBGtoColor(monoByte, monoByte,monoByte);  Winni ---------------------------------------------------------------------- wp · 2019-12-20 13:46 Adjusting to the sensitivity of the eye on the three base colors? https://en.wikipedia.org/wiki/Grayscale ---------------------------------------------------------------------- lainz · 2019-12-20 13:54 Quote from: wp on December 20, 2019, 02:46:01 pmAdjusting to the sensitivity of the eye on the three base colors? https://en.wikipedia.org/wiki/Grayscale He means line 30 , this line 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 Image7.Canv as.pixels[X,Y]:=StringToColor(IntToStr(Round(mono)*256*256+Round(mono) *256+Round(mono))); Replacing with 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 Image7.Canvas.pixels[X,Y]:= RBGtoColor(monoByte, monoByte,monoByte); ---------------------------------------------------------------------- winni · 2019-12-20 14:33 Thanx @lainz! And @wp: I don't get you. I know that stuff described in wikipedia. Since 40 years. Winni ---------------------------------------------------------------------- wp · 2019-12-20 15:57 Quote from: winni on December 20, 2019, 03:33:14 pmAnd @wp: I don't get you. I know that stuff described in wikipedia. Since 40 years. Sorry. Careful reading is not by best strength... ---------------------------------------------------------------------- fpauw · 2019-12-23 07:32 Quote from: marcov on December 20, 2019, 12:52:42 pmMake sure your BMP is the good format, 24 or 32-bit RGB(A)? Thanks after converting the BMP to 32-bit the issue is gone! ---------------------------------------------------------------------- fpauw · 2019-12-23 10:39 Quote from: winni on December 20, 2019, 01:53:02 pmHi! What the hell do you code complicated stuff in line 30? Simplify it 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 Var monoByte : byte; …....MonoByte := round(0.2125 * ValR + 0.7154 * ValG + 0.0721 * ValB);Image7.Canvas.pixels[X,Y]:= RBGtoColor(monoByte, monoByte,monoByte);  Winni Thanks I tried this but than I get unit1.pas(63,35) Error: Identifier not found "RBGtoColor" ---------------------------------------------------------------------- lucamar · 2019-12-23 10:44 Quote from: fpauw on December 23, 2019, 11:39:48 amThanks I tried this but than I get unit1.pas(63,35) Error: Identifier not found "RBGtoColor" Because it is RGBToColor(), not RBGToColor(). A simple typo ---------------------------------------------------------------------- fpauw · 2019-12-23 10:49 Quote from: lucamar on December 23, 2019, 11:44:58 amQuote from: fpauw on December 23, 2019, 11:39:48 amThanks I tried this but than I get unit1.pas(63,35) Error: Identifier not found "RBGtoColor" Because it is RGBToColor(), not RBGToColor(). A simple typo Thanks It works ---------------------------------------------------------------------- fpauw · 2019-12-23 13:09 Now I try to speed it up with writing it back with a ScanLine. But than I get the error "Raised exception class 'External:SIGSEGV" 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 TForm1.Button1Click(Sender: TObject);Var  ScanData, ResultData: PRGBQuad;  ValR, ValG, ValB: Byte;  X, Y: Integer;  monoByte : byte;begin  if openpicturedialog1.Execute then     Begin       Image1.Picture.LoadFromFile(openpicturedialog1.FileName);     end;   Image7.Picture.Bitmap.Height := Image1.Height;  Image7.Picture.Bitmap.Width := Image1.Width;   For Y:=0 To Image1.Height-1 Do  Begin    ScanData := Image1.Picture.Bitmap.ScanLine[Y];    ResultData := Image7.Picture.Bitmap.ScanLine[Y];    For X:=0 To Image1.Width-1 Do  Begin      //   X, Y, TotalTime, DistCurve: Integer;Point to the pixel location      // Get RGB value of the pixel      ValR := ScanData^.rgbRed;      ValG := ScanData^.rgbGreen;      ValB := ScanData^.rgbBlue;       MonoByte := round(0.2125 * ValR + 0.7154 * ValG + 0.0721 * ValB);       ResultData^.rgbRed   :=monoByte;      ResultData^.rgbGreen :=monoByte;      ResultData^.rgbBlue  :=monoByte;       Inc(ScanData);      Inc(ResultData);    end;  end;end; ---------------------------------------------------------------------- wp · 2019-12-23 13:36 Setting width and height of the destination bitmap (Image7.Picture.Bitmap) equal to the size of the source control (Image1) is not correct because the source bitmap (Image1.Picture.Bitmap, or more general: Image1.Picture) may have a different size than the source control. Try this instead of lines 13 and 14: 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 Image7.Picture.Bitmap.Width := Image1.Picture.Width;Image7.Picture.Bitmap.Height := Image1.Picture.Height; // or: combine both inImage7.Picture.Bitmap.SetSize(Image1.Picture.Width, Image1.Picture.Height); ---------------------------------------------------------------------- fpauw · 2019-12-23 13:58 Quote from: wp on December 23, 2019, 02:36:08 pmSetting width and height of the destination bitmap (Image7.Picture.Bitmap) equal to the size of the source control (Image1) is not correct because the source bitmap (Image1.Picture.Bitmap, or more general: Image1.Picture) may have a different size than the source control. Clear Thanks! But I have still the  "Raised exception class 'External:SIGSEGV" error... ---------------------------------------------------------------------- winni · 2019-12-23 17:25 Hi! Before you resize Image7 you have to initialize the internal bitmap. The easiest way is to fill it white 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 image7.canvas.brush.color := clWhite;image7.Canvas.fillrect(0,0,image7.width,image7.height); Now you can resize the bitmap of image7 without crash. Winni ---------------------------------------------------------------------- fpauw · 2019-12-24 07:35 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Button1Click(Sender: TObject);Var  ScanData, ResultData: PRGBQuad;  ValR, ValG, ValB, monoByte : Byte;  X, Y: Integer;begin  if openpicturedialog1.Execute then     Begin       Image1.Picture.LoadFromFile(openpicturedialog1.FileName);     end;   Image7.Picture.Bitmap.Width := Image1.Picture.Width;  Image7.Picture.Bitmap.Height := Image1.Picture.Height;  image7.canvas.brush.color := clWhite;  image7.Canvas.fillrect(0,0,image7.width,image7.height);   For Y:=0 To Image1.Height-1 Do  Begin    ScanData := Image1.Picture.Bitmap.ScanLine[Y];    ResultData := Image7.Picture.Bitmap.ScanLine[Y];    For X:=0 To Image1.Width-1 Do  Begin      ValR := ScanData^.rgbRed;      ValG := ScanData^.rgbGreen;      ValB := ScanData^.rgbBlue;       MonoByte := round(0.2125 * ValR + 0.7154 * ValG + 0.0721 * ValB);       ResultData^.rgbRed:=MonoByte;    ResultData^.rgbGreen:=MonoByte;      ResultData^.rgbBlue:=MonoByte;       Inc(ScanData);      Inc(ResultData);    end;  end;end; ---------------------------------------------------------------------- martijnn · 2019-12-24 08:16 Have a look at comment on the ScanLine property: 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 property ScanLine[Row: Integer]: Pointer read GetScanLine; platform; // Use only when wrpped by a begin/endupdate Adding the following... 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 Image7.Picture.Bitmap.BeginUpdate;// Double loop hereImage7.Picture.Bitmap.EndUpdate; ... at least starts to show a grayscale picture (still with some issues left) ---------------------------------------------------------------------- fpauw · 2019-12-24 08:29 Thanks I have output now! And yes there are still errors now I have vertical white lines. ---------------------------------------------------------------------- martijnn · 2019-12-24 08:50 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i image7.Picture.Bitmap.PixelFormat := image1.Picture.Bitmap.PixelFormat; ---------------------------------------------------------------------- fpauw · 2019-12-24 09:28 Thanks a Lot! Now it works without errors! This is the result 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 TForm1.Button1Click(Sender: TObject);Var  ScanData, ResultData: PRGBQuad;  ValR, ValG, ValB, monoByte : Byte;  X, Y: Integer;begin  if openpicturedialog1.Execute then     Begin       Image1.Picture.LoadFromFile(openpicturedialog1.FileName);     end;  Image7.Picture.Bitmap.Width := Image1.Picture.Width;  Image7.Picture.Bitmap.Height := Image1.Picture.Height;  image7.Picture.Bitmap.PixelFormat := image1.Picture.Bitmap.PixelFormat;  Image7.Picture.Bitmap.BeginUpdate;   For Y:=0 To Image1.Height-1 Do  Begin    ScanData := Image1.Picture.Bitmap.ScanLine[Y];    ResultData := Image7.Picture.Bitmap.ScanLine[Y];    For X:=0 To Image1.Width-1 Do    Begin      //   X, Y, TotalTime, DistCurve: Integer;Point to the pixel location      // Get RGB value of the pixel      ValR := ScanData^.rgbRed;      ValG := ScanData^.rgbGreen;      ValB := ScanData^.rgbBlue;       MonoByte := round(0.2125 * ValR + 0.7154 * ValG + 0.0721 * ValB);       ResultData^.rgbRed:=MonoByte;      ResultData^.rgbGreen:=MonoByte;      ResultData^.rgbBlue:=MonoByte;     Inc(ScanData);      Inc(ResultData);    end;  end;  Image7.Picture.Bitmap.EndUpdate;end; ---------------------------------------------------------------------- trheo · 2021-10-21 07:23 Please, does anyone have an idea on how to convert directly Image1 to grey (without creating a new Image) ? Thank you !! ---------------------------------------------------------------------- Ally · 2021-10-21 08:20 Hallo trheo, rhsBitmapGrayscale uses TLazIntfImage, which speeds up processing significantly. 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 // rhsBitmapGrayscale// ------------------------------------------------- --------------------------- unit rhsBitmapGrayscale; {$mode objfpc}{$H+} interface uses  SysUtils, Classes, Graphics, IntfGraphics, FPImage;   procedure BitmapGrayscale(BM: TCustomBitmap; R, G, B: Single); implementation procedure BitmapGrayscale(BM: TCustomBitmap; R, G, B: Single);var  IntfImg: TLazIntfImage = nil;  x, y: Integer;  TempColor: TFPColor;  Gray: Word;begin  try    IntfImg := BM.CreateIntfImage;     IntfImg.BeginUpdate;    for y := 0 to IntfImg.Height - 1 do      for x := 0 to IntfImg.Width - 1 do      begin        TempColor := IntfImg.Colors[x, y];        Gray := Round(TempColor.Red * R + TempColor.Green * G + TempColor.Blue * B);      TempColor.Red := Gray;        TempColor.Green := Gray;        TempColor.Blue := Gray;        IntfImg.Colors[x, y] := TempColor;    end;    IntfImg.EndUpdate;     BM.LoadFromIntfImage(IntfImg);  finally    IntfImg.Free;  end;end; end. Here is another example for the use of rhsBitmapGrayscale. 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 BitmapGrayscale(Image1.Picture.Bitmap, 0.30, 0.59, 0.11);  // Neutral filterBitmapGrayscale(Image1.Picture.Bitmap, 1.00, 0.00, 0.00);  // Red filterBitmapGrayscale(Image1.Picture.Bitmap, 0.00, 1.00, 0.00);  // Green filterBitmapGrayscale(Image1.Picture.Bitmap, 0.00, 0.00, 1.00);  // Blue filterBitmapGrayscale(Image1.Picture.Bitmap, 0.00, 0.50, 0.50);  // Cyan filterBitmapGrayscale(Image1.Picture.Bitmap, 0.50, 0.00, 0.50);  // Magenta filterBitmapGrayscale(Image1.Picture.Bitmap, 0.50, 0.50, 0.00);  // Yellow filter ---------------------------------------------------------------------- trheo · 2021-10-21 08:45 Thank you very much Ally ---------------------------------------------------------------------- wp · 2021-10-21 09:46 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. ---------------------------------------------------------------------- Ally · 2021-10-21 10:28 Hello wp, that's true, of course. But in the case of images with an alpha channel, the transparency would be lost. ---------------------------------------------------------------------- Slawek · 2022-05-23 14:10 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. Quote from: fpauw on December 24, 2019, 10:28:10 amCode: 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 TForm1.Button1Click(Sender: TObject);Var  ScanData, ResultData: PRGBQuad;  ValR, ValG, ValB, monoByte : Byte;  X, Y: Integer;begin  if openpicturedialog1.Execute then     Begin       Image1.Picture.LoadFromFile(openpicturedialog1.FileName);     end;  Image7.Picture.Bitmap.Width := Image1.Picture.Width;  Image7.Picture.Bitmap.Height := Image1.Picture.Height;  image7.Picture.Bitmap.PixelFormat := image1.Picture.Bitmap.PixelFormat;  Image7.Picture.Bitmap.BeginUpdate;   For Y:=0 To Image1.Height-1 Do  Begin    ScanData := Image1.Picture.Bitmap.ScanLine[Y];    ResultData := Image7.Picture.Bitmap.ScanLine[Y];    For X:=0 To Image1.Width-1 Do    Begin      //   X, Y, TotalTime, DistCurve: Integer;Point to the pixel location      // Get RGB value of the pixel      ValR := ScanData^.rgbRed;      ValG := ScanData^.rgbGreen;      ValB := ScanData^.rgbBlue;       MonoByte := round(0.2125 * ValR + 0.7154 * ValG + 0.0721 * ValB);       ResultData^.rgbRed:=MonoByte;      ResultData^.rgbGreen:=MonoByte;      ResultData^.rgbBlue:=MonoByte;     Inc(ScanData);      Inc(ResultData);    end;  end;  Image7.Picture.Bitmap.EndUpdate;end;    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 Quote from: trheo on October 21, 2021, 09:23:38 amPlease, 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Button1Click(Sender: TObject);Var  LineData: PRGBQuad;  ValR, ValG, ValB, monoByte : Byte;  X, Y: Integer;begin  if openpicturedialog1.Execute then     Begin       Image1.Picture.LoadFromFile(openpicturedialog1.FileName);     end;  Image1.Picture.Bitmap.BeginUpdate;   For Y:=0 To Image1.Picture.Height-1 Do  Begin    LineData := Image1.Picture.Bitmap.ScanLine[Y];    For X:=0 To Image1.Picture.Width-1 Do    Begin      //   X, Y, TotalTime, DistCurve: Integer;Point to the pixel location      // Get RGB value of the pixel      ValR := LineData^.rgbRed;      ValG := LineData^.rgbGreen;      ValB := LineData^.rgbBlue;       MonoByte := round(0.2125 * ValR + 0.7154 * ValG + 0.0721 * ValB);       LineData^.rgbRed:=MonoByte;      LineData^.rgbGreen:=MonoByte;      LineData^.rgbBlue:=MonoByte;       Inc(LineData);    end;  end;  Image1.Picture.Bitmap.EndUpdate;end; ---------------------------------------------------------------------- winni · 2022-05-23 14:31 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses ...BGRAbitmap;...procedure TForm1.Button3Click(Sender: TObject);var tmp: TBGRABitmap;begin  if openpicturedialog1.Execute then       tmp := TBGRABitmap.Create ( openpicturedialog1.FileName) else exit;  tmp.InplaceGrayscale;  BGRAReplace (tmp,tmp.resample(Image1.width, Image1.height));  tmp.Draw (Image1.Canvas,0,0,true);  tmp.saveToFile('Gray.png');  tmp.free;end;  Winni ---------------------------------------------------------------------- Ally · 2022-05-23 14:49 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