diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e9e3aa..c6417b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ * better handling of implicit mode in dcm-dump [jcupitt] * better handling of trailing spaces in string values [y-baba-isb] * much faster read of files with an EOT but no FGS [pcram-techcyte] +* add `dcm_filehandle_frame_position()` [jcupitt] +* add DICOM catenation support [jcupitt] ## 1.1.0, 28/3/24 diff --git a/include/dicom/dicom.h b/include/dicom/dicom.h index fce8b59..4ba7c65 100644 --- a/include/dicom/dicom.h +++ b/include/dicom/dicom.h @@ -1767,7 +1767,7 @@ bool dcm_filehandle_prepare_read_frame(DcmError **error, * * :param error: Pointer to error object * :param filehandle: File - * :param index: One-based frame number + * :param frame_number: One-based frame number * * :return: Frame */ @@ -1776,6 +1776,28 @@ DcmFrame *dcm_filehandle_read_frame(DcmError **error, DcmFilehandle *filehandle, uint32_t frame_number); +/** + * Get the frame number at a position. + * + * Given a tile row and column, get the number of the frame that should be + * displayed at that position, taking into account any frame-positioning + * metadata. + * + * :param error: Pointer to error object + * :param filehandle: File + * :param column: Column number, from 0 + * :param row: Row number, from 0 + * :param frame_number: Return one-based frame number + * + * :return: true on success, false for no frame available + */ +DCM_EXTERN +bool dcm_filehandle_get_frame_number(DcmError **error, + DcmFilehandle *filehandle, + uint32_t column, + uint32_t row, + uint32_t *frame_number); + /** * Read the frame at a position in a File. * diff --git a/src/dicom-file.c b/src/dicom-file.c index e9e1f46..7a39120 100644 --- a/src/dicom-file.c +++ b/src/dicom-file.c @@ -51,6 +51,7 @@ struct _DcmFilehandle { uint32_t frame_width; uint32_t frame_height; uint32_t num_frames; + uint32_t frame_offset; struct PixelDescription desc; DcmLayout layout; @@ -312,6 +313,23 @@ static bool get_num_frames(DcmError **error, } +static bool get_frame_offset(DcmError **error, + const DcmDataSet *metadata, + uint32_t *frame_offset) +{ + // optional, defaults to 0 + int64_t value; + if (!get_tag_int(error, + metadata, "ConcatenationFrameOffsetNumber", &value)) { + value = 0; + } + + *frame_offset = value; + + return true; +} + + static bool get_frame_size(DcmError **error, const DcmDataSet *metadata, uint32_t *frame_width, @@ -867,6 +885,7 @@ const DcmDataSet *dcm_filehandle_get_metadata_subset(DcmError **error, &filehandle->frame_width, &filehandle->frame_height) || !get_num_frames(error, meta, &filehandle->num_frames) || + !get_frame_offset(error, meta, &filehandle->frame_offset) || !get_tiles(error, meta, &filehandle->tiles_across, &filehandle->tiles_down) || !set_pixel_description(error, meta, &filehandle->desc)) { @@ -1344,15 +1363,16 @@ DcmFrame *dcm_filehandle_read_frame(DcmError **error, } -DcmFrame *dcm_filehandle_read_frame_position(DcmError **error, - DcmFilehandle *filehandle, - uint32_t column, - uint32_t row) +bool dcm_filehandle_get_frame_number(DcmError **error, + DcmFilehandle *filehandle, + uint32_t column, + uint32_t row, + uint32_t *frame_number) { - dcm_log_debug("Read frame position (%u, %u)", column, row); + dcm_log_debug("Get frame number at (%u, %u)", column, row); if (!dcm_filehandle_prepare_read_frame(error, filehandle)) { - return NULL; + return false; } if (column >= filehandle->tiles_across || @@ -1362,22 +1382,50 @@ DcmFrame *dcm_filehandle_read_frame_position(DcmError **error, "column and Row must be less than %u, %u", filehandle->tiles_across, filehandle->tiles_down); - return NULL; + return false; } - uint32_t index = column + row * filehandle->tiles_across; + int64_t index = column + row * filehandle->tiles_across; if (filehandle->layout == DCM_LAYOUT_SPARSE) { index = filehandle->frame_index[index]; - if (index == 0xffffffff) { + if (index == -1) { dcm_error_set(error, DCM_ERROR_CODE_MISSING_FRAME, "no frame", "no frame at position (%u, %u)", column, row); - return NULL; + return false; + } + } else { + // subtract the start of this file, for catenation support + index -= filehandle->frame_offset; + if (index < 0 || index >= (int64_t) filehandle->num_frames) { + dcm_error_set(error, DCM_ERROR_CODE_MISSING_FRAME, + "no frame", + "no frame at position (%u, %u)", column, row); + return false; } } - // read_frame() numbers from 1 - return dcm_filehandle_read_frame(error, filehandle, index + 1); + // frame numbers are from 1 + *frame_number = index + 1; + + return true; +} + + +DcmFrame *dcm_filehandle_read_frame_position(DcmError **error, + DcmFilehandle *filehandle, + uint32_t column, + uint32_t row) +{ + dcm_log_debug("Read frame position (%u, %u)", column, row); + + uint32_t frame_number; + if (!dcm_filehandle_get_frame_number(error, + filehandle, column, row, &frame_number)) { + return NULL; + } + + return dcm_filehandle_read_frame(error, filehandle, frame_number); }