Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: Can I improve TImage's interpolation quality?


This question is answered.


Permlink Replies: 5 - Last Post: Feb 6, 2018 8:29 PM Last Post By: Startek Startek Threads: [ Previous | Next ]
Inmatrix CEO

Posts: 5
Registered: 6/19/03
Can I improve TImage's interpolation quality?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 6, 2018 3:47 AM
After upgrading from Delphi 7, I was happy to find the new TImage supports image interpolation.

However, it seems interpolation is either broken or so low quality that it is basically useless.

For the test I took at 512x512 PNG image of a translucent sphere:
http://zoomplayer.com/t/sample_sphere.png

I then placed it in a 48x48 TImage with "DisableInterpolation" set to false.

For comparison, I interpolated the same image using a photo editor using both bilinear and bicubic interpolation and included all three in this comparison image:
http://zoomplayer.com/t/interpolation_quality.png

Is there a method to instruct TImage to use a better interpolation algorithm ?
loki loki

Posts: 787
Registered: 7/1/02
Re: Can I improve TImage's interpolation quality?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 6, 2018 7:24 AM   in response to: Inmatrix CEO in response to: Inmatrix CEO
What I do is to use the native api to resize image, they are quite easy
to use and you will have the best image quality with good interpolation.

take a look at image resize functions in alfmxcommon.pas of alcinoe
(https://github.com/Zeus64/alcinoe)
Inmatrix CEO

Posts: 5
Registered: 6/19/03
Re: Can I improve TImage's interpolation quality?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 6, 2018 7:52 AM   in response to: loki loki in response to: loki loki
That's an interesting read, but I'm not sure I want to include something this complex just for this particular issue.

In fact, I'm already hedging my bets by implementing a software-only approach (still in progress):
https://github.com/bLightZP/ImageInterpolation

loki loki wrote:
What I do is to use the native api to resize image, they are quite easy
to use and you will have the best image quality with good interpolation.

take a look at image resize functions in alfmxcommon.pas of alcinoe
(https://github.com/Zeus64/alcinoe)
loki loki

Posts: 787
Registered: 7/1/02
Re: Can I improve TImage's interpolation quality?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 6, 2018 8:22 AM   in response to: Inmatrix CEO in response to: Inmatrix CEO
take a look at image resize functions in alfmxcommon.pas of alcinoe
(https://github.com/Zeus64/alcinoe)

Will be an huge task if you want the be in pair with the performance :(
better to use the system api (every system have such api, windows, ios,
android, macos)
loki loki

Posts: 787
Registered: 7/1/02
Re: Can I improve TImage's interpolation quality?
Correct
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 6, 2018 8:29 AM   in response to: loki loki in response to: loki loki
On 2/6/2018 7:22 PM, loki loki wrote:
take a look at image resize functions in alfmxcommon.pas of alcinoe
(https://github.com/Zeus64/alcinoe)

Will be an huge task if you want the be in pair with the performance :(
better to use the system api (every system have such api, windows, ios,
android, macos)

i have this in alcinoe also, but was made (not by me) 10 years ago

TALResamplingFilter = (sfBox, sfTriangle, sfHermite, sfBell,
sfSpline, sfLanczos3, sfMitchell);

{*******************************************************************************************************}
// This is the actual scaling routine. Target must be allocated already
with sufficient size. Source must
// contain valid data, Radius must not be 0 and Filter must not be nil.
procedure ALDoStretch(Filter: TALFilterFunction; Radius: Single; Source,
Target: TBitmap);
var
ScaleX,
ScaleY: Single; // Zoom scale factors
I, J,
K, N: Integer; // Loop variables
Center: Single; // Filter calculation variables
Width: Single;
Weight: Integer; // Filter calculation variables
Left,
Right: Integer; // Filter calculation variables
Work: TBitmap;
ContributorList: TALContributorList;
SourceLine,
DestLine: PALPixelArray;
DestPixel: PALBGR;
Delta,
DestDelta: Integer;
SourceHeight,
SourceWidth,
TargetHeight,
TargetWidth: Integer;
CurrentLineR: array of Integer;
CurrentLineG: array of Integer;
CurrentLineB: array of Integer;

begin
// shortcut variables
SourceHeight := Source.Height;
SourceWidth := Source.Width;
TargetHeight := Target.Height;
TargetWidth := Target.Width;

if (SourceHeight = 0) or (SourceWidth = 0) or
(TargetHeight = 0) or (TargetWidth = 0) then Exit;

// create intermediate image to hold horizontal zoom
Work := TBitmap.Create;
try
Work.PixelFormat := pf24Bit;
Work.Height := SourceHeight;
Work.Width := TargetWidth;
if SourceWidth = 1 then ScaleX := TargetWidth / SourceWidth
else ScaleX := (TargetWidth - 1) / (SourceWidth
- 1);
if (SourceHeight = 1) or (TargetHeight = 1) then ScaleY :=
TargetHeight / SourceHeight
else ScaleY :=
(TargetHeight - 1) / (SourceHeight - 1);

// pre-calculate filter contributions for a row
SetLength(ContributorList, TargetWidth);
// horizontal sub-sampling
if ScaleX < 1 then
begin
// scales from bigger to smaller Width
Width := Radius / ScaleX;
for I := 0 to TargetWidth - 1 do
begin
ContributorList[I].N := 0;
SetLength(ContributorList[I].Contributors, Trunc(2 * Width + 1));
Center := I / ScaleX;
Left := Floor(Center - Width);
Right := Ceil(Center + Width);
for J := Left to Right do
begin
Weight := Round(Filter((Center - J) * ScaleX) * ScaleX * 256);
if Weight <> 0 then
begin
if J < 0 then N := -J
else
if J >= SourceWidth then N := SourceWidth - J +
SourceWidth - 1
else N := J;
K := ContributorList[I].N;
Inc(ContributorList[I].N);
ContributorList[I].Contributors[K].Pixel := N;
ContributorList[I].Contributors[K].Weight := Weight;
end;
end;
end;
end
else
begin
// horizontal super-sampling
// scales from smaller to bigger Width
for I := 0 to TargetWidth - 1 do
begin
ContributorList[I].N := 0;
SetLength(ContributorList[I].Contributors, Trunc(2 * Radius + 1));
Center := I / ScaleX;
Left := Floor(Center - Radius);
Right := Ceil(Center + Radius);
for J := Left to Right do
begin
Weight := Round(Filter(Center - J) * 256);
if Weight <> 0 then
begin
if J < 0 then N := -J
else
if J >= SourceWidth then N := SourceWidth - J +
SourceWidth - 1
else N := J;
K := ContributorList[I].N;
Inc(ContributorList[I].N);
ContributorList[I].Contributors[K].Pixel := N;
ContributorList[I].Contributors[K].Weight := Weight;
end;
end;
end;
end;

// now apply filter to sample horizontally from Src to Work
SetLength(CurrentLineR, SourceWidth);
SetLength(CurrentLineG, SourceWidth);
SetLength(CurrentLineB, SourceWidth);
for K := 0 to SourceHeight - 1 do
begin
SourceLine := Source.ScanLine[K];
ALFillLineChache(SourceWidth, 3, SourceLine, CurrentLineR,
CurrentLineG, CurrentLineB);
DestPixel := Work.ScanLine[K];
for I := 0 to TargetWidth - 1 do
with ContributorList[I] do
begin
DestPixel^ := ALApplyContributors(N,
ContributorList[I].Contributors, CurrentLineR, CurrentLineG, CurrentLineB);
// move on to next column
Inc(DestPixel);
end;
end;

// free the memory allocated for horizontal filter weights, since
we need the stucture again
for I := 0 to TargetWidth - 1 do ContributorList[I].Contributors :=
nil;
ContributorList := nil;

// pre-calculate filter contributions for a column
SetLength(ContributorList, TargetHeight);
// vertical sub-sampling
if ScaleY < 1 then
begin
// scales from bigger to smaller height
Width := Radius / ScaleY;
for I := 0 to TargetHeight - 1 do
begin
ContributorList[I].N := 0;
SetLength(ContributorList[I].Contributors, Trunc(2 * Width + 1));
Center := I / ScaleY;
Left := Floor(Center - Width);
Right := Ceil(Center + Width);
for J := Left to Right do
begin
Weight := Round(Filter((Center - J) * ScaleY) * ScaleY * 256);
if Weight <> 0 then
begin
if J < 0 then N := -J
else
if J >= SourceHeight then N := SourceHeight - J +
SourceHeight - 1
else N := J;
K := ContributorList[I].N;
Inc(ContributorList[I].N);
ContributorList[I].Contributors[K].Pixel := N;
ContributorList[I].Contributors[K].Weight := Weight;
end;
end;
end
end
else
begin
// vertical super-sampling
// scales from smaller to bigger height
for I := 0 to TargetHeight - 1 do
begin
ContributorList[I].N := 0;
SetLength(ContributorList[I].Contributors, Trunc(2 * Radius + 1));
Center := I / ScaleY;
Left := Floor(Center - Radius);
Right := Ceil(Center + Radius);
for J := Left to Right do
begin
Weight := Round(Filter(Center - J) * 256);
if Weight <> 0 then
begin
if J < 0 then N := -J
else
if J >= SourceHeight then N := SourceHeight - J +
SourceHeight - 1
else N := J;
K := ContributorList[I].N;
Inc(ContributorList[I].N);
ContributorList[I].Contributors[K].Pixel := N;
ContributorList[I].Contributors[K].Weight := Weight;
end;
end;
end;
end;

// apply filter to sample vertically from Work to Target
SetLength(CurrentLineR, SourceHeight);
SetLength(CurrentLineG, SourceHeight);
SetLength(CurrentLineB, SourceHeight);

SourceLine := Work.ScanLine[0];
Delta := Integer(Work.ScanLine[1]) - Integer(SourceLine);
DestLine := Target.ScanLine[0];
DestDelta := Integer(Target.ScanLine[1]) - Integer(DestLine);
for K := 0 to TargetWidth - 1 do
begin
DestPixel := Pointer(DestLine);
ALFillLineChache(SourceHeight, Delta, SourceLine, CurrentLineR,
CurrentLineG, CurrentLineB);
for I := 0 to TargetHeight - 1 do
with ContributorList[I] do
begin
DestPixel^ := ALApplyContributors(N,
ContributorList[I].Contributors, CurrentLineR, CurrentLineG, CurrentLineB);
Inc(NativeInt(DestPixel), DestDelta);
end;
Inc(SourceLine);
Inc(DestLine);
end;

// free the memory allocated for vertical filter weights
for I := 0 to TargetHeight - 1 do ContributorList[I].Contributors
:= nil;
// this one is done automatically on exit, but is here for completeness
ContributorList := nil;

finally
Work.Free;
CurrentLineR := nil;
CurrentLineG := nil;
CurrentLineB := nil;
end;
end;

Startek Startek

Posts: 16
Registered: 2/4/17
Re: Can I improve TImage's interpolation quality?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 6, 2018 8:29 PM   in response to: Inmatrix CEO in response to: Inmatrix CEO
Th interpolation is actually working very well (in two dimensions). To
me the problem seems to be the transparency isn't being "interpolated"
or handled very well (which is giving you all those edge problems).

To check Delphi you could first paint your sphere onto a blank canvas
with the background color you desire (you have a dark grey in your
output sample) and which has no transparency or alpha channel
information and see how the interpolation works.

Reiner

PS. "DisableInterpolation" - yuck - I hate "negative" booleans.

On 6/02/2018 10:47 PM, Inmatrix CEO wrote:

After upgrading from Delphi 7, I was happy to find the new TImage supports image interpolation.

However, it seems interpolation is either broken or so low quality that it is basically useless.

For the test I took at 512x512 PNG image of a translucent sphere:
http://zoomplayer.com/t/sample_sphere.png

I then placed it in a 48x48 TImage with "DisableInterpolation" set to false.

For comparison, I interpolated the same image using a photo editor using both bilinear and bicubic interpolation and included all three in this comparison image:
http://zoomplayer.com/t/interpolation_quality.png

Is there a method to instruct TImage to use a better interpolation algorithm ?
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02