Search in sources :

Example 1 with BitInputStream

use of maspack.util.BitInputStream in project artisynth_core by artisynth.

the class DicomImageDecoderImageMagick method decodeFrame.

@Override
public DicomPixelBuffer decodeFrame(DicomHeader header, DicomPixelData data) {
    if (convertCmd == null) {
        throw new IllegalArgumentException("ImageMagick's \"convert\" command not found");
    }
    DicomPixelBufferBase out = null;
    // get dimensions, whether RGB or grayscale, and bit depth
    int nSamples = header.getIntValue(DicomTag.SAMPLES_PER_PIXEL, 1);
    // 0:
    int planarConf = header.getIntValue(DicomTag.PLANAR_CONFIGURATION, 0);
    // interlaced
    int rows = header.getIntValue(DicomTag.ROWS, 0);
    int cols = header.getIntValue(DicomTag.COLUMNS, 0);
    int bitsAllocated = header.getIntValue(DicomTag.BITS_ALLOCATED, 8);
    int bitsStored = header.getIntValue(DicomTag.BITS_STORED, 8);
    int highBit = header.getIntValue(DicomTag.HIGH_BIT, 7);
    int diffBits = highBit + 1 - bitsStored;
    int maxMask = (1 << bitsStored) - 1;
    int pixelRepresentation = // 0: unsigned,
    header.getIntValue(DicomTag.PIXEL_REPRESENTATION, 0);
    // 1: signed
    double rescaleSlope = header.getDecimalValue(DicomTag.RESCALE_SLOPE, 1);
    double rescaleIntercept = header.getDecimalValue(DicomTag.RESCALE_INTERCEPT, 0);
    // RGB, MONOCHROME1, MONOCHROME2
    String photoInterp = header.getStringValue(DicomTag.PHOTOMETRIC_ITERPRETATION);
    boolean flipGrayscale = ("MONOCHROME1".equalsIgnoreCase(photoInterp));
    // little endian by default
    String[] cmdArray = { convertCmd, "-", "-endian", "MSB", "<type>:-" };
    int OUTTYPE_IDX = 4;
    if (nSamples == 1) {
        cmdArray[OUTTYPE_IDX] = "gray:-";
        if ((bitsAllocated != 8) && !(bitsAllocated == 16)) {
            throw new IllegalArgumentException("Decoder only supports 8- or 16-bit grayscale");
        }
    } else if (nSamples == 3) {
        cmdArray[OUTTYPE_IDX] = "rgb:-";
        if (bitsAllocated != 8) {
            throw new IllegalArgumentException("Decoder only supports 8-bit RGB");
        }
    } else {
        throw new IllegalArgumentException("Decoder only supports grayscale or RGB");
    }
    // need to read in byte buffer in case short not yet complete
    int frameLength = nSamples * rows * cols;
    int bufferLength = frameLength * (bitsAllocated >>> 3);
    byte[] bbuff = new byte[bufferLength];
    // run conversion process
    ProcessBuilder procBuild = new ProcessBuilder(cmdArray);
    StringBuilder errorMsg = new StringBuilder();
    try {
        Process proc = procBuild.start();
        // ProcessMonitor.create(proc);
        ProcessMonitor procMon = new ProcessMonitor(proc);
        executor.execute(procMon);
        BufferedReader errorReader = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
        BinaryInputStream outputReader = new BinaryInputStream(proc.getInputStream());
        BufferedOutputStream inputWriter = new BufferedOutputStream(proc.getOutputStream());
        // send all input
        inputWriter.write(data.b);
        inputWriter.flush();
        inputWriter.close();
        int offset = 0;
        while (!procMon.isComplete()) {
            // read output
            boolean eatMore = true;
            while (eatMore) {
                int length = outputReader.read(bbuff, offset, bufferLength - offset);
                if (length < 0 || (bufferLength == offset)) {
                    eatMore = false;
                } else {
                    offset = offset + length;
                }
            }
            // clear error stream
            int val;
            while ((val = errorReader.read()) >= 0) {
                errorMsg.append((char) val);
            }
        }
        // read last of data
        // clear error stream
        int val;
        while ((val = errorReader.read()) >= 0) {
            errorMsg.append((char) val);
        }
        // read output
        if (offset < bufferLength) {
            boolean eatMore = true;
            while (eatMore) {
                int length = outputReader.read(bbuff, offset, bufferLength - offset);
                if (length < 0) {
                    eatMore = false;
                } else {
                    offset = offset + length;
                }
            }
        }
        // done reading
        outputReader.close();
        errorReader.close();
        bufferLength = offset;
        if (offset == 0 && errorMsg.length() > 0) {
            String err = errorMsg.toString();
            System.err.println(err);
            throw new IllegalArgumentException("Error from ImageMagick: " + err);
        }
    } catch (Exception e) {
        throw new IllegalStateException("Failed to decode image data", e);
    }
    // buffer is full, determine how many actual bits per sample in decoded
    // stream
    int nTrueBitsPerSample = (bufferLength << 3) / frameLength;
    BitInputStream bitStream = new BitInputStream(new ByteArrayInputStream(bbuff));
    try {
        // fix up values
        if (nSamples == 1) {
            // single byte grayscale
            if (bitsAllocated == 8) {
                if (pixelRepresentation == 0) {
                    out = new UBytePixelBuffer(frameLength);
                } else {
                    out = new BytePixelBuffer(frameLength);
                }
                byte[] buff = (byte[]) out.getBuffer();
                for (int i = 0; i < frameLength; i++) {
                    // adjust for mis-matched high bit?
                    buff[i] = (byte) bitStream.readBits(nTrueBitsPerSample);
                    if (diffBits > 0) {
                        buff[i] = (byte) (buff[i] >>> diffBits);
                    }
                    // remove any outside bits
                    buff[i] = (byte) (maxMask & buff[i]);
                    // adjust monochrome
                    if (flipGrayscale) {
                        buff[i] = (byte) (maxMask - (0xFF & buff[i]));
                    }
                    // rescale
                    out.setRescale(rescaleSlope, rescaleIntercept);
                }
            } else if (bitsAllocated == 16) {
                // separate sequences into appropriate frames
                if (pixelRepresentation == 0) {
                    out = new UShortPixelBuffer(frameLength);
                } else {
                    out = new ShortPixelBuffer(frameLength);
                }
                short[] buff = (short[]) out.getBuffer();
                for (int i = 0; i < frameLength; i++) {
                    // convert little-endian bytes
                    buff[i] = (short) bitStream.readBits(nTrueBitsPerSample);
                    // adjust for mis-matched high bit?
                    if (diffBits > 0) {
                        buff[i] = (short) (buff[i] >>> diffBits);
                    }
                    // remove any outside bits
                    buff[i] = (short) (maxMask & buff[i]);
                    // adjust monochrome
                    if (flipGrayscale) {
                        buff[i] = (short) (maxMask - (0xFFFF & buff[i]));
                    }
                    // rescale
                    out.setRescale(rescaleSlope, rescaleIntercept);
                }
            } else {
                throw new IllegalArgumentException("Only support one- or two-byte monochrome pixels");
            }
        } else if (nSamples == 3) {
            // RGB
            cmdArray[OUTTYPE_IDX] = "rgb:-";
            if (bitsAllocated != 8) {
                throw new IllegalArgumentException("Only one-byte RGB implemented");
            }
            // separate sequences into appropriate frames
            out = new RGBPixelBuffer(3 * frameLength);
            byte[] buff = (byte[]) out.getBuffer();
            byte[] rgb = new byte[3];
            for (int i = 0; i < frameLength; i++) {
                for (int j = 0; j < 3; j++) {
                    rgb[j] = (byte) bitStream.readBits(nTrueBitsPerSample);
                    // adjust for mis-matched high bit?
                    if (diffBits > 0) {
                        rgb[j] = (byte) (rgb[j] >>> diffBits);
                    }
                    // remove any outside bits
                    rgb[j] = (byte) (maxMask & rgb[j]);
                }
                if (planarConf == 1) {
                    buff[i] = rgb[0];
                    buff[i + frameLength] = rgb[1];
                    buff[i + 2 * frameLength] = rgb[2];
                } else {
                    buff[3 * i] = rgb[0];
                    buff[3 * i + 1] = rgb[1];
                    buff[3 * i + 2] = rgb[2];
                }
            }
        } else {
            throw new IllegalArgumentException("Only 1-byte and 3-byte samples implemented");
        }
    } catch (Exception e) {
        throw new RuntimeException("Something bad happened due to this: ", e);
    } finally {
        try {
            bitStream.close();
        } catch (IOException e) {
        }
    }
    return out;
}
Also used : BufferedOutputStream(java.io.BufferedOutputStream) InputStreamReader(java.io.InputStreamReader) BinaryInputStream(maspack.util.BinaryInputStream) BitInputStream(maspack.util.BitInputStream) IOException(java.io.IOException) ProcessMonitor(maspack.util.ProcessMonitor) IOException(java.io.IOException) ByteArrayInputStream(java.io.ByteArrayInputStream) BufferedReader(java.io.BufferedReader)

Aggregations

BufferedOutputStream (java.io.BufferedOutputStream)1 BufferedReader (java.io.BufferedReader)1 ByteArrayInputStream (java.io.ByteArrayInputStream)1 IOException (java.io.IOException)1 InputStreamReader (java.io.InputStreamReader)1 BinaryInputStream (maspack.util.BinaryInputStream)1 BitInputStream (maspack.util.BitInputStream)1 ProcessMonitor (maspack.util.ProcessMonitor)1