Pixel extraction using DCM4CHE
take time 1 or 2 hours.What you can do?
1: load dicom data(2D image only, this do not explain about video format.) as Attributes objects2: Extract pixel array (in this case, only use LEE, JPEG2000, JPEG Baseline that are major transfer-syntax and 8 or 16 bit grayscale.)
3: show pixels as images
What prerequisite?
1: have java programming skills (need not high skill)2: can use eclipse 2018 version or higher
3: can use maven project in eclipse
4: download sample dicom dataset from weasis projects(https://github.com/nroduit/dcm-samples)
5: use jdk 1.8 or higher (and set to maven projects it version)
Project Set-Up
1. create dcm4che_tutorial maven projects- File > New > Other > Maven Projects
- check Simple archtype and setup on your need.
2. edit pom.xml
- add dcm4che Repository.
- add three libraries (dcm4che-core, jai_imageio, ij) to dependencies
in this case, for instance,
Example my pom.xml
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelversion>4.0.0</modelversion>
<groupid>com.visionary</groupid>
<artifactid>dcm4che_tutorial</artifactid>
<version>0.0.1-SNAPSHOT</version>
<repositories>
<repository>
<id>www.dcm4che.org</id>
<name>dcm4che Repository</name>
<url>https://www.dcm4che.org/maven2/</url>
</repository>
</repositories>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.dcm4che/dcm4che-core -->
<dependency>
<groupid>org.dcm4che</groupid>
<artifactid>dcm4che-core</artifactid>
<version>5.22.1</version>
</dependency>
<dependency>
<groupid>com.sun.media</groupid>
<artifactid>jai_imageio</artifactid>
<version>1.2-pre-dr-b04</version>
</dependency>
<!-- https://mvnrepository.com/artifact/net.imagej/ij -->
<dependency>
<groupid>net.imagej</groupid>
<artifactid>ij</artifactid>
<version>1.52p</version>
</dependency>
</dependencies>
</project>
3. copy sample dicom dataset to project
- in this case, create "sample" folder in project then copy dcm-samples-master folder (this file is weasis sample dicom dataset)
Extract pixels
At first, create new class "tutorial_opendcm.java" in project.
Case 1 : No compressed/decompressed images
These Transfer syntax are ;- 1.2.840.10008.1.2 Implicit VR Endian: Default Transfer Syntax for DICOM
- 1.2.840.10008.1.2.1 Explicit VR Little Endian
- 1.2.840.10008.1.2.1.99 Deflated Explicit VR Little Endian
- 1.2.840.10008.1.2.2 Explicit VR Big Endian
Typing code in main method
1.Set sample dicom file path
//8 bits gray
String gray8 = "sample/dcm-samples-master/archive/1.2.276.0.7230010.3.200.1.1.1";
//16 bits grayString gray16 = "sample/dcm-samples-master/archive/2.16.840.1.113662.2.2344286425913116080603163842909806166139";
//32 bits gray (important for parametric images, e.g, pet, spect ...) sorry, future work...
//...
//COLOR
//RGB
String rgb = "sample/dcm-samples-master/archive/1.2.40.0.13.1.1.75699434012420281431899874274436428076";
//set image
String img_path = gray8;//in this case, set gray8 image.
2.Load dcm file
File f = new File(img_path);
Attributes dcm = null;
String tsuid = "";
try( DicomInputStream dis = new DicomInputStream(f); ){
dcm = dis.readDataset(-1, -1);
tsuid = dis.getTransferSyntax();
SafeClose.close(dis);
}
3.get pixel data as Objects
Object pixelData = dcm.getValue(Tag.PixelData);
4.check null for pixel data
//if pixelData is null, this dicom file is not dicom "image" objects.
if(pixelData == null) {
System.out.println("this dicom file dose not have pixelData. return.");
return;
}
5.check pixel data type(compress or not).
//no compressed
if(pixelData instanceof byte[]) {
System.out.println("this dcm file is raw image (no compressed).");
//compressed
}else {
//check Fragments for compressed pixel data.
//in this tutorial, skip this topics to keep simplicity.
//hint
//Fragments fragmObj = (Fragments)pixelData;
//BulkData pixelsBulk = frageObj.get(1);//get(0) is just Item tag, so start 1.
//bulk to byte ... and read using ImageReader specified format e.g, JPEG2000, JPEG, RLE etc.
System.out.println("this dcm file is compressed. return.");
return;
}
6.cast to byte array from pixel data
byte[] pixelDataByte = (byte[])pixelData;
7. collect image construction info
int w = dcm.getInt(Tag.Columns, 0);
int h= dcm.getInt(Tag.Rows, 0);
int channel = dcm.getInt(Tag.SamplesPerPixel, -1);
int bitsAllocated = dcm.getInt(Tag.BitsAllocated,-1);
//check meta info
System.out.println("img width:"+w+" img height:"+h);
System.out.println("BitsAllocated:"+bitsAllocated);
System.out.println("Tag.SamplesPerPixel:"+channel);
System.out.println("SOPClassUID:"+dcm.getString(Tag.SOPClassUID));
System.out.println("TransferSyntaxUID:"+tsuid);
//get bytebuffer for correspond byte order(littele endian, big endian)
ByteBuffer buffer = ByteBuffer.wrap(pixelDataByte).order(ByteOrder.LITTLE_ENDIAN);
byte[] sortedPixel = buffer.array();
8.should mention about gray or color
//grayscale
if(channel == 1 && bitsAllocated == 8) {
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorModel cm = new ComponentColorModel(cs, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
SampleModel sm = cm.createCompatibleSampleModel(w, h);
//construct buffer as byte
DataBufferByte db = new DataBufferByte(sortedPixel, w*h);
WritableRaster raster = Raster.createWritableRaster(sm, db, null);
BufferedImage bi = new BufferedImage(cm, raster, false, null);
ImagePlus imp = new ImagePlus("",bi);
imp.show();
}else if(channel == 1 && bitsAllocated == 16) {
//reconstruct pixel array as short.
short[] shortArray = new short[sortedPixel.length/2];
buffer.asShortBuffer().get(shortArray);
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorModel cm = new ComponentColorModel(cs, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
ShortProcessor sp = new ShortProcessor(w, h, shortArray, cm);
ImagePlus imp = new ImagePlus("",sp);
imp.show();
//color(rgb)
}else if(channel == 3) {
//please check RGB byte order.
//should consider which order is RGBRGBRGB... or RRRGGGBBB...
//in this case, use RRRGGGBBB
byte[] alpha = new byte[sortedPixel.length/3];
byte[] R = new byte[sortedPixel.length/3];
byte[] G = new byte[sortedPixel.length/3];
byte[] B = new byte[sortedPixel.length/3];
for (int i = 0; i < R.length; i++) {
alpha[i] = (byte)0;
R[i] = pixelDataByte[i];
G[i] = pixelDataByte[R.length + i];
B[i] = pixelDataByte[(R.length*2) + i];
}
ColorProcessor cp = new ColorProcessor(w, h);
//0=alpha, 1=red, 2=green, 3=blue
cp.setChannel(0, new ByteProcessor(w, h, alpha));
cp.setChannel(1, new ByteProcessor(w, h, R));
cp.setChannel(2, new ByteProcessor(w, h, G));
cp.setChannel(3, new ByteProcessor(w, h, B));
ImagePlus imp = new ImagePlus("RGB DICOM",cp);
imp.show();
//if image pixels constructed RGBRGBRGB order,
// Convert interleaved byte RGB to packed int ARGB
// int[] rgb_array = new int[sortedPixel.length/3];
// //https://stackoverflow.com/questions/29748021/properly-load-24-bit-bitmap-data-into-a-32-bit-bitmap-object
// //https://www.dyclassroom.com/image-processing-project/how-to-create-a-random-pixel-image-in-java
// for (int i = 0; i < rgb_array.length; i++) {
// rgb_array[i] = 0xff << 24 // Alpha (all opaque)
// | ((pixelDataByte[i * 3] & 0xff) << 16)
// | ((pixelDataByte[i * 3 + 1] & 0xff) << 8)
// | ((pixelDataByte[i * 3 + 2] & 0xff));
// }
// ColorProcessor cp = new ColorProcessor(w, h,rgb_array);
//then, show image.
//and also you can use,
// ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
// ColorModel cm = new ComponentColorModel(cs, false, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
// SampleModel sm = cm.createCompatibleSampleModel(w, h);
// DataBufferByte db = new DataBufferByte(sortedPixel, w*h);
// WritableRaster raster = Raster.createWritableRaster(sm, db, null);
// BufferedImage bi = new BufferedImage(cm, raster, false, null);
// ImagePlus imp = new ImagePlus("RGB DICOM",bi);
// imp.show();
}else {
//out of range channels in this tutorial...
System.out.println("channel is not 1 or 3... return.");
return;
}
Case 2 : compressed imagessorry, this topics is skipped in this tutorial.
2020/04/23
tatsuaki kobayashi
with best regards.
Appendix:whole source code
import java.awt.Transparency;import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferShort;
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.Tag;
import org.dcm4che3.io.DicomInputStream;
import org.dcm4che3.util.SafeClose;
import ij.ImagePlus;
import ij.process.ByteProcessor;
import ij.process.ColorProcessor;
import ij.process.ImageProcessor;
import ij.process.ShortProcessor;
public class tutorial_opendcm {
public static void main(String[] args) throws IOException {
//you can choose some pixels type...
//GRAYSCALE
//8 bits gray
String gray8 = "sample/dcm-samples-master/archive/1.2.276.0.7230010.3.200.1.1.1";
//16 bits gray
String gray16 = "sample/dcm-samples-master/archive/2.16.840.1.113662.2.2344286425913116080603163842909806166139";
//32 bits gray (important for parametric images, e.g, pet, spect ...) sorry, future work...
//...
//COLOR
//RGB
String rgb = "sample/dcm-samples-master/archive/1.2.40.0.13.1.1.75699434012420281431899874274436428076";
String img_path = rgb;
File f = new File(img_path);
Attributes dcm = null;
String tsuid = "";
try( DicomInputStream dis = new DicomInputStream(f); ){
dcm = dis.readDataset(-1, -1);
tsuid = dis.getTransferSyntax();
SafeClose.close(dis);
}
Object pixelData = dcm.getValue(Tag.PixelData);
//if pixelData is null, this dicom file is not dicom "image" objects.
if(pixelData == null) {
System.out.println("this dicom file dose not have pixelData. return.");
return;
}
//check pixel data type.
//no compressed
if(pixelData instanceof byte[]) {
System.out.println("this dcm file is raw image (no compressed).");
//compressed
}else {
//check Fragments for compressed pixel data.
//in this tutorial, skip this topics to keep simplicity.
//hint
//Fragments fragmObj = (Fragments)pixelData;
//BulkData pixelsBulk = frageObj.get(1);//get(0) is just Item tag, so start 1.
// bulk to byte ... and read using ImageReader specified format e.g, JPEG2000.
System.out.println("this dcm file is compressed. return.");
return;
}
//cast to byte array from pixel data
byte[] pixelDataByte = (byte[])pixelData;
int w = dcm.getInt(Tag.Columns, 0);
int h= dcm.getInt(Tag.Rows, 0);
int channel = dcm.getInt(Tag.SamplesPerPixel, -1);
int bitsAllocated = dcm.getInt(Tag.BitsAllocated,-1);
//check meta info
System.out.println("img width:"+w+" img height:"+h);
System.out.println("BitsAllocated:"+bitsAllocated);
System.out.println("Tag.SamplesPerPixel:"+channel);
System.out.println("SOPClassUID:"+dcm.getString(Tag.SOPClassUID));
System.out.println("TransferSyntaxUID:"+tsuid);
//get bytebuffer for correspond byte order(littele endian, big endian)
ByteBuffer buffer = ByteBuffer.wrap(pixelDataByte).order(ByteOrder.LITTLE_ENDIAN);
byte[] sortedPixel = buffer.array();
//you should mention about gray or color,
//grayscale
if(channel == 1 && bitsAllocated == 8) {
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorModel cm = new ComponentColorModel(cs, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
SampleModel sm = cm.createCompatibleSampleModel(w, h);
//construct buffer as byte
DataBufferByte db = new DataBufferByte(sortedPixel, w*h);
WritableRaster raster = Raster.createWritableRaster(sm, db, null);
BufferedImage bi = new BufferedImage(cm, raster, false, null);
ImagePlus imp = new ImagePlus("",bi);
imp.show();
}else if(channel == 1 && bitsAllocated == 16) {
//reconstruct pixel array as short.
short[] shortArray = new short[sortedPixel.length/2];
buffer.asShortBuffer().get(shortArray);
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorModel cm = new ComponentColorModel(cs, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
ShortProcessor sp = new ShortProcessor(w, h, shortArray, cm);
ImagePlus imp = new ImagePlus("",sp);
imp.show();
//color(rgb)
}else if(channel == 3) {
//please check RGB byte order.
//should consider which order is RGBRGBRGB... or RRRGGGBBB...
//in this case, use RRRGGGBBB
byte[] alpha = new byte[sortedPixel.length/3];
byte[] R = new byte[sortedPixel.length/3];
byte[] G = new byte[sortedPixel.length/3];
byte[] B = new byte[sortedPixel.length/3];
for (int i = 0; i < R.length; i++) {
alpha[i] = (byte)0;
R[i] = pixelDataByte[i];
G[i] = pixelDataByte[R.length + i];
B[i] = pixelDataByte[(R.length*2) + i];
}
ColorProcessor cp = new ColorProcessor(w, h);
//0=alpha, 1=red, 2=green, 3=blue
cp.setChannel(0, new ByteProcessor(w, h, alpha));
cp.setChannel(1, new ByteProcessor(w, h, R));
cp.setChannel(2, new ByteProcessor(w, h, G));
cp.setChannel(3, new ByteProcessor(w, h, B));
ImagePlus imp = new ImagePlus("RGB DICOM",cp);
imp.show();
//if image pixels constructed RGBRGBRGB order,
// Convert interleaved byte RGB to packed int ARGB
// int[] rgb_array = new int[sortedPixel.length/3];
// //https://stackoverflow.com/questions/29748021/properly-load-24-bit-bitmap-data-into-a-32-bit-bitmap-object
// //https://www.dyclassroom.com/image-processing-project/how-to-create-a-random-pixel-image-in-java
// for (int i = 0; i < rgb_array.length; i++) {
// rgb_array[i] = 0xff << 24 // Alpha (all opaque)
// | ((pixelDataByte[i * 3] & 0xff) << 16)
// | ((pixelDataByte[i * 3 + 1] & 0xff) << 8)
// | ((pixelDataByte[i * 3 + 2] & 0xff));
// }
// ColorProcessor cp = new ColorProcessor(w, h,rgb_array);
//then, show image.
//and also you can use,
// ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
// ColorModel cm = new ComponentColorModel(cs, false, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
// SampleModel sm = cm.createCompatibleSampleModel(w, h);
// DataBufferByte db = new DataBufferByte(sortedPixel, w*h);
// WritableRaster raster = Raster.createWritableRaster(sm, db, null);
// BufferedImage bi = new BufferedImage(cm, raster, false, null);
// ImagePlus imp = new ImagePlus("RGB DICOM",bi);
// imp.show();
}else {
//out of range channels in this tutorial...
System.out.println("channel is not 1 or 3... return.");
return;
}
}
}
コメント
コメントを投稿