Skip to content

快速开始 ​

环境要求 ​

Go 1.26 或更新版本。不需要 C 工具链,也不需要 CMake —— 压缩 Pixel Data 的编解码器通过 purego 加载原生库,所以 CGO_ENABLED=0 能正常构建。这些库为哪些平台构建过,见 生态总览。

安装 ​

bash
go get github.com/godicom-dev/godicom@latest

读写文件(包括压缩 Pixel Data)只需要这一个模块 —— 编解码器模块会跟着一起来。如果还要用 DIMSE 或 DICOMweb,再单独加 gonetdicom。

bash
go get github.com/godicom-dev/gonetdicom@latest

读一个文件 ​

go
package main

import (
	"fmt"
	"log"

	"github.com/godicom-dev/godicom"
	"github.com/godicom-dev/godicom/tag"
)

func main() {
	ds, err := godicom.ReadFile("ct.dcm", nil)
	if err != nil {
		log.Fatal(err)
	}

	name, ok := ds.GetString(tag.PatientName)
	if !ok {
		log.Fatal("no PatientName")
	}
	fmt.Println(name)
}

ReadFile 返回一个 *godicom.FileDataset:数据集本身,加上 Part 10 文件在它周围携带的 preamble 与 File Meta Information。元素用带类型的 getter 读取,标签用 tag 包里的常量。

改一处再写回去 ​

go
if err := ds.SetString(tag.PatientID, "12345678"); err != nil {
	log.Fatal(err)
}
if err := ds.SaveAs("ct_updated.dcm", nil); err != nil {
	log.Fatal(err)
}

setter 从数据字典取 VR,所以对数值标签调 SetString、对整数标签调 SetFloat,会在调用处就失败,而不是产出一个接收方会拒收的文件。数据集一页完整覆盖 getter 与 setter。

全程在内存里走一遍 ​

适合做冒烟测试。本站大多数示例也是这么写的:每个字节都在内存里造出来,不需要测试文件就能跑:

go
package main

import (
	"fmt"
	"log"

	"github.com/godicom-dev/godicom"
	"github.com/godicom-dev/godicom/tag"
	"github.com/godicom-dev/godicom/uid"
)

func main() {
	ds := godicom.NewDataset()
	if err := ds.SetString(tag.PatientID, "12345678"); err != nil {
		log.Fatal(err)
	}
	if err := ds.SetString(tag.PatientName, "Doe^Jane"); err != nil {
		log.Fatal(err)
	}

	// PS3.10 requires File Meta to name the SOP Class and Instance;
	// EnforceFileFormat fills the File Meta in from the dataset.
	if err := ds.SetString(tag.SOPClassUID, string(uid.CTImageStorage)); err != nil {
		log.Fatal(err)
	}
	if err := ds.SetString(tag.SOPInstanceUID, "1.2.826.0.1.3680043.10.1337.1"); err != nil {
		log.Fatal(err)
	}

	fd := &godicom.FileDataset{Dataset: ds, FileMeta: godicom.NewFileMetaDataset()}
	data, err := godicom.EncodeFile(fd, &godicom.WriteOptions{EnforceFileFormat: true})
	if err != nil {
		log.Fatal(err)
	}

	reread, err := godicom.ReadBytes(data, nil)
	if err != nil {
		log.Fatal(err)
	}
	id, _ := reread.GetString(tag.PatientID)
	name, _ := reread.GetString(tag.PatientName)
	fmt.Println(id, name) // 12345678 Doe^Jane
}

TIP

这段就是 godicom example_test.go 里的 Example。每次提交时 go test ./... 都会编译它并核对输出。示例坏了,CI 会失败,不会静静地误导读者。

看一个文件到底有什么问题 ​

默认情况下,读到文件开始不对劲的地方就停,之前已经解析出来的内容会留下来。多数 DICOM 工具都这样,所以坏文件经常看起来好好的。设上 ReadOptions.OnDiagnostic,这些异常就会交给你:

go
ds, err := godicom.ReadFile("suspect.dcm", &godicom.ReadOptions{
	OnDiagnostic: func(d godicom.Diagnostic) error {
		log.Printf("%s", d) // return nil: keep parsing
		return nil
	},
})

Diagnostic 本身就是一个 error,所以把它返回出去就等于拒收该文件。诊断一页说明会报哪些东西,以及为什么写入侧也有同一个钩子。

读出像素 ​

go
import "github.com/godicom-dev/godicom/pixels"

ds, err := godicom.ReadFile("mr_j2k.dcm", nil)
if err != nil {
	log.Fatal(err)
}

arr, err := ds.PixelArray(pixels.WithRaw(true)) // decoded samples + shape
frame, err := ds.DisplayFrame(0)                // 8-bit, display-ready

压缩传输语法通过编解码器模块解码,不需要额外设置。Pixel Data 一页覆盖帧、原始与归一化输出、LUT,以及压缩。

接下来看哪里 ​

基于 MIT 许可证发布。