PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1
18#include "console_utils.h"
19
26
27#include <iomanip>
28#include <iostream>
29
30namespace core = kcenon::pacs::core;
31namespace enc = kcenon::pacs::encoding;
32namespace tags = kcenon::pacs::core::tags;
33
34int main() {
35 kcenon::pacs::samples::print_header("Hello DICOM - Level 1 Sample");
36
37 // ═══════════════════════════════════════════════════════════════════════════
38 // Part 1: Understanding DICOM Tags
39 // ═══════════════════════════════════════════════════════════════════════════
40 // DICOM tags identify data elements using (Group, Element) pairs.
41 // - Group 0010 = Patient information
42 // - Group 0008 = Study/Series/Instance identification
43 // - Group 0028 = Image pixel parameters
44
45 kcenon::pacs::samples::print_section("Part 1: DICOM Tags");
46
47 std::cout << "DICOM tags are the fundamental identifiers for data elements.\n";
48 std::cout << "Each tag is a (Group, Element) pair of 16-bit hexadecimal numbers.\n\n";
49
50 // Create tags using different methods
51 core::dicom_tag patient_name_tag{0x0010, 0x0010}; // Explicit group/element
52 core::dicom_tag patient_id_tag = tags::patient_id; // Using predefined constants
53
54 std::cout << "Patient Name Tag: " << patient_name_tag.to_string() << "\n";
55 std::cout << " Group: 0x" << std::hex << std::setw(4) << std::setfill('0')
56 << patient_name_tag.group() << std::dec << "\n";
57 std::cout << " Element: 0x" << std::hex << std::setw(4) << std::setfill('0')
58 << patient_name_tag.element() << std::dec << "\n";
59 std::cout << " Private? " << (patient_name_tag.is_private() ? "Yes" : "No") << "\n\n";
60
61 // Parse tag from string representation
62 auto parsed = core::dicom_tag::from_string("(0008,0020)");
63 if (parsed) {
64 std::cout << "Parsed Study Date tag from string: " << parsed->to_string() << "\n";
65 }
66
67 // Demonstrate tag comparison
68 std::cout << "\nTag comparison:\n";
69 std::cout << " (0010,0010) == tags::patient_name? "
70 << (patient_name_tag == tags::patient_name ? "Yes" : "No") << "\n";
71
72 kcenon::pacs::samples::print_success("Part 1 complete - DICOM tags understood!");
73
74 // ═══════════════════════════════════════════════════════════════════════════
75 // Part 2: Creating a DICOM Dataset
76 // ═══════════════════════════════════════════════════════════════════════════
77 // A dataset is an ordered collection of DICOM elements.
78 // Each element has: Tag + VR (Value Representation) + Value
79
80 kcenon::pacs::samples::print_section("Part 2: DICOM Dataset");
81
82 std::cout << "A DICOM dataset is an ordered collection of data elements.\n";
83 std::cout << "Each element consists of: Tag + VR + Value\n\n";
84
85 std::cout << "Common VR (Value Representation) types:\n";
86 std::cout << " PN = Person Name DA = Date TM = Time\n";
87 std::cout << " LO = Long String SH = Short String CS = Code String\n";
88 std::cout << " UI = Unique ID US = Unsigned Short IS = Integer String\n\n";
89
90 core::dicom_dataset dataset;
91
92 // --- Patient Module (Group 0010) ---
93 // PN = Person Name format: Family^Given^Middle^Prefix^Suffix
94 dataset.set_string(tags::patient_name, enc::vr_type::PN, "DOE^JOHN^M");
95 dataset.set_string(tags::patient_id, enc::vr_type::LO, "PAT001");
96 dataset.set_string(tags::patient_birth_date, enc::vr_type::DA, "19850315");
97 dataset.set_string(tags::patient_sex, enc::vr_type::CS, "M");
98
99 // --- Study Module (Group 0008) ---
100 dataset.set_string(tags::study_date, enc::vr_type::DA, "20240115");
101 dataset.set_string(tags::study_time, enc::vr_type::TM, "143022");
102 dataset.set_string(tags::study_description, enc::vr_type::LO, "CT CHEST W/CONTRAST");
103 dataset.set_string(tags::accession_number, enc::vr_type::SH, "ACC123456");
104
105 // --- Series Module ---
106 dataset.set_string(tags::modality, enc::vr_type::CS, "CT");
107 dataset.set_string(tags::series_description, enc::vr_type::LO, "AXIAL 3mm");
108 dataset.set_numeric<uint32_t>(tags::series_number, enc::vr_type::IS, 1);
109 dataset.set_numeric<uint32_t>(tags::instance_number, enc::vr_type::IS, 1);
110
111 // --- UID elements (required for valid DICOM) ---
112 // UIDs uniquely identify study/series/instance across the world
113 dataset.set_string(tags::study_instance_uid, enc::vr_type::UI,
114 "1.2.410.200001.1.1.20240115.143022.1");
115 dataset.set_string(tags::series_instance_uid, enc::vr_type::UI,
116 "1.2.410.200001.1.1.20240115.143022.1.1");
117 dataset.set_string(tags::sop_class_uid, enc::vr_type::UI,
118 "1.2.840.10008.5.1.4.1.1.2"); // CT Image Storage
119 dataset.set_string(tags::sop_instance_uid, enc::vr_type::UI,
120 "1.2.410.200001.1.1.20240115.143022.1.1.1");
121
122 // --- Image Pixel Module ---
123 // US = Unsigned Short (16-bit integer)
124 dataset.set_numeric<uint16_t>(tags::rows, enc::vr_type::US, 512);
125 dataset.set_numeric<uint16_t>(tags::columns, enc::vr_type::US, 512);
126 dataset.set_numeric<uint16_t>(tags::bits_allocated, enc::vr_type::US, 16);
127 dataset.set_numeric<uint16_t>(tags::bits_stored, enc::vr_type::US, 12);
128 dataset.set_numeric<uint16_t>(tags::high_bit, enc::vr_type::US, 11);
129 dataset.set_numeric<uint16_t>(tags::pixel_representation, enc::vr_type::US, 0);
130 dataset.set_numeric<uint16_t>(tags::samples_per_pixel, enc::vr_type::US, 1);
131 dataset.set_string(tags::photometric_interpretation, enc::vr_type::CS, "MONOCHROME2");
132
133 // Reading values back from the dataset
134 std::cout << "Dataset created with " << dataset.size() << " elements:\n\n";
135
136 kcenon::pacs::samples::print_table("Patient Information", {
137 {"Name", dataset.get_string(tags::patient_name)},
138 {"ID", dataset.get_string(tags::patient_id)},
139 {"Birth Date", dataset.get_string(tags::patient_birth_date)},
140 {"Sex", dataset.get_string(tags::patient_sex)}
141 });
142
143 kcenon::pacs::samples::print_table("Study Information", {
144 {"Date", dataset.get_string(tags::study_date)},
145 {"Description", dataset.get_string(tags::study_description)},
146 {"Modality", dataset.get_string(tags::modality)},
147 {"Accession", dataset.get_string(tags::accession_number)}
148 });
149
150 // Reading numeric values
151 auto rows_val = dataset.get_numeric<uint16_t>(tags::rows);
152 auto cols_val = dataset.get_numeric<uint16_t>(tags::columns);
153 std::cout << "\nImage dimensions: "
154 << rows_val.value_or(0) << " x " << cols_val.value_or(0) << " pixels\n";
155
156 kcenon::pacs::samples::print_success("Part 2 complete - Dataset created with patient and study info!");
157
158 // ═══════════════════════════════════════════════════════════════════════════
159 // Part 3: Saving as DICOM File
160 // ═══════════════════════════════════════════════════════════════════════════
161 // DICOM Part 10 file format:
162 // - 128 bytes preamble (typically zeros)
163 // - "DICM" magic number (4 bytes)
164 // - File Meta Information (Group 0002, always Explicit VR LE)
165 // - Dataset (encoded per specified Transfer Syntax)
166
167 kcenon::pacs::samples::print_section("Part 3: DICOM File I/O");
168
169 std::cout << "DICOM Part 10 file structure:\n";
170 std::cout << " 1. 128-byte preamble (typically zeros)\n";
171 std::cout << " 2. 'DICM' magic number\n";
172 std::cout << " 3. File Meta Information (always Explicit VR Little Endian)\n";
173 std::cout << " 4. Dataset (encoded per Transfer Syntax)\n\n";
174
175 // Create file with Explicit VR Little Endian transfer syntax
176 // This is the most widely compatible format
177 auto dicom_file = core::dicom_file::create(
178 dataset,
179 enc::transfer_syntax::explicit_vr_little_endian);
180
181 std::cout << "Transfer Syntax: "
182 << dicom_file.transfer_syntax().name() << "\n\n";
183
184 // Save to disk
185 const std::string output_path = "hello_dicom_output.dcm";
186 auto save_result = dicom_file.save(output_path);
187
188 if (save_result.is_ok()) {
189 kcenon::pacs::samples::print_success("File saved: " + output_path);
190 } else {
191 kcenon::pacs::samples::print_error("Save failed: " + save_result.error().message);
192 return 1;
193 }
194
195 // ═══════════════════════════════════════════════════════════════════════════
196 // Part 4: Reading DICOM File
197 // ═══════════════════════════════════════════════════════════════════════════
198
199 kcenon::pacs::samples::print_section("Part 4: Reading DICOM File");
200
201 auto read_result = core::dicom_file::open(output_path);
202
203 if (read_result.is_ok()) {
204 auto& loaded_file = read_result.value();
205 const auto& loaded_ds = loaded_file.dataset();
206
207 kcenon::pacs::samples::print_success("File loaded successfully!");
208
209 std::cout << "\nFile properties:\n";
210 std::cout << " Transfer Syntax: " << loaded_file.transfer_syntax().name() << "\n";
211 std::cout << " SOP Class UID: " << loaded_file.sop_class_uid() << "\n";
212 std::cout << " Element count: " << loaded_ds.size() << "\n\n";
213
214 // Iterate through all elements and display them
215 std::cout << "All elements in the dataset:\n";
216 std::cout << std::string(60, '-') << "\n";
217
218 for (const auto& [tag, element] : loaded_ds) {
219 auto value_result = element.as_string();
220 std::string value_str = value_result.is_ok()
221 ? value_result.value()
222 : "<binary data>";
223
224 // Truncate long values for display
225 if (value_str.length() > 40) {
226 value_str = value_str.substr(0, 37) + "...";
227 }
228
229 std::cout << tag.to_string() << " "
230 << enc::to_string(element.vr()) << " = "
231 << value_str << "\n";
232 }
233 std::cout << std::string(60, '-') << "\n";
234
235 } else {
236 kcenon::pacs::samples::print_error("Read failed: " + read_result.error().message);
237 return 1;
238 }
239
240 // ═══════════════════════════════════════════════════════════════════════════
241 // Summary
242 // ═══════════════════════════════════════════════════════════════════════════
243
245
247 "Congratulations! You have learned:",
248 "",
249 "1. DICOM Tags - (Group, Element) pairs that identify data",
250 "2. Value Representations - Data types like PN, DA, UI, US",
251 "3. DICOM Dataset - Ordered collection of elements",
252 "4. DICOM File - Part 10 format for file storage",
253 "",
254 "Next step: Level 2 - Echo Server (DICOM networking)"
255 });
256
257 std::cout << "\nYou can verify the output file with DCMTK:\n";
258 std::cout << " dcmdump " << output_path << "\n\n";
259
260 return 0;
261}
int main()
Definition main.cpp:34
Represents a DICOM Dataset (ordered collection of Data Elements)
void set_numeric(dicom_tag tag, encoding::vr_type vr, T value)
Set a numeric value for the given tag.
auto size() const noexcept -> size_t
Get the number of elements in the dataset.
auto get_numeric(dicom_tag tag) const -> std::optional< T >
Get the numeric value of an element.
void set_string(dicom_tag tag, encoding::vr_type vr, std::string_view value)
Set a string value for the given tag.
auto get_string(dicom_tag tag, std::string_view default_value="") const -> std::string
Get the string value of an element.
Represents a DICOM tag (Group, Element pair)
Definition dicom_tag.h:51
auto to_string() const -> std::string
Convert to string representation.
Console output utilities for developer samples.
DICOM Dataset - ordered collection of Data Elements.
DICOM Part 10 file handling for reading/writing DICOM files.
DICOM Tag representation (Group, Element pairs)
Compile-time constants for commonly used DICOM tags.
constexpr std::string_view to_string(vr_type vr) noexcept
Converts a vr_type to its two-character string representation.
Definition vr_type.h:83
void print_error(std::string_view message)
Print an error message.
void print_table(std::string_view title, const std::vector< std::pair< std::string, std::string > > &rows)
Print a key-value table.
void print_header(std::string_view title)
Print a header/title with decorative border.
void print_success(std::string_view message)
Print a success message.
void print_section(std::string_view title)
Print a section header.
void print_box(const std::vector< std::string > &lines)
Print text in a decorative box.