/******************************************************************************* * Copyright (c) 2016 Weasis Team and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Nicolas Roduit - initial API and implementation *******************************************************************************/ package org.weasis.dicom.codec.utils; import java.awt.image.BufferedImage; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferShort; import java.awt.image.DataBufferUShort; import java.awt.image.IndexColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.util.Map; import org.dcm4che3.data.Attributes; import org.dcm4che3.data.Tag; import org.dcm4che3.image.Overlays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.weasis.core.api.media.data.ImageElement; import org.weasis.core.api.media.data.TagW; import org.weasis.core.api.util.FileUtil; import org.weasis.dicom.codec.PRSpecialElement; import org.weasis.dicom.codec.display.OverlayOp; public class OverlayUtils { private static final Logger LOGGER = LoggerFactory.getLogger(OverlayUtils.class); private static final byte[] icmColorValues = new byte[] { (byte) 0xFF, (byte) 0x00 }; private OverlayUtils() { } /** * Merge the overlays into the buffered image. This method apply only white pixel overlays. * * @param params * */ public static RenderedImage getBinaryOverlays(ImageElement image, Attributes attributes, int frameIndex, int width, int height, Map<String, Object> params) throws IOException { // Default grayscale value for overlay int grayscaleValue = 0xFFFF; int outBits = 1; IndexColorModel icm = new IndexColorModel(outBits, icmColorValues.length, icmColorValues, icmColorValues, icmColorValues, 0); BufferedImage overBi = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY, icm); WritableRaster raster = overBi.getRaster(); // Get serialized overlay (from pixel data) byte[][] data = null; String filePath = (String) image.getTagValue(TagW.OverlayBurninDataPath); if (filePath != null) { FileInputStream fileIn = null; ObjectInputStream objIn = null; try { fileIn = new FileInputStream(filePath); objIn = new ObjectInputStream(fileIn); Object o = objIn.readObject(); if (o instanceof byte[][]) { data = (byte[][]) o; } } catch (Exception e) { LOGGER.error("Cannot read serialized overlay", e); //$NON-NLS-1$ } finally { FileUtil.safeClose(objIn); FileUtil.safeClose(fileIn); } } int[] overlayGroupOffsets = Overlays.getActiveOverlayGroupOffsets(attributes, 0xffff); for (int i = 0; i < overlayGroupOffsets.length; i++) { byte[] ovlyData = null; // Get bitmap overlay from pixel data if (data != null && attributes.getInt(Tag.OverlayBitsAllocated | overlayGroupOffsets[i], 1) != 1 && data.length > i) { ovlyData = data[i]; } // If onlyData is null, get bitmap overlay from dicom attributes Overlays.applyOverlay(ovlyData != null ? 0 : frameIndex, raster, attributes, overlayGroupOffsets[i], grayscaleValue >>> (16 - outBits), ovlyData); } Object pr = params.get(OverlayOp.P_PR_ELEMENT); if (pr instanceof PRSpecialElement) { Attributes ovlyAttrs = ((PRSpecialElement) pr).getMediaReader().getDicomObject(); overlayGroupOffsets = Overlays.getActiveOverlayGroupOffsets(ovlyAttrs, 0xffff); Integer shuttOverlayGroup = DicomMediaUtils.getIntegerFromDicomElement(ovlyAttrs, Tag.ShutterOverlayGroup, Integer.MIN_VALUE); // grayscaleValue = Overlays.getRecommendedDisplayGrayscaleValue(psAttrs, gg0000); for (int i = 0; i < overlayGroupOffsets.length; i++) { if (shuttOverlayGroup != overlayGroupOffsets[i]) { Overlays.applyOverlay(frameIndex, raster, ovlyAttrs, overlayGroupOffsets[i], grayscaleValue >>> (16 - outBits), null); } } } return overBi; } public static RenderedImage getShutterOverlay(Attributes attributes, int frameIndex, int width, int height, int shuttOverlayGroup) throws IOException { IndexColorModel icm = new IndexColorModel(1, icmColorValues.length, icmColorValues, icmColorValues, icmColorValues, 0); BufferedImage overBi = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY, icm); Overlays.applyOverlay(frameIndex, overBi.getRaster(), attributes, shuttOverlayGroup - 0x6000, 1, null); return overBi; } public static byte[] extractOverlay(int gg0000, Raster raster, Attributes attrs) { if (attrs.getInt(Tag.OverlayBitsAllocated | gg0000, 1) == 1) { return null; } int ovlyRows = attrs.getInt(Tag.OverlayRows | gg0000, 0); int ovlyColumns = attrs.getInt(Tag.OverlayColumns | gg0000, 0); int bitPosition = attrs.getInt(Tag.OverlayBitPosition | gg0000, 0); int mask = 1 << bitPosition; int length = ovlyRows * ovlyColumns; // Binary size = ((imageSize + 7) / 8 ) + 1 & (-2) = 32769 & (-2) = 1000000000000001 & 1111111111111111110 byte[] ovlyData = new byte[(((length + 7) >>> 3) + 1) & (~1)]; extractFromPixeldata(raster, mask, ovlyData, 0, length); return ovlyData; } public static void extractFromPixeldata(Raster raster, int mask, byte[] ovlyData, int off, int length) { ComponentSampleModel sm = (ComponentSampleModel) raster.getSampleModel(); int columns = raster.getWidth(); int stride = sm.getScanlineStride(); DataBuffer db = raster.getDataBuffer(); switch (db.getDataType()) { case DataBuffer.TYPE_BYTE: extractFromPixeldata(((DataBufferByte) db).getData(), columns, stride, mask, ovlyData, off, length); break; case DataBuffer.TYPE_USHORT: extractFromPixeldata(((DataBufferUShort) db).getData(), columns, stride, mask, ovlyData, off, length); break; case DataBuffer.TYPE_SHORT: extractFromPixeldata(((DataBufferShort) db).getData(), columns, stride, mask, ovlyData, off, length); break; default: throw new UnsupportedOperationException("Unsupported DataBuffer type: " + db.getDataType()); //$NON-NLS-1$ } } private static void extractFromPixeldata(byte[] pixeldata, int columns, int stride, int mask, byte[] ovlyData, int off, int length) { for (int y = 0, i = off, imax = off + length; y < columns && i < imax; y++) { for (int j = y * stride; j < imax && i < imax; j++, i++) { if ((pixeldata[j] & mask) != 0) { ovlyData[i >>> 3] |= 1 << (i & 7); } } } } private static void extractFromPixeldata(short[] pixeldata, int columns, int stride, int mask, byte[] ovlyData, int off, int length) { for (int y = 0, i = off, imax = off + length; y < columns && i < imax; y++) { for (int j = y * stride; j < imax && i < imax; j++, i++) { if ((pixeldata[j] & mask) != 0) { ovlyData[i >>> 3] |= 1 << (i & 7); } } } } }