private BufferedImage windowMonochrome( ImageListViewCell displayedCell, BufferedImage srcImg, float windowLocation, float windowWidth) { BufferedImage destImg = new BufferedImage(srcImg.getWidth(), srcImg.getHeight(), BufferedImage.TYPE_INT_RGB); boolean isSigned = false; int minValue = 0; { // hack: try to determine signedness and minValue from DICOM metadata if available -- // the BufferedImage's metadata don't contain that information reliably. // Only works for some special cases ImageListViewModelElement elt = displayedCell.getDisplayedModelElement(); if (elt instanceof DicomImageListViewModelElement) { DicomImageListViewModelElement delt = (DicomImageListViewModelElement) elt; DicomObject imgMetadata = delt.getDicomImageMetaData(); int bitsAllocated = imgMetadata.getInt(Tag.BitsAllocated); isSigned = (1 == imgMetadata.getInt(Tag.PixelRepresentation)); if (isSigned && (bitsAllocated > 0)) { minValue = -(1 << (bitsAllocated - 1)); } } } final int windowedImageGrayscalesCount = 256; // for BufferedImage.TYPE_INT_RGB float scale = windowedImageGrayscalesCount / windowWidth; float offset = (windowWidth / 2 - windowLocation) * scale; if (!(srcImg.getColorModel().getColorSpace().getType() == ColorSpace.TYPE_GRAY)) { throw new IllegalArgumentException("source image must be grayscales"); } Raster srcRaster = srcImg.getRaster(); if (srcRaster.getNumBands() != 1) { throw new IllegalArgumentException( "grayscale source image must have one color band, but has " + srcRaster.getNumBands() + "??"); } WritableRaster resultRaster = destImg.getRaster(); for (int x = 0; x < srcImg.getWidth(); x++) { for (int y = 0; y < srcImg.getHeight(); y++) { int srcGrayValue = srcRaster.getSample(x, y, 0); if (isSigned) { srcGrayValue = (int) (short) srcGrayValue; // will only work for 16-bit signed... } float destGrayValue = scale * srcGrayValue + offset; // clamp if (destGrayValue < 0) { destGrayValue = 0; } else if (destGrayValue >= windowedImageGrayscalesCount) { destGrayValue = windowedImageGrayscalesCount - 1; } resultRaster.setSample(x, y, 0, destGrayValue); resultRaster.setSample(x, y, 1, destGrayValue); resultRaster.setSample(x, y, 2, destGrayValue); } } return destImg; }
protected BufferedImage getWindowedImage(ImageListViewCell displayedCell) { BufferedImage windowedImage = tryWindowRawImage(displayedCell); if (windowedImage != null) { return windowedImage; } else { // TODO: caching of windowed images, probably using // displayedCell.getDisplayedModelElement().getImageKey() and the windowing parameters // as the cache key BufferedImage srcImg = displayedCell.getDisplayedModelElement().getImage().getBufferedImage(); // TODO: use the model element's RawImage instead of the BufferedImage when possible /// * if (srcImg.getColorModel().getColorSpace().getType() == ColorSpace.TYPE_GRAY) { try { windowedImage = windowMonochrome( displayedCell, srcImg, displayedCell.getWindowLocation(), displayedCell.getWindowWidth()); } catch (IllegalArgumentException e) { // TODO: store message somehow so ILVInitStateController can render it // either into ILVModelElt#errorInfo (in which case errorInfo would be a warning for // initialized elements), // or, probably better, into a new attribute in the cell logger.warn("couldn't window grayscale image: " + e.getLocalizedMessage() /*, e*/); return srcImg; } } else if (srcImg.getColorModel().getColorSpace().isCS_sRGB()) { try { windowedImage = windowRGB(srcImg, displayedCell.getWindowLocation(), displayedCell.getWindowWidth()); } catch (IllegalArgumentException e) { // TODO: store message somehow so ILVInitStateController can render it (see // windowMonochrome above) logger.warn("couldn't window RGB image: " + e.getLocalizedMessage() /*, e*/); return srcImg; } } else { throw new IllegalStateException( "don't know how to window image with color space " + srcImg.getColorModel().getColorSpace()); // TODO: do something cleverer here? Like, create windowedImage // with a color space that's "compatible" to srcImg (using // some createCompatibleImage() method in BufferedImage or elsewhere), // window all bands of that, and let the JRE figure out how to draw the result? } // */ // windowedImage = windowWithRasterOp(srcImg, windowLocation, windowWidth); // windowedImage = srcImg; return windowedImage; } }
private BufferedImage tryWindowRawImage(ImageListViewCell displayedCell) { ImageListViewModelElement elt = displayedCell.getDisplayedModelElement(); ViskitImage img = elt.getImage(); if (!(img.hasRawImage() && img.isRawImagePreferable())) { return null; } // logger.debug("trying to create windowed BufferedImage for: " + elt.getImageKey()); float[] pixelTransform = new float[] {1, 0}; // compute the complete raw input => output pixel transformation in here // rescale/slope tags if (elt instanceof DicomImageListViewModelElement) { try { DicomImageListViewModelElement delt = (DicomImageListViewModelElement) elt; if (delt.getDicomImageMetaData().contains(Tag.RescaleSlope) && delt.getDicomImageMetaData().contains(Tag.RescaleIntercept)) { float rscSlope = delt.getDicomImageMetaData().getFloat(Tag.RescaleSlope); float rscIntercept = delt.getDicomImageMetaData().getFloat(Tag.RescaleIntercept); LinAlg.matrMult1D(new float[] {rscSlope, rscIntercept}, pixelTransform, pixelTransform); } } catch (Exception e) { // ignore -- no error } } // normalization to [0,1] FloatRange pxValuesRange = img.getMaximumPixelValuesRange(); float minGrayvalue = pxValuesRange.getMin(); float nGrayvalues = pxValuesRange.getDelta(); LinAlg.matrMult1D( new float[] {1.0F / nGrayvalues, -minGrayvalue / nGrayvalues}, pixelTransform, pixelTransform); // window level/width float wl = (displayedCell.getWindowLocation() - minGrayvalue) / nGrayvalues; float ww = displayedCell.getWindowWidth() / nGrayvalues; float scale = 1F / ww; float offset = (ww / 2 - wl) * scale; LinAlg.matrMult1D(new float[] {scale, offset}, pixelTransform, pixelTransform); LookupTable lut = displayedCell.getLookupTable(); /*if (displayedCell.isOutputGrayscaleRGBs()) { //TODO: perform 12-bit grayscale output in J2D as well } else */ if (lut != null) { // transformation to output LUT index int lutLength = lut.getRGBAValues().limit() / 4; LinAlg.matrMult1D(new float[] {lutLength, 0}, pixelTransform, pixelTransform); } else { // transformation to output grayscale range ( [0,256) ) LinAlg.matrMult1D(new float[] {256, 0}, pixelTransform, pixelTransform); } RawImage rimg = img.getRawImage(); if (rimg.getPixelFormat() != RawImage.PIXEL_FORMAT_LUMINANCE) { return null; // can't window raw RGB images for now } else { switch (rimg.getPixelType()) { // will only window RawImages whose pixelData buffer is a ShortBuffer for now case RawImage.PIXEL_TYPE_UNSIGNED_BYTE: case RawImage.PIXEL_TYPE_SIGNED_12BIT: case RawImage.PIXEL_TYPE_SIGNED_16BIT: case RawImage.PIXEL_TYPE_UNSIGNED_12BIT: { // TODO: maybe reuse BufferedImages of the same size to relieve the GC float txscale = pixelTransform[0]; float txoffset = pixelTransform[1]; ShortBuffer srcBuffer = (ShortBuffer) rimg.getPixelData(); int w = rimg.getWidth(); int h = rimg.getHeight(); BufferedImage result = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); WritableRaster resultRaster = result.getRaster(); if (lut != null) { windowLUTShort(srcBuffer, w, h, txscale, txoffset, lut, resultRaster); } else { windowGrayShort(srcBuffer, w, h, txscale, txoffset, resultRaster); } return result; } // ...and IntBuffer case RawImage.PIXEL_TYPE_UNSIGNED_16BIT: { // TODO: maybe reuse BufferedImages of the same size to relieve the GC float txscale = pixelTransform[0]; float txoffset = pixelTransform[1]; IntBuffer srcBuffer = (IntBuffer) rimg.getPixelData(); int w = rimg.getWidth(); int h = rimg.getHeight(); BufferedImage result = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); WritableRaster resultRaster = result.getRaster(); if (lut != null) { windowLUTInt(srcBuffer, w, h, txscale, txoffset, lut, resultRaster); } else { windowGrayInt(srcBuffer, w, h, txscale, txoffset, resultRaster); } return result; } default: return null; } } }
private Point2D getScaledImageSize(ImageListViewCell cell) { ViskitImage img = cell.getDisplayedModelElement().getImage(); return getDicomToUiTransform(cell) .transform(new Point2D.Double(img.getWidth(), img.getHeight()), null); }