その前に、準備が必要な資料は次のとおり
です。1。JavaでコンパイルされたVTKバージョン、Camkeを自分で検索してください。VTKのコンパイルに関するチュートリアルはたくさんあります。可能であれば、英語が上手であれば、VTKの公式ウェブサイトに直接アクセスして検索してください。独自の編集プロセスがあります。私は個人的にVTKの7.1.1バージョンを使用しています。
2. dcm4che-core5.12.0依存関係パッケージ。
開発環境jdk1.864ビット、Eclipse。
1
ナンセンスな話をするのではなく、トピックを開始するだけです。コードは自分でテストし、指定した層の厚さをカットすることができます。
java.io.Fileをインポートします。
import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.Implementation;
import org.dcm4che3.data.Tag;
import org.dcm4che3.data.VR;
import org.dcm4che3.io.DicomEncodingOptions;
import org.dcm4che3.io.DicomOutputStream;
import org.dcm4che3.util.UIDUtils;
インポートvtk.vtkAlgorithmOutput;
インポートvtk.vtkDICOMImageReader;
インポートvtk.vtkImageData;
インポートvtk.vtkImageReslice;
インポートvtk.vtkMatrix4x4;
インポートvtk.vtkNativeLibrary;
/ **
- 切断機
- @author Jmx 2020-09-03
* /
public class NewMPRReslicer {
// 需要静态引用vtk的dll类库等,否则会出错: java.lang.UnsatisfiedLinkLOG.error: VTKInit()
static {
if (!vtkNativeLibrary.LoadAllNativeLibraries()) {
for (vtkNativeLibrary lib : vtkNativeLibrary.values()) {
if (!lib.IsLoaded()) {
System.err.println(lib.GetLibraryName() + " not loaded");
}
}
}
//只是隐藏了在VTK错误或警告出现时可能会弹出的任何调试信息。您还可以提供一个文件路径,以便将任何错误写入磁盘。
vtkNativeLibrary.DisableOutputWindow(null);
}
/**
* 切割横断位
* seriesDIR 要切割的序列文件夹
* outDir 输出的文件夹
* thickness 要输出的层厚,不写默认就是dicom序列原始层厚
*/
public static void excute(String seriesDIR, String outDir, Double thickness) {
//加载体数据
vtkDICOMImageReader imageReader = new vtkDICOMImageReader();
imageReader.SetDirectoryName(seriesDIR);
imageReader.Update();
vtkImageData imageData = imageReader.GetOutput();
//加载体数据结束
double bounds[] = imageData.GetBounds();//获取体数据的边界
double spacing[] = imageData.GetSpacing();//图像的像素间距
int extent[] = imageData.GetExtent();//图像的宽高和图像数量
double origin[] = imageData.GetOrigin();//图像的坐标原点
// 找到中心点位置(贯穿点)
double center[] = new double[3];
center[0] = origin[0] + spacing[0] * 0.5 * (extent[0] + extent[1]);// x
center[1] = origin[1] + spacing[1] * 0.5 * (extent[2] + extent[3]);// y
center[2] = origin[2] + spacing[2] * 0.5 * (extent[4] + extent[5]);// z
// 通过修改切面和贯穿点,可以得到需要的重建图层(冠状、矢状、横断、斜切等)
int columneIndex = 2;//横断位
double[] imageOrientationPatient = new double[] { 1, 0, 0, 0, 1, 0 };//
vtkMatrix4x4 resliceAxes = new vtkMatrix4x4();
// 此矩阵为横断面(方式1)
// double axialElements[] = {
// 1, 0, 0, 0,
// 0, 1, 0, 0,
// 0, 0, 1, 0,
// 0, 0, 0, 1
// };
// resliceAxes.DeepCopy(axialElements);
//方式2
resliceAxes.SetElement(0, 0, 1);
resliceAxes.SetElement(0, 1, 0);
resliceAxes.SetElement(0, 2, 0);
resliceAxes.SetElement(1, 0, 0);
resliceAxes.SetElement(1, 1, 1);
resliceAxes.SetElement(1, 2, 0);
resliceAxes.SetElement(2, 0, 0);
resliceAxes.SetElement(2, 1, 0);
resliceAxes.SetElement(2, 2, 1);
if (null != thickness && thickness > 0) {
spacing[2] = thickness;//以用户传的层厚为准
}
int instanceCount = 0;
//通过层厚计算dicom输出的数量
double count = (bounds[5] - bounds[4]) / spacing[2] + 1;
if (count - (int) count != 0) {
//说明除不尽
instanceCount = (int) count + 1;
} else {
instanceCount = (int) count;
}
// 设置中心点
resliceAxes.SetElement(0, 3, center[0]);
resliceAxes.SetElement(1, 3, center[1]);
resliceAxes.SetElement(2, 3, center[2]);
// 图像重取filter(从体数据中)
vtkImageReslice imageReslice = new vtkImageReslice();
vtkAlgorithmOutput algorithmOutput = imageReader.GetOutputPort();
imageReslice.SetInputConnection(algorithmOutput);
imageReslice.SetOutputDimensionality(2);// 输出为2D数据
imageReslice.SetResliceAxes(resliceAxes);
imageReslice.SetOutputSpacing(spacing);
//设置插值法
imageReslice.SetInterpolationModeToLinear();// 双线性插值法
// 此处从中心点预切割一幅,目的为获取各种参数
imageReslice.Update();
//=======================重建结束,准备输出图像======
vtkImageData imageResliceData = imageReslice.GetOutput();
extent = imageResliceData.GetExtent();
spacing = imageResliceData.GetSpacing();
origin = imageResliceData.GetOrigin();
bounds = imageResliceData.GetBounds();//获取体数据的边界
int volume_x = extent[1] + 1;
int volume_y = extent[3] + 1;
Attributes fmi = new Attributes(6);
fmi.setBytes(Tag.FileMetaInformationVersion, VR.OB, new byte[] { 0, 1 });
fmi.setString(Tag.MediaStorageSOPClassUID, VR.UI, "1.2.840.10008.5.1.4.1.1.128");//这里要动态获取dicom的值
fmi.setString(Tag.MediaStorageSOPInstanceUID, VR.UI, UIDUtils.createUID("2.25"));
fmi.setString(Tag.TransferSyntaxUID, VR.UI, "1.2.840.10008.1.2.1");
fmi.setString(Tag.ImplementationClassUID, VR.UI, Implementation.getClassUID());
fmi.setString(Tag.ImplementationVersionName, VR.SH, Implementation.getVersionName());
Attributes data = new Attributes();
//图像必要的一些tag
String studuUID = UIDUtils.createUID("1.1");
String seriesUID = UIDUtils.createUID("1.2");
//0020 Group
data.setString(Tag.StudyInstanceUID, VR.UI, studuUID);
data.setString(Tag.SeriesInstanceUID, VR.UI, seriesUID);
//0028 Group
data.setString(Tag.SamplesPerPixel, VR.US, "1");
data.setString(Tag.PhotometricInterpretation, VR.CS, "MONOCHROME2");
data.setInt(Tag.Rows, VR.IS, volume_x);
data.setInt(Tag.Columns, VR.IS, volume_y);
data.setInt(Tag.BitsAllocated, VR.IS, 16);
data.setInt(Tag.BitsStored, VR.IS, 16);
data.setInt(Tag.HighBit, VR.IS, 15);
data.setInt(Tag.PixelRepresentation, VR.IS, 0);
data.setInt(Tag.Rows, VR.US, volume_y);//必要的Tag
data.setInt(Tag.Columns, VR.US, volume_x);//必要的Tag
data.setInt(Tag.NumberOfSlices, VR.US, instanceCount);
data.setDouble(Tag.ImageOrientationPatient, VR.DS, imageOrientationPatient);
data.setDouble(Tag.PixelSpacing, VR.DS, spacing[0], spacing[1]);
data.setDouble(Tag.SliceThickness, VR.DS, spacing[2]);
double[] imagePositionPatient = new double[3];
imagePositionPatient[0] = origin[0];
imagePositionPatient[1] = origin[1];
new File(outDir).mkdirs();
for (int i = 0; i < instanceCount; i++) {
double sliceLocation = i * spacing[2];
resliceAxes.SetElement(columneIndex, 3, sliceLocation);//设置第4个切面,来切要的体数据
imageReslice.SetResliceAxes(resliceAxes);
imageReslice.Update();
vtkImageData vtkImageData = imageReslice.GetOutput();
data.setString(Tag.SeriesDescription, VR.LO, "JMX SC AXIL");
imagePositionPatient[2] = sliceLocation;
data.setDouble(Tag.ImagePositionPatient, VR.DS, imagePositionPatient);
data.setDouble(Tag.SliceLocation, VR.DS, sliceLocation);
data.setInt(Tag.InstanceNumber, VR.IS, i + 1);
byte[] dicomarray = new byte[volume_x * volume_y * 2];
int dicomindex = 0;
for (int y = 0; y < volume_y; y++) {
for (int x = 0; x < volume_x; x++) {
double pixelvalue = vtkImageData.GetScalarComponentAsDouble(x, volume_y - y - 1, 0, 0);
int intvalue = (int) pixelvalue;
int highByte = (intvalue & 0xFF00) >> 8;
int lowByte = intvalue & 0xFF;
// 低位
dicomarray[dicomindex++] = (byte) lowByte;
// 高位
dicomarray[dicomindex++] = (byte) highByte;
}
}
String outPath = outDir + File.separator + (i + 1) + ".dcm";
createdicomfile(new File(outPath), dicomarray, fmi, data);
}
}
public static void createdicomfile(File outdcmfile, byte[] buffer, Attributes fmi, Attributes data) {
try (DicomOutputStream dicomoutstream = new DicomOutputStream(outdcmfile);) {
dicomoutstream.setEncodingOptions(DicomEncodingOptions.DEFAULT);
// 使用meta信息,去构建数据集;(meta中含有传输语法)
dicomoutstream.writeDataset(fmi, data);
// 写入图像数据部分
dicomoutstream.writeHeader(Tag.PixelData, VR.OB, buffer.length);
// offset为输入buffer本身的偏移量
dicomoutstream.write(buffer, 0, buffer.length);
} catch (Exception e) {
e.printStackTrace();
}
}
}