PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
dx_iod_validator.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2021-2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
13
14#include <sstream>
15
17
18using namespace kcenon::pacs::core;
19
20// =============================================================================
21// DX-Specific DICOM Tags
22// =============================================================================
23
24namespace dx_tags {
25
26// DX Anatomy Imaged Module
27inline constexpr dicom_tag body_part_examined{0x0018, 0x0015};
28inline constexpr dicom_tag anatomic_region_sequence{0x0008, 0x2218};
29inline constexpr dicom_tag primary_anatomic_structure_sequence{0x0008, 0x2228};
30
31// DX Positioning Module
32inline constexpr dicom_tag view_position{0x0018, 0x5101};
33inline constexpr dicom_tag view_code_sequence{0x0054, 0x0220};
34inline constexpr dicom_tag patient_orientation{0x0020, 0x0020};
35inline constexpr dicom_tag distance_source_to_detector{0x0018, 0x1110};
36inline constexpr dicom_tag distance_source_to_patient{0x0018, 0x1111};
37
38// DX Detector Module
39inline constexpr dicom_tag detector_type{0x0018, 0x7004};
40inline constexpr dicom_tag detector_configuration{0x0018, 0x7005};
41inline constexpr dicom_tag detector_id{0x0018, 0x700A};
42inline constexpr dicom_tag detector_description{0x0018, 0x7006};
43inline constexpr dicom_tag field_of_view_shape{0x0018, 0x1147};
44inline constexpr dicom_tag imager_pixel_spacing{0x0018, 0x1164};
45
46// DX Image Module
47inline constexpr dicom_tag image_type{0x0008, 0x0008};
49inline constexpr dicom_tag acquisition_device_processing_code{0x0018, 0x1401};
50inline constexpr dicom_tag pixel_intensity_relationship{0x0028, 0x1040};
51inline constexpr dicom_tag pixel_intensity_relationship_sign{0x0028, 0x1041};
52inline constexpr dicom_tag presentation_intent_type{0x0008, 0x0068};
53
54// Exposure Module
55inline constexpr dicom_tag kvp{0x0018, 0x0060};
56inline constexpr dicom_tag exposure_time{0x0018, 0x1150};
57inline constexpr dicom_tag exposure{0x0018, 0x1152};
58inline constexpr dicom_tag exposure_in_uas{0x0018, 0x1153};
59
60} // namespace dx_tags
61
62// =============================================================================
63// dx_iod_validator Implementation
64// =============================================================================
65
67 : options_(options) {}
68
70 validation_result result;
71 result.is_valid = true;
72
73 // Validate mandatory modules
75 validate_patient_module(dataset, result.findings);
78 validate_sop_common_module(dataset, result.findings);
79 }
80
83 }
84
86 validate_dx_image_module(dataset, result.findings);
88 }
89
93 }
94
95 // Check for errors
96 for (const auto& finding : result.findings) {
97 if (finding.severity == validation_severity::error) {
98 result.is_valid = false;
99 break;
100 }
101 if (options_.strict_mode && finding.severity == validation_severity::warning) {
102 result.is_valid = false;
103 break;
104 }
105 }
106
107 return result;
108}
109
112 auto result = validate(dataset);
113
114 // Additional For Presentation validation
116 validate_voi_lut_module(dataset, result.findings);
117
118 // Check Presentation Intent Type
120 auto intent = dataset.get_string(dx_tags::presentation_intent_type);
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,
127 "DX-ERR-010"
128 });
129 }
130 }
131 }
132
133 // Re-check validity
134 for (const auto& finding : result.findings) {
135 if (finding.severity == validation_severity::error) {
136 result.is_valid = false;
137 break;
138 }
139 }
140
141 return result;
142}
143
146 auto result = validate(dataset);
147
148 // Additional For Processing validation
150 // Check Presentation Intent Type
152 auto intent = dataset.get_string(dx_tags::presentation_intent_type);
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,
159 "DX-ERR-011"
160 });
161 }
162 }
163
164 // For Processing images should have linear pixel intensity relationship
166 auto relationship = dataset.get_string(dx_tags::pixel_intensity_relationship);
167 if (relationship != "LIN") {
168 result.findings.push_back({
171 "For Processing images typically have linear (LIN) pixel "
172 "intensity relationship, found: " + relationship,
173 "DX-INFO-002"
174 });
175 }
176 }
177 }
178
179 // Re-check validity
180 for (const auto& finding : result.findings) {
181 if (finding.severity == validation_severity::error) {
182 result.is_valid = false;
183 break;
184 }
185 }
186
187 return result;
188}
189
191 // Check only critical Type 1 attributes for quick validation
192
193 // General Study Module Type 1
194 if (!dataset.contains(tags::study_instance_uid)) return false;
195
196 // General Series Module Type 1
197 if (!dataset.contains(tags::modality)) return false;
198 if (!dataset.contains(tags::series_instance_uid)) return false;
199
200 // Check modality is DX
201 auto modality = dataset.get_string(tags::modality);
202 if (modality != "DX") return false;
203
204 // Image Pixel Module Type 1
205 if (!dataset.contains(tags::samples_per_pixel)) return false;
206 if (!dataset.contains(tags::photometric_interpretation)) return false;
207 if (!dataset.contains(tags::rows)) return false;
208 if (!dataset.contains(tags::columns)) return false;
209 if (!dataset.contains(tags::bits_allocated)) return false;
210 if (!dataset.contains(tags::bits_stored)) return false;
211 if (!dataset.contains(tags::high_bit)) return false;
212 if (!dataset.contains(tags::pixel_representation)) return false;
213 if (!dataset.contains(tags::pixel_data)) return false;
214
215 // SOP Common Module Type 1
216 if (!dataset.contains(tags::sop_class_uid)) return false;
217 if (!dataset.contains(tags::sop_instance_uid)) return false;
218
219 return true;
220}
221
223 return options_;
224}
225
229
230// =============================================================================
231// Module Validation Methods
232// =============================================================================
233
235 const dicom_dataset& dataset,
236 std::vector<validation_finding>& findings) const {
237
238 // Patient Module - All attributes are Type 2
239 if (options_.check_type2) {
240 check_type2_attribute(dataset, tags::patient_name, "PatientName", findings);
241 check_type2_attribute(dataset, tags::patient_id, "PatientID", findings);
242 check_type2_attribute(dataset, tags::patient_birth_date, "PatientBirthDate", findings);
243 check_type2_attribute(dataset, tags::patient_sex, "PatientSex", findings);
244 }
245}
246
248 const dicom_dataset& dataset,
249 std::vector<validation_finding>& findings) const {
250
251 // Type 1
252 if (options_.check_type1) {
253 check_type1_attribute(dataset, tags::study_instance_uid, "StudyInstanceUID", findings);
254 }
255
256 // Type 2
257 if (options_.check_type2) {
258 check_type2_attribute(dataset, tags::study_date, "StudyDate", findings);
259 check_type2_attribute(dataset, tags::study_time, "StudyTime", findings);
260 check_type2_attribute(dataset, tags::referring_physician_name, "ReferringPhysicianName", findings);
261 check_type2_attribute(dataset, tags::study_id, "StudyID", findings);
262 check_type2_attribute(dataset, tags::accession_number, "AccessionNumber", findings);
263 }
264}
265
267 const dicom_dataset& dataset,
268 std::vector<validation_finding>& findings) const {
269
270 // Type 1
271 if (options_.check_type1) {
272 check_type1_attribute(dataset, tags::modality, "Modality", findings);
273 check_type1_attribute(dataset, tags::series_instance_uid, "SeriesInstanceUID", findings);
274
275 // Special check: Modality must be "DX"
276 check_modality(dataset, findings);
277 }
278
279 // Type 2
280 if (options_.check_type2) {
281 check_type2_attribute(dataset, tags::series_number, "SeriesNumber", findings);
282 }
283}
284
286 const dicom_dataset& dataset,
287 std::vector<validation_finding>& findings) const {
288
289 // Body Part Examined (0018,0015) - Type 2
290 if (options_.check_type2) {
291 check_type2_attribute(dataset, dx_tags::body_part_examined, "BodyPartExamined", findings);
292 }
293
294 // Anatomic Region Sequence (0008,2218) - Type 1C
295 // Required if Body Part Examined is not sufficient to describe the anatomy
299 findings.push_back({
302 "Neither Body Part Examined nor Anatomic Region Sequence present - "
303 "anatomy information may be insufficient for clinical use",
304 "DX-WARN-001"
305 });
306 }
307 }
308}
309
311 const dicom_dataset& dataset,
312 std::vector<validation_finding>& findings) const {
313
314 // Image Type (0008,0008) - Type 1
315 if (options_.check_type1) {
316 check_type1_attribute(dataset, dx_tags::image_type, "ImageType", findings);
317
318 // Validate Image Type values for DX
319 if (dataset.contains(dx_tags::image_type)) {
320 auto image_type = dataset.get_string(dx_tags::image_type);
321 // First value should be ORIGINAL or DERIVED
322 if (image_type.find("ORIGINAL") == std::string::npos &&
323 image_type.find("DERIVED") == std::string::npos) {
324 findings.push_back({
327 "Image Type first value should be ORIGINAL or DERIVED",
328 "DX-WARN-002"
329 });
330 }
331 // Second value should be PRIMARY or SECONDARY
332 if (image_type.find("PRIMARY") == std::string::npos &&
333 image_type.find("SECONDARY") == std::string::npos) {
334 findings.push_back({
337 "Image Type second value should be PRIMARY or SECONDARY",
338 "DX-WARN-003"
339 });
340 }
341 }
342 }
343
344 // Acquisition Device Processing Description (0018,1400) - Type 3
345 // No validation required, but informational if missing for presentation images
346
347 // Pixel Intensity Relationship (0028,1040) - Type 1 for DX
348 if (options_.check_type1) {
350 "PixelIntensityRelationship", findings);
351
353 auto relationship = dataset.get_string(dx_tags::pixel_intensity_relationship);
354 if (relationship != "LIN" && relationship != "LOG") {
355 findings.push_back({
358 "Pixel Intensity Relationship must be LIN or LOG, found: " + relationship,
359 "DX-ERR-003"
360 });
361 }
362 }
363 }
364
365 // Pixel Intensity Relationship Sign (0028,1041) - Type 1 for DX
366 if (options_.check_type1) {
368 "PixelIntensityRelationshipSign", findings);
369
371 auto sign = dataset.get_numeric<int16_t>(dx_tags::pixel_intensity_relationship_sign);
372 if (sign && *sign != 1 && *sign != -1) {
373 findings.push_back({
376 "Pixel Intensity Relationship Sign must be 1 or -1, found: " +
377 std::to_string(*sign),
378 "DX-ERR-004"
379 });
380 }
381 }
382 }
383}
384
386 const dicom_dataset& dataset,
387 std::vector<validation_finding>& findings) const {
388
389 // Detector Type (0018,7004) - Type 2
390 if (options_.check_type2) {
391 check_type2_attribute(dataset, dx_tags::detector_type, "DetectorType", findings);
392
393 if (dataset.contains(dx_tags::detector_type)) {
394 auto type = dataset.get_string(dx_tags::detector_type);
395 if (type != "DIRECT" && type != "SCINTILLATOR" &&
396 type != "STORAGE" && type != "FILM") {
397 findings.push_back({
400 "Unusual Detector Type for DX: " + type,
401 "DX-WARN-004"
402 });
403 }
404 }
405 }
406
407 // Imager Pixel Spacing (0018,1164) - Type 1 for DX
408 if (options_.check_type1) {
410 "ImagerPixelSpacing", findings);
411 }
412}
413
415 const dicom_dataset& dataset,
416 std::vector<validation_finding>& findings) const {
417
418 // View Position (0018,5101) - Type 2C
419 // Required if Patient Orientation (0020,0020) is not present
423 findings.push_back({
426 "Neither Patient Orientation nor View Position present - "
427 "image orientation may be unclear",
428 "DX-WARN-005"
429 });
430 }
431 }
432
433 // Patient Orientation (0020,0020) - Type 2C
435 auto orientation = dataset.get_string(dx_tags::patient_orientation);
436 if (orientation.empty()) {
437 findings.push_back({
440 "Patient Orientation is present but empty",
441 "DX-INFO-001"
442 });
443 }
444 }
445
446 // Distance Source to Detector (0018,1110) - Type 3, but useful for calibration
447 // Distance Source to Patient (0018,1111) - Type 3, but useful for calibration
448}
449
451 const dicom_dataset& dataset,
452 std::vector<validation_finding>& findings) const {
453
454 // Type 1 attributes
455 if (options_.check_type1) {
456 check_type1_attribute(dataset, tags::samples_per_pixel, "SamplesPerPixel", findings);
457 check_type1_attribute(dataset, tags::photometric_interpretation, "PhotometricInterpretation", findings);
458 check_type1_attribute(dataset, tags::rows, "Rows", findings);
459 check_type1_attribute(dataset, tags::columns, "Columns", findings);
460 check_type1_attribute(dataset, tags::bits_allocated, "BitsAllocated", findings);
461 check_type1_attribute(dataset, tags::bits_stored, "BitsStored", findings);
462 check_type1_attribute(dataset, tags::high_bit, "HighBit", findings);
463 check_type1_attribute(dataset, tags::pixel_representation, "PixelRepresentation", findings);
464 check_type1_attribute(dataset, tags::pixel_data, "PixelData", findings);
465 }
466
467 // Validate pixel data consistency
469 check_pixel_data_consistency(dataset, findings);
470 check_photometric_interpretation(dataset, findings);
471 }
472}
473
475 const dicom_dataset& dataset,
476 std::vector<validation_finding>& findings) const {
477
478 // Window Center (0028,1050) and Window Width (0028,1051) - Type 1C for For Presentation
480 bool has_window = dataset.contains(tags::window_center) &&
482
483 constexpr dicom_tag voi_lut_sequence{0x0028, 0x3010};
484 bool has_voi_lut = dataset.contains(voi_lut_sequence);
485
486 if (!has_window && !has_voi_lut) {
487 findings.push_back({
490 "For Presentation images should have Window Center/Width or VOI LUT Sequence "
491 "for proper display",
492 "DX-WARN-006"
493 });
494 }
495 }
496}
497
499 const dicom_dataset& dataset,
500 std::vector<validation_finding>& findings) const {
501
502 // Type 1
503 if (options_.check_type1) {
504 check_type1_attribute(dataset, tags::sop_class_uid, "SOPClassUID", findings);
505 check_type1_attribute(dataset, tags::sop_instance_uid, "SOPInstanceUID", findings);
506 }
507
508 // Validate SOP Class UID is a DX storage class
509 if (dataset.contains(tags::sop_class_uid)) {
510 auto sop_class = dataset.get_string(tags::sop_class_uid);
511 if (!sop_classes::is_dx_storage_sop_class(sop_class)) {
512 findings.push_back({
515 "SOPClassUID is not a recognized DX Storage SOP Class: " + sop_class,
516 "DX-ERR-001"
517 });
518 }
519 }
520}
521
522// =============================================================================
523// Attribute Validation Helpers
524// =============================================================================
525
527 const dicom_dataset& dataset,
528 dicom_tag tag,
529 std::string_view name,
530 std::vector<validation_finding>& findings) const {
531
532 if (!dataset.contains(tag)) {
533 findings.push_back({
535 tag,
536 std::string("Type 1 attribute missing: ") + std::string(name) +
537 " (" + tag.to_string() + ")",
538 "DX-TYPE1-MISSING"
539 });
540 } else {
541 // Type 1 must have a value (cannot be empty)
542 auto value = dataset.get_string(tag);
543 if (value.empty()) {
544 findings.push_back({
546 tag,
547 std::string("Type 1 attribute has empty value: ") +
548 std::string(name) + " (" + tag.to_string() + ")",
549 "DX-TYPE1-EMPTY"
550 });
551 }
552 }
553}
554
556 const dicom_dataset& dataset,
557 dicom_tag tag,
558 std::string_view name,
559 std::vector<validation_finding>& findings) const {
560
561 // Type 2 must be present but can be empty
562 if (!dataset.contains(tag)) {
563 findings.push_back({
565 tag,
566 std::string("Type 2 attribute missing: ") + std::string(name) +
567 " (" + tag.to_string() + ")",
568 "DX-TYPE2-MISSING"
569 });
570 }
571}
572
574 const dicom_dataset& dataset,
575 std::vector<validation_finding>& findings) const {
576
577 if (!dataset.contains(tags::modality)) {
578 return; // Already reported as Type 1 missing
579 }
580
581 auto modality = dataset.get_string(tags::modality);
582 if (modality != "DX") {
583 findings.push_back({
586 "Modality must be 'DX' for digital X-ray images, found: " + modality,
587 "DX-ERR-002"
588 });
589 }
590}
591
593 const dicom_dataset& dataset,
594 std::vector<validation_finding>& findings) const {
595
596 // Check BitsStored <= BitsAllocated
597 auto bits_allocated = dataset.get_numeric<uint16_t>(tags::bits_allocated);
598 auto bits_stored = dataset.get_numeric<uint16_t>(tags::bits_stored);
599 auto high_bit = dataset.get_numeric<uint16_t>(tags::high_bit);
600
601 if (bits_allocated && bits_stored) {
602 if (*bits_stored > *bits_allocated) {
603 findings.push_back({
606 "BitsStored (" + std::to_string(*bits_stored) +
607 ") exceeds BitsAllocated (" + std::to_string(*bits_allocated) + ")",
608 "DX-ERR-005"
609 });
610 }
611
612 // DX typically uses 10-16 bits stored
613 if (*bits_stored < 10 || *bits_stored > 16) {
614 findings.push_back({
617 "DX images typically use 10-16 bits, found: " +
618 std::to_string(*bits_stored),
619 "DX-INFO-003"
620 });
621 }
622 }
623
624 // Check HighBit == BitsStored - 1
625 if (bits_stored && high_bit) {
626 if (*high_bit != *bits_stored - 1) {
627 findings.push_back({
630 "HighBit (" + std::to_string(*high_bit) +
631 ") should typically be BitsStored - 1 (" +
632 std::to_string(*bits_stored - 1) + ")",
633 "DX-WARN-007"
634 });
635 }
636 }
637
638 // DX must be grayscale - check SamplesPerPixel
639 auto samples = dataset.get_numeric<uint16_t>(tags::samples_per_pixel);
640 if (samples && *samples != 1) {
641 findings.push_back({
644 "DX images must be grayscale (SamplesPerPixel = 1), found: " +
645 std::to_string(*samples),
646 "DX-ERR-006"
647 });
648 }
649}
650
652 const dicom_dataset& dataset,
653 std::vector<validation_finding>& findings) const {
654
656 return; // Already reported as missing
657 }
658
659 auto photometric = dataset.get_string(tags::photometric_interpretation);
660 if (!sop_classes::is_valid_dx_photometric(photometric)) {
661 findings.push_back({
664 "DX images must use MONOCHROME1 or MONOCHROME2, found: " + photometric,
665 "DX-ERR-007"
666 });
667 }
668}
669
670// =============================================================================
671// Convenience Functions
672// =============================================================================
673
675 dx_iod_validator validator;
676 return validator.validate(dataset);
677}
678
679bool is_valid_dx_dataset(const dicom_dataset& dataset) {
680 dx_iod_validator validator;
681 return validator.quick_check(dataset);
682}
683
685 if (!dataset.contains(tags::sop_class_uid)) {
686 return false;
687 }
688 auto sop_class = dataset.get_string(tags::sop_class_uid);
690}
691
693 if (!dataset.contains(tags::sop_class_uid)) {
694 return false;
695 }
696 auto sop_class = dataset.get_string(tags::sop_class_uid);
698}
699
700} // namespace kcenon::pacs::services::validation
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 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.
constexpr dicom_tag high_bit
High Bit.
constexpr dicom_tag referring_physician_name
Referring Physician's Name.
constexpr dicom_tag window_width
Window Width.
constexpr dicom_tag window_center
Window Center.
constexpr dicom_tag patient_id
Patient ID.
constexpr dicom_tag bits_allocated
Bits Allocated.
constexpr dicom_tag rows
Rows.
constexpr dicom_tag columns
Columns.
constexpr dicom_tag sop_instance_uid
SOP Instance UID.
constexpr dicom_tag bits_stored
Bits Stored.
constexpr dicom_tag patient_birth_date
Patient's Birth Date.
constexpr dicom_tag pixel_data
Pixel Data.
constexpr dicom_tag pixel_representation
Pixel Representation.
constexpr dicom_tag accession_number
Accession Number.
constexpr dicom_tag modality
Modality.
constexpr dicom_tag study_time
Study Time.
constexpr dicom_tag patient_sex
Patient's Sex.
constexpr dicom_tag samples_per_pixel
Samples per Pixel.
constexpr dicom_tag study_instance_uid
Study Instance UID.
constexpr dicom_tag series_number
Series Number.
constexpr dicom_tag photometric_interpretation
Photometric Interpretation.
constexpr dicom_tag sop_class_uid
SOP Class UID.
constexpr dicom_tag study_id
Study ID.
constexpr dicom_tag patient_name
Patient's Name.
constexpr dicom_tag study_date
Study Date.
constexpr dicom_tag series_instance_uid
Series Instance UID.
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.
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.
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.
std::vector< validation_finding > findings
All findings during validation.
std::string_view name