Search code examples
delphiimage-processingpascalfreepascallazarus

Issue converting an image to gray scale in Lazarus


I've written a procedure called gray_scale that is suppose to have an input TImage component, turn the image that it contains into gray scale and then print it in the same TImage component. The issue with it is that it prints an black image. My code is below, please note that I've created a local variable called img2: TPicture which purpose is to be like a middle stage between the input and the output of the procedure.

procedure gray(var img1: TImage);
var
  i,j: Integer;
  y: integer;
  color: TColor;
  img2: TPicture;
begin
  i := 0; j := 0;
  img2 := TPicture.create;
  img2.Bitmap.Width:= img1.width;
  img2.Bitmap.Height:= img1.height;
  for i := 0 to img1.width  do begin
    for j := 0 to img1.height do begin
      y:= trunc((255 * luminance(img1,i,j)));
      color := RGBToColor(byte(y), byte(y), byte(y));
      img2.Bitmap.Canvas.DrawPixel(i,j, TColorToFPColor(color));
    end;
    img1.Picture.Assign(img2);
  end;
end;                                                 

Solution

  • Look at this piece of code where you have two nested loops.

      for i := 0 to img1.width  do begin
        for j := 0 to img1.height do begin
          y:= trunc((255 * luminance(img1,i,j)));
          color := RGBToColor(byte(y), byte(y), byte(y));
          img2.Bitmap.Canvas.DrawPixel(i,j, TColorToFPColor(color));
        end;
        img1.Picture.Assign(img2);
      end;
    

    After the inner loop you assign img2 to img1.Picture which you will continue to read after a visit to the outer loop. As a result, img1 becomes empty (except for the leftmost pixel column) at the time the outer loop enters the second iteration.

    Change the code as follows:

      for i := 0 to img1.width  do begin
        for j := 0 to img1.height do begin
          y:= trunc((255 * luminance(img1,i,j)));
          color := RGBToColor(byte(y), byte(y), byte(y));
          img2.Bitmap.Canvas.DrawPixel(i,j, TColorToFPColor(color));
        end;
      end;
      img1.Picture.Assign(img2);
    

    It is also misleading to name a TPicture to img2, especially when img1 refers to a TImage.

    Further, there are a few points you should consider in order to make your code more efficient. The most important is to scan a bitmap image one row at a time with the help of scanlines.

    Look at this SO post