PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
kcenon::pacs::services::validation::nm_iod_validator Class Reference

#include <nm_iod_validator.h>

Collaboration diagram for kcenon::pacs::services::validation::nm_iod_validator:
Collaboration graph

Public Member Functions

 nm_iod_validator ()=default
 Construct validator with default options.
 
 nm_iod_validator (const nm_validation_options &options)
 Construct validator with custom options.
 
validation_result validate (const core::dicom_dataset &dataset) const
 Validate a DICOM dataset against NM IOD.
 
validation_result validate_multiframe (const core::dicom_dataset &dataset) const
 Validate a multi-frame NM dataset.
 
bool quick_check (const core::dicom_dataset &dataset) const
 Quick check if dataset has minimum required attributes.
 
const nm_validation_optionsoptions () const noexcept
 Get the validation options.
 
void set_options (const nm_validation_options &options)
 Set validation options.
 

Private Member Functions

void validate_patient_module (const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
 
void validate_general_study_module (const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
 
void validate_general_series_module (const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
 
void validate_nm_series_module (const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
 
void validate_nm_image_module (const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
 
void validate_nm_isotope_module (const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
 
void validate_nm_detector_module (const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
 
void validate_nm_tomo_module (const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
 
void validate_nm_gated_module (const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
 
void validate_image_pixel_module (const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
 
void validate_multiframe_module (const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
 
void validate_sop_common_module (const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
 
void validate_energy_window_info (const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
 
void validate_radiopharmaceutical_info (const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
 
void check_type1_attribute (const core::dicom_dataset &dataset, core::dicom_tag tag, std::string_view name, std::vector< validation_finding > &findings) const
 
void check_type2_attribute (const core::dicom_dataset &dataset, core::dicom_tag tag, std::string_view name, std::vector< validation_finding > &findings) const
 
void check_modality (const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
 
void check_pixel_data_consistency (const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
 

Private Attributes

nm_validation_options options_
 

Detailed Description

Definition at line 113 of file nm_iod_validator.h.

Constructor & Destructor Documentation

◆ nm_iod_validator() [1/2]

kcenon::pacs::services::validation::nm_iod_validator::nm_iod_validator ( )
default

◆ nm_iod_validator() [2/2]

kcenon::pacs::services::validation::nm_iod_validator::nm_iod_validator ( const nm_validation_options & options)
explicit

Construct validator with custom options.

Parameters
optionsValidation options

Definition at line 56 of file nm_iod_validator.cpp.

Member Function Documentation

◆ check_modality()

void kcenon::pacs::services::validation::nm_iod_validator::check_modality ( const core::dicom_dataset & dataset,
std::vector< validation_finding > & findings ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 595 of file nm_iod_validator.cpp.

597 {
598
599 if (!dataset.contains(tags::modality)) {
600 return; // Already reported as Type 1 missing
601 }
602
603 auto modality = dataset.get_string(tags::modality);
604 if (modality != "NM") {
605 findings.push_back({
608 "Modality must be 'NM' for nuclear medicine images, found: " + modality,
609 "NM-ERR-002"
610 });
611 }
612}
constexpr dicom_tag modality
Modality.

References kcenon::pacs::core::dicom_dataset::contains(), kcenon::pacs::services::validation::error, kcenon::pacs::core::dicom_dataset::get_string(), and kcenon::pacs::core::tags::modality.

Referenced by validate_general_series_module().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ check_pixel_data_consistency()

void kcenon::pacs::services::validation::nm_iod_validator::check_pixel_data_consistency ( const core::dicom_dataset & dataset,
std::vector< validation_finding > & findings ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 614 of file nm_iod_validator.cpp.

616 {
617
618 // Check BitsStored <= BitsAllocated
619 auto bits_allocated = dataset.get_numeric<uint16_t>(tags::bits_allocated);
620 auto bits_stored = dataset.get_numeric<uint16_t>(tags::bits_stored);
621 auto high_bit = dataset.get_numeric<uint16_t>(tags::high_bit);
622
623 if (bits_allocated && bits_stored) {
624 if (*bits_stored > *bits_allocated) {
625 findings.push_back({
628 "BitsStored (" + std::to_string(*bits_stored) +
629 ") exceeds BitsAllocated (" + std::to_string(*bits_allocated) + ")",
630 "NM-ERR-003"
631 });
632 }
633 }
634
635 // Check HighBit == BitsStored - 1
636 if (bits_stored && high_bit) {
637 if (*high_bit != *bits_stored - 1) {
638 findings.push_back({
641 "HighBit (" + std::to_string(*high_bit) +
642 ") should typically be BitsStored - 1 (" +
643 std::to_string(*bits_stored - 1) + ")",
644 "NM-WARN-009"
645 });
646 }
647 }
648
649 // Check photometric interpretation is valid for NM
650 if (dataset.contains(tags::photometric_interpretation)) {
651 auto photometric = dataset.get_string(tags::photometric_interpretation);
652 if (!sop_classes::is_valid_nm_photometric(photometric)) {
653 findings.push_back({
656 "Unusual photometric interpretation for NM: " + photometric +
657 " (expected MONOCHROME2 or PALETTE COLOR)",
658 "NM-WARN-010"
659 });
660 }
661 }
662
663 // NM images should be grayscale (SamplesPerPixel = 1)
664 auto samples = dataset.get_numeric<uint16_t>(tags::samples_per_pixel);
665 if (samples && *samples != 1) {
666 findings.push_back({
669 "NM images should have SamplesPerPixel = 1, found: " +
670 std::to_string(*samples),
671 "NM-WARN-011"
672 });
673 }
674}
constexpr dicom_tag high_bit
High Bit.
constexpr dicom_tag bits_allocated
Bits Allocated.
constexpr dicom_tag bits_stored
Bits Stored.
constexpr dicom_tag samples_per_pixel
Samples per Pixel.
constexpr dicom_tag photometric_interpretation
Photometric Interpretation.
bool is_valid_nm_photometric(std::string_view value) noexcept
Check if photometric interpretation is valid for NM.
@ warning
Non-critical - IOD may have issues.

References kcenon::pacs::core::tags::bits_allocated, kcenon::pacs::core::tags::bits_stored, kcenon::pacs::core::dicom_dataset::contains(), kcenon::pacs::services::validation::error, kcenon::pacs::core::dicom_dataset::get_numeric(), kcenon::pacs::core::dicom_dataset::get_string(), kcenon::pacs::core::tags::high_bit, kcenon::pacs::services::sop_classes::is_valid_nm_photometric(), kcenon::pacs::core::tags::photometric_interpretation, kcenon::pacs::core::tags::samples_per_pixel, and kcenon::pacs::services::validation::warning.

Referenced by validate_image_pixel_module().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ check_type1_attribute()

void kcenon::pacs::services::validation::nm_iod_validator::check_type1_attribute ( const core::dicom_dataset & dataset,
core::dicom_tag tag,
std::string_view name,
std::vector< validation_finding > & findings ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 548 of file nm_iod_validator.cpp.

552 {
553
554 if (!dataset.contains(tag)) {
555 findings.push_back({
557 tag,
558 std::string("Type 1 attribute missing: ") + std::string(name) +
559 " (" + tag.to_string() + ")",
560 "NM-TYPE1-MISSING"
561 });
562 } else {
563 // Type 1 must have a value (cannot be empty)
564 auto value = dataset.get_string(tag);
565 if (value.empty()) {
566 findings.push_back({
568 tag,
569 std::string("Type 1 attribute has empty value: ") +
570 std::string(name) + " (" + tag.to_string() + ")",
571 "NM-TYPE1-EMPTY"
572 });
573 }
574 }
575}
std::string_view name

References kcenon::pacs::core::dicom_dataset::contains(), kcenon::pacs::services::validation::error, kcenon::pacs::core::dicom_dataset::get_string(), name, and kcenon::pacs::core::dicom_tag::to_string().

Referenced by validate_general_series_module(), validate_general_study_module(), validate_image_pixel_module(), validate_multiframe_module(), validate_nm_image_module(), validate_nm_series_module(), and validate_sop_common_module().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ check_type2_attribute()

void kcenon::pacs::services::validation::nm_iod_validator::check_type2_attribute ( const core::dicom_dataset & dataset,
core::dicom_tag tag,
std::string_view name,
std::vector< validation_finding > & findings ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 577 of file nm_iod_validator.cpp.

581 {
582
583 // Type 2 must be present but can be empty
584 if (!dataset.contains(tag)) {
585 findings.push_back({
587 tag,
588 std::string("Type 2 attribute missing: ") + std::string(name) +
589 " (" + tag.to_string() + ")",
590 "NM-TYPE2-MISSING"
591 });
592 }
593}

References kcenon::pacs::core::dicom_dataset::contains(), name, kcenon::pacs::core::dicom_tag::to_string(), and kcenon::pacs::services::validation::warning.

Referenced by validate_general_series_module(), validate_general_study_module(), validate_nm_detector_module(), validate_nm_gated_module(), validate_nm_isotope_module(), validate_nm_tomo_module(), and validate_patient_module().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ options()

const nm_validation_options & kcenon::pacs::services::validation::nm_iod_validator::options ( ) const
nodiscardnoexcept

Get the validation options.

Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 166 of file nm_iod_validator.cpp.

166 {
167 return options_;
168}

References options_.

Referenced by set_options().

Here is the caller graph for this function:

◆ quick_check()

bool kcenon::pacs::services::validation::nm_iod_validator::quick_check ( const core::dicom_dataset & dataset) const
nodiscard

Quick check if dataset has minimum required attributes.

Faster than full validation - only checks Type 1 attributes.

Parameters
datasetThe dataset to check
Returns
true if all Type 1 attributes are present
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 134 of file nm_iod_validator.cpp.

134 {
135 // Check only Type 1 required attributes for quick validation
136
137 // General Study Module Type 1
138 if (!dataset.contains(tags::study_instance_uid)) return false;
139
140 // General Series Module Type 1
141 if (!dataset.contains(tags::modality)) return false;
142 if (!dataset.contains(tags::series_instance_uid)) return false;
143
144 // Check modality is NM
145 auto modality = dataset.get_string(tags::modality);
146 if (modality != "NM") return false;
147
148 // Image Pixel Module Type 1
149 if (!dataset.contains(tags::samples_per_pixel)) return false;
150 if (!dataset.contains(tags::photometric_interpretation)) return false;
151 if (!dataset.contains(tags::rows)) return false;
152 if (!dataset.contains(tags::columns)) return false;
153 if (!dataset.contains(tags::bits_allocated)) return false;
154 if (!dataset.contains(tags::bits_stored)) return false;
155 if (!dataset.contains(tags::high_bit)) return false;
156 if (!dataset.contains(tags::pixel_representation)) return false;
157 if (!dataset.contains(tags::pixel_data)) return false;
158
159 // SOP Common Module Type 1
160 if (!dataset.contains(tags::sop_class_uid)) return false;
161 if (!dataset.contains(tags::sop_instance_uid)) return false;
162
163 return true;
164}
constexpr dicom_tag rows
Rows.
constexpr dicom_tag columns
Columns.
constexpr dicom_tag sop_instance_uid
SOP Instance UID.
constexpr dicom_tag pixel_data
Pixel Data.
constexpr dicom_tag pixel_representation
Pixel Representation.
constexpr dicom_tag study_instance_uid
Study Instance UID.
constexpr dicom_tag sop_class_uid
SOP Class UID.
constexpr dicom_tag series_instance_uid
Series Instance UID.

References kcenon::pacs::core::tags::bits_allocated, kcenon::pacs::core::tags::bits_stored, kcenon::pacs::core::tags::columns, kcenon::pacs::core::dicom_dataset::contains(), kcenon::pacs::core::dicom_dataset::get_string(), kcenon::pacs::core::tags::high_bit, kcenon::pacs::core::tags::modality, kcenon::pacs::core::tags::photometric_interpretation, kcenon::pacs::core::tags::pixel_data, kcenon::pacs::core::tags::pixel_representation, kcenon::pacs::core::tags::rows, kcenon::pacs::core::tags::samples_per_pixel, kcenon::pacs::core::tags::series_instance_uid, kcenon::pacs::core::tags::sop_class_uid, kcenon::pacs::core::tags::sop_instance_uid, and kcenon::pacs::core::tags::study_instance_uid.

Referenced by kcenon::pacs::services::validation::is_valid_nm_dataset().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_options()

void kcenon::pacs::services::validation::nm_iod_validator::set_options ( const nm_validation_options & options)

Set validation options.

Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 170 of file nm_iod_validator.cpp.

170 {
172}
const nm_validation_options & options() const noexcept
Get the validation options.

References options(), and options_.

Here is the call graph for this function:

◆ validate()

validation_result kcenon::pacs::services::validation::nm_iod_validator::validate ( const core::dicom_dataset & dataset) const
nodiscard

Validate a DICOM dataset against NM IOD.

Parameters
datasetThe dataset to validate
Returns
Validation result with all findings
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 59 of file nm_iod_validator.cpp.

59 {
60 validation_result result;
61 result.is_valid = true;
62
63 // Validate mandatory modules
65 validate_patient_module(dataset, result.findings);
66 validate_general_study_module(dataset, result.findings);
67 validate_general_series_module(dataset, result.findings);
68 validate_nm_series_module(dataset, result.findings);
69 validate_nm_image_module(dataset, result.findings);
70 validate_sop_common_module(dataset, result.findings);
71 }
72
74 validate_image_pixel_module(dataset, result.findings);
75 }
76
77 // NM-specific validation
80 validate_energy_window_info(dataset, result.findings);
81 }
83 validate_nm_isotope_module(dataset, result.findings);
84 validate_radiopharmaceutical_info(dataset, result.findings);
85 }
86 validate_nm_detector_module(dataset, result.findings);
87 }
88
89 // Check for errors
90 for (const auto& finding : result.findings) {
91 if (finding.severity == validation_severity::error) {
92 result.is_valid = false;
93 break;
94 }
95 if (options_.strict_mode && finding.severity == validation_severity::warning) {
96 result.is_valid = false;
97 break;
98 }
99 }
100
101 return result;
102}
void validate_energy_window_info(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void validate_general_series_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void validate_general_study_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void validate_nm_isotope_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void validate_nm_detector_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void validate_patient_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void validate_nm_series_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void validate_image_pixel_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void validate_nm_image_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void validate_radiopharmaceutical_info(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void validate_sop_common_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
bool validate_pixel_data
Validate pixel data consistency (rows, columns, bits)
bool validate_energy_windows
Validate energy window information.
bool validate_nm_specific
Validate NM-specific attributes (detector, collimator, etc.)
bool check_type2
Check Type 2 (required, can be empty) attributes.
bool check_type1
Check Type 1 (required) attributes.
bool strict_mode
Strict mode - treat warnings as errors.

References kcenon::pacs::services::validation::nm_validation_options::check_type1, kcenon::pacs::services::validation::nm_validation_options::check_type2, kcenon::pacs::services::validation::error, kcenon::pacs::services::validation::validation_result::findings, kcenon::pacs::services::validation::validation_result::is_valid, options_, kcenon::pacs::services::validation::nm_validation_options::strict_mode, validate_energy_window_info(), kcenon::pacs::services::validation::nm_validation_options::validate_energy_windows, validate_general_series_module(), validate_general_study_module(), validate_image_pixel_module(), kcenon::pacs::services::validation::nm_validation_options::validate_isotope, validate_nm_detector_module(), validate_nm_image_module(), validate_nm_isotope_module(), validate_nm_series_module(), kcenon::pacs::services::validation::nm_validation_options::validate_nm_specific, validate_patient_module(), kcenon::pacs::services::validation::nm_validation_options::validate_pixel_data, validate_radiopharmaceutical_info(), validate_sop_common_module(), and kcenon::pacs::services::validation::warning.

Referenced by validate_multiframe(), and kcenon::pacs::services::validation::validate_nm_iod().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ validate_energy_window_info()

void kcenon::pacs::services::validation::nm_iod_validator::validate_energy_window_info ( const core::dicom_dataset & dataset,
std::vector< validation_finding > & findings ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 485 of file nm_iod_validator.cpp.

487 {
488
489 // Energy Window Information Sequence is Type 1
490 if (!dataset.contains(nm_tags::energy_window_info_sequence)) {
491 // Already reported as Type 1 missing
492 return;
493 }
494
495 // Energy Window Range Sequence should be present
496 if (!dataset.contains(nm_tags::energy_window_range_sequence)) {
497 findings.push_back({
500 "EnergyWindowRangeSequence not present - energy window details missing",
501 "NM-WARN-008"
502 });
503 }
504}

References kcenon::pacs::core::dicom_dataset::contains(), kcenon::pacs::services::validation::nm_tags::energy_window_info_sequence, kcenon::pacs::services::validation::nm_tags::energy_window_range_sequence, and kcenon::pacs::services::validation::warning.

Referenced by validate().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ validate_general_series_module()

void kcenon::pacs::services::validation::nm_iod_validator::validate_general_series_module ( const core::dicom_dataset & dataset,
std::vector< validation_finding > & findings ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 210 of file nm_iod_validator.cpp.

212 {
213
214 // Type 1
215 if (options_.check_type1) {
216 check_type1_attribute(dataset, tags::modality, "Modality", findings);
217 check_type1_attribute(dataset, tags::series_instance_uid, "SeriesInstanceUID", findings);
218
219 // Special check: Modality must be "NM"
220 check_modality(dataset, findings);
221 }
222
223 // Type 2
224 if (options_.check_type2) {
225 check_type2_attribute(dataset, tags::series_number, "SeriesNumber", findings);
226 }
227}
void check_type1_attribute(const core::dicom_dataset &dataset, core::dicom_tag tag, std::string_view name, std::vector< validation_finding > &findings) const
void check_modality(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void check_type2_attribute(const core::dicom_dataset &dataset, core::dicom_tag tag, std::string_view name, std::vector< validation_finding > &findings) const
constexpr dicom_tag series_number
Series Number.

References check_modality(), kcenon::pacs::services::validation::nm_validation_options::check_type1, check_type1_attribute(), kcenon::pacs::services::validation::nm_validation_options::check_type2, check_type2_attribute(), kcenon::pacs::core::tags::modality, options_, kcenon::pacs::core::tags::series_instance_uid, and kcenon::pacs::core::tags::series_number.

Referenced by validate().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ validate_general_study_module()

void kcenon::pacs::services::validation::nm_iod_validator::validate_general_study_module ( const core::dicom_dataset & dataset,
std::vector< validation_finding > & findings ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 191 of file nm_iod_validator.cpp.

193 {
194
195 // Type 1
196 if (options_.check_type1) {
197 check_type1_attribute(dataset, tags::study_instance_uid, "StudyInstanceUID", findings);
198 }
199
200 // Type 2
201 if (options_.check_type2) {
202 check_type2_attribute(dataset, tags::study_date, "StudyDate", findings);
203 check_type2_attribute(dataset, tags::study_time, "StudyTime", findings);
204 check_type2_attribute(dataset, tags::referring_physician_name, "ReferringPhysicianName", findings);
205 check_type2_attribute(dataset, tags::study_id, "StudyID", findings);
206 check_type2_attribute(dataset, tags::accession_number, "AccessionNumber", findings);
207 }
208}
constexpr dicom_tag referring_physician_name
Referring Physician's Name.
constexpr dicom_tag accession_number
Accession Number.
constexpr dicom_tag study_time
Study Time.
constexpr dicom_tag study_id
Study ID.
constexpr dicom_tag study_date
Study Date.

References kcenon::pacs::core::tags::accession_number, kcenon::pacs::services::validation::nm_validation_options::check_type1, check_type1_attribute(), kcenon::pacs::services::validation::nm_validation_options::check_type2, check_type2_attribute(), options_, kcenon::pacs::core::tags::referring_physician_name, kcenon::pacs::core::tags::study_date, kcenon::pacs::core::tags::study_id, kcenon::pacs::core::tags::study_instance_uid, and kcenon::pacs::core::tags::study_time.

Referenced by validate().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ validate_image_pixel_module()

void kcenon::pacs::services::validation::nm_iod_validator::validate_image_pixel_module ( const core::dicom_dataset & dataset,
std::vector< validation_finding > & findings ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 408 of file nm_iod_validator.cpp.

410 {
411
412 // Type 1 attributes
413 if (options_.check_type1) {
414 check_type1_attribute(dataset, tags::samples_per_pixel, "SamplesPerPixel", findings);
416 "PhotometricInterpretation", findings);
417 check_type1_attribute(dataset, tags::rows, "Rows", findings);
418 check_type1_attribute(dataset, tags::columns, "Columns", findings);
419 check_type1_attribute(dataset, tags::bits_allocated, "BitsAllocated", findings);
420 check_type1_attribute(dataset, tags::bits_stored, "BitsStored", findings);
421 check_type1_attribute(dataset, tags::high_bit, "HighBit", findings);
422 check_type1_attribute(dataset, tags::pixel_representation, "PixelRepresentation", findings);
423 check_type1_attribute(dataset, tags::pixel_data, "PixelData", findings);
424 }
425
426 // Validate pixel data consistency
428 check_pixel_data_consistency(dataset, findings);
429 }
430}
void check_pixel_data_consistency(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const

References kcenon::pacs::core::tags::bits_allocated, kcenon::pacs::core::tags::bits_stored, check_pixel_data_consistency(), kcenon::pacs::services::validation::nm_validation_options::check_type1, check_type1_attribute(), kcenon::pacs::core::tags::columns, kcenon::pacs::core::tags::high_bit, options_, kcenon::pacs::core::tags::photometric_interpretation, kcenon::pacs::core::tags::pixel_data, kcenon::pacs::core::tags::pixel_representation, kcenon::pacs::core::tags::rows, kcenon::pacs::core::tags::samples_per_pixel, and kcenon::pacs::services::validation::nm_validation_options::validate_pixel_data.

Referenced by validate().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ validate_multiframe()

validation_result kcenon::pacs::services::validation::nm_iod_validator::validate_multiframe ( const core::dicom_dataset & dataset) const
nodiscard

Validate a multi-frame NM dataset.

Performs additional validation for SPECT, dynamic, and gated acquisitions.

Parameters
datasetThe dataset to validate
Returns
Validation result with all findings
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 105 of file nm_iod_validator.cpp.

105 {
106 // First do standard validation
107 auto result = validate(dataset);
108
109 // Then add multi-frame specific validation
110 validate_multiframe_module(dataset, result.findings);
111
112 // Validate SPECT-specific if applicable
113 if (dataset.contains(nm_tags::type_of_data)) {
114 auto type = dataset.get_string(nm_tags::type_of_data);
115 if (type.find("TOMO") != std::string::npos) {
116 validate_nm_tomo_module(dataset, result.findings);
117 }
118 if (type.find("GATED") != std::string::npos) {
119 validate_nm_gated_module(dataset, result.findings);
120 }
121 }
122
123 // Re-check validity after multi-frame validation
124 for (const auto& finding : result.findings) {
125 if (finding.severity == validation_severity::error) {
126 result.is_valid = false;
127 break;
128 }
129 }
130
131 return result;
132}
validation_result validate(const core::dicom_dataset &dataset) const
Validate a DICOM dataset against NM IOD.
void validate_multiframe_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void validate_nm_tomo_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void validate_nm_gated_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const

References kcenon::pacs::core::dicom_dataset::contains(), kcenon::pacs::services::validation::error, kcenon::pacs::core::dicom_dataset::get_string(), kcenon::pacs::services::validation::nm_tags::type_of_data, validate(), validate_multiframe_module(), validate_nm_gated_module(), and validate_nm_tomo_module().

Here is the call graph for this function:

◆ validate_multiframe_module()

void kcenon::pacs::services::validation::nm_iod_validator::validate_multiframe_module ( const core::dicom_dataset & dataset,
std::vector< validation_finding > & findings ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 432 of file nm_iod_validator.cpp.

434 {
435
436 // NumberOfFrames should be present for multi-frame NM
437 if (options_.check_type1) {
438 check_type1_attribute(dataset, nm_tags::number_of_frames, "NumberOfFrames", findings);
439 }
440
441 // FrameIncrementPointer is Type 1C for multi-frame
443 auto num_frames = dataset.get_numeric<int32_t>(nm_tags::number_of_frames);
444 if (num_frames && *num_frames > 1) {
445 if (!dataset.contains(nm_tags::frame_increment_pointer)) {
446 findings.push_back({
449 "FrameIncrementPointer recommended for multi-frame NM images",
450 "NM-WARN-007"
451 });
452 }
453 }
454 }
455}
bool check_conditional
Check Type 1C/2C (conditionally required) attributes.

References kcenon::pacs::services::validation::nm_validation_options::check_conditional, kcenon::pacs::services::validation::nm_validation_options::check_type1, check_type1_attribute(), kcenon::pacs::core::dicom_dataset::contains(), kcenon::pacs::services::validation::nm_tags::frame_increment_pointer, kcenon::pacs::core::dicom_dataset::get_numeric(), kcenon::pacs::services::validation::nm_tags::number_of_frames, options_, and kcenon::pacs::services::validation::warning.

Referenced by validate_multiframe().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ validate_nm_detector_module()

void kcenon::pacs::services::validation::nm_iod_validator::validate_nm_detector_module ( const core::dicom_dataset & dataset,
std::vector< validation_finding > & findings ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 301 of file nm_iod_validator.cpp.

303 {
304
305 // Detector Information Sequence is Type 2
306 if (options_.check_type2) {
308 "DetectorInformationSequence", findings);
309 }
310
311 // Collimator Type is informational but important
312 if (!dataset.contains(nm_tags::collimator_type)) {
313 findings.push_back({
316 "CollimatorType not specified - helps with interpretation",
317 "NM-INFO-002"
318 });
319 } else {
320 auto collimator = dataset.get_string(nm_tags::collimator_type);
321 // Validate collimator type
322 if (collimator != "PARA" && collimator != "FANB" && collimator != "CONE" &&
323 collimator != "PINH" && collimator != "DIVG" && collimator != "NONE") {
324 findings.push_back({
327 "Non-standard CollimatorType: " + collimator,
328 "NM-WARN-002"
329 });
330 }
331 }
332}
@ info
Informational - suggestion for improvement.

References kcenon::pacs::services::validation::nm_validation_options::check_type2, check_type2_attribute(), kcenon::pacs::services::validation::nm_tags::collimator_type, kcenon::pacs::core::dicom_dataset::contains(), kcenon::pacs::services::validation::nm_tags::detector_info_sequence, kcenon::pacs::core::dicom_dataset::get_string(), kcenon::pacs::services::validation::info, options_, and kcenon::pacs::services::validation::warning.

Referenced by validate().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ validate_nm_gated_module()

void kcenon::pacs::services::validation::nm_iod_validator::validate_nm_gated_module ( const core::dicom_dataset & dataset,
std::vector< validation_finding > & findings ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 377 of file nm_iod_validator.cpp.

379 {
380
381 // Gated Information Sequence is Type 2 for gated acquisitions
382 if (options_.check_type2) {
384 "GatedInformationSequence", findings);
385 }
386
387 // Trigger Source for gated acquisition
388 if (!dataset.contains(nm_tags::trigger_source_or_type)) {
389 findings.push_back({
392 "TriggerSourceOrType not specified for gated acquisition",
393 "NM-INFO-003"
394 });
395 }
396
397 // Number of Time Slots is important for gated analysis
398 if (!dataset.contains(nm_tags::number_of_time_slots)) {
399 findings.push_back({
402 "NumberOfTimeSlots not specified for gated acquisition",
403 "NM-WARN-006"
404 });
405 }
406}

References kcenon::pacs::services::validation::nm_validation_options::check_type2, check_type2_attribute(), kcenon::pacs::core::dicom_dataset::contains(), kcenon::pacs::services::validation::nm_tags::gated_info_sequence, kcenon::pacs::services::validation::info, kcenon::pacs::services::validation::nm_tags::number_of_time_slots, options_, kcenon::pacs::services::validation::nm_tags::trigger_source_or_type, and kcenon::pacs::services::validation::warning.

Referenced by validate_multiframe().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ validate_nm_image_module()

void kcenon::pacs::services::validation::nm_iod_validator::validate_nm_image_module ( const core::dicom_dataset & dataset,
std::vector< validation_finding > & findings ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 255 of file nm_iod_validator.cpp.

257 {
258
259 // Type 1
260 if (options_.check_type1) {
261 check_type1_attribute(dataset, nm_tags::image_type, "ImageType", findings);
262 }
263
264 // NumberOfFrames is Type 1 for NM (often multi-frame)
265 if (options_.check_type1) {
266 check_type1_attribute(dataset, nm_tags::number_of_frames, "NumberOfFrames", findings);
267 }
268
269 // Validate ImageType format
270 if (dataset.contains(nm_tags::image_type)) {
271 auto image_type = dataset.get_string(nm_tags::image_type);
272 if (image_type.find('\\') == std::string::npos) {
273 findings.push_back({
276 "ImageType should contain backslash separators",
277 "NM-INFO-001"
278 });
279 }
280 }
281}
constexpr dicom_tag image_type
Image Type.

References kcenon::pacs::services::validation::nm_validation_options::check_type1, check_type1_attribute(), kcenon::pacs::core::dicom_dataset::contains(), kcenon::pacs::core::dicom_dataset::get_string(), kcenon::pacs::services::validation::nm_tags::image_type, kcenon::pacs::services::validation::info, kcenon::pacs::services::validation::nm_tags::number_of_frames, and options_.

Referenced by validate().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ validate_nm_isotope_module()

void kcenon::pacs::services::validation::nm_iod_validator::validate_nm_isotope_module ( const core::dicom_dataset & dataset,
std::vector< validation_finding > & findings ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 283 of file nm_iod_validator.cpp.

285 {
286
287 // Energy Window Information Sequence is Type 2 per DICOM standard
288 // (Required, but may be empty)
289 if (options_.check_type2) {
291 "EnergyWindowInformationSequence", findings);
292 }
293
294 // Radiopharmaceutical Information Sequence is Type 2
295 if (options_.check_type2) {
297 "RadiopharmaceuticalInformationSequence", findings);
298 }
299}

References kcenon::pacs::services::validation::nm_validation_options::check_type2, check_type2_attribute(), kcenon::pacs::services::validation::nm_tags::energy_window_info_sequence, options_, and kcenon::pacs::services::validation::nm_tags::radiopharmaceutical_info_sequence.

Referenced by validate().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ validate_nm_series_module()

void kcenon::pacs::services::validation::nm_iod_validator::validate_nm_series_module ( const core::dicom_dataset & dataset,
std::vector< validation_finding > & findings ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 229 of file nm_iod_validator.cpp.

231 {
232
233 // Type of Data is important for NM series classification
234 if (options_.check_type1) {
235 check_type1_attribute(dataset, nm_tags::type_of_data, "TypeOfData", findings);
236 }
237
238 // Validate TypeOfData value
239 if (dataset.contains(nm_tags::type_of_data)) {
240 auto type = dataset.get_string(nm_tags::type_of_data);
241 // Valid values: STATIC, DYNAMIC, GATED, WHOLE BODY, TOMO, GATED TOMO, RECON TOMO, RECON GATED TOMO
242 if (type != "STATIC" && type != "DYNAMIC" && type != "GATED" &&
243 type != "WHOLE BODY" && type != "TOMO" && type != "GATED TOMO" &&
244 type != "RECON TOMO" && type != "RECON GATED TOMO") {
245 findings.push_back({
248 "Unusual TypeOfData value for NM: " + type,
249 "NM-WARN-001"
250 });
251 }
252 }
253}

References kcenon::pacs::services::validation::nm_validation_options::check_type1, check_type1_attribute(), kcenon::pacs::core::dicom_dataset::contains(), kcenon::pacs::core::dicom_dataset::get_string(), options_, kcenon::pacs::services::validation::nm_tags::type_of_data, and kcenon::pacs::services::validation::warning.

Referenced by validate().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ validate_nm_tomo_module()

void kcenon::pacs::services::validation::nm_iod_validator::validate_nm_tomo_module ( const core::dicom_dataset & dataset,
std::vector< validation_finding > & findings ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 334 of file nm_iod_validator.cpp.

336 {
337
338 // Rotation Information Sequence is Type 2 for SPECT
339 if (options_.check_type2) {
341 "RotationInformationSequence", findings);
342 }
343
344 // Start Angle and Scan Arc are important for SPECT reconstruction
345 if (!dataset.contains(nm_tags::start_angle)) {
346 findings.push_back({
349 "StartAngle not specified for SPECT acquisition",
350 "NM-WARN-003"
351 });
352 }
353
354 if (!dataset.contains(nm_tags::scan_arc)) {
355 findings.push_back({
358 "ScanArc not specified for SPECT acquisition",
359 "NM-WARN-004"
360 });
361 }
362
363 // Rotation Direction
364 if (dataset.contains(nm_tags::rotation_direction)) {
365 auto direction = dataset.get_string(nm_tags::rotation_direction);
366 if (direction != "CW" && direction != "CC") {
367 findings.push_back({
370 "Invalid RotationDirection: " + direction + " (expected CW or CC)",
371 "NM-WARN-005"
372 });
373 }
374 }
375}

References kcenon::pacs::services::validation::nm_validation_options::check_type2, check_type2_attribute(), kcenon::pacs::core::dicom_dataset::contains(), kcenon::pacs::core::dicom_dataset::get_string(), options_, kcenon::pacs::services::validation::nm_tags::rotation_direction, kcenon::pacs::services::validation::nm_tags::rotation_info_sequence, kcenon::pacs::services::validation::nm_tags::scan_arc, kcenon::pacs::services::validation::nm_tags::start_angle, and kcenon::pacs::services::validation::warning.

Referenced by validate_multiframe().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ validate_patient_module()

void kcenon::pacs::services::validation::nm_iod_validator::validate_patient_module ( const core::dicom_dataset & dataset,
std::vector< validation_finding > & findings ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 178 of file nm_iod_validator.cpp.

180 {
181
182 // Patient Module - All attributes are Type 2
183 if (options_.check_type2) {
184 check_type2_attribute(dataset, tags::patient_name, "PatientName", findings);
185 check_type2_attribute(dataset, tags::patient_id, "PatientID", findings);
186 check_type2_attribute(dataset, tags::patient_birth_date, "PatientBirthDate", findings);
187 check_type2_attribute(dataset, tags::patient_sex, "PatientSex", findings);
188 }
189}
constexpr dicom_tag patient_id
Patient ID.
constexpr dicom_tag patient_birth_date
Patient's Birth Date.
constexpr dicom_tag patient_sex
Patient's Sex.
constexpr dicom_tag patient_name
Patient's Name.

References kcenon::pacs::services::validation::nm_validation_options::check_type2, check_type2_attribute(), options_, kcenon::pacs::core::tags::patient_birth_date, kcenon::pacs::core::tags::patient_id, kcenon::pacs::core::tags::patient_name, and kcenon::pacs::core::tags::patient_sex.

Referenced by validate().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ validate_radiopharmaceutical_info()

void kcenon::pacs::services::validation::nm_iod_validator::validate_radiopharmaceutical_info ( const core::dicom_dataset & dataset,
std::vector< validation_finding > & findings ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 506 of file nm_iod_validator.cpp.

508 {
509
510 // If sequence is present, validate its contents
511 if (dataset.contains(nm_tags::radiopharmaceutical_info_sequence)) {
512 // Radionuclide Total Dose is important for quantitative analysis
513 if (!dataset.contains(nm_tags::radionuclide_total_dose)) {
514 findings.push_back({
517 "RadionuclideTotalDose not specified",
518 "NM-INFO-004"
519 });
520 }
521
522 // Radionuclide Half Life is important for decay correction
523 if (!dataset.contains(nm_tags::radionuclide_half_life)) {
524 findings.push_back({
527 "RadionuclideHalfLife not specified",
528 "NM-INFO-005"
529 });
530 }
531
532 // Radiopharmaceutical Start Time for timing calculations
533 if (!dataset.contains(nm_tags::radiopharmaceutical_start_time)) {
534 findings.push_back({
537 "RadiopharmaceuticalStartTime not specified",
538 "NM-INFO-006"
539 });
540 }
541 }
542}

References kcenon::pacs::core::dicom_dataset::contains(), kcenon::pacs::services::validation::info, kcenon::pacs::services::validation::nm_tags::radionuclide_half_life, kcenon::pacs::services::validation::nm_tags::radionuclide_total_dose, kcenon::pacs::services::validation::nm_tags::radiopharmaceutical_info_sequence, and kcenon::pacs::services::validation::nm_tags::radiopharmaceutical_start_time.

Referenced by validate().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ validate_sop_common_module()

void kcenon::pacs::services::validation::nm_iod_validator::validate_sop_common_module ( const core::dicom_dataset & dataset,
std::vector< validation_finding > & findings ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/services/validation/nm_iod_validator.h.

Definition at line 457 of file nm_iod_validator.cpp.

459 {
460
461 // Type 1
462 if (options_.check_type1) {
463 check_type1_attribute(dataset, tags::sop_class_uid, "SOPClassUID", findings);
464 check_type1_attribute(dataset, tags::sop_instance_uid, "SOPInstanceUID", findings);
465 }
466
467 // Validate SOP Class UID is a NM storage class
468 if (dataset.contains(tags::sop_class_uid)) {
469 auto sop_class = dataset.get_string(tags::sop_class_uid);
470 if (!sop_classes::is_nm_storage_sop_class(sop_class)) {
471 findings.push_back({
474 "SOPClassUID is not a recognized NM Storage SOP Class: " + sop_class,
475 "NM-ERR-001"
476 });
477 }
478 }
479}
bool is_nm_storage_sop_class(std::string_view uid) noexcept
Check if a SOP Class UID is a NM Storage SOP Class.

References kcenon::pacs::services::validation::nm_validation_options::check_type1, check_type1_attribute(), kcenon::pacs::core::dicom_dataset::contains(), kcenon::pacs::services::validation::error, kcenon::pacs::core::dicom_dataset::get_string(), kcenon::pacs::services::sop_classes::is_nm_storage_sop_class(), options_, kcenon::pacs::core::tags::sop_class_uid, and kcenon::pacs::core::tags::sop_instance_uid.

Referenced by validate().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ options_


The documentation for this class was generated from the following files: