Bicubic interpolation suddenly is no better than bilinear.

D

Dangling Pointer

For some reason, I'm suddenly seeing bilinear filtering when using
TYPE_BICUBIC filtering with AffineTransformOp in Java2D. This change
happened without my changing the Java version (1.6.0_24) being used!
What could be causing this, and how do I fix it?
 
R

Roedy Green

For some reason, I'm suddenly seeing bilinear filtering when using
TYPE_BICUBIC filtering with AffineTransformOp in Java2D. This change
happened without my changing the Java version (1.6.0_24) being used!
What could be causing this, and how do I fix it?

You are claiming a "virgin birth" here. Nothing charged, yet the
program gave different results. What could possibly have changed?

Do you have two different jars? two different JVMs? Some sort of
config file? Is there any use of Random number generators in your
code?

Can your restore from backup and see the old behaviour, and then
compare files till you see what changed?
--
Roedy Green Canadian Mind Products
http://mindprod.com
"Plants" with "leaves" no more efficient than today's solar cells
could out-compete real plants, crowding the biosphere with an
inedible foliage. Tough omnivorous "bacteria" could out-compete
real bacteria: They could spread like blowing pollen, replicate
swiftly, and reduce the biosphere to dust in a matter of days.
Dangerous replicators could easily be too tough, small, and
rapidly spreading to stop -- at least if we make no preparation.
We have trouble enough controlling viruses and fruit flies.
~ Eric Drexler (born: 1955-04-25 age: 57)
Engines of Creation: The Coming Era of Nanotechnology.
..
 
J

John B. Matthews

Dangling Pointer said:
For some reason, I'm suddenly seeing bilinear filtering when using
TYPE_BICUBIC filtering with AffineTransformOp in Java2D. This change
happened without my changing the Java version (1.6.0_24) being used!
What could be causing this, and how do I fix it?

How is the AffineTransformOp constructed?

What is the result of getInterpolationType()?
 
D

Dangling Pointer

How is the AffineTransformOp constructed?

What is the result of getInterpolationType()?

new AffineTransformOp (someAffineTransform,
AffineTransformOp.TYPE_BICUBIC).

3.

Believe me, I double-checked *everything* when I noticed lower output
quality than I expected. I eyeballed every line of code dealing with
those particular images ten times, added debugging prints asking
things for their getInterpolationType or other attributes, etc. One
test I ran was to vary the interpolation type; I got worse quality
with TYPE_NEAREST_NEIGHBOR and the exact same with TYPE_BILINEAR which
is made me think it was suddenly acting like Java 4 instead of Java 6.
Of course I threw in System.out.println(System.getProperty
"java.version") once I had that thought, but out popped "1.6.0_24".

In fact, the quality I get (with TYPE_BICUBIC and TYPE_BILINEAR) is
worse than I'd even expect with TYPE_BILINEAR.

Oh, and another thing I tried was using a RenderingHints object
instead of the integer type in the ato's constructor.
KEY_INTERPOLATION, VALUE_INTERPOLATION_BICUBIC of course. When that
had no effect I tried adding KEY_RENDERING, VALUE_RENDER_QUALITY to
that, which didn't change anything either. Meanwhile, using
Graphics.drawImage(img,x,y,w,h,null) instead of
AffineTransformOp.filter (after setting the Graphics's RenderingHints
to the same) results in the quality I get using AffineTransformOp with
TYPE_NEAREST_NEIGHBOR (regardless of the RenderingHints).

Obviously *some* condition has changed. And it is, at least in the
latter case, a "hint" rather than a "guarantee". Is there some
circumstance in which it will fall back on bilinear even when
rendering hints tell it the caller prefers bicubic and prefers quality
over speed? This is an offline render from a BufferedImage to a
BufferedImage with no screen drawing surfaces used.

I suppose I could fall back on Image.getScaledInstance with
SCALE_AREA_AVERAGING, if I don't mind jobs that took seconds taking
minutes and giving only the quality that TYPE_BILINEAR *used* to give
me ...
 
D

Dangling Pointer

Well, I can confirm that something's hinky. I've now tested
downsampling a fairly noisy 9600x5400 image to 1280x720 in each of the
following ways:

* With Photoshop, using bicubic resampling.
* With Java, using the process of repeatedly halving the size of the
input with AffineTransformOp and then making the final rescale when
the input is less than or equal to twice the size of the
destination, and using each of:
* TYPE_NEAREST_NEIGHBOR
* TYPE_BILINEAR
* TYPE_BICUBIC
* RenderingHints with interpolation set to bicubic and render set
to quality.
* With Java, using Graphics.drawImage and RenderingHints set to
bicubic/quality
* With Java, using getScaledInstance with SCALE_AREA_AVERAGING

The results were:

Best quality: Photoshop and SCALE_AREA_AVERAGING are about on a par
and heads and shoulders above the rest.

Next: iterative rescaling using either TYPE_BILINEAR, TYPE_BICUBIC, or
RenderingHints bicubic/quality (three-way tie).

Then: iterative rescaling using TYPE_NEAREST_NEIGHBOR and drawImage
using bicubic/quality (two-way tie).

I'm no longer sure that there's a change from the earlier operation of
this particular Java install. On closer inspection of older outputs of
similar code, I don't see a quality difference with iterative
"bicubic" rescaling when the downsampling ratio and input noisiness
were comparable; the better results were from less-noisy input images
and/or larger downsampling ratios (e.g. a 64x48 thumbnail from a
4000x3000 TIF).

Still, something is wrong. Iterative BILINEAR rescaling is *supposed*
to achieve comparable quality to SCALE_AREA_AVERAGING, and bicubic is
supposed to run rings around BILINEAR for quality in turn. So
iterative BILINEAR should be as good as SCALE_AREA_AVERAGING and
iterative BICUBIC should be better still. Instead, they're equal to
each other and significantly worse than SCALE_AREA_AVERAGING.

On the positive side, SCALE_AREA_AVERAGING doesn't seem to be as slow
in Java 6 as it is reputed to be (possibly this reputation is based on
the performance of older versions). So I'll be using that for now.
 
J

Jan Burse

Dangling said:
Still, something is wrong. Iterative BILINEAR rescaling is*supposed*
to achieve comparable quality to SCALE_AREA_AVERAGING, and bicubic is

Most probably your claim is wrong, even with the best
rounding in the world. We have ideally:

x1 + .... + x2n x1 + x2 x2n-1 + x2n
--------------- = ------- -----------
2^n 2 2
....
-------------------
2

Now a rounding function r(x/n) can be viewed as
an nummerator correction c(x/n) function:

r(x/n) = (x + c(x/n)) / n

For /2 the nummerator correction is either -1 or 0
when you round down. So there might be a maximal
correction of -1 * 2^(n-1) in the first iteration of
bilinear. Then -1 * 2 * 2^(n-2) in the next iteration,
and so on: Total maximal correction:

-n*2^(n-1)

For /2^n the numeration correction is somewhere
between -2^n+1 and 0 when you round down. So maximal
correction is:

-2^n+1

When is the iteration correction by 2^n bigger
than the area correction? Lets make a little table:

n -n*2^(n-1) -2^n+1 diff 2^n
1 -1 -1 0 2
2 -4 -3 1 4
3 -12 -7 5 8
4 -32 -15 17 16
5 -80 -31 49 32
6 -192 -63 129 64
7 -448 -127 321 128

So I guess for 4 iteration we could eventually
construct an example where the error would be
at least one color value step, since the
maximal numerator correction difference is
then greater than the denumerator 2^n.

Let's give it a try:

Iterative:
3 0 1 0 0 1 0 3
1 0 0 1
0 0
0

Area:
3 0 1 0 0 1 0 3
1

Yes we have found an example where iterative is
different from area by one color pixel value step.

Bye
 
J

John B. Matthews

Dangling Pointer said:
On the positive side, SCALE_AREA_AVERAGING doesn't seem to be as slow
in Java 6 as it is reputed to be (possibly this reputation is based
on the performance of older versions). So I'll be using that for now.

Thank you for reporting your (extensive) findings. It sounds like you
are aware, but for reference: The Perils of Image.getScaledInstance()

<http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html>

The last time I encountered such resampling problems, it turned out
to be an artifact inserted earlier in my pipeline that only became
apparent in a later stage. FWIW, I had good results from ImageJ's
bicubic interpolation implementation. I needed both 8- and 16-bit
versions, with endian control for the latter, and it proved easy to
script.

I'm not sure it's relevant, but I've occasionally had to specify
the platform renderer instead of Sun’s 2D renderer:

if (System.getProperty("os.name").startsWith("Mac OS X")) {
System.setProperty("apple.awt.graphics.UseQuartz", "true");
}
 
B

BGB

Well, I can confirm that something's hinky. I've now tested
downsampling a fairly noisy 9600x5400 image to 1280x720 in each of the
following ways:

* With Photoshop, using bicubic resampling.
* With Java, using the process of repeatedly halving the size of the
input with AffineTransformOp and then making the final rescale when
the input is less than or equal to twice the size of the
destination, and using each of:
* TYPE_NEAREST_NEIGHBOR
* TYPE_BILINEAR
* TYPE_BICUBIC
* RenderingHints with interpolation set to bicubic and render set
to quality.
* With Java, using Graphics.drawImage and RenderingHints set to
bicubic/quality
* With Java, using getScaledInstance with SCALE_AREA_AVERAGING

The results were:

Best quality: Photoshop and SCALE_AREA_AVERAGING are about on a par
and heads and shoulders above the rest.

Next: iterative rescaling using either TYPE_BILINEAR, TYPE_BICUBIC, or
RenderingHints bicubic/quality (three-way tie).

Then: iterative rescaling using TYPE_NEAREST_NEIGHBOR and drawImage
using bicubic/quality (two-way tie).

I'm no longer sure that there's a change from the earlier operation of
this particular Java install. On closer inspection of older outputs of
similar code, I don't see a quality difference with iterative
"bicubic" rescaling when the downsampling ratio and input noisiness
were comparable; the better results were from less-noisy input images
and/or larger downsampling ratios (e.g. a 64x48 thumbnail from a
4000x3000 TIF).

Still, something is wrong. Iterative BILINEAR rescaling is *supposed*
to achieve comparable quality to SCALE_AREA_AVERAGING, and bicubic is
supposed to run rings around BILINEAR for quality in turn. So
iterative BILINEAR should be as good as SCALE_AREA_AVERAGING and
iterative BICUBIC should be better still. Instead, they're equal to
each other and significantly worse than SCALE_AREA_AVERAGING.

On the positive side, SCALE_AREA_AVERAGING doesn't seem to be as slow
in Java 6 as it is reputed to be (possibly this reputation is based on
the performance of older versions). So I'll be using that for now.



quick comment:
note that you are downsampling (and by a significant factor as well),
whereas bicubic is a filter better suited for upsampling (if used
directly for downsampling, the quality will be terrible).

the direction that the resampling is being done is fairly important for
which filter is being used (for general purpose image resampling, it is
typical to detect whether the image is being upsampled or downsampled,
and select which filter is used based on this).


instead, what is needed is to use a filter which averages the samples
for downsampling (this is the best "simple" option). hence, something
more like SCALE_AREA_AVERAGING.

whereas, bicubic will likely give the best quality when upsampling the
image to a higher resolution.


otherwise, if general purpose resampling is needed (where the same image
can be used for either upsampling or downsampling at the same time, such
as a texture in 3D rendering), one needs a filter such as trilinear or
tricubic.

( decided to leave out a description of how to go about implementing a
tricubic filter, as this is a little more advanced. )
 
R

Roedy Green

You are claiming a "virgin birth" here. Nothing charged, yet the
program gave different results. What could possibly have changed?

You are aware that any time you change a constant, you must do a clean
compile to propagate the value to all classes.
--
Roedy Green Canadian Mind Products
http://mindprod.com
"Plants" with "leaves" no more efficient than today's solar cells
could out-compete real plants, crowding the biosphere with an
inedible foliage. Tough omnivorous "bacteria" could out-compete
real bacteria: They could spread like blowing pollen, replicate
swiftly, and reduce the biosphere to dust in a matter of days.
Dangerous replicators could easily be too tough, small, and
rapidly spreading to stop -- at least if we make no preparation.
We have trouble enough controlling viruses and fruit flies.
~ Eric Drexler (born: 1955-04-25 age: 57)
Engines of Creation: The Coming Era of Nanotechnology.
..
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,576
Members
45,054
Latest member
LucyCarper

Latest Threads

Top