-
Notifications
You must be signed in to change notification settings - Fork 11
Fix encapsulated pixeldata handling #103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
… specific e.g. in case of CT it can be 12,13,14,15,16
…ally not read, undefined length was assumed use more intuitive offset calculation: previous offset + tag and length size + current length
Hi, thank you for your response. |
OK, let's tag this as a draft for now. |
fix indexing problem when creating offsets table fix frame size calculation: bits allocated was not considered
I think I solved the problems. |
@jcupitt What's your opinion about this PR? The other PR (the one before this) is updated. If that is merged, then I'll rebase and update my PR. |
Hi @weanti, sorry, I was on holiday and then got distracted by other projects. I'll look this over again now. |
... I saw one final tiny issue in #102, when that's resolved I'll look at this more closely. |
Thank you for the feedback. |
Resolved conflicts. |
I'll read this tomorrow. Thanks for the update! |
I downloaded the zip file, these are useful samples! What's the licence? Could we add them to the test suite? |
|
src/dicom-file.c
Outdated
&length); | ||
uint32_t length = 0; | ||
char* frame_data = NULL; | ||
if ( dcm_is_encapsulated_transfer_syntax(filehandle->desc.transfer_syntax_uid) ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a getter for this, I would write:
const char *syntax = dcm_filehandle_get_transfer_syntax_uid(filehandle);
uint32_t length = 0;
char* frame_data = NULL;
if (dcm_is_encapsulated_transfer_syntax(syntax)) {
src/dicom-parse.c
Outdated
.big_endian = is_big_endian(), | ||
}; | ||
|
||
const uint8_t bytes_per_pixel = desc->bits_allocated/8; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bits_allocated
is defined as a uint16
, so I think uint8
could (potentially!) be too small here. Let's make this uint16
as well.
libdicom style is for spaces around operators, so:
const uint16_t bytes_per_pixel = desc->bits_allocated / 8;
src/dicom-parse.c
Outdated
while( position < frame_end_offset && tag != TAG_SQ_DELIM ) | ||
{ | ||
read_uint32(&state, &fragment_length, &position); | ||
dcm_read(&state, fragment, fragment_length, &position ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need dcm_require()
here, you must loop on dcm_read()
if you need a certain number of bytes.
I would also check the return status of require in case of IO errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this second loop could perhaps be (untested!):
char *fragment = value;
while (position < frame_end_offset) {
uint32_t tag;
read_tag(&state, &tag, &position);
if (tag == TAG_SQ_DELIM) {
break;
}
uint32_t fragment_length;
read_uint32(&state, &fragment_length, &position);
if (!dcm_require(&state, fragment, fragment_length, &position)) {
free(value);
return NULL;
}
fragment += fragment_length;
}
src/dicom-parse.c
Outdated
} | ||
uint32_t fragment_length = 0; | ||
// first determine the total length of bytes to be read | ||
while( position < frame_end_offset && tag != TAG_SQ_DELIM ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two tag reads and checking the first tag at the head of the loop is a little ugly. How about (untested!):
int64_t position = 0;
// first determine the total length of bytes to be read
*length = 0;
while (position < frame_end_offset) {
uint32_t tag;
if (!read_tag(&state, &tag, &position)) {
return NULL;
}
if (tag == TAG_SQ_DELIM) {
break;
}
if (tag != TAG_ITEM) {
dcm_error_set(error, DCM_ERROR_CODE_PARSE,
"reading frame item failed",
"no item tag found for frame item");
return NULL;
}
uint32_t fragment_length;
if (!read_uint32(&state, &fragment_length, &position)) {
return NULL;
}
*length += fragment_length;
dcm_seekcur(&state, fragment_length, &position);
}
Now you only have one tag read and the tag check always tests the tag read just before.
You can reorder the loop below in the same way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. I managed to simplify the other loop.
} | ||
if ( fragment_idx < num_frames ) | ||
{ | ||
offsets[fragment_idx] = offsets[fragment_idx-1] + 8/*tag and length field size*/ + length; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand. I think you can have many fragments in each frame, so don't you need two loops here?
for (i = 0; i < num_frames; i++) {
record position as start of frame i
while (frame has fragments)
skip fragment
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part is a kind of an assumption. It may be according to the standard, but the standard is not an easy reading.
This part covers the case where there is no Basic Offset Table (so the frame offsets are absent). In this case we need to find the frame offsets ourselves.
Here is the assumption: if there is no BOT then I assume that each frame consists of only 1 fragment.
Why do I assume this: If each frame could have arbitrary number of fragments then it would be impossible to find out which fagment belongs to which frame. This means that each frame shall have the same amount of fragments.
We could use a different assumption here: number of fragments = C * number of frames, where C >= 1 and C is an integer. This would complicate things a bit. I think encapsulation is used mainly for compressed Transfer Syntaxes and there is no guarantee that each frame can be compressed in a way that they span across exactly C fragment.
Feel free to ask or propose a different approach to assign fragments to frames.
Great! Still to do:
|
@dclunie can you confirm what is the license for the images available from the NEMA FTP server? @michaelonken @jriesmeier how about the images shared in https://www.dcmtk.org/download/images/ ? |
There is no specified license for the CAR97, NEMA97, or WG04 images - we had intended all of these to be publicly usable without restriction (we gave away the CDs at meetings, and shared the images online by anonymous ftp), but never defined a license. |
David said it all ☝️ [Edit: The folder ddsm/ has its own README. The collection of images stems from the University of South Florida and has originally been provided in non-DICOM format. We converted it (I don't remember the exact context) and offered to host these images on the OFFIS servers (which they were fine with). I found a copy of the now-offline original website in the Internet archive. The images have been part of a research grant; maybe you find more information if you dig through the site.] |
I'm working on tests. Will take some time due to limited availability. |
Added some tests. These are rather functional test, because the implementation that handles encapsulated pixel data is not on the public API. |
Fix constructing frame offsets for encapsulated pixel data.
Fix reading frames from encapsulated pixel data.
Remove restrictions for bits stored value, because it is not valid in all cases e.g. CT.