diff --git a/common/source/java/ch/systemsx/cisd/common/image/IntensityRescaling.java b/common/source/java/ch/systemsx/cisd/common/image/IntensityRescaling.java new file mode 100644 index 0000000000000000000000000000000000000000..83ce2001aec8af6b1fa5c2d896a920df2fa328fd --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/image/IntensityRescaling.java @@ -0,0 +1,127 @@ +/* + * Copyright 2011 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.common.image; + +import java.awt.image.BufferedImage; + +/** + * Methods for performing an intensity rescaling to 8bits for gray-scale images with a color-depth + * of more than 8bits. + * + * @author Bernd Rinn + */ +public class IntensityRescaling +{ + + private static int getGrayIntensity(BufferedImage image, int x, int y) + { + return image.getRaster().getSample(x, y, 0); + } + + private static int getBitShiftLowerThanThreshold(int[] b0, float pixels, float threshold) + { + int shift = b0.length - 1; + while (shift >= 0 && (b0[shift] / pixels) < threshold) + { + --shift; + } + return shift + 1; + } + + /** + * Computes the number of significant bits in an image, minus 8. A bit position is considered + * significant if only a small fraction (given by <var>threshold</var> of all pixels has a + * value of 1 in this bit position. + * <p> + * For example, if the image is 16-bit and only uses 10-bits, this method will return 2. + * + * @param image The image to compute the bits for. + * @param threshold The threshold of pixels (divided by the total number of pixels) that can be + * '1' in bit position so that the bit-position is still consider insignificant. A + * typical value will be 0.001f (one per-mill). + * @return The number of significant bits of the intensity minus 8. + */ + public static int computeBitShift(BufferedImage image, float threshold) + { + if (image.getColorModel().getColorSpace().getNumComponents() > 1) + { + throw new IllegalArgumentException( + "computeBitShift() is only applicable to gray scale images."); + } + float pixels = image.getWidth() * image.getHeight(); + final int[] b0 = new int[image.getColorModel().getPixelSize() - 8]; + for (int x = 0; x < image.getWidth(); ++x) + { + for (int y = 0; y < image.getHeight(); ++y) + { + final int intensity = getGrayIntensity(image, x, y); + for (int b = 0; b < b0.length; ++b) + { + if (((intensity >>> (b + 8)) & 1) == 1) + { + ++b0[b]; + } + } + } + } + return getBitShiftLowerThanThreshold(b0, pixels, threshold); + } + + /** + * Performs an intensity rescaling on a gray-scale image by shifting all intensities so that + * only significant bits are kept. A bit position is considered significant if only a small + * fraction (given by <var>threshold</var> of all pixels has a value of 1 in this bit position. + * + * @param image The original n-bit gray-scale image (n>8). + * @param threshold The threshold of pixels (divided by the total number of pixels) that can be + * '1' in bit position so that the bit-position is still consider insignificant. A + * typical value will be 0.001f (one per-mill). + * @return The rescaled 8-bit gray-scale image. + */ + public static BufferedImage rescaleIntensityBitShiftTo8Bits(BufferedImage image, float threshold) + { + return rescaleIntensityBitShiftTo8Bits(image, computeBitShift(image, threshold)); + } + + /** + * Performs an intensity rescaling on a gray-scale image by shifting all intensities by + * <var>shiftBits</var> bits. + * + * @param image The original n-bit gray-scale image (n>8). + * @param shiftBits The number of bits to shift the image by. + * @return The rescaled 8-bit gray-scale image. + */ + public static BufferedImage rescaleIntensityBitShiftTo8Bits(BufferedImage image, int shiftBits) + { + if (image.getColorModel().getColorSpace().getNumComponents() > 1) + { + throw new IllegalArgumentException( + "rescaleIntensityBitShiftTo8Bits() is only applicable to gray scale images."); + } + final BufferedImage rescaledImage = + new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_GRAY); + for (int x = 0; x < image.getWidth(); ++x) + { + for (int y = 0; y < image.getHeight(); ++y) + { + final int intensity = Math.min(255, getGrayIntensity(image, x, y) >>> shiftBits); + rescaledImage.getRaster().setSample(x, y, 0, intensity); + } + } + return rescaledImage; + } +}