58 auto now = std::chrono::system_clock::now();
59 auto time_t = std::chrono::system_clock::to_time_t(now);
62 localtime_s(&tm, &time_t);
64 localtime_r(&time_t, &tm);
67 std::ostringstream oss;
68 oss << std::setfill(
'0')
69 << std::setw(4) << (tm.tm_year + 1900)
70 << std::setw(2) << (tm.tm_mon + 1)
71 << std::setw(2) << tm.tm_mday;
76 auto now = std::chrono::system_clock::now();
77 auto time_t = std::chrono::system_clock::to_time_t(now);
78 auto ms = std::chrono::duration_cast<std::chrono::microseconds>(
79 now.time_since_epoch()).count() % 1000000;
82 localtime_s(&tm, &time_t);
84 localtime_r(&time_t, &tm);
87 std::ostringstream oss;
88 oss << std::setfill(
'0')
89 << std::setw(2) << tm.tm_hour
90 << std::setw(2) << tm.tm_min
91 << std::setw(2) << tm.tm_sec
92 <<
"." << std::setw(6) << ms;
104 using namespace core::tags;
110 ds.
set_string(patient_name, vr::PN, patient.patient_name);
111 ds.
set_string(patient_id, vr::LO, patient.patient_id);
112 ds.
set_string(patient_birth_date, vr::DA, patient.birth_date);
113 ds.
set_string(patient_sex, vr::CS, patient.sex);
116 auto study_uid = generate_uid();
117 ds.
set_string(study_instance_uid, vr::UI, study_uid);
118 ds.
set_string(study_date, vr::DA, current_date());
119 ds.
set_string(study_time, vr::TM, current_time());
120 ds.
set_string(accession_number, vr::SH,
"ACC" + patient.patient_id);
122 ds.
set_string(study_description, vr::LO,
"Test Study");
123 ds.
set_string(referring_physician_name, vr::PN,
"REFERRING^PHYSICIAN");
126 auto series_uid = generate_uid();
127 ds.
set_string(series_instance_uid, vr::UI, series_uid);
128 ds.
set_string(modality, vr::CS, params.modality);
130 ds.
set_string(series_date, vr::DA, current_date());
131 ds.
set_string(series_time, vr::TM, current_time());
132 ds.
set_string(series_description, vr::LO, params.modality +
" Series");
135 ds.
set_string(frame_of_reference_uid, vr::UI, generate_uid());
138 ds.
set_string(manufacturer, vr::LO,
"PACS System");
139 ds.
set_string(station_name, vr::SH,
"SAMPLE_STATION");
140 ds.
set_string(manufacturers_model_name, vr::LO,
"Sample Generator");
143 auto instance_uid = generate_uid();
144 ds.
set_string(sop_instance_uid, vr::UI, instance_uid);
145 ds.
set_numeric<int32_t>(instance_number, vr::IS, 1);
146 ds.
set_string(content_date, vr::DA, current_date());
147 ds.
set_string(content_time, vr::TM, current_time());
148 ds.
set_string(image_type, vr::CS,
"ORIGINAL\\PRIMARY\\AXIAL");
151 ds.
set_numeric<uint16_t>(samples_per_pixel, vr::US, 1);
152 ds.
set_string(photometric_interpretation, vr::CS, params.photometric);
153 ds.
set_numeric<uint16_t>(rows, vr::US, params.rows);
154 ds.
set_numeric<uint16_t>(columns, vr::US, params.columns);
155 ds.
set_numeric<uint16_t>(bits_allocated, vr::US, params.bits_allocated);
156 ds.
set_numeric<uint16_t>(bits_stored, vr::US, params.bits_stored);
157 ds.
set_numeric<uint16_t>(high_bit, vr::US, params.high_bit);
158 ds.
set_numeric<uint16_t>(pixel_representation, vr::US, params.pixel_representation);
161 ds.
set_string(window_center, vr::DS, std::to_string(params.window_center));
162 ds.
set_string(window_width, vr::DS, std::to_string(params.window_width));
174 uint16_t bits_alloc) -> std::vector<uint8_t> {
176 size_t bytes_per_pixel = bits_alloc / 8;
177 size_t total_pixels =
static_cast<size_t>(row_count) * col_count;
178 std::vector<uint8_t> data(total_pixels * bytes_per_pixel);
181 uint16_t max_value =
static_cast<uint16_t
>((1 << (bits_alloc > 16 ? 16 : bits_alloc)) - 1);
183 for (uint16_t y = 0; y < row_count; ++y) {
184 for (uint16_t x = 0; x < col_count; ++x) {
185 size_t idx = (
static_cast<size_t>(y) * col_count + x) * bytes_per_pixel;
188 double cx = col_count / 2.0;
189 double cy = row_count / 2.0;
192 double dist = std::sqrt(dx * dx + dy * dy);
193 double max_dist = std::sqrt(cx * cx + cy * cy);
194 double normalized = 1.0 - (dist / max_dist);
196 uint16_t value =
static_cast<uint16_t
>(normalized * max_value * 0.8);
199 data[idx] =
static_cast<uint8_t
>(value & 0xFF);
200 if (bytes_per_pixel > 1) {
201 data[idx + 1] =
static_cast<uint8_t
>((value >> 8) & 0xFF);
217 using namespace core::tags;
226 auto ds = create_base_dataset(patient, ct_params);
232 ds.set_string(rescale_intercept, vr::DS,
"-1024");
233 ds.set_string(rescale_slope, vr::DS,
"1");
234 ds.set_string(rescale_type, vr::LO,
"HU");
235 ds.set_string(pixel_spacing, vr::DS,
"0.5\\0.5");
236 ds.set_string(slice_location, vr::DS,
"0.0");
237 ds.set_string(image_position_patient, vr::DS,
"0.0\\0.0\\0.0");
238 ds.set_string(image_orientation_patient, vr::DS,
"1.0\\0.0\\0.0\\0.0\\1.0\\0.0");
241 auto pixel_bytes = generate_pixel_data(ct_params.
rows, ct_params.
columns,
256 using namespace core::tags;
259 params.modality =
"MR";
260 params.photometric =
"MONOCHROME2";
261 params.window_center = 500.0;
262 params.window_width = 1000.0;
264 auto ds = create_base_dataset(patient, params);
270 ds.set_string(pixel_spacing, vr::DS,
"1.0\\1.0");
271 ds.set_string(slice_location, vr::DS,
"0.0");
272 ds.set_string(image_position_patient, vr::DS,
"0.0\\0.0\\0.0");
273 ds.set_string(image_orientation_patient, vr::DS,
"1.0\\0.0\\0.0\\0.0\\1.0\\0.0");
276 auto pixel_bytes = generate_pixel_data(params.rows, params.columns,
277 params.bits_allocated);
291 using namespace core::tags;
294 params.modality =
"CR";
295 params.photometric =
"MONOCHROME2";
297 params.columns = 2048;
298 params.bits_allocated = 16;
299 params.bits_stored = 14;
300 params.high_bit = 13;
301 params.window_center = 8192.0;
302 params.window_width = 16384.0;
304 auto ds = create_base_dataset(patient, params);
310 auto pixel_bytes = generate_pixel_data(params.rows, params.columns,
311 params.bits_allocated);
325 using namespace core::tags;
328 params.modality =
"DX";
329 params.photometric =
"MONOCHROME2";
331 params.columns = 3000;
332 params.bits_allocated = 16;
333 params.bits_stored = 14;
334 params.high_bit = 13;
335 params.window_center = 8192.0;
336 params.window_width = 16384.0;
338 auto ds = create_base_dataset(patient, params);
344 ds.set_string(pixel_spacing, vr::DS,
"0.15\\0.15");
345 ds.set_string(image_orientation_patient, vr::DS,
"1.0\\0.0\\0.0\\0.0\\1.0\\0.0");
348 auto pixel_bytes = generate_pixel_data(params.rows, params.columns,
349 params.bits_allocated);
363 int instances_per_series) -> std::vector<core::dicom_dataset> {
365 using namespace core::tags;
368 std::vector<core::dicom_dataset> datasets;
369 datasets.reserve(
static_cast<size_t>(series_count * instances_per_series));
371 auto study_uid = study.study_uid.empty() ? generate_uid() : study.study_uid;
372 auto study_dt = study.study_date.empty() ? current_date() : study.study_date;
373 auto study_tm = study.study_time.empty() ? current_time() : study.study_time;
375 for (
int series_idx = 0; series_idx < series_count; ++series_idx) {
376 auto series_uid = generate_uid();
377 auto frame_uid = generate_uid();
379 for (
int instance_idx = 0; instance_idx < instances_per_series; ++instance_idx) {
380 auto ds = create_ct_dataset(patient);
383 ds.set_string(study_instance_uid, vr::UI, study_uid);
384 ds.set_string(study_date, vr::DA, study_dt);
385 ds.set_string(study_time, vr::TM, study_tm);
387 if (!study.accession_number.empty()) {
388 ds.set_string(accession_number, vr::SH, study.accession_number);
390 if (!study.description.empty()) {
391 ds.set_string(study_description, vr::LO, study.description);
393 if (!study.referring_physician.empty()) {
394 ds.set_string(referring_physician_name, vr::PN, study.referring_physician);
398 ds.set_string(series_instance_uid, vr::UI, series_uid);
399 ds.set_numeric<int32_t>(series_number, vr::IS, series_idx + 1);
400 ds.set_string(frame_of_reference_uid, vr::UI, frame_uid);
403 ds.set_string(sop_instance_uid, vr::UI, generate_uid());
404 ds.set_numeric<int32_t>(instance_number, vr::IS, instance_idx + 1);
407 double slice_loc =
static_cast<double>(instance_idx) * 5.0;
408 ds.set_string(slice_location, vr::DS, std::to_string(slice_loc));
409 ds.set_string(image_position_patient, vr::DS,
410 "0.0\\0.0\\" + std::to_string(slice_loc));
412 datasets.push_back(std::move(ds));
424 const std::vector<core::dicom_dataset>& datasets,
425 const std::filesystem::path& directory) {
427 using namespace core::tags;
428 namespace fs = std::filesystem;
430 fs::create_directories(directory);
432 for (
const auto& ds : datasets) {
433 auto pat_id = ds.get_string(patient_id,
"UNKNOWN");
434 auto study_uid_str = ds.get_string(study_instance_uid,
"UNKNOWN");
435 auto series_uid_str = ds.get_string(series_instance_uid,
"UNKNOWN");
436 auto instance_uid_str = ds.get_string(sop_instance_uid,
"UNKNOWN");
439 auto path = directory / pat_id / study_uid_str / series_uid_str;
440 fs::create_directories(path);
442 auto file_path = path / (instance_uid_str +
".dcm");
447 auto result = file.save(file_path);
449 if (!result.is_ok()) {
450 throw std::runtime_error(
"Failed to save DICOM file: " +