PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
mg_storage.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
11
12#include <algorithm>
13#include <array>
14#include <cctype>
15
17
18// =============================================================================
19// Breast Laterality Implementation
20// =============================================================================
21
22std::string_view to_string(breast_laterality laterality) noexcept {
23 switch (laterality) {
24 case breast_laterality::left: return "L";
25 case breast_laterality::right: return "R";
26 case breast_laterality::bilateral: return "B";
27 case breast_laterality::unknown: return "";
28 }
29 return "";
30}
31
32breast_laterality parse_breast_laterality(std::string_view value) noexcept {
33 if (value.empty()) {
35 }
36
37 // Handle single character codes
38 if (value.size() == 1) {
39 switch (std::toupper(static_cast<unsigned char>(value[0]))) {
40 case 'L': return breast_laterality::left;
41 case 'R': return breast_laterality::right;
42 case 'B': return breast_laterality::bilateral;
43 }
44 }
45
46 // Handle full words (case-insensitive)
47 std::string lower_value;
48 lower_value.reserve(value.size());
49 for (char c : value) {
50 lower_value += static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
51 }
52
53 if (lower_value == "left") return breast_laterality::left;
54 if (lower_value == "right") return breast_laterality::right;
55 if (lower_value == "bilateral" || lower_value == "both") {
57 }
58
60}
61
62bool is_valid_breast_laterality(std::string_view value) noexcept {
63 return value == "L" || value == "R" || value == "B";
64}
65
66// =============================================================================
67// Mammography View Position Implementation
68// =============================================================================
69
70std::string_view to_string(mg_view_position position) noexcept {
71 switch (position) {
72 case mg_view_position::cc: return "CC";
73 case mg_view_position::mlo: return "MLO";
74 case mg_view_position::ml: return "ML";
75 case mg_view_position::lm: return "LM";
76 case mg_view_position::xccl: return "XCCL";
77 case mg_view_position::xccm: return "XCCM";
78 case mg_view_position::fb: return "FB";
79 case mg_view_position::sio: return "SIO";
80 case mg_view_position::iso: return "ISO";
81 case mg_view_position::cv: return "CV";
82 case mg_view_position::at: return "AT";
83 case mg_view_position::spot: return "SPOT";
84 case mg_view_position::mag: return "MAG";
85 case mg_view_position::spot_mag: return "SPOT MAG";
86 case mg_view_position::rl: return "RL";
87 case mg_view_position::rm: return "RM";
88 case mg_view_position::rs: return "RS";
89 case mg_view_position::ri: return "RI";
90 case mg_view_position::tangen: return "TAN";
91 case mg_view_position::implant: return "ID";
92 case mg_view_position::id: return "ID";
93 case mg_view_position::other: return "";
94 }
95 return "";
96}
97
98mg_view_position parse_mg_view_position(std::string_view value) noexcept {
99 if (value.empty()) {
101 }
102
103 // Convert to uppercase for comparison
104 std::string upper_value;
105 upper_value.reserve(value.size());
106 for (char c : value) {
107 upper_value += static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
108 }
109
110 // Standard views
111 if (upper_value == "CC") return mg_view_position::cc;
112 if (upper_value == "MLO") return mg_view_position::mlo;
113 if (upper_value == "ML") return mg_view_position::ml;
114 if (upper_value == "LM") return mg_view_position::lm;
115
116 // Extended CC views
117 if (upper_value == "XCCL") return mg_view_position::xccl;
118 if (upper_value == "XCCM") return mg_view_position::xccm;
119
120 // Other standard views
121 if (upper_value == "FB") return mg_view_position::fb;
122 if (upper_value == "SIO") return mg_view_position::sio;
123 if (upper_value == "ISO") return mg_view_position::iso;
124 if (upper_value == "CV") return mg_view_position::cv;
125 if (upper_value == "AT") return mg_view_position::at;
126
127 // Spot/magnification views
128 if (upper_value == "SPOT") return mg_view_position::spot;
129 if (upper_value == "MAG") return mg_view_position::mag;
130 if (upper_value == "SPOT MAG" || upper_value == "SPOTMAG") {
132 }
133
134 // Rolled views
135 if (upper_value == "RL") return mg_view_position::rl;
136 if (upper_value == "RM") return mg_view_position::rm;
137 if (upper_value == "RS") return mg_view_position::rs;
138 if (upper_value == "RI") return mg_view_position::ri;
139
140 // Specialized views
141 if (upper_value == "TAN" || upper_value == "TANGENTIAL") {
143 }
144 if (upper_value == "ID" || upper_value == "IMPLANT DISPLACED" ||
145 upper_value == "IMPLANTDISPLACED") {
147 }
148
150}
151
152bool is_screening_view(mg_view_position position) noexcept {
153 return position == mg_view_position::cc || position == mg_view_position::mlo;
154}
155
157 return position == mg_view_position::mag ||
158 position == mg_view_position::spot_mag;
159}
160
162 return position == mg_view_position::spot ||
163 position == mg_view_position::spot_mag;
164}
165
166std::vector<std::string_view> get_valid_mg_view_positions() noexcept {
167 return {
168 "CC", "MLO", "ML", "LM", "XCCL", "XCCM", "FB", "SIO", "ISO",
169 "CV", "AT", "SPOT", "MAG", "SPOT MAG", "RL", "RM", "RS", "RI",
170 "TAN", "ID"
171 };
172}
173
174// =============================================================================
175// Compression Force Validation
176// =============================================================================
177
178bool is_valid_compression_force(double force_n) noexcept {
179 // Typical mammography compression force: 50-200 N
180 // Extended range to account for variations: 20-300 N
181 return force_n >= 20.0 && force_n <= 300.0;
182}
183
184std::pair<double, double> get_typical_compression_force_range() noexcept {
185 return {50.0, 200.0}; // Newtons
186}
187
188bool is_valid_compressed_breast_thickness(double thickness_mm) noexcept {
189 // Typical compressed breast thickness: 20-100 mm
190 // Extended range: 10-150 mm
191 return thickness_mm >= 10.0 && thickness_mm <= 150.0;
192}
193
194// =============================================================================
195// Image Type Implementation
196// =============================================================================
197
198std::string_view to_string(mg_image_type type) noexcept {
199 switch (type) {
200 case mg_image_type::for_presentation: return "FOR PRESENTATION";
201 case mg_image_type::for_processing: return "FOR PROCESSING";
202 }
203 return "";
204}
205
206// =============================================================================
207// CAD Processing Status Implementation
208// =============================================================================
209
210std::string_view to_string(cad_processing_status status) noexcept {
211 switch (status) {
213 return "NOT PROCESSED";
215 return "PROCESSED - NO FINDINGS";
217 return "PROCESSED - FINDINGS";
219 return "PROCESSING FAILED";
221 return "PENDING";
222 }
223 return "";
224}
225
226// =============================================================================
227// SOP Class Information
228// =============================================================================
229
230namespace {
231
232// Static array of mammography SOP class information
233constexpr std::array<mg_sop_class_info, 5> mg_sop_classes = {{
234 {
236 "Digital Mammography X-Ray Image Storage - For Presentation",
237 "Standard 2D mammography images ready for display",
239 false, // not tomosynthesis
240 false // no multiframe
241 },
242 {
244 "Digital Mammography X-Ray Image Storage - For Processing",
245 "Raw 2D mammography images requiring processing",
247 false, // not tomosynthesis
248 false // no multiframe
249 },
250 {
252 "Breast Tomosynthesis Image Storage",
253 "3D breast tomosynthesis image set",
255 true, // is tomosynthesis
256 true // multiframe
257 },
258 {
260 "Breast Projection X-Ray Image Storage - For Presentation",
261 "Synthesized 2D from tomosynthesis - for display",
263 true, // related to tomosynthesis
264 false // no multiframe
265 },
266 {
268 "Breast Projection X-Ray Image Storage - For Processing",
269 "Synthesized 2D from tomosynthesis - raw",
271 true, // related to tomosynthesis
272 false // no multiframe
273 }
274}};
275
276} // namespace
277
278std::vector<std::string> get_mg_storage_sop_classes(bool include_tomosynthesis) {
279 std::vector<std::string> result;
280 result.reserve(mg_sop_classes.size());
281
282 for (const auto& info : mg_sop_classes) {
283 if (!include_tomosynthesis && info.is_tomosynthesis) {
284 continue;
285 }
286 result.emplace_back(info.uid);
287 }
288
289 return result;
290}
291
292const mg_sop_class_info* get_mg_sop_class_info(std::string_view uid) noexcept {
293 auto it = std::find_if(mg_sop_classes.begin(), mg_sop_classes.end(),
294 [uid](const mg_sop_class_info& info) {
295 return info.uid == uid;
296 });
297
298 if (it != mg_sop_classes.end()) {
299 return &(*it);
300 }
301 return nullptr;
302}
303
304bool is_mg_storage_sop_class(std::string_view uid) noexcept {
305 return get_mg_sop_class_info(uid) != nullptr;
306}
307
308bool is_breast_tomosynthesis_sop_class(std::string_view uid) noexcept {
309 auto info = get_mg_sop_class_info(uid);
310 return info && info->is_tomosynthesis;
311}
312
313bool is_mg_for_processing_sop_class(std::string_view uid) noexcept {
314 auto info = get_mg_sop_class_info(uid);
315 return info && info->image_type == mg_image_type::for_processing;
316}
317
318bool is_mg_for_presentation_sop_class(std::string_view uid) noexcept {
319 auto info = get_mg_sop_class_info(uid);
320 return info && info->image_type == mg_image_type::for_presentation;
321}
322
323// =============================================================================
324// Transfer Syntax Recommendations
325// =============================================================================
326
327std::vector<std::string> get_mg_transfer_syntaxes() {
328 return {
329 // HTJ2K Lossless - high-throughput lossless for large mammography images
330 "1.2.840.10008.1.2.4.201",
331 // JPEG 2000 Lossless - excellent for high-resolution mammography
332 "1.2.840.10008.1.2.4.90",
333 // JPEG Lossless - widely supported
334 "1.2.840.10008.1.2.4.70",
335 // JPEG 2000 Lossy - good compression for storage
336 "1.2.840.10008.1.2.4.91",
337 // Explicit VR Little Endian - universal compatibility
338 "1.2.840.10008.1.2.1",
339 // Implicit VR Little Endian - legacy support
340 "1.2.840.10008.1.2"
341 };
342}
343
344// =============================================================================
345// Utility Functions
346// =============================================================================
347
349 breast_laterality laterality,
350 mg_view_position view) noexcept {
351
352 // Bilateral laterality should only be used for comparison views
353 // or special study types, not for standard single-breast views
354 if (laterality == breast_laterality::bilateral) {
355 // CV (cleavage view) can legitimately be bilateral
356 if (view == mg_view_position::cv) {
357 return true;
358 }
359 // Most standard views should specify L or R
360 return false;
361 }
362
363 // Unknown laterality with a specific view is problematic
364 if (laterality == breast_laterality::unknown) {
365 // Allow if view is also unspecified
366 return view == mg_view_position::other;
367 }
368
369 // Left or Right with any view is valid
370 return true;
371}
372
373std::vector<std::pair<breast_laterality, mg_view_position>>
382
384 bool is_original,
385 bool is_primary,
386 mg_image_type type) {
387
388 std::string result;
389
390 // Value 1: ORIGINAL or DERIVED
391 result += is_original ? "ORIGINAL" : "DERIVED";
392 result += "\\";
393
394 // Value 2: PRIMARY or SECONDARY
395 result += is_primary ? "PRIMARY" : "SECONDARY";
396 result += "\\";
397
398 // Value 3: Image flavor - empty for mammography typically
399 result += "\\";
400
401 // Value 4: Derived Pixel Contrast - specific for For Presentation/Processing
403 result += "NONE"; // or specific processing type
404 } else {
405 result += ""; // empty for processing images
406 }
407
408 return result;
409}
410
411} // namespace kcenon::pacs::services::sop_classes
Digital Mammography X-Ray Image Storage SOP Classes.
std::vector< std::string > get_mg_storage_sop_classes(bool include_tomosynthesis=true)
Get all Mammography Storage SOP Class UIDs.
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.
constexpr std::string_view breast_tomosynthesis_image_storage_uid
Breast Tomosynthesis Image Storage SOP Class UID.
Definition mg_storage.h:54
constexpr std::string_view mg_image_storage_for_presentation_uid
Digital Mammography X-Ray Image Storage - For Presentation SOP Class UID.
Definition mg_storage.h:46
std::vector< std::pair< breast_laterality, mg_view_position > > get_standard_screening_views() noexcept
Get standard four-view screening exam views.
mg_view_position
Mammography-specific view positions.
Definition mg_storage.h:122
@ lm
Lateromedial - true lateral (lateral to medial)
@ spot_mag
Spot compression with magnification.
@ fb
From Below - inferior to superior view.
@ sio
Superolateral to Inferomedial Oblique.
@ xccl
Exaggerated CC Laterally - for lateral breast tissue.
@ xccm
Exaggerated CC Medially - for medial breast tissue.
@ mlo
Mediolateral Oblique - angled lateral view (most common)
@ at
Axillary Tail - for axillary extension.
@ ml
Mediolateral - true lateral (medial to lateral)
@ implant
Implant displaced view (Eklund technique)
@ id
Implant Displaced (alternate code)
@ cv
Cleavage View - for medial breast tissue.
@ cc
Craniocaudal - standard superior-inferior view.
@ iso
Inferomedial to Superolateral Oblique.
const mg_sop_class_info * get_mg_sop_class_info(std::string_view uid) noexcept
Get information about a specific Mammography SOP Class.
mg_image_type
Mammography image purpose classification.
Definition mg_storage.h:304
@ for_presentation
Ready for display and diagnosis.
@ for_processing
Raw data requiring further processing.
bool is_breast_tomosynthesis_sop_class(std::string_view uid) noexcept
Check if a SOP Class UID is a breast tomosynthesis SOP Class.
mg_view_position parse_mg_view_position(std::string_view value) noexcept
Parse DICOM view position string to mammography view enum.
std::string create_mg_image_type(bool is_original, bool is_primary, mg_image_type type)
Create DICOM-compliant image type value for mammography.
constexpr std::string_view breast_projection_image_storage_for_processing_uid
Breast Projection X-Ray Image Storage - For Processing SOP Class UID.
Definition mg_storage.h:62
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.
bool is_magnification_view(mg_view_position position) noexcept
Check if a view position requires magnification.
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.
std::vector< std::string > get_mg_transfer_syntaxes()
Get recommended transfer syntaxes for mammography images.
cad_processing_status
CAD (Computer-Aided Detection) processing status.
Definition mg_storage.h:325
@ not_processed
CAD has not been run on this image.
@ processed_no_findings
CAD completed with no findings.
bool is_spot_compression_view(mg_view_position position) noexcept
Check if a view position involves spot compression.
bool is_screening_view(mg_view_position position) noexcept
Check if a view position is a standard screening view.
breast_laterality
Breast laterality enumeration.
Definition mg_storage.h:80
@ unknown
Unknown or unspecified laterality.
@ bilateral
Both breasts (DICOM value: "B") - used for comparison views.
constexpr std::string_view mg_image_storage_for_processing_uid
Digital Mammography X-Ray Image Storage - For Processing SOP Class UID.
Definition mg_storage.h:50
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.
constexpr std::string_view breast_projection_image_storage_for_presentation_uid
Breast Projection X-Ray Image Storage - For Presentation SOP Class UID.
Definition mg_storage.h:58
std::string_view to_string(dx_photometric_interpretation interp) noexcept
Convert photometric interpretation enum to DICOM string.
Information about a Mammography Storage SOP Class.
Definition mg_storage.h:347
std::string_view uid