Lazarus

Programming => Graphics and Multimedia => Graphics => Topic started by: xinyiman on October 18, 2019, 01:51:28 pm

Title: (SOLVED) Change gamma image
Post by: xinyiman on October 18, 2019, 01:51:28 pm
Hi guys, I was wondering if there is a library for manipulating the gamma value of an image. It must be disconnected from LCL because I then have to use it in a program for android. Some examples of how to use it would be very welcome. Because I am not very grasped in the manipulation of images.
Title: Re: Change gamma image
Post by: xinyiman on October 20, 2019, 10:06:06 pm
No suggestioni?!
Title: Re: Change gamma image
Post by: winni on October 20, 2019, 10:42:31 pm
Encoding

Code: Text  [Select]
  1. encoded = ((original / 255) ^ (1 / gamma)) * 255

Decode back

Code: Text  [Select]
  1. original = ((encoded / 255) ^ gamma) * 255

Do that for every channel of RGB.

Test it with default gamma = 2.2

Winni
Title: Re: Change gamma image
Post by: xinyiman on October 21, 2019, 08:07:55 am
Encoding

Code: Text  [Select]
  1. encoded = ((original / 255) ^ (1 / gamma)) * 255

Decode back

Code: Text  [Select]
  1. original = ((encoded / 255) ^ gamma) * 255

Do that for every channel of RGB.

Test it with default gamma = 2.2

Winni

Thank you so much winni. But as I have already said I am not very practical with image manipulation. That's why I still ask you a very trivial question. How do I read and set the gamma value of a jpg image?

Title: Re: Change gamma image
Post by: Thaddy on October 21, 2019, 08:59:21 am
Basically, uncompress the image, use the above formulae and re-save the image.
Title: Re: Change gamma image
Post by: xinyiman on October 21, 2019, 09:07:08 am
If I understood the concept, I would need a very small example to understand how to do it technically.
Title: Re: Change gamma image
Post by: Handoko on October 21, 2019, 09:41:49 am
Not sure will it give the result you want, but I found an interesting code ChangeLightness posted by Circular:

https://forum.lazarus.freepascal.org/index.php/topic,12390.msg176665.html#msg176665
Title: Re: Change gamma image
Post by: xinyiman on October 21, 2019, 10:35:23 am
Hi write this example

Code: Pascal  [Select]
  1.  
  2. procedure TForm1.EncodeImage;
  3. var
  4.   p: PBGRAPixel;
  5.   ABitmap: TBGRABitmap;
  6.   gamma : single;
  7.   i     : integer;
  8. begin
  9.   ABitmap := TBGRABitmap.Create();
  10.   ABitmap.LoadFromFile(Self.GetApplicationLocation() + 'abc.jpg');
  11.  
  12.  
  13.   p := ABitmap.Data;
  14.  
  15.   gamma := 2.2;
  16.   for i := ABitmap.NBPixels-1 downto 0 do
  17.   begin
  18.     p^.red := ((p^.red / 255) ^ (1 / gamma)) * 255;
  19.     p^.green := ((p^.green / 255) ^ (1 / gamma)) * 255;
  20.     p^.blue := ((p^.blue / 255) ^ (1 / gamma)) * 255;
  21.     p^.alpha := ((p^.alpha / 255) ^ (1 / gamma)) * 255;
  22.     Inc(p);
  23.   end;
  24.  
  25.   ABitmap.SaveToFile(Self.GetApplicationLocation() + 'abc.jpg');
  26.   ABitmap.Free;
  27.  
  28.   Self.Image1.picture.LoadFromFile(Self.GetApplicationLocation() + 'abc.jpg');
  29.  
  30. end;        
  31.  
  32.  

But when build return this error

unit1.pas(72,33) Error: Illegal qualifier

line error is: p^.red := ((p^.red / 255) ^ (1 / gamma)) * 255;

Why?
Title: Re: Change gamma image
Post by: xinyiman on October 21, 2019, 11:17:53 am
So, I found an example in the BGRABitmap library. It's called aa_demo. The original example works, but if you want to replace the triangle that draws the original example with a photo, the program stops working. Who explains to me what the mistake is?

To reproduce the error just comment on the line
       bmp.FillPolyAntialias([coordToBmp(pts[0]),coordToBmp(pts[1]),coordToBmp(pts[2])],BGRABlack);
and uncomment the line
       //bmp.LoadFromFile(GetApplicationLocation() + 'abc.png');
Title: Re: Change gamma image
Post by: circular on October 21, 2019, 03:45:34 pm
The power operator ^ is not defined in Pascal. Instead you need to use Power function of math unit.

Also you need to round the result, because division and power returns floating point values, but channel values are integers (byte to be specific).

Code: Delphi  [Select]
  1. uses math; // <- you need to include this unit with uses clause
  2. ...
  3. begin
  4.    ...
  5.    p^.red := round( power(p^.red / 255, 1 / gamma) * 255);
  6.    ...
  7. end;
Title: Re: Change gamma image
Post by: circular on October 21, 2019, 03:51:49 pm
So, I found an example in the BGRABitmap library. It's called aa_demo. The original example works, but if you want to replace the triangle that draws the original example with a photo, the program stops working. Who explains to me what the mistake is?[/code]
Ok so the bitmap is expected to have a certain size, in this case very small. When calling load function, it resizes the bitmap. In this case, the problem is that it is then upscaled a lot and it may become too big.

Also you would not load the image each time the screen is rendered so rather load it when the form is created and free it on destroy. Then draw it by calling
Code: Delphi  [Select]
  1. bmp.PutImage(0,0,myImage,dmDrawWithTransparency);
Title: Re: Change gamma image
Post by: xinyiman on October 21, 2019, 04:07:38 pm
Thanks, now run. Look attachment.
Title: Re: (SOLVED) Change gamma image
Post by: circular on October 21, 2019, 04:15:45 pm
Wonderful  :)

Note that this may be a bit slow, especially if the image is big. You can speed up things by precomputing an array[0..255] of byte with the formula used.

Then instead of applying the formula for each component and pixel, simply access the array for that value.
Title: Re: (SOLVED) Change gamma image
Post by: xinyiman on October 21, 2019, 04:24:13 pm
Wonderful  :)

Note that this may be a bit slow, especially if the image is big. You can speed up things by precomputing an array[0..255] of byte with the formula used.

Then instead of applying the formula for each component and pixel, simply access the array for that value.

Ok, is a great idea. Thanks a lot
Title: Re: Change gamma image
Post by: wp on October 21, 2019, 07:13:12 pm
The power operator ^ is not defined in Pascal.
True for ^, but there is a ** which can be used instead (requires unit "math").

@xinyiman:
Here is another version for gamma correction based on winni's post, but which does not require neither LCL nor BGRABitmap, and is satisfied with the built-in FCL-Image machinery (https://wiki.freepascal.org/fcl-image).  It works on files directly. I double-checked with GIMP's gamma correction and found good agreement.

Code: Pascal  [Select]
  1. uses
  2.   fpimage, fpreadjpeg, fpwritejpeg, math;
  3.  
  4. procedure GammaCorrection(const ASrcFile, ADestFile: String; AGamma: Double);
  5. var
  6.   img: TFPMemoryImage;
  7.   reader: TFPCustomImageReader;
  8.   writer: TFPCustomImageWriter;
  9.   i, j: Integer;
  10.   clr: TFPColor;
  11.   invGamma: Double;
  12. begin
  13.   img := TFPMemoryImage.Create(10, 10);
  14.   try
  15.     reader := TFPReaderJpeg.Create;
  16.     try
  17.       img.LoadFromFile(ASrcFile, reader);
  18.     finally
  19.       reader.Free;
  20.     end;
  21.  
  22.     invGamma := 1.0 / AGamma;
  23.     for j := 0 to img.Height - 1 do
  24.       for i := 0 to img.Width - 1 do
  25.       begin
  26.         clr := img.Colors[i, j];
  27.         clr.Red := round(((clr.Red / 65535)**invGamma)*65535);
  28.         clr.Green := round(((clr.Green / 65535)**invGamma)*65535);
  29.         clr.Blue := round(((clr.Blue / 65535)**invGamma)*65535);
  30.         img.Colors[i, j] := clr;
  31.       end;
  32.  
  33.     writer := TFPWriterJpeg.Create;
  34.     try
  35.       img.SaveToFile(ADestFile, writer);
  36.     finally
  37.       writer.Free;
  38.     end;
  39.   finally
  40.     img.Free;
  41.   end;
  42. end;
Note that we must use the factor 65535 here because FPImage has 16-bit color channels.

I added the (slightly modified) code to the wiki article (https://wiki.freepascal.org/fcl-image#Gamma_correction).
Title: Re: Change gamma image
Post by: circular on October 21, 2019, 10:19:29 pm
That's an elegant solution.  8)

The power operator ^ is not defined in Pascal.
True for ^, but there is a ** which can be used instead (requires unit "math")
Oh that's true

2**8 = 256 
2**16 = 65536
 :)