Sign in

    moondicom

    Pure MoonBit toolkit for DICOM Part 10 metadata parsing, validation, anonymization and JSON reporting

    dicom
    medical-imaging
    privacy
    deidentification
    binary-parser
    Download zip
    Version
    0.1.2
    License
    Apache-2.0
    Last updated
    last month
    Downloads
    16

    Dependencies

    #MoonDICOM

    MoonDICOM is a pure MoonBit toolkit for reading DICOM Part 10 metadata safely and preparing it for validation, privacy anonymization, and reporting.

    Author: CCllff-jpg 347921583@qq.com

    Repository: https://github.com/CCllff-jpg/MoonDICOM-MoonBit-.git

    #Current status

    The first implementation provides an in-memory core for:

    • DICOM Tag, common tag names, and dictionary VR lookup;
    • Part 10 preamble and DICM marker validation;
    • Explicit VR Little Endian parsing;
    • basic Implicit VR Little Endian parsing;
    • short and long explicit-VR length headers;
    • text, integer, and raw byte values;
    • ordered DataSet lookup, replacement, and removal;
    • byte-offset-aware errors for truncated or malformed input;
    • semantic validation reports with stable issue codes;
    • conservative, policy-driven metadata anonymization with an audit trail.

    ///|
    let dataset = @moondicom.parse(file_bytes)

    ///|
    let patient_name = dataset.get_text(@moondicom.patient_name())

    ///|
    let image_rows = dataset.get_int(@moondicom.rows())

    ///|
    let report = @moondicom.validate(dataset)

    ///|
    let public_copy = @moondicom.anonymize(
    dataset,
    @moondicom.default_anonymization_policy(),
    )

    Validation checks required identity metadata, UID and date syntax, image dimensions, bit depth relationships, pixel representation, and missing pixel data warnings. Anonymization can remove common identifying fields, replace configured text values, remove private groups, and record each change without mutating the source data set.

    #Command line

    The native CLI can inspect and validate a real Part 10 file and export metadata as JSON:

    moon run --target native examples/generate moon run --target native examples/generate -- examples/sample.dcm moon run --target native cmd/main -- inspect examples/sample.dcm moon run --target native cmd/main -- validate examples/sample.dcm moon run --target native cmd/main -- convert examples/sample.dcm --format json moon run --target native cmd/main -- anonymize examples/sample.dcm --output anonymized.json

    The anonymize command writes an anonymized metadata JSON report and an audit count. It does not rewrite a binary .dcm file; a DICOM encoder is intentionally not claimed until it can preserve transfer syntax and unsupported data safely.

    This is a metadata parser, not a medical diagnostic system. The current version does not decode compressed pixel formats, interpret all DICOM value representations, parse undefined-length sequences, provide DICOM network services, or guarantee regulatory compliance. Pixel data is retained as raw bytes when it has a defined length.

    Anonymization only changes represented metadata. It does not inspect burned-in text inside pixels, remove every possible identifying attribute, apply date shifting, remap UIDs, or establish legal compliance. It must be followed by domain review and any applicable institutional privacy process.

    #Development

    Run these commands from the module directory:

    moon check moon check --target all moon test moon fmt moon info

    Tests construct small synthetic DICOM byte sequences in memory; no real patient data is included.

    ParseError

    pub(all) suberror ParseError {
    InvalidMagic(offset~ : Int)
    Truncated(offset~ : Int, needed~ : Int, remaining~ : Int)
    InvalidVr(offset~ : Int, code~ : String)
    InvalidLength(offset~ : Int, length~ : Int)
    Unsupported(offset~ : Int, message~ : String)
    }

    A parse failure with the byte offset where it was detected.

    AnonymizationAction

    pub(all) enum AnonymizationAction {
    Removed
    Replaced
    } derive(Eq,
    Debug
    )

    One change made during anonymization.

    AnonymizationAudit

    pub(all) struct AnonymizationAudit {
    tag : Tag
    action : AnonymizationAction
    } derive(Eq,
    Debug
    )

    An audit record for one anonymized tag.

    AnonymizationAudit::action

    Return the action recorded for this audit entry.

    AnonymizationAudit::tag

    Return the tag changed by this audit entry.

    AnonymizationPolicy

    pub(all) struct AnonymizationPolicy {
    remove : Array[Tag]
    replacements : Array[(Tag, String)]
    remove_private : Bool
    } derive(Eq,
    Debug
    )

    A conservative metadata anonymization policy.

    AnonymizationPolicy::with_private_removal

    fn AnonymizationPolicy::with_private_removal(self : AnonymizationPolicy) -> AnonymizationPolicy

    Return a policy with private-tag removal enabled.

    AnonymizationPolicy::with_remove

    Return a policy that removes the given tag.

    AnonymizationPolicy::with_replacement

    fn AnonymizationPolicy::with_replacement(self : AnonymizationPolicy, tag : Tag, value : String) -> AnonymizationPolicy

    Return a policy that replaces a text value for the given tag.

    AnonymizationResult

    pub(all) struct AnonymizationResult {
    dataset : DataSet
    audit : Array[AnonymizationAudit]
    } derive(Eq,
    Debug
    )

    The result of applying an anonymization policy.

    AnonymizationResult::audit

    Return the audit entries in source order.

    AnonymizationResult::dataset

    Return the anonymized data set.

    ByteReader

    pub struct ByteReader {
    bytes : Bytes
    position : Int
    }

    A bounded little-endian reader for DICOM byte streams.

    ByteReader::position

    fn ByteReader::position(self : ByteReader) -> Int

    Return the current byte offset.

    ByteReader::remaining

    fn ByteReader::remaining(self : ByteReader) -> Int

    Return the number of unread bytes.

    DataElement

    pub(all) struct DataElement {
    tag : Tag
    vr : ValueRepresentation
    value : Value
    } derive(Eq,
    Debug
    )

    One DICOM data element.

    DataSet

    pub(all) struct DataSet {
    elements : Array[DataElement]
    } derive(Eq,
    Debug
    )

    The ordered elements in a DICOM data set.

    DataSet::elements

    fn DataSet::elements(self : DataSet) -> Array[DataElement]

    Return all elements in file order.

    DataSet::get

    fn DataSet::get(self : DataSet, tag : Tag) -> DataElement?

    Find an element by tag.

    DataSet::get_bytes

    fn DataSet::get_bytes(self : DataSet, tag : Tag) -> Bytes?

    Return raw bytes when the element contains bytes.

    DataSet::get_int

    fn DataSet::get_int(self : DataSet, tag : Tag) -> Int?

    Return an integer value when the element contains an integer.

    DataSet::get_text

    fn DataSet::get_text(self : DataSet, tag : Tag) -> String?

    Return a text value when the element contains text.

    DataSet::length

    fn DataSet::length(self : DataSet) -> Int

    Return the number of elements.

    DataSet::remove

    fn DataSet::remove(self : DataSet, tag : Tag) -> Bool

    Remove an element by tag.

    DataSet::set

    fn DataSet::set(self : DataSet, element : DataElement) -> Unit

    Add or replace an element while preserving its position when replacing.

    Tag

    pub(all) struct Tag {
    group : Int
    element : Int
    } derive(Eq,
    Debug
    )

    Core DICOM tag identifier.

    Tag::is_private

    fn Tag::is_private(self : Tag) -> Bool

    True when the tag is a private creator or private data element.

    Tag::to_string

    fn Tag::to_string(self : Tag) -> String

    Render a tag using DICOM notation, for example (0010,0010).

    ValidationIssue

    pub(all) struct ValidationIssue {
    severity : ValidationSeverity
    code : String
    tag : Tag?
    message : String
    } derive(Eq,
    Debug
    )

    One structural or semantic validation finding.

    ValidationIssue::code

    fn ValidationIssue::code(self : ValidationIssue) -> String

    Return the stable machine-readable issue code.

    ValidationIssue::message

    fn ValidationIssue::message(self : ValidationIssue) -> String

    Return the human-readable issue message.

    ValidationIssue::severity

    Return the severity of an issue.

    ValidationIssue::tag

    fn ValidationIssue::tag(self : ValidationIssue) -> Tag?

    Return the related tag, when one exists.

    ValidationReport

    pub(all) struct ValidationReport {
    issues : Array[ValidationIssue]
    } derive(Eq,
    Debug
    )

    The complete validation result for a data set.

    ValidationReport::error_count

    fn ValidationReport::error_count(self : ValidationReport) -> Int

    Return the number of errors.

    ValidationReport::issues

    Return all findings in insertion order.

    ValidationReport::warning_count

    fn ValidationReport::warning_count(self : ValidationReport) -> Int

    Return the number of warnings.

    ValidationSeverity

    pub(all) enum ValidationSeverity {
    Error
    Warning
    } derive(Eq,
    Debug
    )

    Severity of a validation issue.

    Value

    pub(all) enum Value {
    Text(String)
    Integer(Int)
    Bytes(Bytes)
    } derive(Eq,
    Debug
    )

    A decoded DICOM element value.

    ValueRepresentation

    pub(all) enum ValueRepresentation {
    AE
    AS
    AT
    CS
    DA
    DS
    DT
    FD
    FL
    IS
    LO
    LT
    OB
    OD
    OF
    OL
    OV
    OW
    PN
    SH
    SL
    SQ
    SS
    ST
    SV
    TM
    UC
    UI
    UL
    UN
    UR
    US
    UT
    UV
    } derive(Eq,
    Debug
    )

    DICOM Value Representation.

    ValueRepresentation::is_text

    fn ValueRepresentation::is_text(self : ValueRepresentation) -> Bool

    Whether this VR is represented as text in the public value model.

    ValueRepresentation::to_string

    fn ValueRepresentation::to_string(self : ValueRepresentation) -> String

    Render a VR as its DICOM two-byte code.

    ValueRepresentation::uses_long_header

    fn ValueRepresentation::uses_long_header(self : ValueRepresentation) -> Bool

    Whether a VR uses the explicit VR long-length header form.

    accession_number

    fn accession_number() -> Tag

    anonymization_policy

    fn anonymization_policy() -> AnonymizationPolicy

    Create an empty policy.

    anonymize

    fn anonymize(dataset : DataSet, policy : AnonymizationPolicy) -> AnonymizationResult

    Apply a policy without modifying the source data set.

    bits_allocated

    fn bits_allocated() -> Tag

    bits_stored

    fn bits_stored() -> Tag

    byte_reader

    fn byte_reader(bytes : Bytes) -> ByteReader

    Create a reader positioned at the first byte.

    columns

    fn columns() -> Tag

    data_element

    fn data_element(tag : Tag, vr : ValueRepresentation, value : Value) -> DataElement

    Construct a data element.

    data_set

    fn data_set() -> DataSet

    Create an empty data set.

    dataset_to_json

    fn dataset_to_json(dataset : DataSet) -> Json

    Convert a data set to DICOMweb-style JSON.

    default_anonymization_policy

    fn default_anonymization_policy() -> AnonymizationPolicy

    Return a conservative policy for common identifying metadata.

    dictionary_vr

    fn dictionary_vr(tag : Tag) -> String

    Expected VR for common tags. Unknown implicit-VR tags use UN.

    file_meta_group_length

    fn file_meta_group_length() -> Tag

    Commonly used DICOM tags.

    file_meta_information_version

    fn file_meta_information_version() -> Tag

    high_bit

    fn high_bit() -> Tag

    implementation_class_uid

    fn implementation_class_uid() -> Tag

    implementation_version_name

    fn implementation_version_name() -> Tag

    institution_name

    fn institution_name() -> Tag

    media_storage_sop_class_uid

    fn media_storage_sop_class_uid() -> Tag

    media_storage_sop_instance_uid

    fn media_storage_sop_instance_uid() -> Tag

    modality

    fn modality() -> Tag

    parse

    fn parse(bytes : Bytes) -> DataSet raise ParseError

    Parse a DICOM Part 10 byte sequence into its data set.

    patient_birth_date

    fn patient_birth_date() -> Tag

    patient_id

    fn patient_id() -> Tag

    patient_name

    fn patient_name() -> Tag

    patient_sex

    fn patient_sex() -> Tag

    pixel_data

    fn pixel_data() -> Tag

    pixel_representation

    fn pixel_representation() -> Tag

    pixel_spacing

    fn pixel_spacing() -> Tag

    referring_physician_name

    fn referring_physician_name() -> Tag

    rows

    fn rows() -> Tag

    samples_per_pixel

    fn samples_per_pixel() -> Tag

    series_description

    fn series_description() -> Tag

    series_instance_uid

    fn series_instance_uid() -> Tag

    sop_class_uid

    fn sop_class_uid() -> Tag

    sop_instance_uid

    fn sop_instance_uid() -> Tag

    specific_character_set

    fn specific_character_set() -> Tag

    study_date

    fn study_date() -> Tag

    study_description

    fn study_description() -> Tag

    study_instance_uid

    fn study_instance_uid() -> Tag

    study_time

    fn study_time() -> Tag

    tag

    fn tag(group : Int, element : Int) -> Tag

    Construct a DICOM tag from group and element numbers.

    tag_name

    fn tag_name(tag : Tag) -> String

    Human-readable name for a common tag.

    transfer_syntax_uid

    fn transfer_syntax_uid() -> Tag

    validate

    fn validate(dataset : DataSet) -> ValidationReport

    Validate a DICOM data set without modifying it.

    value_representation

    fn value_representation(code : String, offset~ : Int) -> ValueRepresentation raise ParseError

    Parse a two-byte VR code.

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io