Recent

Author Topic: Image contrast adjustment  (Read 1407 times)

user5

  • Full Member
  • ***
  • Posts: 246
Image contrast adjustment
« on: April 07, 2019, 11:53:12 pm »
    The program that I'm working on handles hue, luminance and saturation just fine but I'm not satisfied with the contrast code which is shown below. I want it to make an image have fewer colors that are high contrast but the current code doesn't do this.

    The attached picture fig1.gif shows an example original image. The picture fig2.gif shows the same image adjusted to maximum contrast using HiJaak graphics suite and this is what I want. In other words, semi whites become whiter, semi blues become more standard blue etc..
    The picture fig3.gif is the result of a maximum contrast change using the code below. The contrast trackbar is set at zero with maximum ranges of -128 and +128.

   
Code: Pascal  [Select]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. label 2879;
  3. var factor:single;
  4.     mycolor,newcolor:string;
  5.     newRed,newGreen,newBlue:byte;
  6.     RGB,tempcolor:TColor;
  7.     pointx,pointy:integer;
  8.     i,p:integer;
  9.     H, S, L: Word;
  10.     hvar,svar,lvar:integer;
  11.     var1,var2,var3:integer;
  12.     px,py,q:integer;
  13. begin
  14.  
  15.  haltscan := false;
  16.  button7.enabled := false;
  17.  button4.enabled := false;
  18.  button6.enabled := false;
  19.  button5.enabled := false;
  20.  button1.enabled := false;
  21.  image1.picture := image2.picture;
  22.  image1.visible := true;
  23.  
  24.  if (edit1.text <> '0') then
  25.   begin
  26.    shape1.visible := true;
  27.    statictext8.visible := true;
  28.    imagealtered := true;
  29.    changeapplied := true;
  30.    memo1.text := memo1.text + '-Doing contrast-';
  31.    application.processmessages;
  32.    pointx := 0;
  33.    pointy := 0;
  34.    i := 0;
  35.    while (i < image1.width) and (haltscan = false) do
  36.     begin
  37.      pointy := 0;
  38.      p := 0;
  39.      while (p < image1.height) and (haltscan = false) do
  40.       begin
  41.        mycolor := ColorToString(image1.canvas.pixels[pointx,pointy]);
  42.        if safemode = true then
  43.         application.processmessages;
  44.        if (mycolor = 'clblack') or (mycolor = 'clMaroon') or (mycolor = 'clGreen') or (mycolor = 'clOlive') or
  45.           (mycolor = 'clNavy') or (mycolor = 'clPurple') or (mycolor = 'clTeal') or (mycolor = 'clGray') or
  46.           (mycolor = 'clSilver') or (mycolor = 'clRed') or (mycolor = 'clLime') or (mycolor = 'clYellow') or
  47.           (mycolor = 'clBlue') or (mycolor = 'clFuchsia') or (mycolor = 'clAqua') or (mycolor = 'clLtGray') or
  48.           (mycolor = 'cldkGray') or (mycolor = 'clWhite') then
  49.         begin
  50.          tempcolor := StringToColor(mycolor);
  51.          goto 2879;
  52.         end;
  53.  
  54.        factor := (259 * (contrast + 255)) / (255 * (259 - contrast));
  55.        newRed   := Trunc(factor * (Red(StringToColor(mycolor))   - 128) + 128);
  56.        newGreen := Trunc(factor * (Green(StringToColor(mycolor)) - 128) + 128);
  57.        newBlue  := Trunc(factor * (Blue(StringToColor(mycolor))  - 128) + 128);
  58.        tempcolor := RGBToColor(newRed,newGreen,newBlue);
  59.        2879:
  60.        image1.canvas.pixels[pointx,pointy] := tempcolor;
  61.        pointy := pointy + 1;
  62.        p := p + 1;
  63.        if safemode = true then
  64.         application.processmessages;
  65.       end;
  66.      if safemode = true then
  67.       application.processmessages;
  68.      pointx := pointx + 1;
  69.      i := i + 1;
  70.     end;
  71.   end;
  72.  
  73.  haltscan := false;
  74.  button7.enabled := true;
  75.  button1.enabled := true;
  76.  button4.enabled := true;
  77.  button6.enabled := true;
  78.  button5.enabled := true;
  79.  shape1.visible := false;
  80.  statictext8.visible := false;
  81.  
  82. end;

    Thus my question is: Does anyone know how I could tweak the contrast code so that the resulting image has fewer colors like in example fig2.gif?
    Thanks.

VTwin

  • Hero Member
  • *****
  • Posts: 793
  • Former Turbo Pascal 3 user
Re: Image contrast adjustment
« Reply #1 on: April 08, 2019, 02:46:09 am »
This an incomplete response, but it looks like your code is doing some heavy lifting. Is it very slow? Have you looked into BGRABitmap? It supplies some very fast image transform routines.

I assume converting each pixel to a string must be extremely slow. Maybe round each R, G, B value to 0 or 255.
« Last Edit: April 08, 2019, 03:03:14 am by VTwin »
“Talk is cheap. Show me the code.” -Linus Torvalds

macOS 10.13.6: Lazarus 2.0.7 fixes svn 62300 (64 bit Cocoa)
Ubuntu 18.04.3: Lazarus 2.0.6 (64 bit on VBox)
Windows 7 Pro SP1: Lazarus 2.0.6 (64 bit on VBox)
fpc 3.0.4

Ñuño_Martínez

  • Hero Member
  • *****
  • Posts: 918
    • Burdjia
Re: Image contrast adjustment
« Reply #2 on: April 08, 2019, 10:36:00 am »
Question: Why are you using GOTO instead of ELSE?

Code: Pascal  [Select]
  1. IF (mycolor = 'clblack')  OR (mycolor = 'clMaroon')  OR (mycolor = 'clGreen')
  2. OR (mycolor = 'clOlive')  OR (mycolor = 'clNavy')    OR (mycolor = 'clPurple')
  3. OR (mycolor = 'clTeal')   OR (mycolor = 'clGray')    OR (mycolor = 'clSilver')
  4. OR (mycolor = 'clRed')    OR (mycolor = 'clLime')    OR (mycolor = 'clYellow')
  5. OR (mycolor = 'clBlue')   OR (mycolor = 'clFuchsia') OR (mycolor = 'clAqua')
  6. OR (mycolor = 'clLtGray') OR (mycolor = 'cldkGray')  OR (mycolor = 'clWhite')
  7. THEN
  8.   tempcolor := StringToColor (mycolor)
  9. ELSE BEGIN
  10.   factor := (259 * (contrast + 255)) / (255 * (259 - contrast));
  11.   newRed   := Trunc(factor * (Red(StringToColor(mycolor))   - 128) + 128);
  12.   newGreen := Trunc(factor * (Green(StringToColor(mycolor)) - 128) + 128);
  13.   newBlue  := Trunc(factor * (Blue(StringToColor(mycolor))  - 128) + 128);
  14.   tempcolor := RGBToColor(newRed,newGreen,newBlue);
  15. END;
Are you interested in game programming? Join the Pascal Game Development community!
Also visit the Game Development Portal

Mr.Madguy

  • Sr. Member
  • ****
  • Posts: 457
Re: Image contrast adjustment
« Reply #3 on: April 08, 2019, 12:21:11 pm »
This one works for me:
Code: Pascal  [Select]
  1. procedure ChangeContrast(Bitmap:TBitmap;Contrast:Integer);
  2.   var Factor:Real;
  3.   I, J:Integer;
  4.   Color:TColor;
  5.   R, G, B:Integer;
  6. begin
  7.   Factor := (259 * (255 + Contrast)) / (255 * (259 - Contrast));
  8.   for J := 0 to Bitmap.Height - 1 do begin
  9.     for I := 0 to Bitmap.Width - 1 do begin
  10.       Color := Bitmap.Canvas.Pixels[I, J];
  11.       R := Trunc(Factor * (Red(Color) - 128)) + 128;
  12.       G := Trunc(Factor * (Green(Color) - 128)) + 128;
  13.       B := Trunc(Factor * (Blue(Color) - 128)) + 128;
  14.       if R < 0 then R := 0;
  15.       if R > 255 then R := 255;
  16.       if G < 0 then G := 0;
  17.       if G > 255 then G := 255;
  18.       if B < 0 then B := 0;
  19.       if B > 255 then B := 255;
  20.       Bitmap.Canvas.Pixels[I, J] := RGBToColor(R, G, B);
  21.     end;
  22.   end;
  23. end;
  24.  
DynamicData 3.0 is released!
Since now development is frozen - only optimization passes will be made at some point.
Lack of multiple inheritance turns it into abomination.

wp

  • Hero Member
  • *****
  • Posts: 6450
Re: Image contrast adjustment
« Reply #4 on: April 08, 2019, 12:40:10 pm »
I would not work in RGB color space because color hues will change. Not being a color export, I'd convert each color to HLS color space, find the min and max L, expand the L in each pixel such that the minimum L becomes 0 and the maximum L 255, and finally convert everything back to RGB.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

user5

  • Full Member
  • ***
  • Posts: 246
Re: Image contrast adjustment
« Reply #5 on: April 09, 2019, 07:20:53 am »
    Excuse me for not responding sooner. I've been sick.
    Yeah, the string part of the code probably slows things down a bit and I need to take care of that but the rest of the code uses raw image code that is as fast or faster than BGRABitmap.
    Yes, I should probably use "else" instead of "goto". This particular code isn't perfect.
    Mr.Madguy and wp, I'm checking your suggestions out now and I will report the results. If your code works out for me then I'll have to convert it into raw image code. Thanks so much.
    I want the user to be able to use both methods.
    Will advise...

user5

  • Full Member
  • ***
  • Posts: 246
Re: Image contrast adjustment
« Reply #6 on: April 09, 2019, 08:42:29 am »
    Mr.Madguy, I tried your code and it worked SUPERBLY! This is what I ALWAYS wanted! I praise you because this is a real breakthrough. I do have to convert it to raw image and I'm a little rusty but I think that I can do it.
    It's obvious that I need to speed the code up and make it more efficient. I have also found that a bitmap is faster and more reliable than working with a TImage.
    Your code will be the default contrast method.
    I can't say too much how great this is because not only does it do traditional contrast changes as good as or better than HiJaak Draw (which is saying alot), it also enables all the transparency and color controls to work much, much better and faster than before. I've tested it out and it's really amazing how much it helps.
    This means that the current download version of this program will need to be updated. With your permission I will mention Lazarus and your handle name in the program update logs and instructions (a long readme file) because the credit is yours, not mine.
    wp, I assume that what you suggested is shown in Mr.Madguy's code.
    Thank you again. This is incredible.

Mr.Madguy

  • Sr. Member
  • ****
  • Posts: 457
Re: Image contrast adjustment
« Reply #7 on: April 09, 2019, 12:35:14 pm »
There is no way to speed this process up without using hardware acceleration. Well, at some point (in 95/98 era) SSE instructions were used to speed up image processing. Now, I guess, GPU shaders are used for this purpose. They can do it in real time.
DynamicData 3.0 is released!
Since now development is frozen - only optimization passes will be made at some point.
Lack of multiple inheritance turns it into abomination.

circular

  • Hero Member
  • *****
  • Posts: 3047
    • Personal webpage
Re: Image contrast adjustment
« Reply #8 on: April 10, 2019, 12:23:14 pm »
I would disagree Mr.Maguy. The process can be faster. Using Pixels property is a bit slow as far as I know. It would be faster to use direct pixel access, for example via RawImage or using BGRABitmap. Here is an example of the RawImage method:
https://forum.lazarus.freepascal.org/index.php/topic,43001.msg300332.html#msg300332
Conscience is the debugger of the mind

Mr.Madguy

  • Sr. Member
  • ****
  • Posts: 457
Re: Image contrast adjustment
« Reply #9 on: April 12, 2019, 11:26:08 am »
I would disagree Mr.Maguy. The process can be faster. Using Pixels property is a bit slow as far as I know. It would be faster to use direct pixel access, for example via RawImage or using BGRABitmap. Here is an example of the RawImage method:
https://forum.lazarus.freepascal.org/index.php/topic,43001.msg300332.html#msg300332
But it's still software method and I actually meant all software methods. May be it wasn't obvious. Yeah, that's my mistake. SSEs can be even faster, cuz they manipulate all chanels or even several pixels at once. But nobody uses SSEs directly nowdays, cuz their support isn't guaranteed on all processors. It's usually done automatically through some sort of libraries, like OpenGL, OpenCL or Direct3D, that depend on drivers to do the job.
DynamicData 3.0 is released!
Since now development is frozen - only optimization passes will be made at some point.
Lack of multiple inheritance turns it into abomination.

circular

  • Hero Member
  • *****
  • Posts: 3047
    • Personal webpage
Re: Image contrast adjustment
« Reply #10 on: April 15, 2019, 08:43:52 pm »
Indeed it can be even faster with hardware methods.
Conscience is the debugger of the mind

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 7588
Re: Image contrast adjustment
« Reply #11 on: April 15, 2019, 08:53:17 pm »
There is no way to speed this process up without using hardware acceleration. Well, at some point (in 95/98 era) SSE instructions were used to speed up image processing. Now, I guess, GPU shaders are used for this purpose. They can do it in real time.

Most vision uses SSE, because uploading/downloading the results from GPU also takes time, and is usually longer than the processing time. Worse, it blocks the GPU usage from all threads.


User137

  • Hero Member
  • *****
  • Posts: 1791
    • Nxpascal home
Re: Image contrast adjustment
« Reply #12 on: April 16, 2019, 10:58:20 am »
I did a RawImage conversion of the function, and in my test (Win10) it's instant on a test image of 570x455. I couldn't rely on both bitmaps having valid format so i created them temporary 24-bit buffers. It should work with this regardless of what the original pictures are; JPG, PNG, BMP etc. But transparency is lost of course, many changes would be needed for that. Likely out of scope of the thread though...

Code: Pascal  [Select]
  1. procedure ChangeContrast(inBm, outBm: TBitmap; Contrast: Integer);
  2. var Factor: Single;
  3.     I: Integer;
  4.     bOut: Integer;
  5.     inByte, outByte: PByte;
  6.     tmpOut, tmpIn: Graphics.TBitmap;
  7. begin
  8.   tmpIn:=TBitmap.Create;
  9.   tmpIn.PixelFormat:=pf24bit;
  10.   tmpIn.SetSize(inBm.Width, inBm.Height);
  11.   tmpIn.Canvas.Draw(0, 0, inBm);
  12.   tmpOut:=TBitmap.Create;
  13.   tmpOut.PixelFormat:=pf24bit;
  14.   tmpOut.SetSize(inBm.Width, inBm.Height);
  15.   inByte:=tmpIn.RawImage.Data;
  16.   outByte:=tmpOut.RawImage.Data;
  17.   Factor := (256 * (255 + Contrast)) / (255 * (256 - Contrast));
  18.   for I := 0 to inBm.Height * inBm.Width * 3 - 1 do begin
  19.     bOut := Trunc(Factor * (inByte^ - 128)) + 128;
  20.     if bOut < 0 then bOut := 0
  21.     else if bOut > 255 then bOut := 255;
  22.     outByte^:=byte(bOut);
  23.     inc(inByte);
  24.     inc(outByte);
  25.   end;
  26.   outBm.Canvas.Draw(0, 0, tmpOut);
  27.   tmpOut.Free;
  28.   tmpIn.Free;
  29. end;

circular

  • Hero Member
  • *****
  • Posts: 3047
    • Personal webpage
Re: Image contrast adjustment
« Reply #13 on: April 18, 2019, 07:00:06 pm »
@User137

This may work on some platform but the PixelFormat property is not reliable.
Hence the following method:
https://forum.lazarus.freepascal.org/index.php/topic,43001.msg300332.html#msg300332
Conscience is the debugger of the mind

User137

  • Hero Member
  • *****
  • Posts: 1791
    • Nxpascal home
Re: Image contrast adjustment
« Reply #14 on: April 19, 2019, 09:57:04 am »
Interesting, RawImage.Description.BitsPerPixel div 8 then. It would then also be possible to use the original input and output bitmaps directly, if someone really needs to optimize the function more. The cool thing about the function was that you can treat the image as series of bytes, completely disregarding what is R,G and B bytes. If source or output had alpha it would have to be direct copied every 4 bytes though. Of course other formats than just 24 and 32 bits exist, such as 8 bit grayscale. It would be nice if pixelformat dealt with them too.
« Last Edit: April 19, 2019, 10:00:52 am by User137 »