# Lazarus

## Programming => Graphics => Graphics and Multimedia => BGRABitmap and LazPaint => Topic started by: af0815 on April 19, 2021, 08:36:33 am

Post by: af0815 on April 19, 2021, 08:36:33 am
I have searched for useful function, but not found inside of BGRABitmap, maybe my issue :-)

i use this chunks of code
Code: Pascal  [Select][+][-]
1.   size:= img.Height*img.Width;
2.   p:= img.Data;
3.   for x:= 0 to size-1 do begin
4.     sumB := sumB + p^.blue;
5.     sumG := sumG + p^.green;
6.     sumR := sumR + p^.red;
7.     inc(p);
8.   end;
9.   avgR:= trunc(sumR / size);
10.   avgG:= trunc(sumG / size);
11.   avgB:= trunc(sumB / size);
12.   k := 0.299 * avgR + 0.587 * avgG + 0.114 * avgB;
13.
Is there a sum per color (with BGRA) or mean function/filter/whatelse in BGRABitmap ?

Second: I want to shift color by color save by a factor (for 8-Bit BGRA)
Code: Pascal  [Select][+][-]
1.   p:= img.Data;
2.   for x:= 0 to size-1 do begin
3.     newB:= p^.blue * kb;
4.     newG:= p^.green * kg;
5.     newR:= p^.red * kr;
6.     if trunc(newB) > 255 then
7.       p^.blue:= 255
8.     else
9.       p^.blue:= trunc(newB);
10.     if trunc(newG) > 255 then
11.       p^.green:= 255
12.     else
13.       p^.green:= trunc(newG);
14.     if trunc(newR) > 255 then
15.       p^.red:= 255
16.     else
17.       p^.red:= trunc(newR);
18.     inc(p);
19.   end;
20.

Can i shorten this with builtin functions of BGRABitmap ?

Extra question: is a builtin whitebalance filter in BGRABitmap ? The two snipets are from such a function :-)

Andreas
Post by: circular on April 19, 2021, 08:57:47 am
There is an TBGRABitmap.AveragePixel property that computes the average color taking into account transparency.

Then there is TBGRAPixel.Lightness that returns the a weighted sum of R/G/B taking as a linear value.

There isn't a straightforward multiply function though you could use BlendRect function for some adjustments with blend operations like boMultiply, boSvgSoftLight, boSoftLight, boHardLight.

About light balance, you could convert first your image into XYZ colorspace and then back to RGB but with a different reference white. To do so, you can for example create a TXYZABitmap of the same size as original image, call SetReferenceWhite with the target illuminant, do TXYZA.PutImage of the BGRA image, then call SetReferenceWhite with the original illuminant, then do TBGRABitmap.PutImage of the XYZA image.

That's not the most memory efficient way, but you get the idea. To use less memory, you can do that for each scanline into an array of colors, using the conversion function of the colorspaces. Thinking about it, that could be a new function to add to BGRABitmap.
Post by: af0815 on April 19, 2021, 10:06:33 am
That's not the most memory efficient way, but you get the idea. To use less memory, you can do that for each scanline into an array of colors, using the conversion function of the colorspaces. Thinking about it, that could be a new function to add to BGRABitmap.
Is not the easiest way to make a (shift-)filter , where you can give a RGBA factor and all pixels are multiplied with the given RGBA values ?!
Post by: circular on April 19, 2021, 01:21:09 pm
Well to do a change of white balance, that would be a way to do the chromatic adaptation.

What about the other functions I have mentioned?
Post by: af0815 on April 19, 2021, 02:40:38 pm
AveragePixel have done most of the needed work.

The actual code is
Code: Pascal  [Select][+][-]
1. procedure GrayWorld(var img: TBGRABitmap);
2. var
3.   p: PBGRAPixel;
4.   pavg: TBGRAPixel;
5.   size:integer;
6.   k:double;
7.   newB, newG, newR: double;
8.   x: integer;
9.   kb,kg,kr: double;
10. begin
11.   pavg:= img.AveragePixel;
12.   k := 0.299 * pavg.red + 0.587 * pavg.green + 0.114 * pavg.blue;
13.   // This part can differ if you want another weighting
14.   //see https://www.programmersought.com/article/98291796437/
15.   kr:= k / pavg.red  ;
16.   kg:= k / pavg.green;
17.   kb:= k / pavg.blue ;
18.   size:= img.Height*img.Width;
19.   //
20.   p:= img.Data;
21.   for x:= 0 to size-1 do begin
22.     newB:= p^.blue * kb;
23.     newG:= p^.green * kg;
24.     newR:= p^.red * kr;
25.     if trunc(newB) > 255 then
26.       p^.blue:= 255
27.     else
28.       p^.blue:= trunc(newB);
29.     if trunc(newG) > 255 then
30.       p^.green:= 255
31.     else
32.       p^.green:= trunc(newG);
33.     if trunc(newR) > 255 then
34.       p^.red:= 255
35.     else
36.       p^.red:= trunc(newR);
37.     inc(p);
38.   end;
39. end;
40.
I have to dive a little more in BGRABitmap and the mentinoed function to answer you. I want to understand the functions before i use it.

Post by: af0815 on April 20, 2021, 07:29:50 am
If i use
Code: Pascal  [Select][+][-]
1.   pavg:= img.AveragePixel;
2.   k1:= pavg.Lightness;
3.   k := 0.299 * pavg.red + 0.587 * pavg.green + 0.114 * pavg.blue;
4.
k1 and k are not the same (or nearly the same). Because ligthness is not calculation in the same schema (8-Bit value) it is using a 16Bit schema
Quote
Returns the lightness of a pixel. The lightness is the perceived brightness, 0 being black and 65535 being white

If i dive deeper, it looks like the same calculation, but with a 'shr 10'. So it looks the domain of the calculation is shifted to word instead of real used by the calculation of k.

`newB:= round(GammaExpansionTab[p^.blue] * kb);if newB > 65535 then p^.blue := 255else p^.blue := GammaCompressionTab[round(newB)];`