44enum class output_format { human_readable, json, xml };
50 std::filesystem::path path;
51 std::vector<std::string> filter_tags;
52 std::string search_keyword;
53 bool pixel_info{
false};
54 output_format format{output_format::human_readable};
55 bool recursive{
false};
62 bool show_private{
false};
63 std::string charset{
"UTF-8"};
70 size_t total_files{0};
71 size_t valid_files{0};
72 size_t invalid_files{0};
73 std::map<std::string, size_t> modalities;
74 std::map<std::string, size_t> sop_classes;
81void print_usage(
const char* program_name) {
83DICOM Dump - File Inspection Utility
85Usage: )" << program_name
86 << R"( <path> [options]
89 path DICOM file or directory to inspect
92 -h, --help Show this help message
93 -v, --verbose Verbose output mode
94 -q, --quiet Minimal output mode (errors only)
95 -f, --format <format> Output format: text (default), json, xml
96 -t, --tag <tag> Output specific tag only (e.g., 0010,0010)
97 --tags <list> Show only specific tags (comma-separated keywords)
98 Example: --tags PatientName,PatientID,StudyDate
99 -s, --search <keyword> Search by tag name (case-insensitive)
100 -d, --depth <n> Limit sequence output depth (default: unlimited)
101 --pixel-info Show pixel data information
102 --no-pixel Exclude pixel data from output
103 --show-private Show private tags (hidden by default)
104 --charset <charset> Specify character set (default: UTF-8)
105 --recursive, -r Recursively scan directories
106 --summary Show summary only (for directories)
107 --no-meta Don't show File Meta Information
113 << R"( image.dcm --tags PatientName,PatientID,StudyDate
115 << R"( image.dcm -t 0010,0010
117 << R"( image.dcm --search Patient
119 << R"( image.dcm --pixel-info
121 << R"( image.dcm --format json
123 << R"( image.dcm --format xml
125 << R"( image.dcm -d 2 # Limit sequence depth to 2
127 << R"( ./dicom_folder/ --recursive --summary
130 Human-readable (text) output shows tags in the format:
131 (GGGG,EEEE) VR Keyword [value]
133 JSON output provides DICOM PS3.18-compatible structured data.
134 XML output provides DICOM Native XML format (PS3.19).
137 0 Success - File(s) parsed successfully
138 1 Error - Invalid arguments
139 2 Error - File not found or invalid DICOM file
155std::string parse_tag_string(
const std::string& tag_str) {
156 std::string s = tag_str;
158 if (!s.empty() && s.front() ==
'(') s.erase(0, 1);
159 if (!s.empty() && s.back() ==
')') s.pop_back();
161 if (s.length() == 9 && s[4] ==
',') {
163 }
else if (s.length() == 8) {
164 return s.substr(0, 4) +
"," + s.substr(4, 4);
169bool parse_arguments(
int argc,
char* argv[], options& opts) {
174 for (
int i = 1; i < argc; ++i) {
175 std::string arg = argv[i];
177 if (arg ==
"--help" || arg ==
"-h") {
179 }
else if (arg ==
"--tags" && i + 1 < argc) {
181 std::string tags_str = argv[++i];
182 std::stringstream ss(tags_str);
184 while (std::getline(ss, tag,
',')) {
186 tag.erase(0, tag.find_first_not_of(
" \t"));
187 tag.erase(tag.find_last_not_of(
" \t") + 1);
189 opts.filter_tags.push_back(tag);
192 }
else if ((arg ==
"--tag" || arg ==
"-t") && i + 1 < argc) {
194 std::string tag_str = parse_tag_string(argv[++i]);
195 if (tag_str.empty()) {
196 std::cerr <<
"Error: Invalid tag format. Use GGGG,EEEE (e.g., 0010,0010)\n";
199 opts.filter_tags.push_back(tag_str);
200 }
else if ((arg ==
"--search" || arg ==
"-s") && i + 1 < argc) {
201 opts.search_keyword = argv[++i];
202 }
else if ((arg ==
"--depth" || arg ==
"-d") && i + 1 < argc) {
204 opts.max_depth = std::stoi(argv[++i]);
205 if (opts.max_depth < 0) {
206 std::cerr <<
"Error: Depth must be non-negative\n";
210 std::cerr <<
"Error: Invalid depth value\n";
213 }
else if (arg ==
"--pixel-info") {
214 opts.pixel_info =
true;
215 }
else if (arg ==
"--no-pixel") {
216 opts.no_pixel =
true;
217 }
else if (arg ==
"--show-private") {
218 opts.show_private =
true;
219 }
else if (arg ==
"--charset" && i + 1 < argc) {
220 opts.charset = argv[++i];
221 }
else if ((arg ==
"--format" || arg ==
"-f") && i + 1 < argc) {
222 std::string fmt = argv[++i];
224 opts.format = output_format::json;
225 }
else if (fmt ==
"human" || fmt ==
"text") {
226 opts.format = output_format::human_readable;
227 }
else if (fmt ==
"xml") {
228 opts.format = output_format::xml;
230 std::cerr <<
"Error: Unknown format '" << fmt <<
"'. Use: text, json, xml\n";
233 }
else if (arg ==
"--recursive" || arg ==
"-r") {
234 opts.recursive =
true;
235 }
else if (arg ==
"--summary") {
237 }
else if (arg ==
"--no-meta") {
238 opts.show_meta =
false;
239 }
else if (arg ==
"--verbose" || arg ==
"-v") {
241 }
else if (arg ==
"--quiet" || arg ==
"-q") {
243 }
else if (arg[0] ==
'-') {
244 std::cerr <<
"Error: Unknown option '" << arg <<
"'\n";
246 }
else if (opts.path.empty()) {
249 std::cerr <<
"Error: Multiple paths specified\n";
254 if (opts.path.empty()) {
255 std::cerr <<
"Error: No path specified\n";
261 opts.verbose =
false;
272std::string json_escape(
const std::string& str) {
273 std::ostringstream oss;
298 if (
static_cast<unsigned char>(c) < 0x20) {
299 oss <<
"\\u" << std::hex << std::setfill(
'0') << std::setw(4)
300 <<
static_cast<int>(c);
315std::string format_hex(std::span<const uint8_t> data,
size_t max_bytes = 32) {
316 std::ostringstream oss;
317 size_t count = std::min(data.size(), max_bytes);
319 for (
size_t i = 0; i < count; ++i) {
323 oss << std::hex << std::setfill(
'0') << std::setw(2)
324 <<
static_cast<int>(data[i]);
327 if (data.size() > max_bytes) {
341 size_t max_length = 64) {
344 auto vr = element.
vr();
354 return "SQ (" + std::to_string(items.size()) +
" items)";
360 return "[" + format_hex(data) +
"] (" + std::to_string(data.size()) +
367 if (result.is_ok()) {
368 std::string value = result.value();
369 if (value.length() > max_length) {
370 value = value.substr(0, max_length - 3) +
"...";
381 if (
auto val = element.
as_numeric<uint16_t>(); val.is_ok())
382 return std::to_string(val.value());
385 if (
auto val = element.
as_numeric<int16_t>(); val.is_ok())
386 return std::to_string(val.value());
389 if (
auto val = element.
as_numeric<uint32_t>(); val.is_ok())
390 return std::to_string(val.value());
393 if (
auto val = element.
as_numeric<int32_t>(); val.is_ok())
394 return std::to_string(val.value());
397 if (
auto val = element.
as_numeric<
float>(); val.is_ok())
398 return std::to_string(val.value());
401 if (
auto val = element.
as_numeric<
double>(); val.is_ok())
402 return std::to_string(val.value());
405 if (
auto val = element.
as_numeric<uint64_t>(); val.is_ok())
406 return std::to_string(val.value());
409 if (
auto val = element.
as_numeric<int64_t>(); val.is_ok())
410 return std::to_string(val.value());
420 return "[" + format_hex(data) +
"]";
429bool contains_ci(
const std::string& haystack,
const std::string& needle) {
430 if (needle.empty())
return true;
431 if (haystack.empty())
return false;
433 std::string h = haystack;
434 std::string n = needle;
435 std::transform(h.begin(), h.end(), h.begin(), ::tolower);
436 std::transform(n.begin(), n.end(), n.begin(), ::tolower);
437 return h.find(n) != std::string::npos;
446 return (tag.
group() % 2) != 0;
455 return tag.
group() == 0x7FE0 && tag.
element() == 0x0010;
469 if (opts.no_pixel && is_pixel_data_tag(tag)) {
474 if (is_private_tag(tag) && !opts.show_private) {
479 std::string keyword =
info ? std::string(
info->keyword) :
"";
482 if (!opts.search_keyword.empty()) {
483 if (!contains_ci(keyword, opts.search_keyword) &&
484 !contains_ci(tag.
to_string(), opts.search_keyword)) {
490 if (!opts.filter_tags.empty()) {
491 for (
const auto& filter : opts.filter_tags) {
493 if (filter.find(
',') != std::string::npos || filter.length() == 8) {
497 std::string tag_cmp = tag_str.substr(1, tag_str.length() - 2);
498 if (tag_cmp == filter) {
503 if (keyword == filter) {
522 const options& opts,
int current_depth = 0,
int indent = 0) {
526 auto& dict = dicom_dictionary::instance();
527 std::string indent_str(indent * 2,
' ');
529 for (
const auto& [tag, element] : dataset) {
531 if (!should_display_tag(tag, opts, dict)) {
537 std::string keyword =
538 info ? std::string(
info->keyword) :
"UnknownTag";
541 if (is_private_tag(tag)) {
542 keyword =
"Private: " + keyword;
546 std::cout << indent_str << tag.
to_string() <<
" "
547 << to_string(element.
vr()) <<
" " << std::left
548 << std::setw(36 - indent * 2) << keyword;
553 std::cout <<
"(" << items.size() <<
" items)\n";
556 if (opts.max_depth >= 0 && current_depth >= opts.max_depth) {
557 std::cout << indent_str <<
" ... (depth limit reached)\n";
562 for (
const auto& item : items) {
563 std::cout << indent_str <<
" > Item #" << item_num++ <<
"\n";
564 print_dataset_human(item, opts, current_depth + 1, indent + 2);
567 std::cout <<
"[" << format_value(element) <<
"]\n";
580 const options& opts,
int current_depth = 0,
int indent = 2) {
584 auto& dict = dicom_dictionary::instance();
585 std::string indent_str(indent,
' ');
591 for (
const auto& [tag, element] : dataset) {
593 if (!should_display_tag(tag, opts, dict)) {
603 std::string keyword =
604 info ? std::string(
info->keyword) : tag.to_string();
608 tag_key.erase(std::remove(tag_key.begin(), tag_key.end(),
'('), tag_key.end());
609 tag_key.erase(std::remove(tag_key.begin(), tag_key.end(),
')'), tag_key.end());
610 tag_key.erase(std::remove(tag_key.begin(), tag_key.end(),
','), tag_key.end());
612 std::cout << indent_str <<
" \"" << tag_key <<
"\": {\n";
613 std::cout << indent_str <<
" \"vr\": \"" << to_string(element.
vr()) <<
"\"";
616 std::cout <<
",\n" << indent_str <<
" \"Value\": [\n";
619 if (opts.max_depth >= 0 && current_depth >= opts.max_depth) {
620 std::cout << indent_str <<
" { \"_note\": \"depth limit reached\" }\n";
622 const auto& items = element.sequence_items();
623 for (size_t i = 0; i < items.size(); ++i) {
624 std::cout << indent_str <<
" ";
625 print_dataset_json(items[i], opts, current_depth + 1, indent + 6);
626 if (i < items.size() - 1) {
632 std::cout << indent_str <<
" ]\n";
634 std::string value = json_escape(format_value(element, 256));
635 std::cout <<
",\n" << indent_str <<
" \"Value\": [\"" << value <<
"\"]\n";
638 std::cout << indent_str <<
" }";
641 std::cout <<
"\n" << indent_str <<
"}";
649std::string xml_escape(
const std::string& str) {
650 std::ostringstream oss;
653 case '<': oss <<
"<";
break;
654 case '>': oss <<
">";
break;
655 case '&': oss <<
"&";
break;
656 case '"': oss <<
""";
break;
657 case '\'': oss <<
"'";
break;
659 if (
static_cast<unsigned char>(c) < 0x20 && c !=
'\t' && c !=
'\n' && c !=
'\r') {
660 oss <<
"&#" <<
static_cast<int>(
static_cast<unsigned char>(c)) <<
";";
677 const options& opts,
int current_depth = 0,
int indent = 2) {
681 auto& dict = dicom_dictionary::instance();
682 std::string indent_str(indent,
' ');
684 for (
const auto& [tag, element] : dataset) {
686 if (!should_display_tag(tag, opts, dict)) {
691 std::string keyword =
info ? std::string(
info->keyword) :
"UnknownTag";
692 std::string vr_str{to_string(element.
vr())};
696 tag_str.erase(std::remove(tag_str.begin(), tag_str.end(),
'('), tag_str.end());
697 tag_str.erase(std::remove(tag_str.begin(), tag_str.end(),
')'), tag_str.end());
698 tag_str.erase(std::remove(tag_str.begin(), tag_str.end(),
','), tag_str.end());
700 std::cout << indent_str <<
"<DicomAttribute tag=\"" << tag_str
701 <<
"\" vr=\"" << vr_str
702 <<
"\" keyword=\"" << xml_escape(keyword) <<
"\"";
708 if (opts.max_depth >= 0 && current_depth >= opts.max_depth) {
709 std::cout << indent_str <<
" <!-- depth limit reached -->\n";
713 for (
const auto& item : items) {
714 std::cout << indent_str <<
" <Item number=\"" << item_num++ <<
"\">\n";
715 print_dataset_xml(item, opts, current_depth + 1, indent + 4);
716 std::cout << indent_str <<
" </Item>\n";
719 std::cout << indent_str <<
"</DicomAttribute>\n";
726 if (element.
vr() == vr_type::PN) {
728 if (result.is_ok()) {
729 std::cout << indent_str <<
" <PersonName>\n";
730 std::cout << indent_str <<
" <Alphabetic>\n";
731 std::cout << indent_str <<
" <FamilyName>" << xml_escape(result.value()) <<
"</FamilyName>\n";
732 std::cout << indent_str <<
" </Alphabetic>\n";
733 std::cout << indent_str <<
" </PersonName>\n";
736 std::string value = format_value(element, 1024);
737 std::cout << indent_str <<
" <Value number=\"1\">" << xml_escape(value) <<
"</Value>\n";
740 std::cout << indent_str <<
"</DicomAttribute>\n";
753 std::cout <<
"\n# Pixel Data Information\n";
754 std::cout <<
"----------------------------------------\n";
758 auto cols = dataset.
get_numeric<uint16_t>(tags::columns);
760 std::cout <<
" Dimensions: " << *cols <<
" x " << *
rows <<
"\n";
765 dataset.
get_numeric<uint16_t>(dicom_tag{0x0028, 0x0100});
768 if (bits_allocated) {
772 std::cout <<
" Bits Stored: " << *
bits_stored <<
"\n";
775 std::cout <<
" High Bit: " << *
high_bit <<
"\n";
779 auto pixel_rep = dataset.
get_numeric<uint16_t>(dicom_tag{0x0028, 0x0103});
781 std::cout <<
" Pixel Rep: "
782 << (*pixel_rep == 0 ?
"Unsigned" :
"Signed") <<
"\n";
786 auto samples = dataset.
get_numeric<uint16_t>(dicom_tag{0x0028, 0x0002});
788 std::cout <<
" Samples/Pixel: " << *samples <<
"\n";
792 auto photometric = dataset.
get_string(dicom_tag{0x0028, 0x0004});
793 if (!photometric.empty()) {
794 std::cout <<
" Photometric: " << photometric <<
"\n";
798 auto frames = dataset.
get_string(dicom_tag{0x0028, 0x0008});
799 if (!frames.empty()) {
800 std::cout <<
" Number of Frames: " << frames <<
"\n";
805 if (pixel_data !=
nullptr) {
806 std::cout <<
" Pixel Data: " <<
pixel_data->length()
808 std::cout <<
" Pixel Data VR: " << to_string(
pixel_data->vr())
811 std::cout <<
" Pixel Data: (not present)\n";
814 std::cout <<
"----------------------------------------\n";
823int dump_file(
const std::filesystem::path& file_path,
const options& opts) {
826 auto result = dicom_file::open(file_path);
827 if (result.is_err()) {
828 std::cerr <<
"Error: Failed to open '" << file_path.string()
829 <<
"': " << result.error().message <<
"\n";
838 auto& file = result.value();
840 if (opts.format == output_format::json) {
843 std::cout <<
" \"file\": \"" << json_escape(file_path.string())
845 std::cout <<
" \"transferSyntax\": \""
846 << file.transfer_syntax().name() <<
"\",\n";
847 std::cout <<
" \"sopClassUID\": \"" << file.sop_class_uid() <<
"\",\n";
848 std::cout <<
" \"sopInstanceUID\": \"" << file.sop_instance_uid()
851 if (opts.show_meta) {
852 std::cout <<
" \"metaInformation\": ";
853 print_dataset_json(file.meta_information(), opts);
857 std::cout <<
" \"dataset\": ";
858 print_dataset_json(file.dataset(), opts);
859 std::cout <<
"\n}\n";
860 }
else if (opts.format == output_format::xml) {
862 std::cout <<
"<?xml version=\"1.0\" encoding=\"" << opts.charset <<
"\"?>\n";
863 std::cout <<
"<NativeDicomModel>\n";
864 std::cout <<
" <!-- File: " << xml_escape(file_path.string()) <<
" -->\n";
865 std::cout <<
" <!-- Transfer Syntax: " << file.transfer_syntax().name() <<
" -->\n";
866 std::cout <<
" <!-- SOP Class: " << file.sop_class_uid() <<
" -->\n";
867 std::cout <<
" <!-- SOP Instance: " << file.sop_instance_uid() <<
" -->\n";
869 if (opts.show_meta) {
870 std::cout <<
" <!-- File Meta Information -->\n";
871 print_dataset_xml(file.meta_information(), opts);
874 std::cout <<
" <!-- Dataset -->\n";
875 print_dataset_xml(file.dataset(), opts);
876 std::cout <<
"</NativeDicomModel>\n";
879 std::cout <<
"# File: " << file_path.string() <<
"\n";
880 std::cout <<
"# Transfer Syntax: " << file.transfer_syntax().name()
881 <<
" (" << file.transfer_syntax().uid() <<
")\n";
882 std::cout <<
"# SOP Class: " << file.sop_class_uid() <<
"\n";
883 std::cout <<
"# SOP Instance: " << file.sop_instance_uid() <<
"\n";
886 if (opts.show_meta) {
887 std::cout <<
"# File Meta Information\n";
888 print_dataset_human(file.meta_information(), opts);
892 std::cout <<
"# Dataset\n";
893 print_dataset_human(file.dataset(), opts);
895 if (opts.pixel_info) {
896 print_pixel_info(file.dataset());
909void scan_directory(
const std::filesystem::path& dir_path,
const options& opts,
910 scan_summary& summary) {
913 auto process_file = [&](
const std::filesystem::path& file_path) {
916 auto result = dicom_file::open(file_path);
917 if (result.is_err()) {
920 std::cerr <<
" Invalid: " << file_path.filename().string()
927 auto& file = result.value();
930 auto modality = file.dataset().get_string(dicom_tag{0x0008, 0x0060});
936 auto sop_class = file.sop_class_uid();
937 if (!sop_class.empty()) {
938 summary.sop_classes[sop_class]++;
942 std::cout <<
" OK: " << file_path.filename().string();
944 std::cout <<
" [" <<
modality <<
"]";
950 if (opts.recursive) {
951 for (
const auto& entry :
952 std::filesystem::recursive_directory_iterator(dir_path)) {
953 if (entry.is_regular_file()) {
954 auto ext = entry.path().extension().string();
955 std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
956 if (ext ==
".dcm" || ext ==
".dicom" || ext.empty()) {
957 process_file(entry.path());
962 for (
const auto& entry : std::filesystem::directory_iterator(dir_path)) {
963 if (entry.is_regular_file()) {
964 auto ext = entry.path().extension().string();
965 std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
966 if (ext ==
".dcm" || ext ==
".dicom" || ext.empty()) {
967 process_file(entry.path());
979void print_summary(
const scan_summary& summary,
const options& opts) {
980 if (opts.format == output_format::json) {
982 std::cout <<
" \"totalFiles\": " <<
summary.total_files <<
",\n";
983 std::cout <<
" \"validFiles\": " <<
summary.valid_files <<
",\n";
984 std::cout <<
" \"invalidFiles\": " <<
summary.invalid_files <<
",\n";
986 std::cout <<
" \"modalities\": {\n";
987 size_t mod_count = 0;
988 for (
const auto& [modality, count] :
summary.modalities) {
989 std::cout <<
" \"" <<
modality <<
"\": " << count;
990 if (++mod_count <
summary.modalities.size()) {
995 std::cout <<
" },\n";
997 std::cout <<
" \"sopClasses\": {\n";
998 size_t sop_count = 0;
999 for (
const auto& [sop_class, count] :
summary.sop_classes) {
1000 std::cout <<
" \"" << sop_class <<
"\": " << count;
1001 if (++sop_count <
summary.sop_classes.size()) {
1006 std::cout <<
" }\n";
1010 std::cout <<
"========================================\n";
1011 std::cout <<
" Directory Summary\n";
1012 std::cout <<
"========================================\n";
1013 std::cout <<
" Total files: " <<
summary.total_files <<
"\n";
1014 std::cout <<
" Valid DICOM: " <<
summary.valid_files <<
"\n";
1015 std::cout <<
" Invalid/Other: " <<
summary.invalid_files <<
"\n";
1018 if (!
summary.modalities.empty()) {
1019 std::cout <<
" Modalities:\n";
1020 for (
const auto& [modality, count] :
summary.modalities) {
1021 std::cout <<
" " << std::left << std::setw(10) <<
modality
1022 <<
" " << count <<
" file(s)\n";
1027 if (!
summary.sop_classes.empty() && opts.verbose) {
1028 std::cout <<
" SOP Classes:\n";
1029 for (
const auto& [sop_class, count] :
summary.sop_classes) {
1030 std::cout <<
" " << sop_class <<
": " << count
1035 std::cout <<
"========================================\n";
1044 if (!parse_arguments(argc, argv, opts)) {
1047 ____ ____ __ __ ____ _ _ __ __ ____
1048 | _ \ / ___| \/ | | _ \| | | | \/ | _ \
1049 | | | | | | |\/| | | | | | | | | |\/| | |_) |
1050 | |_| | |___| | | | | |_| | |_| | | | | __/
1051 |____/ \____|_| |_| |____/ \___/|_| |_|_|
1053 DICOM File Inspection Utility
1055 print_usage(argv[0]);
1060 if (!std::filesystem::exists(opts.path)) {
1061 std::cerr <<
"Error: Path does not exist: " << opts.path.string()
1067 if (!opts.quiet && opts.format == output_format::human_readable) {
1069 ____ ____ __ __ ____ _ _ __ __ ____
1070 | _ \ / ___| \/ | | _ \| | | | \/ | _ \
1071 | | | | | | |\/| | | | | | | | | |\/| | |_) |
1072 | |_| | |___| | | | | |_| | |_| | | | | __/
1073 |____/ \____|_| |_| |____/ \___/|_| |_|_|
1075 DICOM File Inspection Utility
1080 if (std::filesystem::is_directory(opts.path)) {
1083 std::cout <<
"Scanning directory: " << opts.path.string() <<
"\n";
1084 if (opts.recursive) {
1085 std::cout <<
"Mode: Recursive\n";
1090 scan_summary summary;
1091 scan_directory(opts.path, opts, summary);
1093 print_summary(summary, opts);
1095 return summary.invalid_files > 0 ? 1 : 0;
1099 auto process = [&](
const std::filesystem::path& file_path) {
1100 auto ext = file_path.extension().string();
1101 std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
1102 if (ext ==
".dcm" || ext ==
".dicom" || ext.empty()) {
1103 if (dump_file(file_path, opts) != 0) {
1110 if (opts.recursive) {
1111 for (
const auto& entry :
1112 std::filesystem::recursive_directory_iterator(opts.path)) {
1113 if (entry.is_regular_file()) {
1114 process(entry.path());
1118 for (
const auto& entry :
1119 std::filesystem::directory_iterator(opts.path)) {
1120 if (entry.is_regular_file()) {
1121 process(entry.path());
1130 return dump_file(opts.path, opts);
if(!color.empty()) style.color
auto get(dicom_tag tag) noexcept -> dicom_element *
Get a pointer to the element with the given tag.
auto get_numeric(dicom_tag tag) const -> std::optional< T >
Get the numeric value of an element.
auto get_string(dicom_tag tag, std::string_view default_value="") const -> std::string
Get the string value of an element.
auto find(dicom_tag tag) const -> std::optional< tag_info >
Find tag metadata by DICOM tag.
auto is_sequence() const noexcept -> bool
Check if this element is a sequence.
auto as_numeric() const -> kcenon::pacs::Result< T >
Get the value as a numeric type.
auto raw_data() const noexcept -> std::span< const uint8_t >
Get the raw data bytes.
constexpr auto vr() const noexcept -> encoding::vr_type
Get the element's VR.
auto as_string() const -> kcenon::pacs::Result< std::string >
Get the value as a string.
auto sequence_items() -> std::vector< dicom_dataset > &
Get mutable access to sequence items.
auto is_empty() const noexcept -> bool
Check if the element has no value.
auto to_string() const -> std::string
Convert to string representation.
constexpr auto group() const noexcept -> uint16_t
Get the group number.
constexpr auto element() const noexcept -> uint16_t
Get the element number.
DICOM Data Dictionary for tag metadata lookup.
DICOM Part 10 file handling for reading/writing DICOM files.
Compile-time constants for commonly used DICOM tags.
constexpr bool is_numeric_vr(vr_type vr) noexcept
Checks if a VR is a numeric type.
constexpr bool is_binary_vr(vr_type vr) noexcept
Checks if a VR is a binary/raw byte type.
constexpr bool is_string_vr(vr_type vr) noexcept
Checks if a VR is a string type.
@ summary
Statistical summary (min, max, mean, percentiles)