ImageEn for Delphi and C++ Builder ImageEn for Delphi and C++ Builder

 

ImageEn Forum
Profile    Join    Active Topics    Forum FAQ    Search this forumSearch
Forum membership is Free!  Click Join to sign-up
Username:
Password:
Save Password
Forgot your Password?

 All Forums
 ImageEn Library for Delphi, C++ and .Net
 ImageEn and IEvolution Support Forum
 3D LUT (*.cube)
 New Topic  Reply to Topic
Author Previous Topic Topic Next Topic  

Fellafoo

USA
49 Posts

Posted - Jul 23 2024 :  07:37:45  Show Profile  Reply
Programs like ACDSee Photo Studio have an option to load a Color LUT (*.cube, *.3dl) into an image. I see that TIEVisionImage has a LUT procedure, but I haven't found an example of how to use it.

Is it possible to import a 3D LUT into an image using ImageEn?

xequte

38418 Posts

Posted - Jul 24 2024 :  05:18:21  Show Profile  Reply
Hi

Can you post or email me some sample files for analysis?



Nigel
Xequte Software
www.imageen.com
Go to Top of Page

Fellafoo

USA
49 Posts

Posted - Jul 24 2024 :  09:15:17  Show Profile  Reply
I've attached a sample cube file.

attach/Fellafoo/202472491239_Chicago.zip
41.82 KB

Here's my test code to parse and apply the LUT to an image. The results I'm getting are very close to what I see in ACDSee but I'm not familiar enough with the process to know whether I'm on the right track.

type
  LUT = array of array of array of TRGB;

function LoadCubeFile(const FileName: string): LUT;
var
  FileLines: TStringList;
  i, j, k, L, Size: Integer;
  Line, FloatStr: string;
  Sz1, Sz2, Sz3: Char;
  Sz3D: string;
  aLUT: LUT;
  LineIndex: Integer;

  function IsFloatChar(ch: Char): Boolean;
  begin
    Result := (ch in ['0' .. '9', '-', '.']);
  end;

begin
  FileLines := TStringList.Create;
  try
    FileLines.LoadFromFile(FileName);
    // Read size from the header (assuming "LUT_3D_SIZE" is present in the file)
    for i := 0 to FileLines.Count - 1 do begin
      Line := Trim(FileLines.Strings[i]);
      if Pos('LUT_3D_SIZE', Line) = 1 then begin
        LineIndex := i + 1;

        Sz3 := Line[Length(Line)];
        Sz2 := Line[Length(Line) - 1];
        Sz1 := Line[Length(Line) - 2];
        Sz3D := Trim(Sz1 + Sz2 + Sz3);
        if Sz3D = '' then Exit;
        Size := StrToInt(Sz3D);
        Break;
      end;
    end;

    SetLength(aLUT, Size, Size, Size);

    // Read LUT values
    for i := 0 to Size - 1 do begin
      for j := 0 to Size - 1 do begin
        for k := 0 to Size - 1 do begin
          if LineIndex >= FileLines.Count then Break;

          Line := Trim(FileLines[LineIndex]);
          Inc(LineIndex);

          L := 1;

          FloatStr := '';
          while (L <= Length(Line)) and IsFloatChar(Line[L]) do begin
            FloatStr := FloatStr + Line[L];
            Inc(L);
          end;
          aLUT[i, j, k].R := Round(StrToFloat(FloatStr) * 255);
          FloatStr := '';

          // Skip whitespace
          while (L <= Length(Line)) and (Line[L] = ' ') do Inc(L);

          // Read Green value
          while (L <= Length(Line)) and IsFloatChar(Line[L]) do begin
            FloatStr := FloatStr + Line[L];
            Inc(L);
          end;
          aLUT[i, j, k].G := Round(StrToFloat(FloatStr) * 255);
          FloatStr := '';

          // Skip whitespace
          while (L <= Length(Line)) and (Line[L] = ' ') do Inc(L);

          // Read Blue value
          while (L <= Length(Line)) and IsFloatChar(Line[L]) do begin
            FloatStr := FloatStr + Line[L];
            Inc(L);
          end;
          aLUT[i, j, k].B := Round(StrToFloat(FloatStr) * 255);
        end;
      end;
    end;
  finally
    FileLines.Free;
  end;
  Result := aLUT;
end;

procedure ApplyLUTToImage(var Image: TIEBitmap; const aLUT: LUT; DomMin, DomMax: TRGB);
var
  x, y, Size: Integer;
  PixelColor, MappedColor: TRGB;
  R, G, B: Byte;
  LUTIndexR, LUTIndexG, LUTIndexB: Integer;
  ScaleR, ScaleG, ScaleB: Single;

begin
  Size := Length(aLUT);
  ScaleR := (Size - 1) / (DomMax.R - DomMin.R);
  ScaleG := (Size - 1) / (DomMax.G - DomMin.G);
  ScaleB := (Size - 1) / (DomMax.B - DomMin.B);

  for y := 0 to Image.Height - 1 do begin
    for x := 0 to Image.Width - 1 do begin
      PixelColor := Image.Pixels[x, y];

      // Normalize and scale the pixel color values to LUT index range
      LUTIndexR := Round((PixelColor.R / 255 - DomMin.R) * ScaleR);
      LUTIndexG := Round((PixelColor.G / 255 - DomMin.G) * ScaleG);
      LUTIndexB := Round((PixelColor.B / 255 - DomMin.B) * ScaleB);

      // Prevent out-of-bound indexing
      LUTIndexR := Max(0, Min(LUTIndexR, Size - 1));
      LUTIndexG := Max(0, Min(LUTIndexG, Size - 1));
      LUTIndexB := Max(0, Min(LUTIndexB, Size - 1));

      // Retrieve mapped color from LUT
      MappedColor := aLUT[LUTIndexR, LUTIndexG, LUTIndexB];

      // Apply mapped color to image
      { Is cube in BGR format? }
      Image.Pixels[x, y] := CreateRGB(MappedColor.B, MappedColor.G, MappedColor.R);
    end;
  end;
end;
Go to Top of Page

xequte

38418 Posts

Posted - Jul 26 2024 :  20:44:43  Show Profile  Reply
Unfortunately I do not know the cube format, so I cannot adequately critique your code. I cannot see any obvious issues with it.


Nigel
Xequte Software
www.imageen.com
Go to Top of Page

Dennis445a

Canada
10 Posts

Posted - Aug 13 2024 :  08:04:33  Show Profile  Reply
I second support for LUT'S in a future version if possible.

dm
Go to Top of Page

xequte

38418 Posts

Posted - Aug 21 2024 :  22:39:42  Show Profile  Reply
Hi

You can email me to test the latest beta which supports loading 3D cube files and applying 3D LUTs.



Nigel
Xequte Software
www.imageen.com
Go to Top of Page

Dennis445a

Canada
10 Posts

Posted - Aug 22 2024 :  21:36:15  Show Profile  Reply
Awesome, I look forward to testing it.

dm
Go to Top of Page
  Previous Topic Topic Next Topic  
 New Topic  Reply to Topic
Jump To: