PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
ai_result_handler.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
12
13#include <algorithm>
14#include <map>
15#include <mutex>
16#include <unordered_map>
17
18namespace kcenon::pacs::ai {
19
20// Use common_system's ok() function
21using kcenon::common::ok;
22using kcenon::common::make_error;
23
24// =============================================================================
25// SOP Class UIDs for AI-related objects
26// =============================================================================
27
28namespace sop_class {
29// Structured Report SOP Classes
30constexpr std::string_view basic_text_sr = "1.2.840.10008.5.1.4.1.1.88.11";
31constexpr std::string_view enhanced_sr = "1.2.840.10008.5.1.4.1.1.88.22";
32constexpr std::string_view comprehensive_sr = "1.2.840.10008.5.1.4.1.1.88.33";
33constexpr std::string_view comprehensive_3d_sr = "1.2.840.10008.5.1.4.1.1.88.34";
34
35// Segmentation SOP Class
36constexpr std::string_view segmentation = "1.2.840.10008.5.1.4.1.1.66.4";
37
38// Presentation State SOP Classes
39constexpr std::string_view grayscale_softcopy_ps = "1.2.840.10008.5.1.4.1.1.11.1";
40constexpr std::string_view color_softcopy_ps = "1.2.840.10008.5.1.4.1.1.11.2";
41constexpr std::string_view pseudo_color_softcopy_ps = "1.2.840.10008.5.1.4.1.1.11.3";
42constexpr std::string_view blending_softcopy_ps = "1.2.840.10008.5.1.4.1.1.11.4";
43} // namespace sop_class
44
45// =============================================================================
46// Helper Functions
47// =============================================================================
48
49namespace {
50
54[[nodiscard]] auto determine_result_type(std::string_view sop_class_uid)
55 -> std::optional<ai_result_type> {
56 // Structured Reports
57 if (sop_class_uid == sop_class::basic_text_sr ||
58 sop_class_uid == sop_class::enhanced_sr ||
59 sop_class_uid == sop_class::comprehensive_sr ||
60 sop_class_uid == sop_class::comprehensive_3d_sr) {
62 }
63
64 // Segmentation
65 if (sop_class_uid == sop_class::segmentation) {
67 }
68
69 // Presentation States
70 if (sop_class_uid == sop_class::grayscale_softcopy_ps ||
71 sop_class_uid == sop_class::color_softcopy_ps ||
72 sop_class_uid == sop_class::pseudo_color_softcopy_ps ||
73 sop_class_uid == sop_class::blending_softcopy_ps) {
75 }
76
77 return std::nullopt;
78}
79
83[[nodiscard]] auto extract_algorithm_info(const core::dicom_dataset& dataset)
84 -> std::pair<std::string, std::string> {
85 std::string name;
86 std::string version;
87
88 // Try Content Creator's Name for SR
89 name = dataset.get_string(core::dicom_tag(0x0070, 0x0084)); // Content Creator's Name
90 if (name.empty()) {
91 // Try Manufacturer for general DICOM objects
92 name = dataset.get_string(core::dicom_tag(0x0008, 0x0070)); // Manufacturer
93 }
94
95 // Try Software Versions
96 version = dataset.get_string(core::dicom_tag(0x0018, 0x1020)); // Software Versions
97
98 return {name, version};
99}
100
101} // anonymous namespace
102
103// =============================================================================
104// Implementation Class (pimpl)
105// =============================================================================
106
108public:
109 impl(std::shared_ptr<storage::storage_interface> storage,
110 std::shared_ptr<storage::index_database> database)
111 : storage_(std::move(storage))
112 , database_(std::move(database)) {}
113
114 // Configuration
116
117 // Callbacks
120
121 // Storage and database
122 std::shared_ptr<storage::storage_interface> storage_;
123 std::shared_ptr<storage::index_database> database_;
124
125 // In-memory cache for AI result metadata (for quick lookups)
126 std::map<std::string, ai_result_info> ai_results_cache_;
127
128 // Source linking map: AI result UID -> source references
129 std::map<std::string, source_reference> source_links_;
130
131 // =========================================================================
132 // Internal Helper Methods
133 // =========================================================================
134
135 [[nodiscard]] auto validate_common_tags(const core::dicom_dataset& dataset)
137 validation_result result;
139
140 // Check required DICOM tags
141 std::vector<std::pair<core::dicom_tag, std::string>> required_tags = {
142 {core::tags::sop_class_uid, "SOP Class UID"},
143 {core::tags::sop_instance_uid, "SOP Instance UID"},
144 {core::tags::study_instance_uid, "Study Instance UID"},
145 {core::tags::series_instance_uid, "Series Instance UID"},
146 {core::tags::modality, "Modality"}
147 };
148
149 for (const auto& [tag, name] : required_tags) {
150 if (dataset.get_string(tag).empty()) {
151 result.missing_tags.push_back(name);
152 }
153 }
154
155 if (!result.missing_tags.empty()) {
157 result.error_message = "Missing required DICOM tags";
158 }
159
160 return result;
161 }
162
164 const core::dicom_dataset& dataset) -> validation_result {
165 validation_result result;
167
169 return result;
170 }
171
172 // Get Study Instance UID
173 auto study_uid = dataset.get_string(core::tags::study_instance_uid);
174 if (study_uid.empty()) {
176 result.error_message = "Missing Study Instance UID for reference validation";
177 return result;
178 }
179
180 // Check if the study exists in the database
181 // Note: This would require a study lookup method in index_database
182 // For now, we just validate the UID format
183 if (study_uid.length() < 10) { // Basic UID validation
185 result.invalid_references.push_back(study_uid);
186 result.error_message = "Invalid Study Instance UID format";
187 }
188
189 return result;
190 }
191
192 [[nodiscard]] auto store_ai_result(
193 const core::dicom_dataset& dataset,
194 ai_result_type type) -> VoidResult {
195 // Store the dataset
196 auto store_result = storage_->store(dataset);
197 if (store_result.is_err()) {
198 return store_result;
199 }
200
201 // Extract metadata for caching
202 auto sop_instance_uid = dataset.get_string(core::tags::sop_instance_uid);
203 auto [algo_name, algo_version] = extract_algorithm_info(dataset);
204
205 ai_result_info info;
206 info.sop_instance_uid = sop_instance_uid;
207 info.type = type;
208 info.sop_class_uid = dataset.get_string(core::tags::sop_class_uid);
209 info.series_instance_uid = dataset.get_string(core::tags::series_instance_uid);
210 info.source_study_uid = dataset.get_string(core::tags::study_instance_uid);
211 info.algorithm_name = algo_name;
212 info.algorithm_version = algo_version;
213 info.received_at = std::chrono::system_clock::now();
214
215 // Cache the result info
216 ai_results_cache_[sop_instance_uid] = info;
217
218 // Auto-link to source if configured
219 if (config_.auto_link_to_source && !info.source_study_uid.empty()) {
221 ref.study_instance_uid = info.source_study_uid;
222 source_links_[sop_instance_uid] = ref;
223 }
224
225 // Call notification callback
226 if (received_callback_) {
227 received_callback_(info);
228 }
229
230 return ok();
231 }
232};
233
234// =============================================================================
235// Construction / Destruction
236// =============================================================================
237
239 std::shared_ptr<storage::storage_interface> storage,
240 std::shared_ptr<storage::index_database> database)
241 -> std::unique_ptr<ai_result_handler> {
242 return std::unique_ptr<ai_result_handler>(
243 new ai_result_handler(std::move(storage), std::move(database)));
244}
245
247 std::shared_ptr<storage::storage_interface> storage,
248 std::shared_ptr<storage::index_database> database)
249 : pimpl_(std::make_unique<impl>(std::move(storage), std::move(database))) {}
250
252
254
255auto ai_result_handler::operator=(ai_result_handler&&) noexcept
256 -> ai_result_handler& = default;
257
258// =============================================================================
259// Configuration
260// =============================================================================
261
262void ai_result_handler::configure(const ai_handler_config& config) {
263 pimpl_->config_ = config;
264}
265
269
273
277
278// =============================================================================
279// Structured Report Operations
280// =============================================================================
281
283 -> VoidResult {
284 // Validate SOP Class
285 auto sop_class = sr.get_string(core::tags::sop_class_uid);
286 auto type = determine_result_type(sop_class);
287 if (!type || *type != ai_result_type::structured_report) {
288#if KCENON_HAS_COMMON_SYSTEM
289 return kcenon::common::error_info("Invalid SOP Class for Structured Report");
290#else
291 return std::string("Invalid SOP Class for Structured Report");
292#endif
293 }
294
295 // Run common validation
296 auto common_validation = pimpl_->validate_common_tags(sr);
297 if (common_validation.status != validation_status::valid) {
298#if KCENON_HAS_COMMON_SYSTEM
299 return kcenon::common::error_info(
300 common_validation.error_message.value_or("Validation failed"));
301#else
302 return common_validation.error_message.value_or("Validation failed");
303#endif
304 }
305
306 // Validate SR template if configured
307 if (pimpl_->config_.validate_sr_templates) {
308 auto template_validation = validate_sr_template(sr);
309 if (template_validation.status != validation_status::valid) {
310#if KCENON_HAS_COMMON_SYSTEM
311 return kcenon::common::error_info(
312 template_validation.error_message.value_or("SR template validation failed"));
313#else
314 return template_validation.error_message.value_or("SR template validation failed");
315#endif
316 }
317 }
318
319 // Validate source references if configured
320 auto ref_validation = pimpl_->validate_source_references_exist(sr);
321 if (ref_validation.status != validation_status::valid) {
322#if KCENON_HAS_COMMON_SYSTEM
323 return kcenon::common::error_info(
324 ref_validation.error_message.value_or("Source reference validation failed"));
325#else
326 return ref_validation.error_message.value_or("Source reference validation failed");
327#endif
328 }
329
330 // Call pre-store validator if set
331 if (pimpl_->pre_store_validator_ &&
332 !pimpl_->pre_store_validator_(sr, ai_result_type::structured_report)) {
333#if KCENON_HAS_COMMON_SYSTEM
334 return kcenon::common::error_info("Pre-store validation rejected the SR");
335#else
336 return std::string("Pre-store validation rejected the SR");
337#endif
338 }
339
340 // Store the SR
341 return pimpl_->store_ai_result(sr, ai_result_type::structured_report);
342}
343
346 validation_result result;
348
349 // Note: Content Sequence (0040,A730) validation would be done here
350 // for full SR template conformance checking
351
352 // Check for Template Identifier if configured
353 if (!pimpl_->config_.accepted_sr_templates.empty()) {
354 // Tag (0040,DB00) Template Identifier
355 const core::dicom_tag template_id_tag(0x0040, 0xDB00);
356 auto template_id = sr.get_string(template_id_tag);
357
358 bool template_found = false;
359 for (const auto& accepted : pimpl_->config_.accepted_sr_templates) {
360 if (template_id == accepted) {
361 template_found = true;
362 break;
363 }
364 }
365
366 if (!template_found && !template_id.empty()) {
368 result.error_message = "SR template not in accepted list: " + template_id;
369 }
370 }
371
372 return result;
373}
374
375auto ai_result_handler::get_cad_findings(std::string_view sr_sop_instance_uid)
377 // Retrieve the SR dataset
378 auto retrieve_result = pimpl_->storage_->retrieve(sr_sop_instance_uid);
379 if (retrieve_result.is_err()) {
380#if KCENON_HAS_COMMON_SYSTEM
381 return kcenon::common::error_info("Failed to retrieve SR: " +
382 std::string(sr_sop_instance_uid));
383#else
384 return std::string("Failed to retrieve SR: ") + std::string(sr_sop_instance_uid);
385#endif
386 }
387
388 // Parse CAD findings from Content Sequence
389 // This is a simplified implementation - full parsing would handle
390 // the complete SR tree structure
391 std::vector<cad_finding> findings;
392
393 // Note: Actual implementation would parse the Content Sequence
394 // recursively to extract CAD findings based on the SR template
395
396 return findings;
397}
398
399// =============================================================================
400// Segmentation Operations
401// =============================================================================
402
404 -> VoidResult {
405 // Validate SOP Class
406 auto sop_class = seg.get_string(core::tags::sop_class_uid);
407 auto type = determine_result_type(sop_class);
408 if (!type || *type != ai_result_type::segmentation) {
409#if KCENON_HAS_COMMON_SYSTEM
410 return kcenon::common::error_info("Invalid SOP Class for Segmentation");
411#else
412 return std::string("Invalid SOP Class for Segmentation");
413#endif
414 }
415
416 // Run common validation
417 auto common_validation = pimpl_->validate_common_tags(seg);
418 if (common_validation.status != validation_status::valid) {
419#if KCENON_HAS_COMMON_SYSTEM
420 return kcenon::common::error_info(
421 common_validation.error_message.value_or("Validation failed"));
422#else
423 return common_validation.error_message.value_or("Validation failed");
424#endif
425 }
426
427 // Validate segmentation data
428 auto seg_validation = validate_segmentation(seg);
429 if (seg_validation.status != validation_status::valid) {
430#if KCENON_HAS_COMMON_SYSTEM
431 return kcenon::common::error_info(
432 seg_validation.error_message.value_or("Segmentation validation failed"));
433#else
434 return seg_validation.error_message.value_or("Segmentation validation failed");
435#endif
436 }
437
438 // Validate source references if configured
439 auto ref_validation = pimpl_->validate_source_references_exist(seg);
440 if (ref_validation.status != validation_status::valid) {
441#if KCENON_HAS_COMMON_SYSTEM
442 return kcenon::common::error_info(
443 ref_validation.error_message.value_or("Source reference validation failed"));
444#else
445 return ref_validation.error_message.value_or("Source reference validation failed");
446#endif
447 }
448
449 // Call pre-store validator if set
450 if (pimpl_->pre_store_validator_ &&
451 !pimpl_->pre_store_validator_(seg, ai_result_type::segmentation)) {
452#if KCENON_HAS_COMMON_SYSTEM
453 return kcenon::common::error_info("Pre-store validation rejected the segmentation");
454#else
455 return std::string("Pre-store validation rejected the segmentation");
456#endif
457 }
458
459 // Store the SEG
460 return pimpl_->store_ai_result(seg, ai_result_type::segmentation);
461}
462
465 validation_result result;
467
468 // Note: Segment Sequence (0062,0002) validation would be done here
469 // for full segmentation conformance checking
470
471 // Check Segmentation Type
472 // Tag (0062,0001) Segmentation Type
473 const core::dicom_tag seg_type_tag(0x0062, 0x0001);
474 auto seg_type = seg.get_string(seg_type_tag);
475
476 if (seg_type != "BINARY" && seg_type != "FRACTIONAL" && !seg_type.empty()) {
478 result.error_message = "Invalid segmentation type: " + seg_type;
479 return result;
480 }
481
482 // Check segment count if max_segments is configured
483 if (pimpl_->config_.max_segments > 0) {
484 // Tag (0062,0004) Number of Segments
485 // This would need proper implementation based on actual tag
486 }
487
488 return result;
489}
490
491auto ai_result_handler::get_segment_info(std::string_view seg_sop_instance_uid)
493 // Retrieve the SEG dataset
494 auto retrieve_result = pimpl_->storage_->retrieve(seg_sop_instance_uid);
495 if (retrieve_result.is_err()) {
496#if KCENON_HAS_COMMON_SYSTEM
497 return kcenon::common::error_info("Failed to retrieve SEG: " +
498 std::string(seg_sop_instance_uid));
499#else
500 return std::string("Failed to retrieve SEG: ") + std::string(seg_sop_instance_uid);
501#endif
502 }
503
504 // Parse segment information from Segment Sequence
505 std::vector<segment_info> segments;
506
507 // Note: Actual implementation would parse the Segment Sequence
508 // to extract segment information
509
510 return segments;
511}
512
513// =============================================================================
514// Presentation State Operations
515// =============================================================================
516
518 -> VoidResult {
519 // Validate SOP Class
520 auto sop_class = pr.get_string(core::tags::sop_class_uid);
521 auto type = determine_result_type(sop_class);
522 if (!type || *type != ai_result_type::presentation_state) {
523#if KCENON_HAS_COMMON_SYSTEM
524 return kcenon::common::error_info("Invalid SOP Class for Presentation State");
525#else
526 return std::string("Invalid SOP Class for Presentation State");
527#endif
528 }
529
530 // Run common validation
531 auto common_validation = pimpl_->validate_common_tags(pr);
532 if (common_validation.status != validation_status::valid) {
533#if KCENON_HAS_COMMON_SYSTEM
534 return kcenon::common::error_info(
535 common_validation.error_message.value_or("Validation failed"));
536#else
537 return common_validation.error_message.value_or("Validation failed");
538#endif
539 }
540
541 // Validate presentation state
542 auto pr_validation = validate_presentation_state(pr);
543 if (pr_validation.status != validation_status::valid) {
544#if KCENON_HAS_COMMON_SYSTEM
545 return kcenon::common::error_info(
546 pr_validation.error_message.value_or("Presentation state validation failed"));
547#else
548 return pr_validation.error_message.value_or("Presentation state validation failed");
549#endif
550 }
551
552 // Validate source references if configured
553 auto ref_validation = pimpl_->validate_source_references_exist(pr);
554 if (ref_validation.status != validation_status::valid) {
555#if KCENON_HAS_COMMON_SYSTEM
556 return kcenon::common::error_info(
557 ref_validation.error_message.value_or("Source reference validation failed"));
558#else
559 return ref_validation.error_message.value_or("Source reference validation failed");
560#endif
561 }
562
563 // Call pre-store validator if set
564 if (pimpl_->pre_store_validator_ &&
565 !pimpl_->pre_store_validator_(pr, ai_result_type::presentation_state)) {
566#if KCENON_HAS_COMMON_SYSTEM
567 return kcenon::common::error_info("Pre-store validation rejected the presentation state");
568#else
569 return std::string("Pre-store validation rejected the presentation state");
570#endif
571 }
572
573 // Store the PR
574 return pimpl_->store_ai_result(pr, ai_result_type::presentation_state);
575}
576
579 validation_result result;
581
582 // Note: Referenced Series Sequence (0008,1115) validation would be done here
583 // for full presentation state conformance checking
584
585 // Basic validation - ensure presentation state has valid SOP Class UID
586 auto sop_class = pr.get_string(core::tags::sop_class_uid);
587 if (sop_class.empty()) {
589 result.missing_tags.push_back("SOP Class UID");
590 }
591
592 return result;
593}
594
595// =============================================================================
596// Source Linking Operations
597// =============================================================================
598
600 std::string_view result_uid,
601 std::string_view source_study_uid) -> VoidResult {
603 ref.study_instance_uid = std::string(source_study_uid);
604 return link_to_source(result_uid, ref);
605}
606
608 std::string_view result_uid,
609 const source_reference& references) -> VoidResult {
610 // Verify the AI result exists
611 auto it = pimpl_->ai_results_cache_.find(std::string(result_uid));
612 if (it == pimpl_->ai_results_cache_.end()) {
613 // Try to check storage
614 if (!pimpl_->storage_->exists(result_uid)) {
615#if KCENON_HAS_COMMON_SYSTEM
616 return kcenon::common::error_info("AI result not found: " +
617 std::string(result_uid));
618#else
619 return std::string("AI result not found: ") + std::string(result_uid);
620#endif
621 }
622 }
623
624 // Store the source link
625 pimpl_->source_links_[std::string(result_uid)] = references;
626
627 // Update cached info if present
628 if (it != pimpl_->ai_results_cache_.end()) {
629 it->second.source_study_uid = references.study_instance_uid;
630 }
631
632 return ok();
633}
634
635auto ai_result_handler::get_source_reference(std::string_view result_uid)
637 auto it = pimpl_->source_links_.find(std::string(result_uid));
638 if (it == pimpl_->source_links_.end()) {
639#if KCENON_HAS_COMMON_SYSTEM
640 return kcenon::common::error_info("No source reference found for: " +
641 std::string(result_uid));
642#else
643 return std::string("No source reference found for: ") + std::string(result_uid);
644#endif
645 }
646 return it->second;
647}
648
649// =============================================================================
650// Query Operations
651// =============================================================================
652
653auto ai_result_handler::find_ai_results_for_study(std::string_view study_instance_uid)
655 std::vector<ai_result_info> results;
656
657 for (const auto& [uid, info] : pimpl_->ai_results_cache_) {
658 if (info.source_study_uid == study_instance_uid) {
659 results.push_back(info);
660 }
661 }
662
663 // Also check source links
664 for (const auto& [uid, ref] : pimpl_->source_links_) {
665 if (ref.study_instance_uid == study_instance_uid) {
666 auto it = pimpl_->ai_results_cache_.find(uid);
667 if (it != pimpl_->ai_results_cache_.end()) {
668 // Already included from cache
669 continue;
670 }
671 // Would need to retrieve from storage for complete info
672 }
673 }
674
675 return results;
676}
677
679 std::string_view study_instance_uid,
681 std::vector<ai_result_info> results;
682
683 for (const auto& [uid, info] : pimpl_->ai_results_cache_) {
684 if (info.source_study_uid == study_instance_uid && info.type == type) {
685 results.push_back(info);
686 }
687 }
688
689 return results;
690}
691
692auto ai_result_handler::get_ai_result_info(std::string_view sop_instance_uid)
693 -> std::optional<ai_result_info> {
694 auto it = pimpl_->ai_results_cache_.find(std::string(sop_instance_uid));
695 if (it != pimpl_->ai_results_cache_.end()) {
696 return it->second;
697 }
698 return std::nullopt;
699}
700
701auto ai_result_handler::exists(std::string_view sop_instance_uid) const -> bool {
702 // Check cache first
703 if (pimpl_->ai_results_cache_.find(std::string(sop_instance_uid)) !=
704 pimpl_->ai_results_cache_.end()) {
705 return true;
706 }
707
708 // Check storage
709 return pimpl_->storage_->exists(sop_instance_uid);
710}
711
712// =============================================================================
713// Removal Operations
714// =============================================================================
715
716auto ai_result_handler::remove(std::string_view sop_instance_uid) -> VoidResult {
717 std::string uid_str(sop_instance_uid);
718
719 // Remove from storage
720 auto remove_result = pimpl_->storage_->remove(sop_instance_uid);
721 if (remove_result.is_err()) {
722 return remove_result;
723 }
724
725 // Remove from cache
726 pimpl_->ai_results_cache_.erase(uid_str);
727
728 // Remove source links
729 pimpl_->source_links_.erase(uid_str);
730
731 return ok();
732}
733
734auto ai_result_handler::remove_ai_results_for_study(std::string_view study_instance_uid)
736 std::size_t removed_count = 0;
737
738 // Find all AI results for this study
739 std::vector<std::string> to_remove;
740 for (const auto& [uid, info] : pimpl_->ai_results_cache_) {
741 if (info.source_study_uid == study_instance_uid) {
742 to_remove.push_back(uid);
743 }
744 }
745
746 // Remove each one
747 for (const auto& uid : to_remove) {
748 auto result = remove(uid);
749 if (result.is_ok()) {
750 ++removed_count;
751 }
752 }
753
754 return removed_count;
755}
756
757} // namespace kcenon::pacs::ai
Handler for AI-generated DICOM objects (SR, SEG, PR)
Simple result type for error handling.
auto validate_source_references_exist(const core::dicom_dataset &dataset) -> validation_result
std::map< std::string, source_reference > source_links_
std::map< std::string, ai_result_info > ai_results_cache_
std::shared_ptr< storage::index_database > database_
impl(std::shared_ptr< storage::storage_interface > storage, std::shared_ptr< storage::index_database > database)
auto store_ai_result(const core::dicom_dataset &dataset, ai_result_type type) -> VoidResult
std::shared_ptr< storage::storage_interface > storage_
auto validate_common_tags(const core::dicom_dataset &dataset) -> validation_result
void set_pre_store_validator(pre_store_validator validator)
Set custom pre-storage validator.
auto receive_presentation_state(const core::dicom_dataset &pr) -> VoidResult
Receive and store an AI-generated Presentation State.
auto get_source_reference(std::string_view result_uid) -> Result< source_reference >
Get source references for an AI result.
auto remove(std::string_view sop_instance_uid) -> VoidResult
Remove an AI result and its source links.
auto receive_structured_report(const core::dicom_dataset &sr) -> VoidResult
Receive and store an AI-generated Structured Report.
auto exists(std::string_view sop_instance_uid) const -> bool
Check if an AI result exists.
auto find_ai_results_by_type(std::string_view study_instance_uid, ai_result_type type) -> Result< std::vector< ai_result_info > >
Find AI results by type.
auto receive_segmentation(const core::dicom_dataset &seg) -> VoidResult
Receive and store an AI-generated Segmentation object.
ai_result_handler(const ai_result_handler &)=delete
auto remove_ai_results_for_study(std::string_view study_instance_uid) -> Result< std::size_t >
Remove all AI results linked to a study.
static auto create(std::shared_ptr< storage::storage_interface > storage, std::shared_ptr< storage::index_database > database) -> std::unique_ptr< ai_result_handler >
Create an AI result handler.
auto find_ai_results_for_study(std::string_view study_instance_uid) -> Result< std::vector< ai_result_info > >
Find all AI results linked to a study.
auto get_ai_result_info(std::string_view sop_instance_uid) -> std::optional< ai_result_info >
Get AI result information by SOP Instance UID.
void set_received_callback(ai_result_received_callback callback)
Set callback for AI result reception notifications.
auto validate_segmentation(const core::dicom_dataset &seg) -> validation_result
Validate segmentation data integrity.
auto get_cad_findings(std::string_view sr_sop_instance_uid) -> Result< std::vector< cad_finding > >
Extract CAD findings from a Structured Report.
auto link_to_source(std::string_view result_uid, std::string_view source_study_uid) -> VoidResult
Link an AI result to its source study.
auto validate_presentation_state(const core::dicom_dataset &pr) -> validation_result
Validate Presentation State.
auto get_segment_info(std::string_view seg_sop_instance_uid) -> Result< std::vector< segment_info > >
Get segment information from a stored Segmentation.
virtual ~ai_result_handler()
Destructor.
auto get_config() const -> ai_handler_config
Get current configuration.
auto validate_sr_template(const core::dicom_dataset &sr) -> validation_result
Validate SR template conformance.
Compile-time constants for commonly used DICOM tags.
constexpr std::string_view pseudo_color_softcopy_ps
constexpr std::string_view comprehensive_3d_sr
constexpr std::string_view grayscale_softcopy_ps
constexpr std::string_view enhanced_sr
constexpr std::string_view comprehensive_sr
constexpr std::string_view blending_softcopy_ps
constexpr std::string_view basic_text_sr
constexpr std::string_view color_softcopy_ps
constexpr std::string_view segmentation
ai_result_type
Types of AI-generated DICOM objects.
@ structured_report
DICOM SR (Structured Report)
@ segmentation
DICOM SEG (Segmentation)
@ presentation_state
DICOM PR (Presentation State)
@ invalid_template
SR template conformance failed.
@ invalid_segment_data
Segmentation data is malformed.
@ valid
All validations passed.
@ invalid_reference
Referenced source images not found.
@ missing_required_tags
Required DICOM tags are missing.
kcenon::common::VoidResult VoidResult
Result type alias for void operations.
std::function< void(const ai_result_info &info)> ai_result_received_callback
Callback for notification when AI result is received.
std::function< bool( const core::dicom_dataset &dataset, ai_result_type type)> pre_store_validator
Callback for pre-storage validation.
constexpr dicom_tag sop_instance_uid
SOP Instance UID.
constexpr dicom_tag modality
Modality.
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.
Configuration for AI result handler.
bool auto_link_to_source
Whether to auto-link results to source studies.
bool validate_source_references
Whether to validate source references exist in storage.
Information about an AI result stored in the system.
Source reference linking AI result to original images.
std::string study_instance_uid
Study Instance UID.
Validation result containing status and details.
std::vector< std::string > missing_tags
List of missing required tags (if applicable)
validation_status status
Overall validation status.
std::optional< std::string > error_message
Detailed error message if validation failed.
std::vector< std::string > invalid_references
List of invalid references (if applicable)
std::string_view uid
std::string_view name