概述
本文主要介绍dicom图像从拿到像素数据到显示到图像的基本过程,通过DCM4CHE获取dicom文件像素数据,通过默认窗宽窗位,显示到java的BufferedImage上,并将BufferedImage保存成jpg到本地
有关窗宽窗位的解释和处理方法可以参考:https://blog.csdn.net/songzitea/article/details/8505469
通过DCM4CHE获取dicom文件像素值
DicomInputStream dis = new DicomInputStream(new File("D:/dcm4che/dcm4che-assembly/src/etc/testdata/dicom/MR01.dcm"));
Attributes d = dis.readDataset(-1, -1);
byte[] pixelData = d.getBytes(Tag.PixelData);
和图像数据相关的还有下面几个字段
Tag.PixelData这个字段里存的就是Dicom文件的图像数据
Tag.BitsAllocated 图像数据存储位数,一般是8位和16位,本文图像处理用的是16位图像
Tag.PixelRepresentation 是否是带符号,0是无符号,1是有符号
Tag.Rows 图像行数(图像高度)
Tag.Columns 图像列数(图像宽度)
Tag.WindowCenter 默认窗位
Tag.WindowWidth 默认窗宽
** 尤其注意(0028,1052)Rescale Intercept, (0028,1053)Rescale Slope, (0028,1054) Rescale Type这三个tag字段,如果dicom有这三个字段说明数据区了存的数据是做过数据偏移的,在取值的时候通过公式(pixelValue*Rescale Slope+Rescale Intercept)得到原始的像素值,切记切记!!!**
完整代码(针对未压缩16位无符号的dicom图像)
pom.xml:
<dependency>
<groupId>org.dcm4che</groupId>
<artifactId>dcm4che-core</artifactId>
<version>5.21.0</version>
</dependency>
java代码
package com.airking.dcm4che.dcm4cheTest;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.Tag;
import org.dcm4che3.io.DicomInputStream;
public class Dcm4cheTest {
public static void main(String[] args) {
System.out.println("DCM4CHE....");
try {
readDicom();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void readDicom() throws IOException{
DicomInputStream dis = new DicomInputStream(new File("D:/dcm4che/dcm4che-assembly/src/etc/testdata/dicom/MR01.dcm"));
Attributes d = dis.readDataset(-1, -1);
byte[] pn = d.getBytes(Tag.PatientName);
System.out.println(new String(pn));
int nBitsAllocated = d.getInt(Tag.BitsAllocated, 0);
int nPixelRepresentation = d.getInt(Tag.PixelRepresentation, -1);
if(nBitsAllocated > 8 && nPixelRepresentation == 0){
processImagePixelFor16BitUnsigned(d);
}
}
private static void processImagePixelFor16BitUnsigned(Attributes d) throws IOException{
if(d == null){
return;
}
int height = d.getInt(Tag.Rows, 0);
int width = d.getInt(Tag.Columns, 0);
float fC = d.getFloat(Tag.WindowCenter, 0);
float fW = d.getFloat(Tag.WindowWidth, 0);
byte[] pixelData = d.getBytes(Tag.PixelData);
short[] shortPixelData = toShortArray(pixelData);
float fMin = (float) ((2.0f*fC - fW) / 2.0f + 0.5);
float fMax = (float) ((2.0f*fC + fW) / 2.0f + 0.5);
BufferedImage BI = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for(int h=0;h<height;h++){
for(int w=0;w<width;w++){
int index = w+h*width;
short pixel = shortPixelData[index];
//int value = (int) (((pixel - fC) / fW + 0.5) * 255.0);
int value = (int) ((pixel - fMin)*255.0/(fMax - fMin));
int newVal = Math.min(Math.max(value, 0), 255);
int pixelValue = 0xffffffff;
pixelValue = (newVal << 16) & 0x00ff0000 | (pixelValue & 0xff00ffff);
pixelValue = (newVal << 8) & 0x0000ff00 | (pixelValue & 0xffff00ff);
pixelValue = (newVal) & 0x000000ff | (pixelValue & 0xffffff00);
BI.setRGB(w, h, pixelValue);
}
}
ImageIO.write(BI, "jpg", new File("D:/test.jpg"));
}
public static short[] toShortArray(byte[] src) {
int count = src.length >> 1;
short[] dest = new short[count];
for (int i = 0; i < count; i++) {
dest[i] = (short) ((src[i * 2] & 0xff) | ((src[2 * i + 1] & 0xff) << 8));
}
return dest;
}
}