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;