PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
kcenon::pacs::web::metadata_service Class Reference

Service for selective metadata retrieval and series navigation. More...

#include <metadata_service.h>

Collaboration diagram for kcenon::pacs::web::metadata_service:
Collaboration graph

Public Member Functions

 metadata_service (std::shared_ptr< storage::index_database > database)
 Construct metadata service with database.
 
 ~metadata_service ()
 Destructor.
 
 metadata_service (const metadata_service &)=delete
 Non-copyable, non-movable.
 
metadata_serviceoperator= (const metadata_service &)=delete
 
 metadata_service (metadata_service &&)=delete
 
metadata_serviceoperator= (metadata_service &&)=delete
 
metadata_response get_metadata (std::string_view sop_instance_uid, const metadata_request &request)
 Get selective metadata for an instance.
 
sorted_instances_response get_sorted_instances (std::string_view series_uid, sort_order order=sort_order::position, bool ascending=true)
 Get sorted instances for a series.
 
navigation_info get_navigation (std::string_view sop_instance_uid)
 Get navigation info for an instance.
 
voi_lut_info get_voi_lut (std::string_view sop_instance_uid)
 Get VOI LUT info from an instance.
 
frame_info get_frame_info (std::string_view sop_instance_uid)
 Get frame information for an instance.
 

Static Public Member Functions

static std::unordered_set< std::string > get_preset_tags (metadata_preset preset)
 Get tags for a specific preset.
 
static std::vector< window_level_presetget_window_level_presets (std::string_view modality)
 Get window/level presets for a modality.
 

Private Member Functions

std::unordered_map< std::string, std::string > read_dicom_tags (std::string_view file_path, const std::unordered_set< std::string > &requested_tags, bool include_private)
 Read DICOM dataset from file.
 
std::optional< std::string > get_series_uid (std::string_view sop_instance_uid)
 Get series UID for an instance.
 

Private Attributes

std::shared_ptr< storage::index_databasedatabase_
 Database for instance lookups.
 

Detailed Description

Service for selective metadata retrieval and series navigation.

Provides APIs for:

  • Selective DICOM tag retrieval with presets
  • Series instance sorting and navigation
  • Window/Level preset management
  • Multi-frame information

Definition at line 344 of file metadata_service.h.

Constructor & Destructor Documentation

◆ metadata_service() [1/3]

kcenon::pacs::web::metadata_service::metadata_service ( std::shared_ptr< storage::index_database > database)
explicit

Construct metadata service with database.

Parameters
databaseIndex database for instance lookups

Definition at line 172 of file metadata_service.cpp.

174 : database_(std::move(database)) {}
std::shared_ptr< storage::index_database > database_
Database for instance lookups.

◆ ~metadata_service()

kcenon::pacs::web::metadata_service::~metadata_service ( )
default

Destructor.

◆ metadata_service() [2/3]

kcenon::pacs::web::metadata_service::metadata_service ( const metadata_service & )
delete

Non-copyable, non-movable.

◆ metadata_service() [3/3]

kcenon::pacs::web::metadata_service::metadata_service ( metadata_service && )
delete

Member Function Documentation

◆ get_frame_info()

frame_info kcenon::pacs::web::metadata_service::get_frame_info ( std::string_view sop_instance_uid)
nodiscard

Get frame information for an instance.

Parameters
sop_instance_uidSOP Instance UID
Returns
Frame information

Definition at line 641 of file metadata_service.cpp.

641 {
642 if (database_ == nullptr) {
643 return frame_info::error("Database not configured");
644 }
645
646 auto instance = database_->find_instance(sop_instance_uid);
647 if (!instance) {
648 return frame_info::error("Instance not found");
649 }
650
651 if (!std::filesystem::exists(instance->file_path)) {
652 return frame_info::error("DICOM file not found");
653 }
654
655 auto file_result =
656 kcenon::pacs::core::dicom_file::open(std::filesystem::path(instance->file_path));
657 if (file_result.is_err()) {
658 return frame_info::error("Failed to open DICOM file");
659 }
660
661 const auto& ds = file_result.value().dataset();
662
663 frame_info info = frame_info::ok();
664
665 // Number of Frames (0028,0008)
666 const auto* nf_elem = ds.get(kcenon::pacs::core::dicom_tag{0x0028, 0x0008});
667 if (nf_elem != nullptr) {
668 auto nf_result = nf_elem->as_string();
669 if (nf_result.is_ok()) {
670 try {
671 info.total_frames = static_cast<uint32_t>(std::stoul(nf_result.value()));
672 } catch (...) {
673 info.total_frames = 1;
674 }
675 }
676 }
677
678 // Frame Time (0018,1063) - in milliseconds
679 const auto* ft_elem = ds.get(kcenon::pacs::core::dicom_tag{0x0018, 0x1063});
680 if (ft_elem != nullptr) {
681 auto ft_result = ft_elem->as_string();
682 if (ft_result.is_ok()) {
683 try {
684 info.frame_time = std::stod(ft_result.value());
685 if (info.frame_time.has_value() && info.frame_time.value() > 0) {
686 info.frame_rate = 1000.0 / info.frame_time.value();
687 }
688 } catch (...) {
689 }
690 }
691 }
692
693 // Rows
694 auto rows_opt = ds.get_numeric<uint16_t>(kcenon::pacs::core::tags::rows);
695 if (rows_opt.has_value()) {
696 info.rows = rows_opt.value();
697 }
698
699 // Columns
700 auto cols_opt = ds.get_numeric<uint16_t>(kcenon::pacs::core::tags::columns);
701 if (cols_opt.has_value()) {
702 info.columns = cols_opt.value();
703 }
704
705 return info;
706}
static auto open(const std::filesystem::path &path) -> kcenon::pacs::Result< dicom_file >
Open and read a DICOM file from disk.
constexpr dicom_tag rows
Rows.
constexpr dicom_tag columns
Columns.
static frame_info ok()
Create a success result.
static frame_info error(std::string message)
Create an error result.

References kcenon::pacs::core::tags::columns, database_, kcenon::pacs::web::frame_info::error(), kcenon::pacs::web::frame_info::ok(), kcenon::pacs::core::dicom_file::open(), and kcenon::pacs::core::tags::rows.

Here is the call graph for this function:

◆ get_metadata()

metadata_response kcenon::pacs::web::metadata_service::get_metadata ( std::string_view sop_instance_uid,
const metadata_request & request )
nodiscard

Get selective metadata for an instance.

Parameters
sop_instance_uidSOP Instance UID
requestMetadata request parameters
Returns
Metadata response with requested tags

Definition at line 259 of file metadata_service.cpp.

260 {
261 if (database_ == nullptr) {
262 return metadata_response::error("Database not configured");
263 }
264
265 // Find instance
266 auto instance = database_->find_instance(sop_instance_uid);
267 if (!instance) {
268 return metadata_response::error("Instance not found");
269 }
270
271 // Check file exists
272 if (!std::filesystem::exists(instance->file_path)) {
273 return metadata_response::error("DICOM file not found");
274 }
275
276 // Build set of requested tags
277 std::unordered_set<std::string> requested_tags;
278
279 // Add preset tags if specified
280 if (request.preset.has_value()) {
281 auto preset_tags = get_preset_tags(request.preset.value());
282 requested_tags.insert(preset_tags.begin(), preset_tags.end());
283 }
284
285 // Add explicitly requested tags
286 for (const auto& tag : request.tags) {
287 requested_tags.insert(tag);
288 }
289
290 // If no tags specified, return error
291 if (requested_tags.empty()) {
293 "No tags specified: provide 'tags' or 'preset' parameter");
294 }
295
296 // Read and filter DICOM tags
297 auto tag_values =
298 read_dicom_tags(instance->file_path, requested_tags, request.include_private);
299
300 return metadata_response::ok(std::move(tag_values));
301}
static std::unordered_set< std::string > get_preset_tags(metadata_preset preset)
Get tags for a specific preset.
std::unordered_map< std::string, std::string > read_dicom_tags(std::string_view file_path, const std::unordered_set< std::string > &requested_tags, bool include_private)
Read DICOM dataset from file.
static metadata_response error(std::string message)
Create an error result.
static metadata_response ok(std::unordered_map< std::string, std::string > tag_map)
Create a success result.

References database_, kcenon::pacs::web::metadata_response::error(), get_preset_tags(), kcenon::pacs::web::metadata_request::include_private, kcenon::pacs::web::metadata_response::ok(), kcenon::pacs::web::metadata_request::preset, read_dicom_tags(), and kcenon::pacs::web::metadata_request::tags.

Here is the call graph for this function:

◆ get_navigation()

navigation_info kcenon::pacs::web::metadata_service::get_navigation ( std::string_view sop_instance_uid)
nodiscard

Get navigation info for an instance.

Parameters
sop_instance_uidSOP Instance UID
Returns
Navigation info with previous/next instance UIDs

Definition at line 480 of file metadata_service.cpp.

481 {
482 if (database_ == nullptr) {
483 return navigation_info::error("Database not configured");
484 }
485
486 // Get series UID for this instance
487 auto series_uid_opt = get_series_uid(sop_instance_uid);
488 if (!series_uid_opt.has_value()) {
489 return navigation_info::error("Instance not found");
490 }
491
492 // Get sorted instances
493 auto sorted_result =
494 get_sorted_instances(series_uid_opt.value(), sort_order::position, true);
495 if (!sorted_result.success) {
496 return navigation_info::error(sorted_result.error_message);
497 }
498
499 const auto& instances = sorted_result.instances;
500 if (instances.empty()) {
501 return navigation_info::error("Series is empty");
502 }
503
504 // Find current instance index
505 size_t current_index = 0;
506 bool found = false;
507 for (size_t i = 0; i < instances.size(); ++i) {
508 if (instances[i].sop_instance_uid == sop_instance_uid) {
509 current_index = i;
510 found = true;
511 break;
512 }
513 }
514
515 if (!found) {
516 return navigation_info::error("Instance not found in series");
517 }
518
519 // Build navigation info
520 navigation_info nav = navigation_info::ok();
521 nav.index = current_index;
522 nav.total = instances.size();
523 nav.first = instances.front().sop_instance_uid;
524 nav.last = instances.back().sop_instance_uid;
525
526 if (current_index > 0) {
527 nav.previous = instances[current_index - 1].sop_instance_uid;
528 }
529
530 if (current_index < instances.size() - 1) {
531 nav.next = instances[current_index + 1].sop_instance_uid;
532 }
533
534 return nav;
535}
sorted_instances_response get_sorted_instances(std::string_view series_uid, sort_order order=sort_order::position, bool ascending=true)
Get sorted instances for a series.
std::optional< std::string > get_series_uid(std::string_view sop_instance_uid)
Get series UID for an instance.
@ position
Sort by ImagePositionPatient/SliceLocation.
static navigation_info error(std::string message)
Create an error result.
static navigation_info ok()
Create a success result.

References database_, kcenon::pacs::web::navigation_info::error(), kcenon::pacs::web::navigation_info::first, get_series_uid(), get_sorted_instances(), kcenon::pacs::web::navigation_info::index, kcenon::pacs::web::navigation_info::last, kcenon::pacs::web::navigation_info::next, kcenon::pacs::web::navigation_info::ok(), kcenon::pacs::web::position, kcenon::pacs::web::navigation_info::previous, and kcenon::pacs::web::navigation_info::total.

Here is the call graph for this function:

◆ get_preset_tags()

std::unordered_set< std::string > kcenon::pacs::web::metadata_service::get_preset_tags ( metadata_preset preset)
staticnodiscard

Get tags for a specific preset.

Parameters
presetThe metadata preset
Returns
Set of DICOM tags (hex format) for the preset

Definition at line 182 of file metadata_service.cpp.

183 {
184 using namespace kcenon::pacs::core::tags;
185
186 std::unordered_set<std::string> tags;
187
188 switch (preset) {
190 // Rows, Columns, BitsAllocated, BitsStored, HighBit,
191 // PixelRepresentation, PhotometricInterpretation, SamplesPerPixel
192 tags.insert(tag_to_hex(rows));
193 tags.insert(tag_to_hex(columns));
194 tags.insert(tag_to_hex(bits_allocated));
195 tags.insert(tag_to_hex(bits_stored));
196 tags.insert(tag_to_hex(high_bit));
197 tags.insert(tag_to_hex(pixel_representation));
198 tags.insert(tag_to_hex(photometric_interpretation));
199 tags.insert(tag_to_hex(samples_per_pixel));
200 break;
201
203 // WindowCenter, WindowWidth, RescaleSlope, RescaleIntercept
204 tags.insert(tag_to_hex(window_center));
205 tags.insert(tag_to_hex(window_width));
206 tags.insert(tag_to_hex(rescale_slope));
207 tags.insert(tag_to_hex(rescale_intercept));
208 // Window Explanation (0028,1055)
209 tags.insert("00281055");
210 // VOI LUT Sequence (0028,3010)
211 tags.insert("00283010");
212 break;
213
215 // PatientName, PatientID, PatientBirthDate, PatientSex, PatientAge
216 tags.insert(tag_to_hex(patient_name));
217 tags.insert(tag_to_hex(patient_id));
218 tags.insert(tag_to_hex(patient_birth_date));
219 tags.insert(tag_to_hex(patient_sex));
220 tags.insert(tag_to_hex(patient_age));
221 break;
222
224 // KVP (0018,0060), ExposureTime (0018,1150), XRayTubeCurrent
225 // (0018,1151) SliceThickness (0018,0050), SpacingBetweenSlices
226 // (0018,0088)
227 tags.insert("00180060"); // KVP
228 tags.insert("00181150"); // ExposureTime
229 tags.insert("00181151"); // XRayTubeCurrent
230 tags.insert("00180050"); // SliceThickness
231 tags.insert("00180088"); // SpacingBetweenSlices
232 break;
233
235 // ImagePositionPatient, ImageOrientationPatient, SliceLocation,
236 // PixelSpacing
237 tags.insert(tag_to_hex(image_position_patient));
238 tags.insert(tag_to_hex(image_orientation_patient));
239 tags.insert(tag_to_hex(slice_location));
240 tags.insert(tag_to_hex(pixel_spacing));
241 break;
242
244 // NumberOfFrames (0028,0008), FrameIncrementPointer (0028,0009),
245 // FrameTime (0018,1063)
246 tags.insert("00280008"); // NumberOfFrames
247 tags.insert("00280009"); // FrameIncrementPointer
248 tags.insert("00181063"); // FrameTime
249 break;
250 }
251
252 return tags;
253}
@ image_display
Rows, Columns, Bits, PhotometricInterpretation.
@ patient_info
Patient demographics.
@ acquisition
KVP, ExposureTime, SliceThickness.
@ positioning
ImagePosition, ImageOrientation, PixelSpacing.
@ multiframe
NumberOfFrames, FrameTime.
@ window_level
WindowCenter, WindowWidth, Rescale values.

References kcenon::pacs::web::acquisition, kcenon::pacs::web::image_display, kcenon::pacs::web::multiframe, kcenon::pacs::web::patient_info, kcenon::pacs::web::positioning, and kcenon::pacs::web::window_level.

Referenced by get_metadata().

Here is the caller graph for this function:

◆ get_series_uid()

std::optional< std::string > kcenon::pacs::web::metadata_service::get_series_uid ( std::string_view sop_instance_uid)
nodiscardprivate

Get series UID for an instance.

Parameters
sop_instance_uidSOP Instance UID
Returns
Series UID if found

Definition at line 348 of file metadata_service.cpp.

349 {
350 if (database_ == nullptr) {
351 return std::nullopt;
352 }
353
354 auto instance = database_->find_instance(sop_instance_uid);
355 if (!instance) {
356 return std::nullopt;
357 }
358
359 // Get series from series_pk
360 auto series = database_->find_series_by_pk(instance->series_pk);
361 if (!series) {
362 return std::nullopt;
363 }
364
365 return series->series_uid;
366}

References database_.

Referenced by get_navigation().

Here is the caller graph for this function:

◆ get_sorted_instances()

sorted_instances_response kcenon::pacs::web::metadata_service::get_sorted_instances ( std::string_view series_uid,
sort_order order = sort_order::position,
bool ascending = true )
nodiscard

Get sorted instances for a series.

Parameters
series_uidSeries Instance UID
orderSort order
ascendingSort direction
Returns
Sorted instances response

Definition at line 368 of file metadata_service.cpp.

369 {
370 if (database_ == nullptr) {
371 return sorted_instances_response::error("Database not configured");
372 }
373
374 // Get all instances in the series
375 auto instances_result = database_->list_instances(series_uid);
376 if (instances_result.is_err()) {
377 return sorted_instances_response::error("Failed to list instances");
378 }
379
380 auto& instances = instances_result.value();
381 if (instances.empty()) {
382 return sorted_instances_response::error("Series not found or empty");
383 }
384
385 // Build sorted_instance list with additional DICOM metadata
386 std::vector<sorted_instance> sorted;
387 sorted.reserve(instances.size());
388
389 for (const auto& inst : instances) {
390 sorted_instance si;
391 si.sop_instance_uid = inst.sop_uid;
392 si.instance_number = inst.instance_number;
393
394 // Read additional positioning data from DICOM file if exists
395 if (std::filesystem::exists(inst.file_path)) {
396 auto file_result =
397 kcenon::pacs::core::dicom_file::open(std::filesystem::path(inst.file_path));
398 if (file_result.is_ok()) {
399 const auto& ds = file_result.value().dataset();
400
401 // Slice location
402 auto slice_str = ds.get_string(kcenon::pacs::core::tags::slice_location);
403 if (!slice_str.empty()) {
404 try {
405 si.slice_location = std::stod(slice_str);
406 } catch (...) {
407 }
408 }
409
410 // Image position patient
411 auto pos_str =
413 if (!pos_str.empty()) {
414 si.image_position_patient = parse_numeric_list(pos_str);
415 }
416
417 // Acquisition time
418 auto time_str = ds.get_string(kcenon::pacs::core::tags::acquisition_time);
419 if (!time_str.empty()) {
420 si.acquisition_time = time_str;
421 }
422 }
423 }
424
425 sorted.push_back(std::move(si));
426 }
427
428 // Sort based on order
429 auto compare = [order, ascending](const sorted_instance& a,
430 const sorted_instance& b) {
431 bool result = false;
432
433 switch (order) {
435 // Sort by slice location or Z position
436 double a_pos = 0.0;
437 double b_pos = 0.0;
438
439 if (a.slice_location.has_value()) {
440 a_pos = a.slice_location.value();
441 } else if (a.image_position_patient.has_value() &&
442 a.image_position_patient->size() >= 3) {
443 a_pos = (*a.image_position_patient)[2]; // Z position
444 }
445
446 if (b.slice_location.has_value()) {
447 b_pos = b.slice_location.value();
448 } else if (b.image_position_patient.has_value() &&
449 b.image_position_patient->size() >= 3) {
450 b_pos = (*b.image_position_patient)[2];
451 }
452
453 result = a_pos < b_pos;
454 break;
455 }
456
458 int a_num = a.instance_number.value_or(0);
459 int b_num = b.instance_number.value_or(0);
460 result = a_num < b_num;
461 break;
462 }
463
465 std::string a_time = a.acquisition_time.value_or("");
466 std::string b_time = b.acquisition_time.value_or("");
467 result = a_time < b_time;
468 break;
469 }
470 }
471
472 return ascending ? result : !result;
473 };
474
475 std::sort(sorted.begin(), sorted.end(), compare);
476
477 return sorted_instances_response::ok(std::move(sorted), instances.size());
478}
constexpr dicom_tag slice_location
Slice Location.
constexpr dicom_tag image_position_patient
Image Position (Patient)
constexpr dicom_tag acquisition_time
Acquisition Time.
@ instance_number
Sort by InstanceNumber.
@ acquisition_time
Sort by AcquisitionTime.
static sorted_instances_response ok(std::vector< sorted_instance > inst, size_t count)
Create a success result.
static sorted_instances_response error(std::string message)
Create an error result.

References kcenon::pacs::core::tags::acquisition_time, kcenon::pacs::web::acquisition_time, kcenon::pacs::web::sorted_instance::acquisition_time, database_, kcenon::pacs::web::sorted_instances_response::error(), kcenon::pacs::core::tags::image_position_patient, kcenon::pacs::web::sorted_instance::image_position_patient, kcenon::pacs::web::instance_number, kcenon::pacs::web::sorted_instance::instance_number, kcenon::pacs::web::sorted_instances_response::ok(), kcenon::pacs::core::dicom_file::open(), kcenon::pacs::web::position, kcenon::pacs::core::tags::slice_location, kcenon::pacs::web::sorted_instance::slice_location, and kcenon::pacs::web::sorted_instance::sop_instance_uid.

Referenced by get_navigation().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_voi_lut()

voi_lut_info kcenon::pacs::web::metadata_service::get_voi_lut ( std::string_view sop_instance_uid)
nodiscard

Get VOI LUT info from an instance.

Parameters
sop_instance_uidSOP Instance UID
Returns
VOI LUT information

Definition at line 570 of file metadata_service.cpp.

570 {
571 if (database_ == nullptr) {
572 return voi_lut_info::error("Database not configured");
573 }
574
575 auto instance = database_->find_instance(sop_instance_uid);
576 if (!instance) {
577 return voi_lut_info::error("Instance not found");
578 }
579
580 if (!std::filesystem::exists(instance->file_path)) {
581 return voi_lut_info::error("DICOM file not found");
582 }
583
584 auto file_result =
585 kcenon::pacs::core::dicom_file::open(std::filesystem::path(instance->file_path));
586 if (file_result.is_err()) {
587 return voi_lut_info::error("Failed to open DICOM file");
588 }
589
590 const auto& ds = file_result.value().dataset();
591
592 voi_lut_info info = voi_lut_info::ok();
593
594 // Window Center
595 auto wc_str = ds.get_string(kcenon::pacs::core::tags::window_center);
596 if (!wc_str.empty()) {
597 info.window_center = parse_numeric_list(wc_str);
598 }
599
600 // Window Width
601 auto ww_str = ds.get_string(kcenon::pacs::core::tags::window_width);
602 if (!ww_str.empty()) {
603 info.window_width = parse_numeric_list(ww_str);
604 }
605
606 // Window Explanation (0028,1055)
607 const auto* we_elem =
608 ds.get(kcenon::pacs::core::dicom_tag{0x0028, 0x1055});
609 if (we_elem != nullptr) {
610 auto we_result = we_elem->as_string();
611 if (we_result.is_ok()) {
612 info.window_explanations = parse_string_list(we_result.value());
613 }
614 }
615
616 // Rescale Slope
617 auto rs_str = ds.get_string(kcenon::pacs::core::tags::rescale_slope);
618 if (!rs_str.empty()) {
619 try {
620 info.rescale_slope = std::stod(rs_str);
621 } catch (...) {
622 }
623 }
624
625 // Rescale Intercept
626 auto ri_str = ds.get_string(kcenon::pacs::core::tags::rescale_intercept);
627 if (!ri_str.empty()) {
628 try {
629 info.rescale_intercept = std::stod(ri_str);
630 } catch (...) {
631 }
632 }
633
634 return info;
635}
constexpr dicom_tag window_width
Window Width.
constexpr dicom_tag window_center
Window Center.
constexpr dicom_tag rescale_intercept
Rescale Intercept.
constexpr dicom_tag rescale_slope
Rescale Slope.
static voi_lut_info error(std::string message)
Create an error result.
static voi_lut_info ok()
Create a success result.

References database_, kcenon::pacs::web::voi_lut_info::error(), kcenon::pacs::web::voi_lut_info::ok(), kcenon::pacs::core::dicom_file::open(), kcenon::pacs::core::tags::rescale_intercept, kcenon::pacs::core::tags::rescale_slope, kcenon::pacs::core::tags::window_center, and kcenon::pacs::core::tags::window_width.

Here is the call graph for this function:

◆ get_window_level_presets()

std::vector< window_level_preset > kcenon::pacs::web::metadata_service::get_window_level_presets ( std::string_view modality)
staticnodiscard

Get window/level presets for a modality.

Parameters
modalityModality code (CT, MR, etc.)
Returns
Vector of window/level presets

Definition at line 541 of file metadata_service.cpp.

542 {
543 std::vector<window_level_preset> presets;
544
545 if (modality == "CT") {
546 presets.push_back({"Lung", -600, 1500});
547 presets.push_back({"Bone", 300, 1500});
548 presets.push_back({"Soft Tissue", 40, 400});
549 presets.push_back({"Brain", 40, 80});
550 presets.push_back({"Liver", 60, 150});
551 presets.push_back({"Mediastinum", 50, 350});
552 } else if (modality == "MR") {
553 presets.push_back({"T1 Brain", 600, 1200});
554 presets.push_back({"T2 Brain", 700, 1400});
555 presets.push_back({"Spine", 500, 1000});
556 } else if (modality == "CR" || modality == "DX") {
557 presets.push_back({"Default", 2048, 4096});
558 presets.push_back({"Bone", 1500, 3000});
559 presets.push_back({"Soft Tissue", 1800, 3600});
560 } else if (modality == "US") {
561 presets.push_back({"Default", 128, 256});
562 } else {
563 // Generic presets
564 presets.push_back({"Default", 128, 256});
565 }
566
567 return presets;
568}

Referenced by kcenon::pacs::web::endpoints::register_metadata_endpoints_impl().

Here is the caller graph for this function:

◆ operator=() [1/2]

metadata_service & kcenon::pacs::web::metadata_service::operator= ( const metadata_service & )
delete

◆ operator=() [2/2]

metadata_service & kcenon::pacs::web::metadata_service::operator= ( metadata_service && )
delete

◆ read_dicom_tags()

std::unordered_map< std::string, std::string > kcenon::pacs::web::metadata_service::read_dicom_tags ( std::string_view file_path,
const std::unordered_set< std::string > & requested_tags,
bool include_private )
nodiscardprivate

Read DICOM dataset from file.

Parameters
file_pathPath to DICOM file
Returns
Map of tag values

Definition at line 303 of file metadata_service.cpp.

306 {
307 std::unordered_map<std::string, std::string> result;
308
309 // Open DICOM file
310 auto file_result = kcenon::pacs::core::dicom_file::open(std::filesystem::path(file_path));
311 if (file_result.is_err()) {
312 return result; // Return empty map on failure
313 }
314
315 const auto& dataset = file_result.value().dataset();
316
317 // Convert requested tags to dicom_tag and extract values
318 for (const auto& tag_hex : requested_tags) {
319 auto tag_opt = hex_to_tag(tag_hex);
320 if (!tag_opt.has_value()) {
321 continue;
322 }
323
324 auto tag = tag_opt.value();
325
326 // Skip private tags if not requested
327 if (tag.is_private() && !include_private) {
328 continue;
329 }
330
331 // Get element value
332 const auto* elem = dataset.get(tag);
333 if (elem != nullptr) {
334 auto str_result = elem->as_string();
335 if (str_result.is_ok()) {
336 result[tag_hex] = str_result.value();
337 }
338 }
339 }
340
341 return result;
342}

References kcenon::pacs::core::dicom_tag::is_private(), and kcenon::pacs::core::dicom_file::open().

Referenced by get_metadata().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ database_

std::shared_ptr<storage::index_database> kcenon::pacs::web::metadata_service::database_
private

Database for instance lookups.

Definition at line 440 of file metadata_service.h.

Referenced by get_frame_info(), get_metadata(), get_navigation(), get_series_uid(), get_sorted_instances(), and get_voi_lut().


The documentation for this class was generated from the following files: