es.urjc.mctwp.image.impl.dicom.DicomImagePlugin.java Source code

Java tutorial

Introduction

Here is the source code for es.urjc.mctwp.image.impl.dicom.DicomImagePlugin.java

Source

//Copyright 2008, 2009, 2010 Miguel ?ngel Laguna Lobato
//
//This file is part of Multiclinical Trial Web-PACS.
//
//Multiclinical  Trial Web-PACS is free  software: you  can redistribute 
//it and/or modify it under  the terms of the GNU General Public License 
//as published by the Free  Software Foundation, either version 3 of the 
//License, or (at your option) any later version.
//
//Multiclinical  Trial Web-PACS is distributed  in the hope that it will 
//be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 
//of  MERCHANTABILITY or  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
//General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with Multiclinical Trial Web-PACS.  If not, see 
//<http://www.gnu.org/licenses/>.

package es.urjc.mctwp.image.impl.dicom;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.dcm4che2.data.BasicDicomObject;
import org.dcm4che2.data.DicomObject;
import org.dcm4che2.data.Tag;
import org.dcm4che2.io.DicomInputStream;
import org.dcm4che2.io.SAXWriter;
import org.w3c.dom.Node;

import es.urjc.mctwp.image.ImageUtils;
import es.urjc.mctwp.image.exception.ImageException;
import es.urjc.mctwp.image.management.ImagePluginDefaultImpl;
import es.urjc.mctwp.image.objects.DicomSCHeaderAttrs;
import es.urjc.mctwp.image.objects.Image;
import es.urjc.mctwp.image.objects.SeriesImage;
import es.urjc.mctwp.image.objects.PatientInfo;
import es.urjc.mctwp.image.objects.SingleImage;

public class DicomImagePlugin extends ImagePluginDefaultImpl {
    private String medconPath = "/usr/bin/medcon";
    private String options = "-c png -e 1";
    private URL xslt = null;

    public String getMedconPath() {
        return medconPath;
    }

    public void setMedconPath(String medconPath) {
        this.medconPath = medconPath;
    }

    public void setXslt(URL xslt) {
        this.xslt = xslt;
    }

    @Override
    public Image createImage(File file) throws ImageException {
        SeriesImageDicomImpl res = null;
        SingleImageDicomImpl aux = null;
        String seriesUid = null;

        seriesUid = getSeriesUID(file);

        // If there is a seriesUid, Image is DICOM.
        if (seriesUid != null) {
            String name = ImageUtils.getFileName(file);

            // Create single image
            aux = new SingleImageDicomImpl();
            aux.setContent(file);
            aux.setId(name);

            // Create series image, first get prop series id
            String seriesId = getMapper().getSeriesPropId(seriesUid);
            res = new SeriesImageDicomImpl();
            res.setId(seriesId);
            res.addImage(aux);
        }

        return res;
    }

    @Override
    public PatientInfo getPatientInfo(Image image) throws ImageException {
        PatientInfo result = null;

        DicomObject dcmObj = getDicomObject(image);
        if (dcmObj != null) {
            result = new PatientInfo();
            result.setCode(dcmObj.getString(Tag.PatientID));
            result.setName(dcmObj.getString(Tag.PatientName));
            result.setStudy(dcmObj.getString(Tag.StudyID));
        }

        return result;
    }

    @Override
    public Image loadImage(File file) throws ImageException {
        Image result = null;

        if (file != null) {
            if (file.isDirectory())
                result = loadSeriesImage(file);
            else if (file.isFile())
                result = loadSingleImage(file);
        }

        return result;
    }

    @Override
    public List<File> toDicom(Image image, File outputDir) throws ImageException {
        List<File> result = null;
        String error = null;

        if (image == null)
            error = "Null dicom image";
        else if (!outputDir.isDirectory())
            error = "Destination [" + outputDir.getAbsolutePath() + "] is not a directory";
        else if (image instanceof SingleImageDicomImpl)
            result = toDicom((SingleImageDicomImpl) image, outputDir);
        else if (image instanceof SeriesImageDicomImpl)
            result = toDicom((SeriesImageDicomImpl) image, outputDir);
        else
            error = "Expecting DICOM image file format";

        if (error != null)
            throw new ImageException(error);

        return result;
    }

    @Override
    public File toPng(Image image) throws ImageException {
        String error = null;
        File result = null;

        if (image == null)
            error = "Null dicom image";
        if (image instanceof SingleImageDicomImpl)
            result = toPng((SingleImageDicomImpl) image);
        else if (image instanceof SeriesImageDicomImpl)
            result = toPng((SeriesImageDicomImpl) image);
        else
            error = "Expecting DICOM image file format";

        if (error != null)
            throw new ImageException(error);

        return result;
    }

    @Override
    public Node toXml(Image image) throws ImageException {
        String error = null;
        Node result = null;

        if (image == null)
            error = "Null dicom image";
        else if (image instanceof SingleImageDicomImpl)
            result = toXml((SingleImageDicomImpl) image);
        else if (image instanceof SeriesImageDicomImpl)
            result = toXml((SeriesImageDicomImpl) image);
        else
            error = "Expecting DICOM image file format";

        if (error != null)
            throw new ImageException(error);

        return result;
    }

    @Override
    public boolean getSupportNoExtension() {
        return true;
    }

    @Override
    public String[] getSupportedExtensions() {
        return new String[] { SingleImageDicomImpl.DCM_EXT };
    }

    public DicomSCHeaderAttrs getDicomHeader(Image image) throws ImageException {
        DicomSCHeaderAttrs result = null;

        DicomObject dcmObj = getDicomObject(image);
        if (dcmObj != null) {
            result = new DicomSCHeaderAttrs();
            result.setAccesionNumber(dcmObj.getString(Tag.AccessionNumber));
            result.setImageDate(dcmObj.getDate(Tag.Date));
            //result.setImageNumber(dcmObj.getString(Tag.num));
            result.setInstitutionName(dcmObj.getString(Tag.InstitutionName));
            result.setPatientBirth(dcmObj.getDate(Tag.PatientBirthDate));
            result.setPatientId(dcmObj.getString(Tag.PatientID));
            result.setPatientName(dcmObj.getString(Tag.PatientName));
            result.setPatientSex(dcmObj.getString(Tag.PatientSex));
            result.setSeriesDate(dcmObj.getString(Tag.SeriesDate));
            result.setSeriesDescription(dcmObj.getString(Tag.SeriesDescription));
            result.setSeriesInstanceUID(dcmObj.getString(Tag.SeriesInstanceUID));
            result.setSeriesNumber(dcmObj.getString(Tag.SeriesNumber));
            result.setStudyDate(dcmObj.getDate(Tag.StudyDate));
            result.setStudyDescription(dcmObj.getString(Tag.StudyDescription));
            result.setStudyInstanceUID(dcmObj.getString(Tag.StudyInstanceUID));
            result.setStudyTime(dcmObj.getDate(Tag.StudyTime));
        }

        return result;
    }

    /**
     * Returns dicom object. It parses image an reports any exception
     * 
     * @param image
     * @return
     * @throws ImageException
     */
    private DicomObject getDicomObject(Image image) throws ImageException {
        SingleImageDicomImpl sidi = null;
        DicomInputStream dis = null;
        DicomObject result = null;
        String error = null;

        if (image == null)
            error = "Null dicom image";
        else if (image instanceof SeriesImageDicomImpl && !((SeriesImageDicomImpl) image).getImages().isEmpty())
            sidi = (SingleImageDicomImpl) ((SeriesImageDicomImpl) image).getImages().get(0);
        else if (image instanceof SingleImageDicomImpl)
            sidi = (SingleImageDicomImpl) image;

        // Retrieve header dicom from file information
        try {
            if (sidi != null) {
                dis = new DicomInputStream(sidi.getContent());
                result = new BasicDicomObject();
                dis.readDicomObject(result, -1);
            } else
                error = "There is no images into serie";
        } catch (IOException ioe) {
            result = null;
            error = "Can't read dicom file";
        }

        if (error != null)
            throw new ImageException(error);

        return result;
    }

    /**
     * Load a singleImage from a file. It checks it is a Dicom file
     * 
     * @param file
     * @return
     * @throws ImageException
     */
    private SingleImage loadSingleImage(File file) throws ImageException {
        SingleImageDicomImpl result = null;
        String seriesUid = null;

        seriesUid = getSeriesUID(file);

        if (seriesUid != null) {
            String name = ImageUtils.getFileName(file);

            // Create image
            result = new SingleImageDicomImpl();
            result.setContent(file);
            result.setId(name);
        }

        return result;
    }

    /**
     * Loads a Dicom serie from a directory containing dicom files from the same
     * serie
     * 
     * @param file
     * @return
     * @throws ImageException
     */
    private SeriesImage loadSeriesImage(File file) throws ImageException {
        SeriesImage result = new SeriesImageDicomImpl();

        result.setId(ImageUtils.getFileName(file));

        // Add every single image directory has
        for (File f : Arrays.asList(file.listFiles())) {
            SingleImage singleImage = loadSingleImage(f);
            if (singleImage != null) {

                //TODO Is it neccesary to check wheter image has same seriesUid?
                result.addImage(singleImage);
            }
        }

        // If no image has been added, it is wrong format
        if (result.getImages() == null)
            result = null;

        return result;
    }

    /**
     * Check if it is a nice dicom file and returns seriesUid
     * 
     * @return series uid if image is dicom, null other case.
     */
    private String getSeriesUID(File file) {
        DicomInputStream dis = null;
        DicomObject dcmObj = null;
        String result = null;

        try {
            dis = new DicomInputStream(file);
            dcmObj = new BasicDicomObject();
            dis.readDicomObject(dcmObj, -1);
            result = dcmObj.getString(Tag.SeriesInstanceUID);
            dis.close();
            dcmObj.clear();
        } catch (IOException ioe) {
            result = null;
        }

        return result;
    }

    /**
     * Obtains a png file from the one singleImage of the series
     * 
     * @param series
     * @return
     * @throws ImageException
     */
    private File toPng(SeriesImageDicomImpl series) throws ImageException {
        File result = null;

        if ((series.getImages() != null) && (!series.getImages().isEmpty())) {
            int i = series.getImages().size();

            if (i > 3)
                result = toPng(series.getImages().get(i / 2));
            else
                result = toPng(series.getImages().get(0));
        }

        return result;
    }

    /**
     * Obtains a png file from a singleImage
     * 
     * @param single
     * @return
     * @throws ImageException
     */
    private File toPng(SingleImageDicomImpl single) throws ImageException {
        File result = null;
        File source = null;

        try {

            // Prepare output file
            source = single.getContent();
            String base = source.getParent();
            String pre = ImageUtils.getFileName(source);
            result = new File(FilenameUtils.concat(base, pre + ".png"));

            // Check if thumbnails exists. Like a cache of thumbnails
            if ((!result.exists()) || (!result.isFile()) || (result.length() == 0)) {

                // Delete a possible erroneus thumbanil file
                if (result.exists())
                    result.delete();

                // Execute transformation and scale
                String cmd = medconPath + " " + options + " -f " + source.getAbsolutePath() + " -o "
                        + result.getAbsolutePath();

                exec(cmd, true);
                scaleThumbnail(result);
            }
        } catch (Exception e) {
            if (result.exists())
                result.delete();
            logger.error(e.getMessage());
            throw new ImageException(e);
        }

        return result;
    }

    /**
     * Obtains an XML node representation from the first singleImage of the
     * series
     * 
     * @param series
     * @return
     * @throws ImageException
     */
    private Node toXml(SeriesImageDicomImpl series) throws ImageException {
        Node result = null;

        if ((series.getImages() != null) && (!series.getImages().isEmpty()))
            result = toXml(series.getImages().get(0));

        return result;
    }

    /**
     * Obtains XML node representation of a singleImage
     * 
     * @param single
     * @return
     * @throws ImageException
     */
    private Node toXml(SingleImage single) throws ImageException {
        DicomInputStream dis = null;
        TransformerHandler th = null;
        DOMResult result = null;
        File source = null;

        try {

            // Prepare XSLT transformer
            result = new DOMResult();
            th = getTransformerHandler();
            th.getTransformer().setOutputProperty(OutputKeys.INDENT, "yes");
            th.setResult(result);

            // Get writer and set image pixel data excluded from XML
            final SAXWriter writer = new SAXWriter(th, null);
            writer.setExclude(new int[] { Tag.PixelData });

            // Parse and convert to XML
            source = single.getContent();
            dis = new DicomInputStream(source);
            dis.setHandler(writer);
            // dis.readDicomObject(new BasicDicomObject(), -1);
            dis.readDicomObject();

        } catch (Exception e) {
            logger.error(e.getMessage());
            throw new ImageException(e);
        } finally {
            try {
                dis.close();
            } catch (IOException ioe) {
                logger.error(ioe.getMessage());
            }
        }

        return result.getNode();
    }

    /**
     * Obtains a DICOM representation for each single image into serie.
     * 
     * @param series
     * @return
     * @throws ImageException
     */
    private List<File> toDicom(SeriesImageDicomImpl series, File outputDir) throws ImageException {
        List<File> result = null;
        List<File> aux = null;

        if ((series.getImages() != null) && (!series.getImages().isEmpty())) {
            result = new ArrayList<File>();

            for (Image i : series.getImages()) {
                aux = toDicom(i, outputDir);

                if ((aux != null) && (!aux.isEmpty()))
                    result.addAll(aux);
            }
        }

        return result.isEmpty() ? null : result;
    }

    /**
     * Obtains a DICOM representation for single image
     * 
     * @param single
     * @return
     * @throws ImageException
     */
    private List<File> toDicom(SingleImage single, File outputDir) throws ImageException {
        List<File> result = new ArrayList<File>();
        File content = single.getContent();

        try {

            String ext = StringUtils.substringAfterLast(content.getName(), FilenameUtils.EXTENSION_SEPARATOR_STR);
            if (ext == null)
                ext = SingleImageDicomImpl.DCM_EXT;
            File dest = new File(FilenameUtils.concat(outputDir.getAbsolutePath(),
                    single.getId() + FilenameUtils.EXTENSION_SEPARATOR_STR + ext));
            FileUtils.copyFile(content, dest);
            result.add(dest);

        } catch (Exception e) {
            logger.error(e.getMessage());
            throw new ImageException(e);
        }

        return result;
    }

    /**
     * Get the XSL transformer to convert dicom into XML. If there is an xslt
     * specified, it is used.
     * 
     * @return TransformerHandler
     * @throws TransformerConfigurationException
     * @throws IOException
     */
    private TransformerHandler getTransformerHandler() throws Exception {
        SAXTransformerFactory stf = null;
        StreamSource sc = null;

        stf = (SAXTransformerFactory) TransformerFactory.newInstance();
        if (xslt != null)
            sc = new StreamSource(xslt.openStream(), xslt.toExternalForm());

        return (sc == null) ? stf.newTransformerHandler() : stf.newTransformerHandler(sc);
    }
}