diff --git a/CHANGELOG.md b/CHANGELOG.md index e9b696e..8c92f8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## main * fix one-byte overread into struct padding [bgilbert] +* support single-frame DICOM images and allow BitsStored > 8 [tokyovigilante] ## 1.2.0, 09/04/2025 diff --git a/README.md b/README.md index 74b4f96..6f7fe75 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ int main() { return 1; } - printf("NumerOfFrames == %s\n", num_frames); + printf("NumberOfFrames == %s\n", num_frames); dcm_filehandle_destroy(filehandle); diff --git a/data/test_files/ct_brain_single.dcm b/data/test_files/ct_brain_single.dcm new file mode 100644 index 0000000..a7a342f Binary files /dev/null and b/data/test_files/ct_brain_single.dcm differ diff --git a/src/dicom-file.c b/src/dicom-file.c index 9faa187..9d476da 100644 --- a/src/dicom-file.c +++ b/src/dicom-file.c @@ -296,7 +296,8 @@ static bool get_num_frames(DcmError **error, { const char *value; if (!get_tag_str(error, metadata, "NumberOfFrames", &value)) { - return false; + *number_of_frames = 1; + return true; } uint32_t num_frames = strtol(value, NULL, 10); @@ -742,6 +743,18 @@ static bool set_pixel_description(DcmError **error, !dcm_element_get_value_integer(error, element, 0, &value)) { return false; } + if (value == 1) { + dcm_error_set(error, DCM_ERROR_CODE_INVALID, + "reading frame item failed", + "1-bit pixel storage (Bits Allocated == 1) not implemented"); + return false; + } + if (value % 8 != 0) { + dcm_error_set(error, DCM_ERROR_CODE_INVALID, + "reading frame item failed", + "BitsAllocated must be a multiple of 8"); + return false; + } desc->bits_allocated = (uint16_t) value; element = dcm_dataset_get(error, metadata, 0x00280101); @@ -1300,6 +1313,7 @@ bool dcm_filehandle_prepare_read_frame(DcmError **error, filehandle->offset_table[i] = i * filehandle->desc.rows * filehandle->desc.columns * + (filehandle->desc.bits_allocated / 8) * filehandle->desc.samples_per_pixel; } diff --git a/src/dicom-parse.c b/src/dicom-parse.c index 2314189..1f7bf12 100644 --- a/src/dicom-parse.c +++ b/src/dicom-parse.c @@ -1038,7 +1038,8 @@ char *dcm_parse_frame(DcmError **error, return NULL; } } else { - *length = desc->rows * desc->columns * desc->samples_per_pixel; + *length = desc->rows * desc->columns * desc->samples_per_pixel * + (desc->bits_allocated / 8); } char *value = DCM_MALLOC(error, *length); diff --git a/tests/check_dicom.c b/tests/check_dicom.c index 20ad729..e99ca1d 100644 --- a/tests/check_dicom.c +++ b/tests/check_dicom.c @@ -767,6 +767,45 @@ START_TEST(test_file_sm_image_file_meta_memory) } END_TEST +START_TEST(test_file_ct_brain_single) +{ + const uint32_t frame_number = 1; + const uint32_t frame_length = 524288; // 512 x 512 x 16 bits stored + + char *file_path = fixture_path("data/test_files/ct_brain_single.dcm"); + DcmFilehandle *filehandle = + dcm_filehandle_create_from_file(NULL, file_path); + free(file_path); + ck_assert_ptr_nonnull(filehandle); + + const DcmDataSet *metadata = + dcm_filehandle_get_metadata_subset(NULL, filehandle); + ck_assert_ptr_nonnull(metadata); + ck_assert_ptr_null(dcm_dataset_get(NULL, metadata, 0x00280008)); // NumberOfFrames should not be present + + ck_assert_int_ne(dcm_filehandle_prepare_read_frame(NULL, filehandle), 0); + + DcmFrame *frame = dcm_filehandle_read_frame(NULL, + filehandle, + frame_number); + ck_assert_uint_eq(dcm_frame_get_number(frame), frame_number); + ck_assert_uint_eq(dcm_frame_get_rows(frame), 512); + ck_assert_uint_eq(dcm_frame_get_columns(frame), 512); + ck_assert_uint_eq(dcm_frame_get_samples_per_pixel(frame), 1); + ck_assert_uint_eq(dcm_frame_get_bits_allocated(frame), 16); + ck_assert_uint_eq(dcm_frame_get_bits_stored(frame), 16); + ck_assert_uint_eq(dcm_frame_get_high_bit(frame), 15); + ck_assert_uint_eq(dcm_frame_get_pixel_representation(frame), 1); + ck_assert_uint_eq(dcm_frame_get_planar_configuration(frame), 0); + ck_assert_str_eq(dcm_frame_get_photometric_interpretation(frame), "MONOCHROME2"); + ck_assert_str_eq(dcm_frame_get_transfer_syntax_uid(frame), + "1.2.840.10008.1.2.1"); + ck_assert_uint_eq(dcm_frame_get_length(frame), frame_length); + + dcm_frame_destroy(frame); + dcm_filehandle_destroy(filehandle); +} +END_TEST static Suite *create_main_suite(void) { @@ -846,12 +885,24 @@ static Suite *create_file_suite(void) return suite; } +static Suite *create_single_frame_suite(void) +{ + Suite *suite = suite_create("single_frame"); + + TCase *frame_case = tcase_create("ct_brain_frame"); + tcase_add_test(frame_case, test_file_ct_brain_single); + suite_add_tcase(suite, frame_case); + + return suite; +} + int main(void) { SRunner *runner = srunner_create(create_main_suite()); srunner_add_suite(runner, create_data_suite()); srunner_add_suite(runner, create_file_suite()); + srunner_add_suite(runner, create_single_frame_suite()); srunner_run_all(runner, CK_VERBOSE); int number_failed = srunner_ntests_failed(runner); srunner_free(runner);