You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中如何为DICOM文件添加元数据?JPG转DICOM代码存问题

解决Java生成DICOM时的必填元数据问题

嘿,我明白你在从JPG生成DICOM文件时遇到的必填标签难题了——DICOM标准确实有一堆必须填的元数据,漏一个都不行。我帮你梳理一下必须补充的内容,再给你完整点的代码示例:

一、文件元信息(File Meta)必须补充的标签

你已经加了FileMetaInformationGroupLength和TransferSyntaxUID,但还缺这几个核心项:

  • MediaStorageSOPClassUID:指定DICOM图像的SOP类,普通JPG转DICOM用Secondary Capture Image Storage(1.2.840.10008.5.1.4.1.1.104.1)最合适
  • MediaStorageSOPInstanceUID:每个DICOM文件唯一的实例ID,得生成一个符合DICOM格式的唯一值(可以用UUID转或者用工具类生成)
  • ImplementationClassUID:你的程序实现的专属UID,自定义一个比如1.2.3.4.5.6.7.8就行,只要符合UID格式
  • ImplementationVersionName:你的程序版本标识,比如MyDicomConverter_v1.0

二、DICOM数据集(Dataset)的核心必填标签

除了StudyDate、StudyTime,还得覆盖这几类关键标签:

1. 患者基础信息

  • PatientName:患者姓名(必填,哪怕填匿名的^Anonymous^)
  • PatientID:患者唯一标识ID
  • PatientBirthDate:患者生日(可以填空,但建议按YYYYMMDD格式填写)

2. 检查/序列/实例信息

  • StudyInstanceUID:检查唯一ID(和SOP实例ID一样,需唯一)
  • SeriesInstanceUID:序列唯一ID
  • SOPInstanceUID:要和文件元里的保持一致
  • Modality:成像模态,JPG转的填SC(Secondary Capture)就行
  • StudyDescription:检查内容描述

3. 图像属性(从JPG转的话这些是核心)

  • Rows/Columns:对应JPG的高度和宽度
  • BitsAllocated/BitsStored:JPG一般都是8位,所以填8
  • HighBit:最高位,8位像素填7(从0开始计数)
  • PixelRepresentation:0代表无符号像素(JPG用这个)
  • PhotometricInterpretation:RGB图填RGB,灰度图填MONOCHROME2
  • PixelData:把JPG的像素数据转成DICOM要求的字节数组格式

三、完整代码示例(基于dcm4che库)

import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.Tag;
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.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

public class JpgToDicomConverter {
    public static void main(String[] args) throws IOException {
        // 1. 读取目标JPG图片
        BufferedImage jpgImage = ImageIO.read(new File("input.jpg"));
        int imgWidth = jpgImage.getWidth();
        int imgHeight = jpgImage.getHeight();

        // 2. 生成唯一DICOM UID(用dcm4che工具类更省心)
        String studyUID = UIDUtils.createUID();
        String seriesUID = UIDUtils.createUID();
        String sopInstanceUID = UIDUtils.createUID();

        // 3. 构建文件元信息
        Attributes fileMeta = new Attributes();
        fileMeta.setInt(Tag.FileMetaInformationGroupLength, VR.UL, 0); // 后续会自动计算,先填0
        fileMeta.setString(Tag.MediaStorageSOPClassUID, VR.UI, "1.2.840.10008.5.1.4.1.1.104.1");
        fileMeta.setString(Tag.MediaStorageSOPInstanceUID, VR.UI, sopInstanceUID);
        fileMeta.setString(Tag.TransferSyntaxUID, VR.UI, "1.2.840.10008.1.2.1"); // 显式小端字节序
        fileMeta.setString(Tag.ImplementationClassUID, VR.UI, "1.2.3.4.5.6.7.8");
        fileMeta.setString(Tag.ImplementationVersionName, VR.SH, "Jpg2Dicom_v1.0");

        // 4. 构建DICOM数据集
        Attributes dataset = new Attributes();
        // 患者信息
        dataset.setString(Tag.PatientName, VR.PN, "^Anonymous^");
        dataset.setString(Tag.PatientID, VR.LO, "PAT_0001");
        dataset.setString(Tag.PatientBirthDate, VR.DA, "");

        // 检查/序列/实例信息
        dataset.setString(Tag.StudyInstanceUID, VR.UI, studyUID);
        dataset.setString(Tag.SeriesInstanceUID, VR.UI, seriesUID);
        dataset.setString(Tag.SOPInstanceUID, VR.UI, sopInstanceUID);
        dataset.setString(Tag.Modality, VR.CS, "SC");
        dataset.setString(Tag.StudyDate, VR.DA, "20110404");
        dataset.setString(Tag.StudyTime, VR.TM, "150000"); // 注意格式必须是HHMMSS,之前的"15"不符合标准
        dataset.setString(Tag.StudyDescription, VR.LO, "JPG转DICOM示例");

        // 图像属性
        dataset.setInt(Tag.Rows, VR.US, imgHeight);
        dataset.setInt(Tag.Columns, VR.US, imgWidth);
        dataset.setInt(Tag.BitsAllocated, VR.US, 8);
        dataset.setInt(Tag.BitsStored, VR.US, 8);
        dataset.setInt(Tag.HighBit, VR.US, 7);
        dataset.setInt(Tag.PixelRepresentation, VR.US, 0);
        dataset.setString(Tag.PhotometricInterpretation, VR.CS, "RGB");
        dataset.setInt(Tag.SamplesPerPixel, VR.US, 3); // RGB是3通道
        dataset.setInt(Tag.PixelSpacing, VR.DS, new float[]{1.0f, 1.0f}); // 像素间距,按需调整

        // 处理像素数据:把BufferedImage转成DICOM要求的字节数组
        ByteBuffer pixelBuffer = ByteBuffer.allocate(imgWidth * imgHeight * 3);
        for (int y = 0; y < imgHeight; y++) {
            for (int x = 0; x < imgWidth; x++) {
                int rgb = jpgImage.getRGB(x, y);
                pixelBuffer.put((byte) ((rgb >> 16) & 0xFF)); // R通道
                pixelBuffer.put((byte) ((rgb >> 8) & 0xFF));  // G通道
                pixelBuffer.put((byte) (rgb & 0xFF));         // B通道
            }
        }
        dataset.setBytes(Tag.PixelData, VR.OW, pixelBuffer.array());

        // 5. 写入DICOM文件
        try (DicomOutputStream dos = new DicomOutputStream(new FileOutputStream("output.dcm"))) {
            dos.writeFileMetaInformation(fileMeta);
            dos.writeDataset(fileMeta.getString(Tag.TransferSyntaxUID, VR.UI), dataset);
        }
    }
}

几个关键提醒

  • 时间格式要合规:StudyTime必须是HHMMSS或HHMMSS.ffffff,你之前写的"15"不符合标准,会导致DICOM验证失败
  • UID要唯一:用UIDUtils.createUID()生成最省心,避免手动拼接出错
  • 像素数据要对应:如果是灰度JPG,记得把SamplesPerPixel改成1,PhotometricInterpretation改成MONOCHROME2
  • 验证工具:生成文件后可以用dcmtk的dcmdump或者Weasis打开检查,确认没有缺失必填标签

内容的提问来源于stack exchange,提问作者celia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:19:26