How to create multi frame dicom file using dcm4che from several JPEG images?

1

I'm developing a DICOM application with dcm4che3, however, I have a problem with multiframe DICOM file. I would like to convert several JPEG images in only one DICOM file (Multiframe DICOm file.

I have extracted all the frames from dicom as Jpeg images and masked. I want to set back again with masked frames.

class Jpg2Dcm {

def FF = 255;

def SOF = 192;

def DHT = 196;

def DAC = 204;

def SOI = 216;

def SOS = 218;

def APP = 224;

private String charset = "ISO_IR 100";

private String transferSyntax = "1.2.840.10008.1.2.4.50";

private byte[] buffer = new byte[8192];
private int jpgHeaderLen;
private int jpgLen;
private boolean noAPPn = false;
private Attributes attrs = null;

public final void setCharset(String charset) {
    this.charset = charset;
}

private final void setTransferSyntax(String uid) {
    this.transferSyntax = uid;
}

private final void setNoAPPn(boolean noAPPn) {
    this.noAPPn = noAPPn;
}

def convertJpegToDicom(List<String>frames,File dcmFile,isClip) {
    try {
        String args = [];
        Jpg2Dcm jpg2Dcm = new Jpg2Dcm();
        jpg2Dcm.setNoAPPn(true);
        jpg2Dcm.setMetaData(dcmFile,isClip);
        long start = System.currentTimeMillis();
        for(int i=0;i<frames.size();i++){
            jpg2Dcm.convert(new File(frames[i]), dcmFile,isClip);
        }

        long fin = System.currentTimeMillis();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

def setMetaData(File dcmFile,boolean isClip) throws IOException{
    DicomInputStream dcmInputStream = null;
    try {
        dcmInputStream = new DicomInputStream(dcmFile);
        this.attrs = isClip?dcmInputStream.readDataset(-1,-1):dcmInputStream.readDataset(-1, Tag.PixelData);
        attrs.setString(Tag.SOPClassUID, VR.UI, attrs.getString(Tag.SOPClassUID));
        //attrs.setString(524310, VR.UI, "1.2.840.10008.5.1.4.1.1.7");
        ensureUS(attrs, Tag.BitsAllocated, 8);
        ensureUS(attrs, Tag.BitsStored, attrs.getInt(Tag.BitsAllocated, ((this.buffer[this.jpgHeaderLen] & 0xFF) > 8) ? 16 : 8));
        ensureUS(attrs, Tag.HighBit, this.attrs.getInt(Tag.BitsStored, this.buffer[this.jpgHeaderLen] & 0xFF) - 1);
        ensureUS(attrs, Tag.PixelRepresentation, 0);
        attrs.setString(Tag.StudyInstanceUID, VR.UI, attrs.getString(Tag.StudyInstanceUID));
        attrs.setString(Tag.SeriesInstanceUID, VR.UI, attrs.getString(Tag.SeriesInstanceUID));
        attrs.setString(Tag.SOPInstanceUID, VR.UI, attrs.getString(Tag.SOPInstanceUID));
        /*ensureUID(attrs, Tag.StudyInstanceUID);
        ensureUID(attrs, Tag.SeriesInstanceUID);
        ensureUID(attrs, Tag.SOPInstanceUID);*/
        Date now = new Date();
        attrs.setDate(524306, VR.DA, [now] as Date[]);
        attrs.setDate(524307, VR.TM, [now] as Date[]);
    }finally{
        dcmInputStream.close();
    }
}

def convert(File jpgFile, File dcmFile,boolean isClip) throws IOException {
    this.jpgHeaderLen = 0;
    this.jpgLen = (int) jpgFile.length();
    DataInputStream jpgInput = new DataInputStream(new BufferedInputStream(new FileInputStream(jpgFile)));
    try {
        if ((this.noAPPn) || (missingRowsColumnsSamplesPMI(this.attrs))) {
            readHeader(this.attrs, jpgInput);
        }
        def tranferSyntaxUid = isClip?UID.RLELossless:UID.JPEGBaseline1
        Attributes fmi = attrs.createFileMetaInformation(tranferSyntaxUid);
        DicomOutputStream dos = new DicomOutputStream(dcmFile);
        try {
            dos.writeDataset(fmi, attrs);
            dos.writeHeader(Tag.PixelData, VR.OB, -1);
            dos.writeHeader(Tag.Item, null, 0);
            Long tempHeaderLength = this.jpgLen + 1 & 0xFFFFFFFE
            int headerLength = tempHeaderLength.intValue()
            dos.writeHeader(Tag.Item, null, headerLength);
            dos.write(this.buffer, 0, this.jpgHeaderLen);

            int r;
            while ((r = jpgInput.read(this.buffer)) > 0) {
                dos.write(this.buffer, 0, r);
            }
            if (((this.jpgLen & 0x1) != 0)) {
                dos.write(0);
            }
            dos.writeHeader(Tag.SequenceDelimitationItem, null, 0);
        } finally {
            dos.close();
        }
    } finally {
        jpgInput.close();
    }
    System.out.println("Done!!!");
}

private boolean missingRowsColumnsSamplesPMI(Attributes attrs) {
    boolean isMissing = ((!(attrs.containsValue(2621456))) || (!(attrs.containsValue(2621457)))
            || (!(attrs.containsValue(2621442))) || (!(attrs.containsValue(2621444))));
    return isMissing;
}

def readHeader(Attributes attrs, DataInputStream jpgInput) throws IOException {
    if ((jpgInput.read() != FF) || (jpgInput.read() != SOI) || (jpgInput.read() != FF)) {
        throw new IOException("JPEG stream does not start with FF D8 FF");
    }
    int marker = jpgInput.read();

    boolean seenSOF = false;
    this.buffer[0] = (byte) FF;
    this.buffer[1] = (byte) SOI;
    this.buffer[2] = (byte) FF;
    this.buffer[3] = (byte) marker;
    this.jpgHeaderLen = 4;
    while (marker != SOS) {
        int segmLen = jpgInput.readUnsignedShort();
        if (this.buffer.length < this.jpgHeaderLen + segmLen + 2) {
            growBuffer(this.jpgHeaderLen + segmLen + 2);
        }
        this.buffer[(this.jpgHeaderLen++)] = (byte) (segmLen >>> 8);
        this.buffer[(this.jpgHeaderLen++)] = (byte) segmLen;
        jpgInput.readFully(this.buffer, this.jpgHeaderLen, segmLen - 2);
        if (((marker & 0xF0) == SOF) && (marker != DHT) && (marker != DAC)) {
            seenSOF = true;
            int p = this.buffer[this.jpgHeaderLen] & 0xFF;
            int y = (this.buffer[(this.jpgHeaderLen + 1)] & 0xFF) << 8 | this.buffer[(this.jpgHeaderLen + 2)] & 0xFF;

            int x = (this.buffer[(this.jpgHeaderLen + 3)] & 0xFF) << 8 | this.buffer[(this.jpgHeaderLen + 4)] & 0xFF;

            int nf = this.buffer[(this.jpgHeaderLen + 5)] & 0xFF;
                attrs.setInt(Tag.SamplesPerPixel, VR.US, [nf] as int[]);
                if (nf == 3) {
                    attrs.setString(Tag.PhotometricInterpretation, VR.CS, "YBR_FULL_422");

                    attrs.setInt(Tag.PlanarConfiguration, VR.US, [0] as int[]);
                } else {
                    attrs.setString(Tag.PhotometricInterpretation, VR.CS, "MONOCHROME2");
                }

                attrs.setInt(Tag.Rows, VR.US, [y] as int[]);
                attrs.setInt(Tag.Columns, VR.US, [x] as int[]);
                attrs.setInt(Tag.BitsAllocated, VR.US, [(p > 8) ? 16 : 8] as int[]);
                attrs.setInt(Tag.BitsStored, VR.US, [p] as int[]);
                attrs.setInt(Tag.HighBit, VR.US, [p - 1] as int[]);
                attrs.setInt(Tag.PixelRepresentation, VR.US, [0] as int[]);
        }
        if ((this.noAPPn & (marker & 0xF0) == APP)) {
            this.jpgLen -= segmLen + 2;
            this.jpgHeaderLen -= 4;
        } else {
            this.jpgHeaderLen += segmLen - 2;
        }
        if (jpgInput.read() != FF) {
            throw new IOException("Missing SOS segment in JPEG stream");
        }
        marker = jpgInput.read();
        this.buffer[(this.jpgHeaderLen++)] = (byte) FF;
        this.buffer[(this.jpgHeaderLen++)] = (byte) marker;
    }
    if (!(seenSOF))
        throw new IOException("Missing SOF segment in JPEG stream");
}

private void growBuffer(int minSize) {
    int newSize = this.buffer.length << 1;
    while (newSize < minSize) {
        newSize <<= 1;
    }
    byte[] tmp = new byte[newSize];
    System.arraycopy(this.buffer, 0, tmp, 0, this.jpgHeaderLen);
    this.buffer = tmp;
}

private void ensureUID(Attributes attrs, int tag) {
    if (!(attrs.containsValue(tag)))
        attrs.setString(tag, VR.UI, UIDUtils.createUID());
}

private void ensureUS(Attributes attrs, int tag, int val) {
    if (!(attrs.containsValue(tag)))
        attrs.setInt(tag, VR.US, [val] as int[]);
}

}

java
asked on Stack Overflow Aug 16, 2018 by UdayKumar • edited Aug 16, 2018 by UdayKumar

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0