Bicubic interpolation suddenly is no better than bilinear.

Discussion in 'Java' started by Dangling Pointer, May 19, 2012.

  1. 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?
    Dangling Pointer, May 19, 2012
    #1
    1. Advertising

  2. Dangling Pointer

    Roedy Green Guest

    On Fri, 18 May 2012 22:58:38 -0700 (PDT), Dangling Pointer
    <> wrote, quoted or indirectly quoted someone who
    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?


    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.
    ..
    Roedy Green, May 19, 2012
    #2
    1. Advertising

  3. In article
    <>,
    Dangling Pointer <> wrote:

    > 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()?

    --
    John B. Matthews
    trashgod at gmail dot com
    <http://sites.google.com/site/drjohnbmatthews>
    John B. Matthews, May 19, 2012
    #3
  4. On May 19, 7:26 am, "John B. Matthews" <> wrote:
    > In article
    > <>,
    >  Dangling Pointer <> wrote:
    >
    > > 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()?


    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 ...
    Dangling Pointer, May 19, 2012
    #4
  5. 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.
    Dangling Pointer, May 19, 2012
    #5
  6. Dangling Pointer

    Jan Burse Guest

    Dangling Pointer schrieb:
    > 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
    Jan Burse, May 19, 2012
    #6
  7. In article
    <>,
    Dangling Pointer <> wrote:

    > 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");
    }

    --
    John B. Matthews
    trashgod at gmail dot com
    <http://sites.google.com/site/drjohnbmatthews>
    John B. Matthews, May 20, 2012
    #7
  8. Dangling Pointer

    BGB Guest

    On 5/19/2012 11:13 AM, Dangling Pointer wrote:
    > 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. )
    BGB, May 20, 2012
    #8
  9. Dangling Pointer

    Roedy Green Guest

    On Fri, 18 May 2012 23:12:32 -0700, Roedy Green
    <> wrote, quoted or indirectly quoted
    someone who said :

    >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.
    ..
    Roedy Green, May 21, 2012
    #9
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Raj
    Replies:
    2
    Views:
    1,856
  2. news.skynet.be
    Replies:
    0
    Views:
    564
    news.skynet.be
    Mar 25, 2005
  3. Tim Fitzpatrick

    Problems with BILINEAR/BICUBIC filters in PIL

    Tim Fitzpatrick, Jul 3, 2003, in forum: Python
    Replies:
    0
    Views:
    419
    Tim Fitzpatrick
    Jul 3, 2003
  4. bilinear or licubic

    , Oct 6, 2006, in forum: Java
    Replies:
    1
    Views:
    423
    Oliver Wong
    Oct 6, 2006
  5. Peter Bencsik
    Replies:
    2
    Views:
    812
Loading...

Share This Page