88 : options_(options) {}
124 for (
const auto& finding : result.
findings) {
149 if (intent !=
"FOR PRESENTATION") {
150 result.findings.push_back({
153 "Presentation Intent Type should be 'FOR PRESENTATION' "
154 "for this SOP Class, found: " + intent,
162 for (
const auto& finding : result.findings) {
164 result.is_valid =
false;
181 if (intent !=
"FOR PROCESSING") {
182 result.findings.push_back({
185 "Presentation Intent Type should be 'FOR PROCESSING' "
186 "for this SOP Class, found: " + intent,
195 if (relationship !=
"LIN") {
196 result.findings.push_back({
199 "For Processing mammography images typically have linear (LIN) "
200 "pixel intensity relationship, found: " + relationship,
208 for (
const auto& finding : result.findings) {
210 result.is_valid =
false;
225 for (
const auto& finding : result.
findings) {
242 for (
const auto& finding : result.
findings) {
259 for (
const auto& finding : result.
findings) {
281 if (modality !=
"MG")
return false;
321 std::vector<validation_finding>& findings)
const {
334 std::vector<validation_finding>& findings)
const {
353 std::vector<validation_finding>& findings)
const {
372 std::vector<validation_finding>& findings)
const {
383 "Request Attributes Sequence not present - may limit workflow integration",
392 std::vector<validation_finding>& findings)
const {
401 if (image_type.find(
"ORIGINAL") == std::string::npos &&
402 image_type.find(
"DERIVED") == std::string::npos) {
406 "Image Type first value should be ORIGINAL or DERIVED",
431 "Partial View is YES but no description or code sequence provided",
440 std::vector<validation_finding>& findings)
const {
448 if (body_part !=
"BREAST") {
452 "Body Part Examined should be 'BREAST' for mammography, found: " + body_part,
466 "Neither Body Part Examined nor Anatomic Region Sequence present - "
467 "anatomy information may be insufficient for clinical use",
476 std::vector<validation_finding>& findings)
const {
485 if (type !=
"DIRECT" && type !=
"SCINTILLATOR" && type !=
"STORAGE") {
489 "Unusual Detector Type for mammography: " + type +
490 " (typical: DIRECT, SCINTILLATOR)",
500 "ImagerPixelSpacing", findings);
509 std::vector<validation_finding>& findings)
const {
524 "Compressed breast thickness outside typical range (10-150mm): " +
525 std::to_string(*thickness) +
" mm",
534 "Compressed breast thickness not documented - "
535 "recommended for dose assessment and quality control",
545 "KVP not documented - recommended for dose assessment",
557 std::vector<validation_finding>& findings)
const {
581 std::vector<validation_finding>& findings)
const {
588 constexpr dicom_tag voi_lut_sequence{0x0028, 0x3010};
589 bool has_voi_lut = dataset.
contains(voi_lut_sequence);
591 if (!has_window && !has_voi_lut) {
595 "For Presentation mammography images should have Window Center/Width "
596 "or VOI LUT Sequence for proper display",
605 std::vector<validation_finding>& findings)
const {
620 "SOPClassUID is not a recognized Mammography Storage SOP Class: " + sop_class,
629 std::vector<validation_finding>& findings)
const {
638 "Breast Implant Present should be 'YES' or 'NO', found: " +
implant,
649 if (parsed_view != mg_view_position::implant &&
650 parsed_view != mg_view_position::id) {
654 "Breast implant present but view is not implant-displaced (ID). "
655 "Implant displacement technique (Eklund) may improve visualization.",
671 std::string_view
name,
672 std::vector<validation_finding>& findings)
const {
678 std::string(
"Type 1 attribute missing: ") + std::string(
name) +
689 std::string(
"Type 1 attribute has empty value: ") +
700 std::string_view
name,
701 std::vector<validation_finding>& findings)
const {
708 std::string(
"Type 2 attribute missing: ") + std::string(
name) +
717 std::vector<validation_finding>& findings)
const {
724 if (modality !=
"MG") {
728 "Modality must be 'MG' for mammography images, found: " + modality,
736 std::vector<validation_finding>& findings)
const {
744 if (!has_series_laterality && !has_image_laterality) {
748 "Neither Laterality (0020,0060) nor Image Laterality (0020,0062) is present. "
749 "Breast laterality must be specified for mammography images.",
756 if (has_series_laterality) {
762 "Invalid Laterality value: '" + lat +
763 "'. Must be 'L' (left), 'R' (right), or 'B' (bilateral).",
770 if (has_image_laterality) {
776 "Invalid Image Laterality value: '" + img_lat +
777 "'. Must be 'L' (left), 'R' (right), or 'B' (bilateral).",
784 if (has_series_laterality && has_image_laterality) {
788 if (series_lat != image_lat) {
792 "Laterality mismatch: Series Laterality is '" + series_lat +
793 "' but Image Laterality is '" + image_lat +
"'.",
802 std::vector<validation_finding>& findings)
const {
809 "View Position (0018,5101) is not present. "
810 "This attribute should be specified for mammography images.",
817 if (view_str.empty()) {
821 "View Position is present but empty.",
829 if (view == mg_view_position::other) {
832 std::string valid_list;
833 for (
size_t i = 0; i < valid_views.size(); ++i) {
834 if (i > 0) valid_list +=
", ";
835 valid_list += std::string(valid_views[i]);
841 "Unrecognized View Position: '" + view_str +
842 "'. Common mammography views include: " + valid_list,
859 "Unusual laterality-view combination: " + lat_str +
" / " + view_str,
868 std::vector<validation_finding>& findings)
const {
875 "Compression Force (0018,11A2) is not present. "
876 "This information is valuable for quality control and patient safety.",
887 "Compression Force is present but could not be parsed as a number.",
899 "Compression Force (" + std::to_string(*force) +
900 " N) is outside the typical range (20-300 N). "
901 "This may indicate a measurement error or non-standard technique.",
904 }
else if (*force < min_typical || *force > max_typical) {
908 "Compression Force (" + std::to_string(*force) +
909 " N) is outside the typical screening range (" +
910 std::to_string(min_typical) +
"-" + std::to_string(max_typical) +
" N).",
918 std::vector<validation_finding>& findings)
const {
925 if (bits_allocated && bits_stored) {
926 if (*bits_stored > *bits_allocated) {
930 "BitsStored (" + std::to_string(*bits_stored) +
931 ") exceeds BitsAllocated (" + std::to_string(*bits_allocated) +
")",
937 if (*bits_stored < 12 || *bits_stored > 16) {
941 "Mammography images typically use 12-16 bits, found: " +
942 std::to_string(*bits_stored),
949 if (bits_stored && high_bit) {
950 if (*high_bit != *bits_stored - 1) {
954 "HighBit (" + std::to_string(*high_bit) +
955 ") should typically be BitsStored - 1 (" +
956 std::to_string(*bits_stored - 1) +
")",
964 if (samples && *samples != 1) {
968 "Mammography images must be grayscale (SamplesPerPixel = 1), found: " +
969 std::to_string(*samples),
977 std::vector<validation_finding>& findings)
const {
984 if (photometric !=
"MONOCHROME1" && photometric !=
"MONOCHROME2") {
988 "Mammography images must use MONOCHROME1 or MONOCHROME2, found: " + photometric,
1000 return validator.
validate(dataset);
1025 constexpr dicom_tag breast_implant_present{0x0028, 0x1300};
1026 if (!dataset.
contains(breast_implant_present)) {
1029 auto value = dataset.
get_string(breast_implant_present);
1030 return value ==
"YES";
1034 constexpr dicom_tag view_position{0x0018, 0x5101};
1035 if (!dataset.
contains(view_position)) {
1038 auto view_str = dataset.
get_string(view_position);
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.
void check_modality(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void check_laterality_consistency(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void check_compression_force_range(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
validation_result validate_compression_force(const core::dicom_dataset &dataset) const
Validate compression force.
void validate_general_study_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void validate_breast_implant_module(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
void validate_dx_anatomy_imaged_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
bool quick_check(const core::dicom_dataset &dataset) const
Quick check if dataset has minimum required attributes.
void check_photometric_interpretation(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 validate_mammography_image_module(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
const mg_validation_options & options() const noexcept
Get the validation options.
void check_view_position_validity(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
validation_result validate_view_position(const core::dicom_dataset &dataset) const
Validate mammography view position.
validation_result validate(const core::dicom_dataset &dataset) const
Validate a DICOM dataset against Mammography IOD.
validation_result validate_laterality(const core::dicom_dataset &dataset) const
Validate breast laterality attribute.
void validate_patient_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void validate_mammography_series_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
void set_options(const mg_validation_options &options)
Set validation options.
mg_iod_validator()=default
Construct validator with default options.
void validate_voi_lut_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_image_pixel_module(const core::dicom_dataset &dataset, std::vector< validation_finding > &findings) const
validation_result validate_for_processing(const core::dicom_dataset &dataset) const
Validate a For Processing mammography dataset.
void validate_acquisition_dose_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 mammography dataset.
mg_validation_options options_
void check_type1_attribute(const core::dicom_dataset &dataset, core::dicom_tag tag, std::string_view name, std::vector< validation_finding > &findings) const
Compile-time constants for commonly used DICOM tags.
Digital Mammography X-Ray Image IOD Validator.
Digital Mammography X-Ray Image Storage SOP Classes.
std::pair< double, double > get_typical_compression_force_range() noexcept
Get typical compression force range.
bool is_valid_compression_force(double force_n) noexcept
Validate compression force value.
bool is_mg_for_presentation_sop_class(std::string_view uid) noexcept
Check if a SOP Class UID is a For Presentation mammography SOP Class.
bool is_valid_compressed_breast_thickness(double thickness_mm) noexcept
Validate compressed breast thickness.
@ implant
Implant displaced view (Eklund technique)
mg_view_position parse_mg_view_position(std::string_view value) noexcept
Parse DICOM view position string to mammography view enum.
@ partial
PARTIAL - Document is not complete.
bool is_valid_laterality_view_combination(breast_laterality laterality, mg_view_position view) noexcept
Check if laterality and view position are consistent.
bool is_mg_storage_sop_class(std::string_view uid) noexcept
Check if a SOP Class UID is a Mammography Storage SOP Class.
breast_laterality parse_breast_laterality(std::string_view value) noexcept
Parse DICOM laterality string to enum.
bool is_valid_breast_laterality(std::string_view value) noexcept
Check if a laterality value is valid for mammography.
std::vector< std::string_view > get_valid_mg_view_positions() noexcept
Get all valid mammography view position strings.
bool is_screening_view(mg_view_position position) noexcept
Check if a view position is a standard screening view.
bool is_mg_for_processing_sop_class(std::string_view uid) noexcept
Check if a SOP Class UID is a For Processing mammography SOP Class.
bool is_screening_mammogram(const core::dicom_dataset &dataset)
Check if dataset is a screening mammogram.
validation_result validate_mg_iod(const core::dicom_dataset &dataset)
Validate a mammography dataset with default options.
bool is_for_presentation_mg(const core::dicom_dataset &dataset)
Check if dataset is a For Presentation mammography image.
bool is_valid_mg_dataset(const core::dicom_dataset &dataset)
Quick check if a dataset is a valid mammography image.
@ warning
Non-critical - IOD may have issues.
@ info
Informational - suggestion for improvement.
@ error
Critical - IOD is non-compliant.
bool has_breast_implant(const core::dicom_dataset &dataset)
Check if dataset indicates breast implant presence.
bool is_for_processing_mg(const core::dicom_dataset &dataset)
Check if dataset is a For Processing mammography image.
Options for Mammography IOD validation.
bool validate_pixel_data
Validate pixel data consistency (rows, columns, bits)
bool validate_dose_parameters
Validate acquisition dose parameters.
bool validate_view_position
Validate mammography view position (0018,5101)
bool validate_mg_specific
Validate mammography-specific attributes.
bool check_type1
Check Type 1 (required) attributes.
bool strict_mode
Strict mode - treat warnings as errors.
bool check_conditional
Check Type 1C/2C (conditionally required) attributes.
bool check_type2
Check Type 2 (required, can be empty) attributes.
bool validate_laterality
Validate breast laterality (0020,0060)
bool validate_processing_requirements
Validate For Processing specific requirements.
bool validate_compression
Validate compression force (0018,11A2)
bool validate_presentation_requirements
Validate For Presentation specific requirements.
bool validate_implant_attributes
Validate breast implant attributes.
Result of IOD validation.
std::vector< validation_finding > findings
All findings during validation.
bool is_valid
Overall validation status.