Skip to content

Commit a4c4e40

Browse files
weantiAntal Ispanovity
andauthored
Fix encapsulated pixeldata handling (#103)
* remove restriction for bits stored, because its value can be modality specific e.g. in case of CT it can be 12,13,14,15,16 * fix offset reading and frame reading for encapsulated pixel data * fix offset calculation for encapsualted fragments: length was essentially not read, undefined length was assumed use more intuitive offset calculation: previous offset + tag and length size + current length * split regular and encapsulated frame parsing fix indexing problem when creating offsets table fix frame size calculation: bits allocated was not considered * fix offset calculation for non-encapsulated frames: bits allocated was not considered * coding style, code documentation, review fixes: simplification, error handling, etc * functional tests for encapsulated pixel data handling * simplify BOT construction avoid overflow for length value in dcm_parse_encapsulated_frame * simplify first BOT offset calculation restore some error handling * review fixesL coding style, comments, error messages * indentation --------- Co-authored-by: Antal Ispanovity <aispanovity@workstation>
1 parent b269219 commit a4c4e40

11 files changed

+250
-34
lines changed

CHANGELOG.md

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

3+
* add support for encapsulated pixel data reading [weanti]
34
* fix one-byte overread into struct padding [bgilbert]
45
* support single-frame DICOM images and allow BitsStored > 8 [tokyovigilante]
56
* fix error handling for string values over max length [arngaillard]
402 Bytes
Binary file not shown.
418 Bytes
Binary file not shown.
422 Bytes
Binary file not shown.
398 Bytes
Binary file not shown.
414 Bytes
Binary file not shown.

src/dicom-data.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1811,13 +1811,6 @@ DcmFrame *dcm_frame_create(DcmError **error,
18111811
return NULL;
18121812
}
18131813

1814-
if (bits_stored != 1 && bits_stored % 8 != 0) {
1815-
dcm_error_set(error, DCM_ERROR_CODE_INVALID,
1816-
"constructing frame item failed",
1817-
"wrong number of bits stored");
1818-
return NULL;
1819-
}
1820-
18211814
if (pixel_representation != 0 && pixel_representation != 1) {
18221815
dcm_error_set(error, DCM_ERROR_CODE_INVALID,
18231816
"constructing frame item failed",

src/dicom-file.c

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,12 +1365,25 @@ DcmFrame *dcm_filehandle_read_frame(DcmError **error,
13651365
return NULL;
13661366
}
13671367

1368-
uint32_t length;
1369-
char *frame_data = dcm_parse_frame(error,
1370-
filehandle->io,
1371-
filehandle->implicit,
1372-
&filehandle->desc,
1373-
&length);
1368+
const char *syntax = dcm_filehandle_get_transfer_syntax_uid(filehandle);
1369+
uint32_t length = 0;
1370+
char* frame_data = NULL;
1371+
if (dcm_is_encapsulated_transfer_syntax(syntax)) {
1372+
int64_t frame_end_offset = frame_number < filehandle->num_frames ?
1373+
filehandle->offset_table[i + 1] : 0xFFFFFFFF;
1374+
frame_data = dcm_parse_encapsulated_frame(error,
1375+
filehandle->io,
1376+
filehandle->implicit,
1377+
frame_end_offset,
1378+
&length );
1379+
} else {
1380+
frame_data = dcm_parse_frame(error,
1381+
filehandle->io,
1382+
filehandle->implicit,
1383+
&filehandle->desc,
1384+
&length);
1385+
}
1386+
13741387
if (frame_data == NULL) {
13751388
return NULL;
13761389
}

src/dicom-parse.c

Lines changed: 82 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -961,8 +961,6 @@ bool dcm_parse_pixeldata_offsets(DcmError **error,
961961

962962
// 0 in the BOT is the offset to the start of frame 1, ie. here
963963
*first_frame_offset = position;
964-
965-
position = 0;
966964
for (int i = 0; i < num_frames; i++) {
967965
if (!read_tag(&state, &tag, &position) ||
968966
!read_uint32(&state, &length, &position)) {
@@ -975,7 +973,7 @@ bool dcm_parse_pixeldata_offsets(DcmError **error,
975973
"too few frames in PixelData");
976974
return false;
977975
}
978-
976+
979977
if (tag != TAG_ITEM) {
980978
dcm_error_set(error, DCM_ERROR_CODE_PARSE,
981979
"building BasicOffsetTable failed",
@@ -984,21 +982,22 @@ bool dcm_parse_pixeldata_offsets(DcmError **error,
984982
tag);
985983
return false;
986984
}
987-
985+
988986
// step back to the start of the item for this frame
989-
offsets[i] = position - 8;
990-
987+
offsets[i] = position - *first_frame_offset - 8;
988+
991989
// and seek forward over the value
992990
if (!dcm_seekcur(&state, length, &position)) {
993991
return false;
994992
}
995993
}
996994

997-
// the next thing should be the end of sequence tag
995+
// in case multiple frames 1:1 frame to fragment mapping is assumed,
996+
// therefore the next thing should be the end of sequence tag
998997
if (!read_tag(&state, &tag, &position)) {
999998
return false;
1000999
}
1001-
if (tag != TAG_SQ_DELIM) {
1000+
if (num_frames != 1 && tag != TAG_SQ_DELIM) {
10021001
dcm_error_set(error, DCM_ERROR_CODE_PARSE,
10031002
"reading BasicOffsetTable failed",
10041003
"too many frames in PixelData");
@@ -1009,6 +1008,7 @@ bool dcm_parse_pixeldata_offsets(DcmError **error,
10091008
return true;
10101009
}
10111010

1011+
10121012
char *dcm_parse_frame(DcmError **error,
10131013
DcmIO *io,
10141014
bool implicit,
@@ -1022,34 +1022,95 @@ char *dcm_parse_frame(DcmError **error,
10221022
.big_endian = is_big_endian(),
10231023
};
10241024

1025+
*length = desc->rows *
1026+
desc->columns *
1027+
desc->samples_per_pixel *
1028+
(desc->bits_allocated / 8);
1029+
1030+
char *value = DCM_MALLOC(error, *length);
1031+
if (value == NULL) {
1032+
return NULL;
1033+
}
10251034
int64_t position = 0;
1035+
if (!dcm_require(&state, value, *length, &position)) {
1036+
free(value);
1037+
return NULL;
1038+
}
10261039

1027-
if (dcm_is_encapsulated_transfer_syntax(desc->transfer_syntax_uid)) {
1028-
uint32_t tag;
1029-
if (!read_tag(&state, &tag, &position) ||
1030-
!read_uint32(&state, length, &position)) {
1040+
return value;
1041+
}
1042+
1043+
/* Read encapsulated frame. Return NULL in case of error.
1044+
*/
1045+
char *dcm_parse_encapsulated_frame(DcmError **error,
1046+
DcmIO *io,
1047+
bool implicit,
1048+
int64_t frame_end_offset,
1049+
uint32_t* length)
1050+
{
1051+
DcmParseState state = {
1052+
.error = error,
1053+
.io = io,
1054+
.implicit = implicit,
1055+
.big_endian = is_big_endian(),
1056+
};
1057+
1058+
int64_t position = 0;
1059+
*length = 0;
1060+
uint32_t tag;
1061+
uint32_t fragment_length = 0;
1062+
uint64_t frame_length = 0;
1063+
1064+
// first determine the total length of bytes to be read
1065+
while (position < frame_end_offset) {
1066+
if (!read_tag(&state, &tag, &position)) {
10311067
return NULL;
10321068
}
1033-
1069+
if (tag == TAG_SQ_DELIM) {
1070+
break;
1071+
}
10341072
if (tag != TAG_ITEM) {
10351073
dcm_error_set(error, DCM_ERROR_CODE_PARSE,
10361074
"reading frame item failed",
10371075
"no item tag found for frame item");
10381076
return NULL;
10391077
}
1040-
} else {
1041-
*length = desc->rows * desc->columns * desc->samples_per_pixel *
1042-
(desc->bits_allocated / 8);
1078+
if (!read_uint32(&state, &fragment_length, &position)) {
1079+
return NULL;
1080+
}
1081+
dcm_seekcur(&state, fragment_length, &position);
1082+
frame_length += fragment_length;
1083+
}
1084+
if (frame_length > 0xFFFFFFFF) {
1085+
dcm_error_set(error, DCM_ERROR_CODE_PARSE,
1086+
"invalid frame size",
1087+
"frame size exceeds 4GB" );
1088+
return NULL;
10431089
}
10441090

1045-
char *value = DCM_MALLOC(error, *length);
1091+
char *value = DCM_MALLOC(error, frame_length);
10461092
if (value == NULL) {
10471093
return NULL;
10481094
}
1049-
if (!dcm_require(&state, value, *length, &position)) {
1050-
free(value);
1051-
return NULL;
1095+
// if frame end is unknown/undefined then update it
1096+
if (frame_end_offset == 0xFFFFFFFF) {
1097+
frame_end_offset = position;
10521098
}
1053-
1099+
// reposition to the beginning of encapsulated pixel data
1100+
dcm_seekcur(&state, -position, &position);
1101+
1102+
fragment_length = 0;
1103+
char* fragment = value;
1104+
position = 0;
1105+
while (position < frame_end_offset) {
1106+
read_tag(&state, &tag, &position);
1107+
read_uint32(&state, &fragment_length, &position);
1108+
if (!dcm_require(&state, fragment, fragment_length, &position)) {
1109+
free(value);
1110+
return NULL;
1111+
}
1112+
fragment += fragment_length;
1113+
}
1114+
*length = (uint32_t) frame_length;
10541115
return value;
10551116
}

src/pdicom.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,9 @@ char *dcm_parse_frame(DcmError **error,
167167
bool implicit,
168168
struct PixelDescription *desc,
169169
uint32_t *length);
170+
171+
char *dcm_parse_encapsulated_frame(DcmError **error,
172+
DcmIO *io,
173+
bool implicit,
174+
int64_t frame_end_offset,
175+
uint32_t* length);

0 commit comments

Comments
 (0)