Skip to content

Commit 87bd8c0

Browse files
Add support for single-frame DICOM images and images with Bits Allocated > 8 (#102)
* Add head CT slice test file * add single-frame DICOM file test suite * Return 1 for single image DICOM files without a NumberOfFrames tag * Return correct frame size when bits_allocated > 8 * update changelog for single-frame support * Typo * Add checks for 1-bit (unimplemented) or non-mod-8 (invalid) BitsAllocated. 8.1.1 Pixel Data Encoding of Related Data Elements Bits Allocated (0028,0100) shall either be 1, or a multiple of 8. https://dicom.nema.org/medical/dicom/current/output/html/part05.html#PS3.5
1 parent a425123 commit 87bd8c0

6 files changed

Lines changed: 70 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## main
22

33
* fix one-byte overread into struct padding [bgilbert]
4+
* support single-frame DICOM images and allow BitsStored > 8 [tokyovigilante]
45

56
## 1.2.0, 09/04/2025
67

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ int main() {
7272
return 1;
7373
}
7474

75-
printf("NumerOfFrames == %s\n", num_frames);
75+
printf("NumberOfFrames == %s\n", num_frames);
7676

7777
dcm_filehandle_destroy(filehandle);
7878

514 KB
Binary file not shown.

src/dicom-file.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,8 @@ static bool get_num_frames(DcmError **error,
296296
{
297297
const char *value;
298298
if (!get_tag_str(error, metadata, "NumberOfFrames", &value)) {
299-
return false;
299+
*number_of_frames = 1;
300+
return true;
300301
}
301302

302303
uint32_t num_frames = strtol(value, NULL, 10);
@@ -742,6 +743,18 @@ static bool set_pixel_description(DcmError **error,
742743
!dcm_element_get_value_integer(error, element, 0, &value)) {
743744
return false;
744745
}
746+
if (value == 1) {
747+
dcm_error_set(error, DCM_ERROR_CODE_INVALID,
748+
"reading frame item failed",
749+
"1-bit pixel storage (Bits Allocated == 1) not implemented");
750+
return false;
751+
}
752+
if (value % 8 != 0) {
753+
dcm_error_set(error, DCM_ERROR_CODE_INVALID,
754+
"reading frame item failed",
755+
"BitsAllocated must be a multiple of 8");
756+
return false;
757+
}
745758
desc->bits_allocated = (uint16_t) value;
746759

747760
element = dcm_dataset_get(error, metadata, 0x00280101);
@@ -1300,6 +1313,7 @@ bool dcm_filehandle_prepare_read_frame(DcmError **error,
13001313
filehandle->offset_table[i] = i *
13011314
filehandle->desc.rows *
13021315
filehandle->desc.columns *
1316+
(filehandle->desc.bits_allocated / 8) *
13031317
filehandle->desc.samples_per_pixel;
13041318
}
13051319

src/dicom-parse.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1038,7 +1038,8 @@ char *dcm_parse_frame(DcmError **error,
10381038
return NULL;
10391039
}
10401040
} else {
1041-
*length = desc->rows * desc->columns * desc->samples_per_pixel;
1041+
*length = desc->rows * desc->columns * desc->samples_per_pixel *
1042+
(desc->bits_allocated / 8);
10421043
}
10431044

10441045
char *value = DCM_MALLOC(error, *length);

tests/check_dicom.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,45 @@ START_TEST(test_file_sm_image_file_meta_memory)
767767
}
768768
END_TEST
769769

770+
START_TEST(test_file_ct_brain_single)
771+
{
772+
const uint32_t frame_number = 1;
773+
const uint32_t frame_length = 524288; // 512 x 512 x 16 bits stored
774+
775+
char *file_path = fixture_path("data/test_files/ct_brain_single.dcm");
776+
DcmFilehandle *filehandle =
777+
dcm_filehandle_create_from_file(NULL, file_path);
778+
free(file_path);
779+
ck_assert_ptr_nonnull(filehandle);
780+
781+
const DcmDataSet *metadata =
782+
dcm_filehandle_get_metadata_subset(NULL, filehandle);
783+
ck_assert_ptr_nonnull(metadata);
784+
ck_assert_ptr_null(dcm_dataset_get(NULL, metadata, 0x00280008)); // NumberOfFrames should not be present
785+
786+
ck_assert_int_ne(dcm_filehandle_prepare_read_frame(NULL, filehandle), 0);
787+
788+
DcmFrame *frame = dcm_filehandle_read_frame(NULL,
789+
filehandle,
790+
frame_number);
791+
ck_assert_uint_eq(dcm_frame_get_number(frame), frame_number);
792+
ck_assert_uint_eq(dcm_frame_get_rows(frame), 512);
793+
ck_assert_uint_eq(dcm_frame_get_columns(frame), 512);
794+
ck_assert_uint_eq(dcm_frame_get_samples_per_pixel(frame), 1);
795+
ck_assert_uint_eq(dcm_frame_get_bits_allocated(frame), 16);
796+
ck_assert_uint_eq(dcm_frame_get_bits_stored(frame), 16);
797+
ck_assert_uint_eq(dcm_frame_get_high_bit(frame), 15);
798+
ck_assert_uint_eq(dcm_frame_get_pixel_representation(frame), 1);
799+
ck_assert_uint_eq(dcm_frame_get_planar_configuration(frame), 0);
800+
ck_assert_str_eq(dcm_frame_get_photometric_interpretation(frame), "MONOCHROME2");
801+
ck_assert_str_eq(dcm_frame_get_transfer_syntax_uid(frame),
802+
"1.2.840.10008.1.2.1");
803+
ck_assert_uint_eq(dcm_frame_get_length(frame), frame_length);
804+
805+
dcm_frame_destroy(frame);
806+
dcm_filehandle_destroy(filehandle);
807+
}
808+
END_TEST
770809

771810
static Suite *create_main_suite(void)
772811
{
@@ -846,12 +885,24 @@ static Suite *create_file_suite(void)
846885
return suite;
847886
}
848887

888+
static Suite *create_single_frame_suite(void)
889+
{
890+
Suite *suite = suite_create("single_frame");
891+
892+
TCase *frame_case = tcase_create("ct_brain_frame");
893+
tcase_add_test(frame_case, test_file_ct_brain_single);
894+
suite_add_tcase(suite, frame_case);
895+
896+
return suite;
897+
}
898+
849899

850900
int main(void)
851901
{
852902
SRunner *runner = srunner_create(create_main_suite());
853903
srunner_add_suite(runner, create_data_suite());
854904
srunner_add_suite(runner, create_file_suite());
905+
srunner_add_suite(runner, create_single_frame_suite());
855906
srunner_run_all(runner, CK_VERBOSE);
856907
int number_failed = srunner_ntests_failed(runner);
857908
srunner_free(runner);

0 commit comments

Comments
 (0)