Java DICOM文件解析:dcm4che(医学数字成像与通信)

在这里插入图片描述
简述:dcm4che 是一个功能强大且开源的 DICOM(医学数字成像与通信)工具集和类库,专为医疗影像处理而设计。
官网:dcm4che
github地址:https://github.com/dcm4che/dcm4che

	<!-- 远程仓库,下载不下来,要指定远程仓库地址-->
    <repositories>
        <repository>
            <id>dcm4che</id>
            <url>https://maven.dcm4che.org/ </url>
        </repository>
    </repositories>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- 核心 -->
        <dependency>
            <groupId>org.dcm4che</groupId>
            <artifactId>dcm4che-core</artifactId>
            <version>5.33.1</version>
        </dependency>
        <!-- 网络 -->
        <dependency>
            <groupId>org.dcm4che</groupId>
            <artifactId>dcm4che-net</artifactId>
            <version>5.33.1</version>
        </dependency>
        <!-- 获取图片 -->
        <dependency>
            <groupId>org.dcm4che</groupId>
            <artifactId>dcm4che-imageio</artifactId>
            <version>5.33.1</version>
        </dependency>
    </dependencies>

一、生成dcm文件,及模拟图片

package org.example.dicom.generator;

import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.Tag;
import org.dcm4che3.data.UID;
import org.dcm4che3.data.VR;
import org.dcm4che3.io.DicomOutputStream;
import org.dcm4che3.util.UIDUtils;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Random;

public class CustomDicomGenerator {

    public static void main(String[] args) {
        String outputFilePath = "src/main/resources/custom_output.dcm";

        try {
            // 创建 DICOM 数据集
            Attributes dcm = new Attributes();
            Attributes fmi = new Attributes();

            // 文件元信息
            fmi.setString(Tag.MediaStorageSOPClassUID, VR.UI, UID.UltrasoundMultiFrameImageStorage);
            fmi.setString(Tag.MediaStorageSOPInstanceUID, VR.UI, UIDUtils.createUID());
            fmi.setString(Tag.TransferSyntaxUID, VR.UI, UID.ExplicitVRLittleEndian);
            fmi.setString(Tag.ImplementationClassUID, VR.UI, UIDUtils.createUID());

            // 设置患者信息(使用 GB18030 编码)
            dcm.setString(Tag.PatientName, VR.PN, "小红");
            dcm.setString(Tag.PatientID, VR.LO, "26154642");
            dcm.setString(Tag.PatientSex, VR.CS, "女");
            dcm.setString(Tag.PatientBirthDate, VR.DA, "20250424");

            // 设置检查时间
            LocalDateTime scanTime = LocalDateTime.of(2025, 4, 14, 14, 43, 5);
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
            dcm.setString(Tag.StudyTime, VR.TM, scanTime.format(formatter));
            dcm.setString(Tag.StudyDate, VR.DA, scanTime.format(DateTimeFormatter.ofPattern("yyyyMMdd")));

            // 设置设备和图像参数
            dcm.setString(Tag.Modality, VR.CS, "US"); // 超声
            dcm.setString(Tag.SpecificCharacterSet, VR.CS, "GB18030"); // 显式指定字符集
            dcm.setString(Tag.SeriesDescription, VR.LO, "测试描述");

            // SOP Class & Instance UID
            dcm.setString(Tag.SOPClassUID, VR.UI, UID.SecondaryCaptureImageStorage);
            dcm.setString(Tag.SOPInstanceUID, VR.UI, UIDUtils.createUID());

            // 图像尺寸(模拟灰度图)
            int width = 512;
            int height = 512;
            dcm.setInt(Tag.Rows, VR.US, height);
            dcm.setInt(Tag.Columns, VR.US, width);
            dcm.setInt(Tag.BitsAllocated, VR.US, 8);
            dcm.setInt(Tag.BitsStored, VR.US, 8);
            dcm.setInt(Tag.HighBit, VR.US, 7);
            dcm.setInt(Tag.PixelRepresentation, VR.US, 0);

            // 生成模拟图像数据
            BufferedImage image = generateSimulatedUltrasoundImage(width, height);
            byte[] pixelData = convertToByteArray(image);

            // 写入 Pixel Data
            dcm.setBytes(Tag.PixelData, VR.OB, pixelData);

            // 写入 DICOM 文件
            try (DicomOutputStream dos = new DicomOutputStream(new File(outputFilePath))) {
                dos.writeDataset(fmi, dcm);
            }

            System.out.println("DICOM 文件已生成: " + outputFilePath);

            // 可选:保存为 PNG 查看效果
            ImageIO.write(image, "jpg", new File("src/main/resources/simulated_ultrasound.jpg"));
            System.out.println("模拟图像已保存为 simulated_ultrasound.jpg");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 生成模拟图像数据
    private static BufferedImage generateSimulatedUltrasoundImage(int width, int height) {
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
        byte[] data = ((java.awt.image.DataBufferByte) image.getRaster().getDataBuffer()).getData();
        Random rand = new Random();

        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                int index = y * width + x;

                // 基础噪声
                int noise = (int) (Math.abs(rand.nextGaussian()) * 32); // 使用高斯分布

                // 中心圆形高亮区域(模拟乳腺)
                double centerX = width / generateRandomNumber();
                double centerY = height / generateRandomNumber();
                double radius = 100 + 20 * Math.sin(x / 50.0 + rand.nextDouble() * 5); // 添加随机偏移
                double dist = Math.sqrt((x - centerX) * (x - centerX) + (y - centerY) * (y - centerY));
                int intensity = 0;

                if (dist < radius) {
                    intensity = 180 + (int)(20 * Math.sin(dist / 10));
                } else if (dist < radius + 20) {
                    intensity = 120 + (int)(40 * Math.sin(dist / 10));
                } else {
                    intensity = noise;
                }

                data[index] = (byte) intensity;
            }
        }

        return image;
    }

    // 1.5 到2.5 之间的随机数
    private static double generateRandomNumber() {
        Random random = new Random();
        return 1.5 + random.nextDouble();
    }

    private static byte[] convertToByteArray(BufferedImage image) {
        int width = image.getWidth();
        int height = image.getHeight();
        byte[] pixels = new byte[width * height];

        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                int rgb = image.getRGB(x, y);
                int gray = (rgb & 0xFF); // 提取灰度值
                pixels[y * width + x] = (byte) gray;
            }
        }

        return pixels;
    }
}

二、读取文件内容

package org.example.dicom.resolver;

import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.Tag;
import org.dcm4che3.io.DicomInputStream;

import java.io.File;
import java.io.IOException;

public class CustomDicomReader {

    public static void main(String[] args) {
        String filePath = "src/main/resources/custom_output.dcm"; // 替换为你的文件路径
        File file = new File(filePath);

        try (DicomInputStream dis = new DicomInputStream(file)) {
            Attributes dataset = dis.readDataset(-1, -1);

            System.out.println("=== 提取的 DICOM 字段 ===");
            System.out.println("患者姓名: " + getString(dataset, Tag.PatientName));
            System.out.println("患者 ID: " + getString(dataset, Tag.PatientID));
            System.out.println("出生日期: " + getString(dataset, Tag.PatientBirthDate));
            System.out.println("性别: " + getString(dataset, Tag.PatientSex));

            System.out.println("检查日期(StudyDate): " + getString(dataset, Tag.StudyDate));
            System.out.println("系列日期(SeriesDate): " + getString(dataset, Tag.SeriesDate));
            System.out.println("内容日期(ContentDate): " + getString(dataset, Tag.ContentDate));

            System.out.println("检查时间(StudyTime): " + getString(dataset, Tag.StudyTime));
            System.out.println("系列时间(SeriesTime): " + getString(dataset, Tag.SeriesTime));
            System.out.println("内容时间(ContentTime): " + getString(dataset, Tag.ContentTime));

            System.out.println("设备类型(Modality): " + getString(dataset, Tag.Modality));
            System.out.println("设备序列号(DeviceSerialNumber): " + getString(dataset, Tag.DeviceSerialNumber));
            System.out.println("软件版本(SoftwareVersions): " + getString(dataset, Tag.SoftwareVersions));
            System.out.println("研究ID(StudyID): " + getString(dataset, Tag.StudyID));
            System.out.println("系列编号(SeriesNumber): " + getInt(dataset, Tag.SeriesNumber));
            System.out.println("实例编号(InstanceNumber): " + getInt(dataset, Tag.InstanceNumber));
            System.out.println("图像行数(Rows): " + getInt(dataset, Tag.Rows));
            System.out.println("图像列数(Columns): " + getInt(dataset, Tag.Columns));

        } catch (IOException e) {
            System.err.println("读取 DICOM 文件失败!");
            e.printStackTrace();
        }
    }

    // 封装安全获取字符串的方法
    private static String getString(Attributes ds, int tag) {
        return ds.getString(tag, null);
    }

    // 封装安全获取整型的方法
    private static Integer getInt(Attributes ds, int tag) {
        return ds.getInt(tag, 0); // 返回 -1 表示未定义
    }
}

三、修改内容

package org.example.dicom.resolver;

import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.Tag;
import org.dcm4che3.data.VR;
import org.dcm4che3.io.DicomInputStream;
import org.dcm4che3.io.DicomOutputStream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class DicomPatientName {

    public static void main(String[] args) {
        String inputPath = "src/main/resources/custom_output.dcm";      // 输入DICOM文件路径
        String outputPath = "src/main/resources/custom_output.dcm";    // 输出DICOM文件路径
        String newPatientName = "张三"; // 新的患者姓名
        String patientID = "8630718"; // 患者ID

        try {
            // 读取DICOM文件
            File inputFile = new File(inputPath);
            DicomInputStream dis = new DicomInputStream(new FileInputStream(inputFile));

            // 读取 FMI 和 数据集
            Attributes fmi = dis.readFileMetaInformation();  // 读取 FMI
            Attributes dataset = dis.readDataset();          // 读取数据集

            // 修改
            dataset.setString(Tag.PatientName, VR.PN, newPatientName);
            dataset.setString(Tag.PatientID, VR.PN, patientID);

            // 写回新的DICOM文件
            try (DicomOutputStream dos = new DicomOutputStream(new File(outputPath))) {
                dos.writeDataset(fmi, dataset);  // 同时写入 FMI 和 数据集
            }

            System.out.println("PatientName 修改成功,新文件保存为: " + outputPath);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值