PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
dcm_to_xml/main.cpp

A command-line utility for converting DICOM files to XML format following the DICOM Native XML representation standard (PS3.19).

See also
Issue #283 - dcm_to_xml / xml_to_dcm Implementation
DICOM PS3.19 - Application Hosting

Usage: dcm_to_xml <dicom-file> [output-file] [options]

Example: dcm_to_xml image.dcm dcm_to_xml image.dcm output.xml –pretty dcm_to_xml image.dcm –bulk-data exclude –no-pixel

#include <algorithm>
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
namespace {
enum class bulk_data_mode {
inline_base64,
uri,
exclude
};
struct options {
std::filesystem::path input_path;
std::filesystem::path output_path;
bool pretty_print{true};
bool compact{false};
bulk_data_mode bulk_mode{bulk_data_mode::exclude};
std::string bulk_data_uri_prefix{"file://"};
std::filesystem::path bulk_data_dir;
std::vector<std::string> filter_tags;
bool no_pixel{false};
bool recursive{false};
bool include_meta{true};
bool verbose{false};
bool quiet{false};
std::string charset{"UTF-8"};
};
constexpr char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
[[nodiscard]] std::string to_base64(std::span<const uint8_t> data) {
std::string result;
result.reserve(((data.size() + 2) / 3) * 4);
size_t i = 0;
while (i < data.size()) {
uint32_t octet_a = i < data.size() ? data[i++] : 0;
uint32_t octet_b = i < data.size() ? data[i++] : 0;
uint32_t octet_c = i < data.size() ? data[i++] : 0;
uint32_t triple = (octet_a << 16) | (octet_b << 8) | octet_c;
result += base64_chars[(triple >> 18) & 0x3F];
result += base64_chars[(triple >> 12) & 0x3F];
result += (i > data.size() + 1) ? '=' : base64_chars[(triple >> 6) & 0x3F];
result += (i > data.size()) ? '=' : base64_chars[triple & 0x3F];
}
return result;
}
[[nodiscard]] std::string xml_escape(const std::string& str) {
std::ostringstream oss;
for (unsigned char c : str) {
switch (c) {
case '<':
oss << "&lt;";
break;
case '>':
oss << "&gt;";
break;
case '&':
oss << "&amp;";
break;
case '"':
oss << "&quot;";
break;
case '\'':
oss << "&apos;";
break;
default:
if (c < 0x20 && c != '\t' && c != '\n' && c != '\r') {
oss << "&#" << static_cast<int>(c) << ";";
} else {
oss << c;
}
}
}
return oss.str();
}
[[nodiscard]] std::string format_tag_key(const kcenon::pacs::core::dicom_tag& tag) {
std::ostringstream oss;
oss << std::hex << std::uppercase << std::setfill('0')
<< std::setw(4) << tag.group()
<< std::setw(4) << tag.element();
return oss.str();
}
[[nodiscard]] bool is_bulk_data_vr(kcenon::pacs::encoding::vr_type vr) {
using namespace kcenon::pacs::encoding;
return vr == vr_type::OB || vr == vr_type::OD || vr == vr_type::OF ||
vr == vr_type::OL || vr == vr_type::OV || vr == vr_type::OW ||
vr == vr_type::UN;
}
[[nodiscard]] bool is_pixel_data_tag(const kcenon::pacs::core::dicom_tag& tag) {
return tag.group() == 0x7FE0 && tag.element() == 0x0010;
}
void print_usage(const char* program_name) {
std::cout << R"(
DICOM to XML Converter (DICOM Native XML PS3.19)
Usage: )" << program_name
<< R"( <dicom-file> [output-file] [options]
Arguments:
dicom-file Input DICOM file or directory
output-file Output XML file (optional, stdout if omitted)
Options:
-h, --help Show this help message
-p, --pretty Pretty-print formatting (default)
-c, --compact Compact output (no formatting)
--bulk-data <mode> Binary data handling:
inline - Include as Base64 (InlineBinary)
uri - Save to file, reference by URI
exclude - Completely exclude (default)
--bulk-data-uri <pfx> BulkDataURI prefix (default: file://)
--bulk-data-dir <dir> Directory for bulk data files
-t, --tag <tag> Output specific tag only (e.g., 0010,0010)
--no-pixel Exclude pixel data
--no-meta Exclude File Meta Information
--charset <charset> XML encoding declaration (default: UTF-8)
-r, --recursive Process directories recursively
-v, --verbose Verbose output
-q, --quiet Quiet mode (errors only)
Examples:
)" << program_name
<< R"( image.dcm
)" << program_name
<< R"( image.dcm output.xml --pretty
)" << program_name
<< R"( image.dcm --bulk-data inline
)" << program_name
<< R"( image.dcm --bulk-data uri --bulk-data-dir ./bulk/
)" << program_name
<< R"( image.dcm -t 0010,0010 -t 0010,0020
)" << program_name
<< R"( ./dicom_folder/ --recursive --no-pixel
Output Format (DICOM Native XML PS3.19):
<?xml version="1.0" encoding="UTF-8"?>
<NativeDicomModel>
<DicomAttribute tag="00100010" vr="PN" keyword="PatientName">
<PersonName>
<Alphabetic>
<FamilyName>DOE</FamilyName>
<GivenName>JOHN</GivenName>
</Alphabetic>
</PersonName>
</DicomAttribute>
</NativeDicomModel>
Exit Codes:
0 Success
1 Invalid arguments
2 File error or invalid DICOM
)";
}
[[nodiscard]] std::optional<kcenon::pacs::core::dicom_tag> parse_tag_string(
const std::string& tag_str) {
std::string s = tag_str;
// Remove parentheses if present
if (!s.empty() && s.front() == '(') s.erase(0, 1);
if (!s.empty() && s.back() == ')') s.pop_back();
// Remove comma
s.erase(std::remove(s.begin(), s.end(), ','), s.end());
if (s.length() != 8) {
return std::nullopt;
}
try {
uint16_t group = static_cast<uint16_t>(std::stoul(s.substr(0, 4), nullptr, 16));
uint16_t elem = static_cast<uint16_t>(std::stoul(s.substr(4, 4), nullptr, 16));
return kcenon::pacs::core::dicom_tag{group, elem};
} catch (...) {
return std::nullopt;
}
}
bool parse_arguments(int argc, char* argv[], options& opts) {
if (argc < 2) {
return false;
}
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
if (arg == "--help" || arg == "-h") {
return false;
} else if (arg == "--pretty" || arg == "-p") {
opts.pretty_print = true;
opts.compact = false;
} else if (arg == "--compact" || arg == "-c") {
opts.compact = true;
opts.pretty_print = false;
} else if (arg == "--bulk-data" && i + 1 < argc) {
std::string mode = argv[++i];
if (mode == "inline") {
opts.bulk_mode = bulk_data_mode::inline_base64;
} else if (mode == "uri") {
opts.bulk_mode = bulk_data_mode::uri;
} else if (mode == "exclude") {
opts.bulk_mode = bulk_data_mode::exclude;
} else {
std::cerr << "Error: Unknown bulk-data mode '" << mode
<< "'. Use: inline, uri, exclude\n";
return false;
}
} else if (arg == "--bulk-data-uri" && i + 1 < argc) {
opts.bulk_data_uri_prefix = argv[++i];
} else if (arg == "--bulk-data-dir" && i + 1 < argc) {
opts.bulk_data_dir = argv[++i];
} else if ((arg == "--tag" || arg == "-t") && i + 1 < argc) {
opts.filter_tags.push_back(argv[++i]);
} else if (arg == "--no-pixel") {
opts.no_pixel = true;
} else if (arg == "--no-meta") {
opts.include_meta = false;
} else if (arg == "--charset" && i + 1 < argc) {
opts.charset = argv[++i];
} else if (arg == "--recursive" || arg == "-r") {
opts.recursive = true;
} else if (arg == "--verbose" || arg == "-v") {
opts.verbose = true;
} else if (arg == "--quiet" || arg == "-q") {
opts.quiet = true;
} else if (arg[0] == '-') {
std::cerr << "Error: Unknown option '" << arg << "'\n";
return false;
} else if (opts.input_path.empty()) {
opts.input_path = arg;
} else if (opts.output_path.empty()) {
opts.output_path = arg;
} else {
std::cerr << "Error: Too many arguments\n";
return false;
}
}
if (opts.input_path.empty()) {
std::cerr << "Error: No input file specified\n";
return false;
}
if (opts.quiet) {
opts.verbose = false;
}
return true;
}
[[nodiscard]] bool should_include_tag(const kcenon::pacs::core::dicom_tag& tag,
const options& opts) {
// Exclude pixel data if requested
if (opts.no_pixel && is_pixel_data_tag(tag)) {
return false;
}
// If filter tags specified, only include those
if (!opts.filter_tags.empty()) {
for (const auto& filter : opts.filter_tags) {
auto parsed = parse_tag_string(filter);
if (parsed && *parsed == tag) {
return true;
}
}
return false;
}
return true;
}
// Forward declaration
void write_dataset_xml(std::ostream& out,
const options& opts,
const std::filesystem::path& base_path,
int indent_level,
size_t& bulk_data_counter);
[[nodiscard]] std::tuple<std::string, std::string, std::string, std::string, std::string>
parse_person_name_components(const std::string& pn_string) {
std::vector<std::string> components;
std::stringstream ss(pn_string);
std::string component;
while (std::getline(ss, component, '^')) {
components.push_back(component);
}
std::string family = components.size() > 0 ? components[0] : "";
std::string given = components.size() > 1 ? components[1] : "";
std::string middle = components.size() > 2 ? components[2] : "";
std::string prefix = components.size() > 3 ? components[3] : "";
std::string suffix = components.size() > 4 ? components[4] : "";
return {family, given, middle, prefix, suffix};
}
void write_element_value_xml(std::ostream& out,
const options& opts,
const std::filesystem::path& base_path,
int indent_level,
size_t& bulk_data_counter) {
using namespace kcenon::pacs::encoding;
using namespace kcenon::pacs::core;
const std::string indent = opts.compact ? "" : std::string(indent_level * 2, ' ');
const std::string newline = opts.compact ? "" : "\n";
auto vr = element.vr();
auto vr_str = to_string(vr);
// Get keyword from dictionary
auto& dict = dicom_dictionary::instance();
auto info = dict.find(tag);
std::string keyword = info ? std::string(info->keyword) : "";
// Build opening tag
std::ostringstream attr_oss;
attr_oss << indent << "<DicomAttribute tag=\"" << format_tag_key(tag)
<< "\" vr=\"" << vr_str << "\"";
if (!keyword.empty()) {
attr_oss << " keyword=\"" << xml_escape(keyword) << "\"";
}
// Handle sequences
if (element.is_sequence()) {
out << attr_oss.str() << ">" << newline;
const auto& items = element.sequence_items();
int item_num = 1;
for (const auto& item : items) {
out << indent << " <Item number=\"" << item_num++ << "\">" << newline;
write_dataset_xml(out, item, opts, base_path, indent_level + 2, bulk_data_counter);
out << indent << " </Item>" << newline;
}
out << indent << "</DicomAttribute>" << newline;
return;
}
// Handle empty elements
if (element.is_empty()) {
out << attr_oss.str() << "/>" << newline;
return;
}
// Handle bulk data (binary VRs)
if (is_bulk_data_vr(vr)) {
switch (opts.bulk_mode) {
case bulk_data_mode::inline_base64: {
auto data = element.raw_data();
out << attr_oss.str() << ">" << newline;
out << indent << " <InlineBinary>" << to_base64(data) << "</InlineBinary>" << newline;
out << indent << "</DicomAttribute>" << newline;
break;
}
case bulk_data_mode::uri: {
std::string filename = "bulk_" + std::to_string(bulk_data_counter++) + ".raw";
std::filesystem::path bulk_path = opts.bulk_data_dir.empty()
? base_path / filename
: opts.bulk_data_dir / filename;
// Write bulk data to file
std::ofstream bulk_file(bulk_path, std::ios::binary);
if (bulk_file) {
auto data = element.raw_data();
bulk_file.write(reinterpret_cast<const char*>(data.data()),
static_cast<std::streamsize>(data.size()));
}
std::string uri = opts.bulk_data_uri_prefix + bulk_path.string();
out << attr_oss.str() << ">" << newline;
out << indent << " <BulkData uri=\"" << xml_escape(uri) << "\"/>" << newline;
out << indent << "</DicomAttribute>" << newline;
break;
}
case bulk_data_mode::exclude:
default:
out << attr_oss.str() << "/>" << newline;
break;
}
return;
}
// Handle PersonName (PN) VR specially per PS3.19
if (vr == vr_type::PN) {
out << attr_oss.str() << ">" << newline;
auto result = element.as_string();
if (result.is_ok()) {
std::string value = result.value();
// Split by backslash for VM > 1
std::vector<std::string> names;
std::stringstream ss(value);
std::string name;
while (std::getline(ss, name, '\\')) {
names.push_back(name);
}
int pn_number = 1;
for (const auto& pn : names) {
// Split by '=' for different name representations
std::vector<std::string> representations;
std::stringstream rep_ss(pn);
std::string rep;
while (std::getline(rep_ss, rep, '=')) {
representations.push_back(rep);
}
out << indent << " <PersonName number=\"" << pn_number++ << "\">" << newline;
// Alphabetic representation
if (!representations.empty() && !representations[0].empty()) {
auto [family, given, middle, prefix, suffix] =
parse_person_name_components(representations[0]);
out << indent << " <Alphabetic>" << newline;
if (!family.empty())
out << indent << " <FamilyName>" << xml_escape(family) << "</FamilyName>" << newline;
if (!given.empty())
out << indent << " <GivenName>" << xml_escape(given) << "</GivenName>" << newline;
if (!middle.empty())
out << indent << " <MiddleName>" << xml_escape(middle) << "</MiddleName>" << newline;
if (!prefix.empty())
out << indent << " <NamePrefix>" << xml_escape(prefix) << "</NamePrefix>" << newline;
if (!suffix.empty())
out << indent << " <NameSuffix>" << xml_escape(suffix) << "</NameSuffix>" << newline;
out << indent << " </Alphabetic>" << newline;
}
// Ideographic representation
if (representations.size() > 1 && !representations[1].empty()) {
auto [family, given, middle, prefix, suffix] =
parse_person_name_components(representations[1]);
out << indent << " <Ideographic>" << newline;
if (!family.empty())
out << indent << " <FamilyName>" << xml_escape(family) << "</FamilyName>" << newline;
if (!given.empty())
out << indent << " <GivenName>" << xml_escape(given) << "</GivenName>" << newline;
if (!middle.empty())
out << indent << " <MiddleName>" << xml_escape(middle) << "</MiddleName>" << newline;
if (!prefix.empty())
out << indent << " <NamePrefix>" << xml_escape(prefix) << "</NamePrefix>" << newline;
if (!suffix.empty())
out << indent << " <NameSuffix>" << xml_escape(suffix) << "</NameSuffix>" << newline;
out << indent << " </Ideographic>" << newline;
}
// Phonetic representation
if (representations.size() > 2 && !representations[2].empty()) {
auto [family, given, middle, prefix, suffix] =
parse_person_name_components(representations[2]);
out << indent << " <Phonetic>" << newline;
if (!family.empty())
out << indent << " <FamilyName>" << xml_escape(family) << "</FamilyName>" << newline;
if (!given.empty())
out << indent << " <GivenName>" << xml_escape(given) << "</GivenName>" << newline;
if (!middle.empty())
out << indent << " <MiddleName>" << xml_escape(middle) << "</MiddleName>" << newline;
if (!prefix.empty())
out << indent << " <NamePrefix>" << xml_escape(prefix) << "</NamePrefix>" << newline;
if (!suffix.empty())
out << indent << " <NameSuffix>" << xml_escape(suffix) << "</NameSuffix>" << newline;
out << indent << " </Phonetic>" << newline;
}
out << indent << " </PersonName>" << newline;
}
}
out << indent << "</DicomAttribute>" << newline;
return;
}
// Handle string VRs
if (is_string_vr(vr)) {
out << attr_oss.str() << ">" << newline;
auto result = element.as_string();
if (result.is_ok()) {
std::string value = result.value();
// Split by backslash for VM > 1
std::vector<std::string> values;
std::stringstream ss(value);
std::string v;
while (std::getline(ss, v, '\\')) {
values.push_back(v);
}
int val_number = 1;
for (const auto& val : values) {
out << indent << " <Value number=\"" << val_number++ << "\">"
<< xml_escape(val) << "</Value>" << newline;
}
}
out << indent << "</DicomAttribute>" << newline;
return;
}
// Handle numeric VRs
if (is_numeric_vr(vr)) {
out << attr_oss.str() << ">" << newline;
auto write_numeric_values = [&]<typename T>() {
auto result = element.as_numeric_list<T>();
if (result.is_ok()) {
const auto& values = result.value();
int val_number = 1;
for (const auto& val : values) {
out << indent << " <Value number=\"" << val_number++ << "\">";
if constexpr (std::is_floating_point_v<T>) {
out << std::setprecision(17) << val;
} else {
out << static_cast<int64_t>(val);
}
out << "</Value>" << newline;
}
}
};
switch (vr) {
case vr_type::US:
write_numeric_values.template operator()<uint16_t>();
break;
case vr_type::SS:
write_numeric_values.template operator()<int16_t>();
break;
case vr_type::UL:
write_numeric_values.template operator()<uint32_t>();
break;
case vr_type::SL:
write_numeric_values.template operator()<int32_t>();
break;
case vr_type::FL:
write_numeric_values.template operator()<float>();
break;
case vr_type::FD:
write_numeric_values.template operator()<double>();
break;
case vr_type::UV:
write_numeric_values.template operator()<uint64_t>();
break;
case vr_type::SV:
write_numeric_values.template operator()<int64_t>();
break;
default:
// Fallback: try as string
if (auto result = element.as_string(); result.is_ok()) {
out << indent << " <Value number=\"1\">" << xml_escape(result.value())
<< "</Value>" << newline;
}
break;
}
out << indent << "</DicomAttribute>" << newline;
return;
}
// Handle AT (Attribute Tag) VR
if (vr == vr_type::AT) {
out << attr_oss.str() << ">" << newline;
auto data = element.raw_data();
int val_number = 1;
for (size_t i = 0; i + 4 <= data.size(); i += 4) {
uint16_t group = static_cast<uint16_t>(data[i] | (data[i + 1] << 8));
uint16_t elem = static_cast<uint16_t>(data[i + 2] | (data[i + 3] << 8));
out << indent << " <Value number=\"" << val_number++ << "\">";
out << std::hex << std::uppercase << std::setfill('0')
<< std::setw(4) << group << std::setw(4) << elem;
out << "</Value>" << newline << std::dec;
}
out << indent << "</DicomAttribute>" << newline;
return;
}
// Fallback: treat as string
out << attr_oss.str() << ">" << newline;
if (auto result = element.as_string(); result.is_ok()) {
out << indent << " <Value number=\"1\">" << xml_escape(result.value())
<< "</Value>" << newline;
}
out << indent << "</DicomAttribute>" << newline;
}
void write_dataset_xml(std::ostream& out,
const options& opts,
const std::filesystem::path& base_path,
int indent_level,
size_t& bulk_data_counter) {
for (const auto& [tag, element] : dataset) {
if (!should_include_tag(tag, opts)) {
continue;
}
write_element_value_xml(out, element, tag, opts, base_path,
indent_level, bulk_data_counter);
}
}
int convert_file(const std::filesystem::path& input_path,
std::ostream& output,
const options& opts) {
using namespace kcenon::pacs::core;
auto result = dicom_file::open(input_path);
if (result.is_err()) {
std::cerr << "Error: Failed to open '" << input_path.string()
<< "': " << result.error().message << "\n";
return 2;
}
auto& file = result.value();
size_t bulk_data_counter = 0;
std::filesystem::path base_path = input_path.parent_path();
const std::string newline = opts.compact ? "" : "\n";
// XML declaration
output << "<?xml version=\"1.0\" encoding=\"" << opts.charset << "\"?>" << newline;
// Root element
output << "<NativeDicomModel";
output << " xmlns=\"http://dicom.nema.org/PS3.19/models/NativeDICOM\"";
output << ">" << newline;
// Include File Meta Information if requested
if (opts.include_meta && !file.meta_information().empty()) {
write_dataset_xml(output, file.meta_information(), opts, base_path, 1, bulk_data_counter);
}
// Write main dataset
write_dataset_xml(output, file.dataset(), opts, base_path, 1, bulk_data_counter);
output << "</NativeDicomModel>" << newline;
return 0;
}
int process_directory(const std::filesystem::path& dir_path, const options& opts) {
int exit_code = 0;
auto process = [&](const std::filesystem::path& file_path) {
auto ext = file_path.extension().string();
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
if (ext != ".dcm" && ext != ".dicom" && !ext.empty()) {
return;
}
// Generate output filename
std::filesystem::path output_path = file_path;
output_path.replace_extension(".xml");
if (!opts.quiet) {
std::cout << "Converting: " << file_path.string() << " -> "
<< output_path.string() << "\n";
}
std::ofstream output(output_path);
if (!output) {
std::cerr << "Error: Cannot create output file: " << output_path.string() << "\n";
exit_code = 2;
return;
}
if (convert_file(file_path, output, opts) != 0) {
exit_code = 2;
}
};
if (opts.recursive) {
for (const auto& entry :
std::filesystem::recursive_directory_iterator(dir_path)) {
if (entry.is_regular_file()) {
process(entry.path());
}
}
} else {
for (const auto& entry : std::filesystem::directory_iterator(dir_path)) {
if (entry.is_regular_file()) {
process(entry.path());
}
}
}
return exit_code;
}
} // namespace
int main(int argc, char* argv[]) {
options opts;
if (!parse_arguments(argc, argv, opts)) {
std::cout << R"(
____ ____ __ __ _____ ___ __ ____ __ _
| _ \ / ___| \/ | |_ _|/ _ \ \ \/ / \/ | |
| | | | | | |\/| | | | | | | | \ /| |\/| | |
| |_| | |___| | | | | | | |_| | / \| | | | |___
|____/ \____|_| |_| |_| \___/ /_/\_\_| |_|_____|
DICOM to XML Converter (PS3.19)
)" << "\n";
print_usage(argv[0]);
return 1;
}
// Check if input path exists
if (!std::filesystem::exists(opts.input_path)) {
std::cerr << "Error: Path does not exist: " << opts.input_path.string() << "\n";
return 2;
}
// Create bulk data directory if needed
if (opts.bulk_mode == bulk_data_mode::uri && !opts.bulk_data_dir.empty()) {
std::filesystem::create_directories(opts.bulk_data_dir);
}
// Show banner in non-quiet mode
if (!opts.quiet) {
std::cout << R"(
____ ____ __ __ _____ ___ __ ____ __ _
| _ \ / ___| \/ | |_ _|/ _ \ \ \/ / \/ | |
| | | | | | |\/| | | | | | | | \ /| |\/| | |
| |_| | |___| | | | | | | |_| | / \| | | | |___
|____/ \____|_| |_| |_| \___/ /_/\_\_| |_|_____|
DICOM to XML Converter (PS3.19)
)" << "\n";
}
// Handle directory vs file
if (std::filesystem::is_directory(opts.input_path)) {
return process_directory(opts.input_path, opts);
} else {
// Single file
if (opts.output_path.empty()) {
// Output to stdout
return convert_file(opts.input_path, std::cout, opts);
} else {
// Output to file
std::ofstream output(opts.output_path);
if (!output) {
std::cerr << "Error: Cannot create output file: "
<< opts.output_path.string() << "\n";
return 2;
}
return convert_file(opts.input_path, output, opts);
}
}
}
auto is_sequence() const noexcept -> bool
Check if this element is a sequence.
auto raw_data() const noexcept -> std::span< const uint8_t >
Get the raw data bytes.
auto as_numeric_list() const -> kcenon::pacs::Result< std::vector< T > >
Get multi-valued numeric data as a list.
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.
constexpr auto group() const noexcept -> uint16_t
Get the group number.
Definition dicom_tag.h:90
constexpr auto element() const noexcept -> uint16_t
Get the element number.
Definition dicom_tag.h:98
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.
int main()
Definition main.cpp:84
vr_type
DICOM Value Representation (VR) types.
Definition vr_type.h:29
vr_encoding vr
std::string_view name