67 : options_(options) {}
96 for (
const auto& finding : result.
findings) {
121 if (intent !=
"FOR PRESENTATION") {
122 result.findings.push_back({
125 "Presentation Intent Type should be 'FOR PRESENTATION' "
126 "for this SOP Class, found: " + intent,
134 for (
const auto& finding : result.findings) {
136 result.is_valid =
false;
153 if (intent !=
"FOR PROCESSING") {
154 result.findings.push_back({
157 "Presentation Intent Type should be 'FOR PROCESSING' "
158 "for this SOP Class, found: " + intent,
167 if (relationship !=
"LIN") {
168 result.findings.push_back({
171 "For Processing images typically have linear (LIN) pixel "
172 "intensity relationship, found: " + relationship,
180 for (
const auto& finding : result.findings) {
182 result.is_valid =
false;
202 if (modality !=
"DX")
return false;
236 std::vector<validation_finding>& findings)
const {
249 std::vector<validation_finding>& findings)
const {
268 std::vector<validation_finding>& findings)
const {
287 std::vector<validation_finding>& findings)
const {
302 "Neither Body Part Examined nor Anatomic Region Sequence present - "
303 "anatomy information may be insufficient for clinical use",
312 std::vector<validation_finding>& findings)
const {
322 if (image_type.find(
"ORIGINAL") == std::string::npos &&
323 image_type.find(
"DERIVED") == std::string::npos) {
327 "Image Type first value should be ORIGINAL or DERIVED",
332 if (image_type.find(
"PRIMARY") == std::string::npos &&
333 image_type.find(
"SECONDARY") == std::string::npos) {
337 "Image Type second value should be PRIMARY or SECONDARY",
350 "PixelIntensityRelationship", findings);
354 if (relationship !=
"LIN" && relationship !=
"LOG") {
358 "Pixel Intensity Relationship must be LIN or LOG, found: " + relationship,
368 "PixelIntensityRelationshipSign", findings);
372 if (sign && *sign != 1 && *sign != -1) {
376 "Pixel Intensity Relationship Sign must be 1 or -1, found: " +
377 std::to_string(*sign),
387 std::vector<validation_finding>& findings)
const {
395 if (type !=
"DIRECT" && type !=
"SCINTILLATOR" &&
396 type !=
"STORAGE" && type !=
"FILM") {
400 "Unusual Detector Type for DX: " + type,
410 "ImagerPixelSpacing", findings);
416 std::vector<validation_finding>& findings)
const {
426 "Neither Patient Orientation nor View Position present - "
427 "image orientation may be unclear",
436 if (orientation.empty()) {
440 "Patient Orientation is present but empty",
452 std::vector<validation_finding>& findings)
const {
476 std::vector<validation_finding>& findings)
const {
483 constexpr dicom_tag voi_lut_sequence{0x0028, 0x3010};
484 bool has_voi_lut = dataset.
contains(voi_lut_sequence);
486 if (!has_window && !has_voi_lut) {
490 "For Presentation images should have Window Center/Width or VOI LUT Sequence "
491 "for proper display",
500 std::vector<validation_finding>& findings)
const {
515 "SOPClassUID is not a recognized DX Storage SOP Class: " + sop_class,
529 std::string_view
name,
530 std::vector<validation_finding>& findings)
const {
536 std::string(
"Type 1 attribute missing: ") + std::string(
name) +
547 std::string(
"Type 1 attribute has empty value: ") +
558 std::string_view
name,
559 std::vector<validation_finding>& findings)
const {
566 std::string(
"Type 2 attribute missing: ") + std::string(
name) +
575 std::vector<validation_finding>& findings)
const {
582 if (modality !=
"DX") {
586 "Modality must be 'DX' for digital X-ray images, found: " + modality,
594 std::vector<validation_finding>& findings)
const {
601 if (bits_allocated && bits_stored) {
602 if (*bits_stored > *bits_allocated) {
606 "BitsStored (" + std::to_string(*bits_stored) +
607 ") exceeds BitsAllocated (" + std::to_string(*bits_allocated) +
")",
613 if (*bits_stored < 10 || *bits_stored > 16) {
617 "DX images typically use 10-16 bits, found: " +
618 std::to_string(*bits_stored),
625 if (bits_stored && high_bit) {
626 if (*high_bit != *bits_stored - 1) {
630 "HighBit (" + std::to_string(*high_bit) +
631 ") should typically be BitsStored - 1 (" +
632 std::to_string(*bits_stored - 1) +
")",
640 if (samples && *samples != 1) {
644 "DX images must be grayscale (SamplesPerPixel = 1), found: " +
645 std::to_string(*samples),
653 std::vector<validation_finding>& findings)
const {
664 "DX images must use MONOCHROME1 or MONOCHROME2, found: " + photometric,
auto get_numeric(dicom_tag tag) const -> std::optional< T >
Get the numeric value of an element.
auto contains(dicom_tag tag) const noexcept -> bool
Check if the dataset contains an element with the given tag.
auto get_string(dicom_tag tag, std::string_view default_value="") const -> std::string
Get the string value of an element.
auto to_string() const -> std::string
Convert to string representation.
dx_validation_options options_
void validate_voi_lut_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void validate_dx_anatomy_imaged_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
validation_result validate(const core::dicom_dataset &dataset) const
Validate a DICOM dataset against DX IOD.
validation_result validate_for_processing(const core::dicom_dataset &dataset) const
Validate a For Processing DX dataset.
void check_photometric_interpretation(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
const dx_validation_options & options() const noexcept
Get the validation options.
void check_type1_attribute(const core::dicom_dataset &dataset, core::dicom_tag tag, std::string_view name, std::vector< validation_finding > &findings) const
dx_iod_validator()=default
Construct validator with default options.
void check_pixel_data_consistency(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_general_study_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_dx_image_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 check_modality(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void validate_dx_detector_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void set_options(const dx_validation_options &options)
Set validation options.
void check_type2_attribute(const core::dicom_dataset &dataset, core::dicom_tag tag, std::string_view name, std::vector< validation_finding > &findings) const
bool quick_check(const core::dicom_dataset &dataset) const
Quick check if dataset has minimum required attributes.
void validate_image_pixel_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void validate_dx_positioning_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
validation_result validate_for_presentation(const core::dicom_dataset &dataset) const
Validate a For Presentation DX dataset.
Compile-time constants for commonly used DICOM tags.
Digital X-Ray (DX) Image IOD Validator.
Digital X-Ray (DX) Image Storage SOP Classes.
bool is_dx_for_processing_sop_class(std::string_view uid) noexcept
Check if a SOP Class UID is a For Processing SOP Class.
bool is_dx_for_presentation_sop_class(std::string_view uid) noexcept
Check if a SOP Class UID is a For Presentation SOP Class.
bool is_valid_dx_photometric(std::string_view value) noexcept
Check if photometric interpretation is valid for DX.
bool is_dx_storage_sop_class(std::string_view uid) noexcept
Check if a SOP Class UID is a DX Storage SOP Class.
bool is_for_presentation_dx(const core::dicom_dataset &dataset)
Check if dataset is a For Presentation DX image.
@ warning
Non-critical - IOD may have issues.
@ info
Informational - suggestion for improvement.
@ error
Critical - IOD is non-compliant.
bool is_valid_dx_dataset(const core::dicom_dataset &dataset)
Quick check if a dataset is a valid DX image.
bool is_for_processing_dx(const core::dicom_dataset &dataset)
Check if dataset is a For Processing DX image.
validation_result validate_dx_iod(const core::dicom_dataset &dataset)
Validate a DX dataset with default options.
Options for DX IOD validation.
bool validate_processing_requirements
Validate For Processing specific requirements.
bool validate_pixel_data
Validate pixel data consistency (rows, columns, bits)
bool check_type1
Check Type 1 (required) attributes.
bool check_type2
Check Type 2 (required, can be empty) attributes.
bool validate_anatomy
Validate body part and view position.
bool validate_presentation_requirements
Validate For Presentation specific requirements.
bool strict_mode
Strict mode - treat warnings as errors.
bool validate_dx_specific
Validate DX-specific attributes (detector, acquisition)
bool check_conditional
Check Type 1C/2C (conditionally required) attributes.
Result of IOD validation.
std::vector< validation_finding > findings
All findings during validation.
bool is_valid
Overall validation status.