diff --git a/common/source/java/ch/systemsx/cisd/common/image/IntensityRescaling.java b/common/source/java/ch/systemsx/cisd/common/image/IntensityRescaling.java index 8b868385436490efef52472e46279dcc3ca56ae4..41c0bd9f607c3730cc0e1c58a39c6555e879408c 100644 --- a/common/source/java/ch/systemsx/cisd/common/image/IntensityRescaling.java +++ b/common/source/java/ch/systemsx/cisd/common/image/IntensityRescaling.java @@ -438,38 +438,11 @@ public class IntensityRescaling */ public static BufferedImage rescaleIntensityLevelTo8Bits(BufferedImage image, Levels levels) { - final int width = image.getWidth(); - final int height = image.getHeight(); - final BufferedImage rescaledImage = - new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); - WritableRaster rescaledRaster = rescaledImage.getRaster(); - - final float dynamicRange = 255f / (levels.maxLevel - levels.minLevel); - - for (int y = 0; y < height; ++y) - { - for (int x = 0; x < width; ++x) - { - for (Channel channel : Channel.values()) - { - int originalIntensity = (image.getRGB(x, y) >> channel.getShift()) & 0xff; - - // cut all intensities above the white point - int intensity = Math.min(levels.maxLevel, originalIntensity); - // cut all intensities below the black point and move the origin to 0 - intensity = Math.max(0, intensity - levels.minLevel); - // normalize to [0, 1] and rescale to 8 bits - intensity = (int) (0.5 + (intensity * dynamicRange)); - - rescaledRaster.setSample(x, y, channel.getBand(), intensity); - } - } - } - return rescaledImage; + return rescaleIntensityLevelTo8Bits(image, levels, Channel.values()); } public static BufferedImage rescaleIntensityLevelTo8Bits(BufferedImage image, Levels levels, - Channel channel) + Channel... channels) { final int width = image.getWidth(); final int height = image.getHeight(); @@ -477,22 +450,16 @@ public class IntensityRescaling new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); WritableRaster rescaledRaster = rescaledImage.getRaster(); - final float dynamicRange = 255f / (levels.maxLevel - levels.minLevel); - for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { - int originalIntensity = (image.getRGB(x, y) >> channel.getShift()) & 0xff; - - // cut all intensities above the white point - int intensity = Math.min(levels.maxLevel, originalIntensity); - // cut all intensities below the black point and move the origin to 0 - intensity = Math.max(0, intensity - levels.minLevel); - // normalize to [0, 1] and rescale to 8 bits - intensity = (int) (0.5 + (intensity * dynamicRange)); - - rescaledRaster.setSample(x, y, channel.getBand(), intensity); + for (Channel channel : channels) + { + int originalIntensity = (image.getRGB(x, y) >> channel.getShift()) & 0xff; + int rescaledIntensity = rescaleIntensity(originalIntensity, levels); + rescaledRaster.setSample(x, y, channel.getBand(), rescaledIntensity); + } } } return rescaledImage; @@ -510,25 +477,26 @@ public class IntensityRescaling WritableRaster rescaledRaster = rescaledImage.getRaster(); final int[] pixelData = image.getPixelData(); - final float dynamicRange = 255f / (levels.maxLevel - levels.minLevel); - int offset = 0; for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { int originalIntensity = pixelData[offset++]; - - // cut all intensities above the white point - int intensity = Math.min(levels.maxLevel, originalIntensity); - // cut all intensities below the black point and move the origin to 0 - intensity = Math.max(0, intensity - levels.minLevel); - // normalize to [0, 1] and rescale to 8 bits - intensity = (int) (0.5 + (intensity * dynamicRange)); - - rescaledRaster.setSample(x, y, 0, intensity); + rescaledRaster.setSample(x, y, 0, rescaleIntensity(originalIntensity, levels)); } } return rescaledImage; } + + private static int rescaleIntensity(int originalIntensity, Levels levels) + { + // cut all intensities above the white point + int intensity = Math.min(levels.maxLevel, originalIntensity); + // cut all intensities below the black point and move the origin to 0 + intensity = Math.max(0, intensity - levels.minLevel); + // normalize to [0, 1] and rescale to 8 bits + int range = levels.maxLevel - levels.minLevel; + return (255 * intensity + range / 2) / range; + } } \ No newline at end of file diff --git a/common/source/java/ch/systemsx/cisd/common/image/MixColors.java b/common/source/java/ch/systemsx/cisd/common/image/MixColors.java index f980fca442bae1fc72084719a3f3ecdbc73c0fb1..a82b3a93204c135080b8e2a8f7e91115a2883a3a 100644 --- a/common/source/java/ch/systemsx/cisd/common/image/MixColors.java +++ b/common/source/java/ch/systemsx/cisd/common/image/MixColors.java @@ -19,6 +19,7 @@ package ch.systemsx.cisd.common.image; import java.awt.Color; import java.awt.image.BufferedImage; +import org.apache.commons.lang.time.StopWatch; import org.apache.log4j.Logger; import ch.systemsx.cisd.common.logging.LogCategory; @@ -302,6 +303,8 @@ public class MixColors { assert colors.length == images.length : "number of colors and images do not match"; + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); ColorMergingAlgorithm mergeColorsAlgorithm = createColorMergingAlgorithm(quadratic, saturationEnhancementFactor, images); @@ -335,6 +338,7 @@ public class MixColors } } } + operationLog.info("MIXING " + images.length + " images (" + width + "x" + height + ") took " + stopWatch); return new MixedImageWithWhitePoint(mixed, whitePointColor); } diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/image/IntensityRescalingTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/image/IntensityRescalingTest.java index 19d835b3503ca1655f36b6ac6a2a38bfcbe71636..fa4eac274d610bd24a0f977502cbbeddd59c1eb6 100644 --- a/common/sourceTest/java/ch/systemsx/cisd/common/image/IntensityRescalingTest.java +++ b/common/sourceTest/java/ch/systemsx/cisd/common/image/IntensityRescalingTest.java @@ -18,8 +18,14 @@ package ch.systemsx.cisd.common.image; import static org.testng.AssertJUnit.assertEquals; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.image.BufferedImage; + import org.testng.annotations.Test; +import ch.systemsx.cisd.common.image.IntensityRescaling.Channel; +import ch.systemsx.cisd.common.image.IntensityRescaling.GrayscalePixels; import ch.systemsx.cisd.common.image.IntensityRescaling.Levels; /** @@ -29,7 +35,7 @@ import ch.systemsx.cisd.common.image.IntensityRescaling.Levels; public class IntensityRescalingTest { - public void testRegularCaseOfIntensityRescaling() + public void testComputeLevelsRegularCaseOfIntensityRescaling() { int[] histogramArray = new int[] @@ -41,7 +47,7 @@ public class IntensityRescalingTest } @Test - public void testBorderCaseOfIntensityRescaling() + public void testComputeLevelsBorderCaseOfIntensityRescaling() { Levels result = IntensityRescaling.computeLevels(14, new int[] { 1098, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }); @@ -50,7 +56,7 @@ public class IntensityRescalingTest } @Test - public void testBorderCaseOfIntensityRescalingOtherWay() + public void testComputeLevelsBorderCaseOfIntensityRescalingOtherWay() { Levels result = IntensityRescaling.computeLevels(14, new int[] { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19876 }); @@ -59,12 +65,87 @@ public class IntensityRescalingTest } @Test - public void testAnotherBorderCase() + public void testComputeLevelsAnotherBorderCase() { Levels result = IntensityRescaling.computeLevels(14, new int[] { 19876, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); assertEquals(0, result.minLevel); assertEquals(1, result.maxLevel); } + + @Test + public void testRescaleIntensityLevelTo8BitsForRGBExampleAndTwoColors() + { + BufferedImage image = new BufferedImage(6, 5, BufferedImage.TYPE_INT_RGB); + Graphics graphics = image.getGraphics(); + graphics.setColor(Color.ORANGE); + graphics.fillRect(0, 0, 4, 3); + graphics.setColor(Color.PINK); + graphics.fillRect(1, 1, 4, 3); + + BufferedImage rescaledImage = IntensityRescaling.rescaleIntensityLevelTo8Bits(image, new Levels(75, 190), + Channel.GREEN, Channel.BLUE); + + ImageHistogram histogram = ImageHistogram.calculateHistogram(rescaledImage); + + assertEquals("[0=30]", renderHistogram(histogram.getRedHistogram())); + assertEquals("[0=12, 222=12, 255=6]", renderHistogram(histogram.getGreenHistogram())); + assertEquals("[0=18, 222=12]", renderHistogram(histogram.getBlueHistogram())); + } -} + @Test + public void testRescaleIntensityLevelTo8BitsForRGBExampleAndAllColors() + { + BufferedImage image = new BufferedImage(6, 5, BufferedImage.TYPE_INT_RGB); + Graphics graphics = image.getGraphics(); + graphics.setColor(Color.ORANGE); + graphics.fillRect(0, 0, 4, 3); + graphics.setColor(Color.PINK); + graphics.fillRect(1, 1, 4, 3); + + BufferedImage rescaledImage = IntensityRescaling.rescaleIntensityLevelTo8Bits(image, new Levels(75, 190)); + + ImageHistogram histogram = ImageHistogram.calculateHistogram(rescaledImage); + + assertEquals("[0=12, 255=18]", renderHistogram(histogram.getRedHistogram())); + assertEquals("[0=12, 222=12, 255=6]", renderHistogram(histogram.getGreenHistogram())); + assertEquals("[0=18, 222=12]", renderHistogram(histogram.getBlueHistogram())); + } + + @Test + public void testRescaleIntensityLevelTo8BitsForGrayExample() + { + BufferedImage image = new BufferedImage(6, 5, BufferedImage.TYPE_BYTE_GRAY); + Graphics graphics = image.getGraphics(); + graphics.setColor(Color.DARK_GRAY); + graphics.fillRect(0, 0, 4, 3); + graphics.setColor(Color.LIGHT_GRAY); + graphics.fillRect(1, 1, 4, 3); + + BufferedImage rescaledImage = IntensityRescaling.rescaleIntensityLevelTo8Bits( + new GrayscalePixels(image), new Levels(75, 200)); + + ImageHistogram histogram = ImageHistogram.calculateHistogram(rescaledImage); + + assertEquals("[0=18, 239=12]", renderHistogram(histogram.getRedHistogram())); + assertEquals("[0=18, 239=12]", renderHistogram(histogram.getGreenHistogram())); + assertEquals("[0=18, 239=12]", renderHistogram(histogram.getBlueHistogram())); + } + + private String renderHistogram(int[] histogram) + { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < histogram.length; i++) + { + int value = histogram[i]; + if (value > 0) + { + if (builder.length() > 0) + { + builder.append(", "); + } + builder.append(i).append("=").append(value); + } + } + return "[" + builder.toString() + "]"; + }}