com.mirth.connect.server.util.DICOMMessageUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.mirth.connect.server.util.DICOMMessageUtil.java

Source

/*
 * Copyright (c) Mirth Corporation. All rights reserved.
 * 
 * http://www.mirthcorp.com
 * 
 * The software in this package is published under the terms of the MPL license a copy of which has
 * been included with this distribution in the LICENSE.txt file.
 */

package com.mirth.connect.server.util;

import ij.ImagePlus;
import ij.ImageStack;
import ij.plugin.DICOM;
import ij.process.ImageProcessor;

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.StringUtils;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.dcm4che2.data.DicomElement;
import org.dcm4che2.data.DicomObject;
import org.dcm4che2.data.Tag;
import org.dcm4che2.data.VR;

import com.mirth.connect.donkey.model.message.ConnectorMessage;
import com.mirth.connect.donkey.model.message.MessageSerializerException;
import com.mirth.connect.donkey.model.message.attachment.Attachment;
import com.mirth.connect.donkey.util.Base64Util;
import com.mirth.connect.model.converters.DICOMConverter;
import com.mirth.connect.server.controllers.MessageController;
import com.mirth.connect.userutil.ImmutableConnectorMessage;
import com.mirth.connect.userutil.ImmutableMessageContent;

public class DICOMMessageUtil {
    private static Logger logger = Logger.getLogger(DICOMMessageUtil.class);

    public static String getDICOMRawData(ImmutableConnectorMessage message) {
        String mergedMessage = null;

        List<Attachment> attachments = MessageController.getInstance().getMessageAttachment(message.getChannelId(),
                message.getMessageId());

        if (attachments != null && attachments.size() > 0) {
            try {
                if (attachments.get(0).getType().equals("DICOM")) {
                    byte[] mergedMessageBytes = mergeHeaderAttachments(message, attachments);

                    // Replace the raw binary with the encoded binary to free up the memory
                    mergedMessageBytes = Base64Util.encodeBase64(mergedMessageBytes);

                    mergedMessage = StringUtils.newStringUsAscii(mergedMessageBytes);
                } else {
                    mergedMessage = message.getRaw().getContent();
                }
            } catch (Exception e) {
                logger.error("Error merging DICOM data", e);
                mergedMessage = message.getRaw().getContent();
            }
        } else {
            mergedMessage = message.getRaw().getContent();
        }

        return mergedMessage;
    }

    public static String getDICOMRawData(ConnectorMessage message) {
        return getDICOMRawData(new ImmutableConnectorMessage(message));
    }

    public static byte[] getDICOMRawBytes(ImmutableConnectorMessage message) {
        byte[] mergedMessage = null;

        List<Attachment> attachments = MessageController.getInstance().getMessageAttachment(message.getChannelId(),
                message.getMessageId());

        if (attachments != null && attachments.size() > 0) {
            try {
                if (attachments.get(0).getType().equals("DICOM")) {
                    mergedMessage = mergeHeaderAttachments(message, attachments);
                } else {
                    mergedMessage = Base64.decodeBase64(StringUtils.getBytesUsAscii(message.getRaw().getContent()));
                }
            } catch (Exception e) {
                logger.error("Error merging DICOM data", e);
                mergedMessage = Base64.decodeBase64(StringUtils.getBytesUsAscii(message.getRaw().getContent()));
            }
        } else {
            mergedMessage = Base64.decodeBase64(StringUtils.getBytesUsAscii(message.getRaw().getContent()));
        }

        return mergedMessage;
    }

    public static byte[] getDICOMRawBytes(ConnectorMessage message) {
        return getDICOMRawBytes(new ImmutableConnectorMessage(message));
    }

    public static byte[] getDICOMMessage(ImmutableConnectorMessage message) {
        return getDICOMRawBytes(message);
    }

    public static byte[] mergeHeaderAttachments(ImmutableConnectorMessage message, List<Attachment> attachments)
            throws MessageSerializerException {
        try {
            byte[] headerBytes;

            ImmutableMessageContent encoded = message.getEncoded();
            ImmutableMessageContent raw = message.getRaw();

            if (encoded != null && encoded.getContent() != null && encoded.getDataType().equals("DICOM")) {
                headerBytes = Base64.decodeBase64(StringUtils.getBytesUsAscii(encoded.getContent()));
            } else if (raw != null && raw.getContent() != null && raw.getDataType().equals("DICOM")) {
                headerBytes = Base64.decodeBase64(StringUtils.getBytesUsAscii(raw.getContent()));
            } else {
                return new byte[0];
            }

            return mergeHeaderPixelData(headerBytes, attachments);
        } catch (IOException e) {
            throw new MessageSerializerException(e);
        }
    }

    public static byte[] mergeHeaderPixelData(byte[] header, List<Attachment> attachments) throws IOException {
        // 1. read in header
        DicomObject dcmObj = DICOMConverter.byteArrayToDicomObject(header, false);

        // 2. Add pixel data to DicomObject
        if (attachments != null && !attachments.isEmpty()) {
            if (attachments.size() > 1) {
                DicomElement dicomElement = dcmObj.putFragments(Tag.PixelData, VR.OB, dcmObj.bigEndian(),
                        attachments.size());

                for (Attachment attachment : attachments) {
                    dicomElement.addFragment(attachment.getContent());
                }

                dcmObj.add(dicomElement);
            } else {
                dcmObj.putBytes(Tag.PixelData, VR.OB, attachments.get(0).getContent());
            }
        }

        // Memory Optimization. Free the references to the data in the attachments list.
        attachments.clear();

        return DICOMConverter.dicomObjectToByteArray(dcmObj);
    }

    public static int getSliceCount(ImmutableConnectorMessage message) {
        ByteArrayInputStream bais = new ByteArrayInputStream(getDICOMRawBytes(message));

        try {
            DICOM dicom = new DICOM(bais);
            // run() is required to create the dicom object. The argument serves multiple purposes. If it is null or empty, it opens a dialog to select a dicom file.
            // Otherwise, if dicom.show() is called, it is the title of the dialog. Since we are not showing any dialogs here, we just need to pass a non-null, non-empty string.
            dicom.run("DICOM");

            return dicom.getImageStack().getSize();
        } finally {
            IOUtils.closeQuietly(bais);
        }
    }

    public static String convertDICOM(String imageType, ImmutableConnectorMessage message, int sliceIndex,
            boolean autoThreshold) {
        byte[] bytes = convertDICOMToByteArray(imageType, message, sliceIndex, autoThreshold);

        if (bytes != null) {
            return new String(Base64.encodeBase64Chunked(bytes));
        }

        return "";
    }

    public static byte[] convertDICOMToByteArray(String imageType, ImmutableConnectorMessage message,
            int sliceIndex, boolean autoThreshold) {
        if (imageType.equalsIgnoreCase("jpg") || imageType.equalsIgnoreCase("jpeg")) {
            return dicomToJpg(sliceIndex, message, autoThreshold);
        }

        ByteArrayInputStream bais = new ByteArrayInputStream(getDICOMRawBytes(message));
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        try {
            DICOM dicom = new DICOM(bais);
            // run() is required to create the dicom object. The argument serves multiple purposes. If it is null or empty, it opens a dialog to select a dicom file.
            // Otherwise, if dicom.show() is called, it is the title of the dialog. Since we are not showing any dialogs here, we just need to pass a non-null, non-empty string.
            dicom.run("DICOM");

            ImageStack imageStack = dicom.getImageStack();

            if (sliceIndex >= 1 && sliceIndex <= imageStack.getSize()) {
                ImageIO.write(imageStack.getProcessor(sliceIndex).getBufferedImage(), imageType, baos);
                return baos.toByteArray();
            } else {
                logger.error(
                        "Image slice " + sliceIndex + " not found for message " + message.getMessageId() + ".");
            }
        } catch (IOException e) {
            logger.error("Error Converting DICOM image", e);
        } finally {
            IOUtils.closeQuietly(bais);
            IOUtils.closeQuietly(baos);
        }

        return null;
    }

    private static byte[] dicomToJpg(int sliceIndex, ImmutableConnectorMessage message, boolean autoThreshold) {
        ByteArrayInputStream bais = new ByteArrayInputStream(getDICOMRawBytes(message));

        try {
            DICOM dicom = new DICOM(bais);
            // run() is required to create the dicom object. The argument serves multiple purposes. If it is null or empty, it opens a dialog to select a dicom file.
            // Otherwise, if dicom.show() is called, it is the title of the dialog. Since we are not showing any dialogs here, we just need to pass a non-null, non-empty string.
            dicom.run("DICOM");

            if (autoThreshold) {
                ImageProcessor im = dicom.getProcessor();
                // Automatically sets the lower and upper threshold levels, where
                // 'method' must be ISODATA or ISODATA2
                im.setAutoThreshold(0, 2);
            }

            ImageStack imageStack = dicom.getImageStack();

            if ((imageStack.getSize() < sliceIndex) || sliceIndex < 1) {
                logger.error(
                        "Image slice " + sliceIndex + " not found for message " + message.getMessageId() + ".");
                return null;
            }

            ImagePlus image = new ImagePlus("ImageName", imageStack.getProcessor(sliceIndex));
            return saveAsJpeg(image, 100);
        } finally {
            IOUtils.closeQuietly(bais);
        }
    }

    private static byte[] saveAsJpeg(ImagePlus imagePlug, int quality) {
        int imageType = BufferedImage.TYPE_INT_RGB;

        if (imagePlug.getProcessor().isDefaultLut()) {
            imageType = BufferedImage.TYPE_BYTE_GRAY;
        }

        BufferedImage bufferedImage = new BufferedImage(imagePlug.getWidth(), imagePlug.getHeight(), imageType);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        try {
            Graphics graphics = bufferedImage.createGraphics();
            graphics.drawImage(imagePlug.getImage(), 0, 0, null);
            graphics.dispose();
            ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next();
            writer.setOutput(ImageIO.createImageOutputStream(baos));
            ImageWriteParam param = writer.getDefaultWriteParam();
            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            param.setCompressionQuality(quality / 100f);

            if (quality == 100) {
                param.setSourceSubsampling(1, 1, 0, 0);
            }

            IIOImage iioImage = new IIOImage(bufferedImage, null, null);
            writer.write(null, iioImage, param);
            return baos.toByteArray();
        } catch (Exception e) {
            logger.error("Error Converting DICOM image", e);
        } finally {
            IOUtils.closeQuietly(baos);
        }

        return null;
    }
}