I think CompositeXYProjector is meant to handle the following cases:

1) Rendering LUT images, a single converter is used. Grayscale images are included here.

2) Rendering RGB images, three converters are used. These use red-only, green-only, and blue-only LUTs.

3) I believe it's also intended to work with images with > 3 channels, using C, M, and Y for the excess channels.

The existing code works well for cases 1 & 2. Case 3 adds the possibility of overflow, if your red converter gives you a value of 255 for the red component but your magenta converter adds another 255. Currently the code just limits the value to 255 in that case. Some sort of blending might work better here, but the bigger issue is RGBCMY is not an additive color system. If you see a cyan blotch you don't know if its in both the G & B channels or just the C channel.

Aivar


On 7/15/13 2:40 PM, Lee Kamentsky wrote:
Thanks for answering Aivar,

I think what your reply did for me is to have me take a step back and consider what we're modeling. If you look at my replies below, I think that the best solution is to use a model where the background is white and each successive layer filters out some of that background, like a gel. A layer attenuates the underlying layer by a fraction of (1 - alpha/255 * (1 - red/255)), resulting in no attenuation for 255 and attenuation of alpha/255 for zero. We can then use a red converter that returns a value of 255 for the blue and green channels and the model and math work correctly.

On Mon, Jul 15, 2013 at 1:59 PM, Aivar Grislis <[email protected] <mailto:[email protected]>> wrote:

    I have an ImgPlus backed by an RGB PlanarImg of UnsignedByteType
    and ARGBType.alpha(value) is 255 for all of them, so aSum is 765.
    It would appear that the correct solution would be to divide aSum
    by 3.
    Isn't it unusual to define an alpha for each color component,
    generally you have a single A associated with a combined RGB?  So
    averaging the three alphas might make sense here, because I think
    they should all be the same value.

I think you're right, the model always is that each pixel has an alpha value that applies to R, G and B. The image I was using was the Clown example image. DefaultDatasetView.initializeView constructs three RealLUTConverters for the projector, one for red, one for green and one for blue which sends you down this rabbit hole.

    In addition, there's no scaling of the individual red, green and
    blue values by their channel's alpha. If the input were two
    index-color images, each of which had different alphas, the code
    should multiply the r, g and b values by the alphas before
    summing and then divide by the total alpha in the end. The alpha
    in this case *should* be the sum of alphas divided by the number
    of channels.
    I think alpha processing is more cumulative, done layer by layer
    in some defined layer order.  For a given pixel say the current
    output pixel value is ARGB1 and you are compositing a second image
    with value ARGB2 on top of it:  For the red channel the output
    color should be ((255 - alpha(ARGB2)) * red(ARGB1) + alpha(ARGB2)
    * red(ARGB2)) / 255.  The alpha of ARGB1 is not involved.

I think that's a valid interpretation. I've always used (alpha(ARGB1) * red(ARGB1) + alpha(ARGB2) * red(ARGB2)) / (alpha(ARGB1) + alpha(ARGB2)) because I assumed the alpha indicated the strength of the blending of each source. In any case, the code as it stands doesn't do either of these.


    In other words, if you add a layer that is completely opaque you
    no longer have to consider any of the colors or alpha values
    underneath it.


    I think the bigger issue here is this code is specifically
    designed to composite red, green and blue image layers.  It's a
    special case since for a given pixel the red comes from the red
    layer, blue from blue layer, and green from green layer.  These
    layers shouldn't be completely opaque, since the colors wouldn't
    combine at all then or completely transparent since then they
    wouldn't contribute any color.  I don't think transparency is
    useful here.

So this is an argument for blending instead of layering - transparency would be useful if the images were blended and treated as if on a par with each other, allowing the user to emphasize one channel or the other.


    It's also possible that a multichannel image with > 3 channels is
    being displayed with more color channels, namely cyan, magenta,
    and yellow.  The code here is designed to stop overflow, but I'm
    not convinced those extended color channels would combine
    meaningfully.

    Aivar

    In addition, there's no scaling of the individual red, green and
    blue values by their channel's alpha. If the input were two
    index-color images, each of which had different alphas, the code
    should multiply the r, g and b values by the alphas before
    summing and then divide by the total alpha in the end. The alpha
    in this case *should* be the sum of alphas divided by the number
    of channels.
    I think alpha processing is cumulative layer by layer.

    This brings up some interesting questions:

    1) If the first, bottom-most layer is transparent, what color
    should show through?  Black, white?  Or perhaps it's best to
    ignore this base layer transparency.

Maybe the model should be that the background is white and successive layers are like gel filters on top. In that case, you'd have:
red = (255 - alpha(ARGB2) *(255 - red(ARGB2))/255) * red(ARGB1)

And maybe that points to what the true solution is. For the default, we could change things so that red channel would have blue = 255 and green = 255 and the first composition would change only the red channel.


    2) If you wanted to composite several transparent images, how do
    you calculate the transparency of the composite?  I'm not sure
    this is something we need to do.

    Aivar


    On 7/15/13 10:31 AM, Lee Kamentsky wrote:
    Hi all,
    I'm looking at the code for
    net.imglib2.display.CompositeXYProjector and as I step through
    it, it's clear that the alpha calculation isn't being handled
    correctly. Here's the code as it stands now, line 190 roughly:

    for ( int i = 0; i < size; i++ )
    {
    sourceRandomAccess.setPosition( currentPositions[ i ], dimIndex );
    currentConverters[ i ].convert( sourceRandomAccess.get(), bi );
    // accumulate converted result
    final int value = bi.get();
    final int a = ARGBType.alpha( value );
    final int r = ARGBType.red( value );
    final int g = ARGBType.green( value );
    final int b = ARGBType.blue( value );
    aSum += a;
    rSum += r;
    gSum += g;
    bSum += b;
    }
    if ( aSum > 255 )
    aSum = 255;
    if ( rSum > 255 )
    rSum = 255;
    if ( gSum > 255 )
    gSum = 255;
    if ( bSum > 255 )
    bSum = 255;
    targetCursor.get().set( ARGBType.rgba( rSum, gSum, bSum, aSum ) );

    I have an ImgPlus backed by an RGB PlanarImg of UnsignedByteType
    and ARGBType.alpha(value) is 255 for all of them, so aSum is 765.
    It would appear that the correct solution would be to divide aSum
    by 3. In addition, there's no scaling of the individual red,
    green and blue values by their channel's alpha. If the input were
    two index-color images, each of which had different alphas, the
    code should multiply the r, g and b values by the alphas before
    summing and then divide by the total alpha in the end. The alpha
    in this case *should* be the sum of alphas divided by the number
    of channels.

    However, I think the problem is deeper than that. For an RGB
    ImgPlus, there are three LUTs and each of them has an alpha of
    255, but that alpha only applies to one of the colors in the LUT.
    When you're compositing images and weighing them equally, if two
    are black and one is white, then the result is 1/3 of the white
    intensity - if you translate that to red, green and blue images,
    the resulting intensity will be 1/3 of that desired. This might
    sound weird, but the only solution that works out mathematically
    is for the defaultLUTs in the DefaultDatasetView to use color
    tables that return values that are 3x those of ColorTables.RED,
    GREEN and BLUE. Thinking about it, I'm afraid this *is* the
    correct model and each channel really is 3x brighter than possible.

    It took me quite a bit of back and forth to come up with the
    above... I hope you all understand what I'm saying and understand
    the problem and counter-intuitive solution and have the patience
    to follow it. Dscho, if you made it this far - you're the
    mathematician, what's your take?

    --Lee


    _______________________________________________
    ImageJ-devel mailing list
    [email protected]  <mailto:[email protected]>
    http://imagej.net/mailman/listinfo/imagej-devel


    _______________________________________________
    ImageJ-devel mailing list
    [email protected] <mailto:[email protected]>
    http://imagej.net/mailman/listinfo/imagej-devel



_______________________________________________
ImageJ-devel mailing list
[email protected]
http://imagej.net/mailman/listinfo/imagej-devel

Reply via email to