208 auto result = dicom_file::open(std::filesystem::path(file_path));
209 if (result.is_err()) {
213 auto& file = result.value();
214 const auto& dataset = file.dataset();
217 auto rows = dataset.get_numeric<uint16_t>(
tags::rows);
220 if (!rows || !columns || *rows == 0 || *columns == 0) {
225 auto bits_allocated =
226 dataset.get_numeric<uint16_t>(dicom_tag{0x0028, 0x0100});
227 auto bits_stored = dataset.get_numeric<uint16_t>(dicom_tag{0x0028, 0x0101});
229 auto pixel_representation =
230 dataset.get_numeric<uint16_t>(dicom_tag{0x0028, 0x0103});
232 if (!bits_allocated || !bits_stored) {
236 uint16_t spp = samples_per_pixel.value_or(1);
237 uint16_t repr = pixel_representation.value_or(0);
244 if (pixel_element ==
nullptr) {
248 auto raw_data = pixel_element->raw_data();
249 if (raw_data.empty()) {
254 size_t frame_size =
static_cast<size_t>(*rows) * (*columns) * spp *
255 ((*bits_allocated + 7) / 8);
257 uint32_t frame_index = params.
frame > 0 ? params.
frame - 1 : 0;
258 size_t frame_offset = frame_index * frame_size;
260 if (frame_offset + frame_size > raw_data.size()) {
266 std::vector<uint8_t> pixels;
267 size_t num_pixels =
static_cast<size_t>(*rows) * (*columns) * spp;
269 if (*bits_allocated == 16) {
272 int max_val = -65536;
275 for (
size_t i = 0; i < num_pixels; ++i) {
276 size_t idx = frame_offset + i * 2;
277 if (idx + 1 >= raw_data.size())
break;
281 pixel =
static_cast<int16_t
>(raw_data[idx] |
282 (raw_data[idx + 1] << 8));
284 pixel =
static_cast<int16_t
>(raw_data[idx] |
285 (raw_data[idx + 1] << 8));
287 min_val = std::min(min_val,
static_cast<int>(pixel));
288 max_val = std::max(max_val,
static_cast<int>(pixel));
291 double window_width =
static_cast<double>(max_val - min_val);
292 double window_center =
static_cast<double>(min_val + max_val) / 2.0;
293 if (window_width < 1) window_width = 1;
295 pixels.resize(num_pixels);
296 for (
size_t i = 0; i < num_pixels; ++i) {
297 size_t idx = frame_offset + i * 2;
298 if (idx + 1 >= raw_data.size())
break;
300 int16_t pixel =
static_cast<int16_t
>(raw_data[idx] |
301 (raw_data[idx + 1] << 8));
303 double lower = window_center - window_width / 2.0;
304 double upper = window_center + window_width / 2.0;
306 if (pixel <= lower) {
308 }
else if (pixel >= upper) {
311 pixels[i] =
static_cast<uint8_t
>(
312 ((pixel - lower) / window_width) * 255.0);
317 pixels.resize(num_pixels);
318 for (
size_t i = 0; i < num_pixels; ++i) {
319 size_t idx = frame_offset + i;
320 if (idx < raw_data.size()) {
321 pixels[i] = raw_data[idx];
327 if (photometric ==
"MONOCHROME1") {
328 for (
auto& p : pixels) {
329 p =
static_cast<uint8_t
>(255 - p);
334 uint16_t src_width = *columns;
335 uint16_t src_height = *rows;
336 uint16_t dst_size = params.
size;
339 uint16_t dst_width, dst_height;
340 if (src_width > src_height) {
341 dst_width = dst_size;
343 static_cast<uint16_t
>(
static_cast<float>(src_height) / src_width * dst_size);
345 dst_height = dst_size;
347 static_cast<uint16_t
>(
static_cast<float>(src_width) / src_height * dst_size);
350 if (dst_width == 0) dst_width = 1;
351 if (dst_height == 0) dst_height = 1;
354 std::vector<uint8_t> resized(
static_cast<size_t>(dst_width) * dst_height * spp);
356 float x_ratio =
static_cast<float>(src_width) / dst_width;
357 float y_ratio =
static_cast<float>(src_height) / dst_height;
359 for (uint16_t y = 0; y < dst_height; ++y) {
360 for (uint16_t x = 0; x < dst_width; ++x) {
361 float src_x = x * x_ratio;
362 float src_y = y * y_ratio;
364 int x0 =
static_cast<int>(src_x);
365 int y0 =
static_cast<int>(src_y);
366 int x1 = std::min(x0 + 1,
static_cast<int>(src_width - 1));
367 int y1 = std::min(y0 + 1,
static_cast<int>(src_height - 1));
369 float x_diff = src_x - x0;
370 float y_diff = src_y - y0;
372 for (uint16_t c = 0; c < spp; ++c) {
373 size_t idx00 = (
static_cast<size_t>(y0) * src_width + x0) * spp + c;
374 size_t idx01 = (
static_cast<size_t>(y0) * src_width + x1) * spp + c;
375 size_t idx10 = (
static_cast<size_t>(y1) * src_width + x0) * spp + c;
376 size_t idx11 = (
static_cast<size_t>(y1) * src_width + x1) * spp + c;
378 float v00 = idx00 < pixels.size() ? pixels[idx00] : 0;
379 float v01 = idx01 < pixels.size() ? pixels[idx01] : 0;
380 float v10 = idx10 < pixels.size() ? pixels[idx10] : 0;
381 float v11 = idx11 < pixels.size() ? pixels[idx11] : 0;
383 float value = v00 * (1 - x_diff) * (1 - y_diff) +
384 v01 * x_diff * (1 - y_diff) +
385 v10 * (1 - x_diff) * y_diff +
386 v11 * x_diff * y_diff;
389 (
static_cast<size_t>(y) * dst_width + x) * spp + c;
390 resized[dst_idx] =
static_cast<uint8_t
>(std::clamp(value, 0.0f, 255.0f));
396 std::vector<uint8_t> output;
398 if (params.
format ==
"jpeg") {
399#ifdef PACS_JPEG_FOUND
401 struct jpeg_compress_struct cinfo {};
402 struct jpeg_error_mgr jerr {};
404 cinfo.err = jpeg_std_error(&jerr);
405 jpeg_create_compress(&cinfo);
408 unsigned char* outbuffer =
nullptr;
409 unsigned long outsize = 0;
410 jpeg_mem_dest(&cinfo, &outbuffer, &outsize);
412 cinfo.image_width = dst_width;
413 cinfo.image_height = dst_height;
414 cinfo.input_components =
static_cast<int>(spp);
415 cinfo.in_color_space = (spp == 1) ? JCS_GRAYSCALE : JCS_RGB;
417 jpeg_set_defaults(&cinfo);
418 jpeg_set_quality(&cinfo, params.
quality, TRUE);
419 jpeg_start_compress(&cinfo, TRUE);
421 JSAMPROW row_pointer[1];
422 int row_stride = dst_width * spp;
424 while (cinfo.next_scanline < cinfo.image_height) {
425 row_pointer[0] = &resized[cinfo.next_scanline *
static_cast<size_t>(row_stride)];
426 jpeg_write_scanlines(&cinfo, row_pointer, 1);
429 jpeg_finish_compress(&cinfo);
430 jpeg_destroy_compress(&cinfo);
432 if (outbuffer !=
nullptr && outsize > 0) {
433 output.assign(outbuffer, outbuffer + outsize);
440 }
else if (params.
format ==
"png") {
443 struct png_mem_buffer {
444 std::vector<uint8_t> data;
447 auto write_callback = [](png_structp png_ptr, png_bytep data,
450 static_cast<png_mem_buffer*
>(png_get_io_ptr(png_ptr));
451 buffer->data.insert(buffer->data.end(), data, data + length);
454 auto flush_callback = [](png_structp ) {
458 png_mem_buffer buffer;
460 png_structp png_ptr = png_create_write_struct(
461 PNG_LIBPNG_VER_STRING,
nullptr,
nullptr,
nullptr);
462 if (png_ptr ==
nullptr) {
466 png_infop info_ptr = png_create_info_struct(png_ptr);
467 if (info_ptr ==
nullptr) {
468 png_destroy_write_struct(&png_ptr,
nullptr);
472 if (setjmp(png_jmpbuf(png_ptr))) {
473 png_destroy_write_struct(&png_ptr, &info_ptr);
477 png_set_write_fn(png_ptr, &buffer, write_callback, flush_callback);
479 int color_type = (spp == 1) ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_RGB;
480 png_set_IHDR(png_ptr, info_ptr, dst_width, dst_height, 8, color_type,
481 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
482 PNG_FILTER_TYPE_DEFAULT);
484 png_write_info(png_ptr, info_ptr);
486 int row_stride = dst_width * spp;
487 for (uint16_t y = 0; y < dst_height; ++y) {
488 png_bytep row = &resized[
static_cast<size_t>(y) * row_stride];
489 png_write_row(png_ptr, row);
492 png_write_end(png_ptr,
nullptr);
493 png_destroy_write_struct(&png_ptr, &info_ptr);
495 output = std::move(buffer.data);