Skip to content

Commit 95bc8db

Browse files
authored
Support dicom catenation (#93)
* add dicom catenation support - new API `dcm_filehandle_get_frame_number()` to read out an element of the postition->frame mapping - add `frame_offset` support for `TILED_FULL` catenations * fix missing tile regression * update changelog * fix MSVC compiler warning * need ldconfig after ununtu install
1 parent 7381e3e commit 95bc8db

4 files changed

Lines changed: 90 additions & 12 deletions

File tree

.github/workflows/run_unit_tests.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ jobs:
7878
DEBUG_DICT=1 meson compile -C builddir
7979
$sudo meson install -C builddir
8080
81+
- name: Rebuild the shared library cache
82+
if: startsWith(matrix.os, 'ubuntu')
83+
run: sudo ldconfig
84+
8185
- name: Run unit tests
8286
# Don't run tests for private copy of Check
8387
run: meson test -C builddir --suite libdicom

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
* better handling of implicit mode in dcm-dump [jcupitt]
66
* better handling of trailing spaces in string values [y-baba-isb]
77
* much faster read of files with an EOT but no FGS [pcram-techcyte]
8+
* add `dcm_filehandle_get_frame_number()` [jcupitt]
9+
* add DICOM catenation support [jcupitt]
810

911
## 1.1.0, 28/3/24
1012

include/dicom/dicom.h

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1767,7 +1767,7 @@ bool dcm_filehandle_prepare_read_frame(DcmError **error,
17671767
*
17681768
* :param error: Pointer to error object
17691769
* :param filehandle: File
1770-
* :param index: One-based frame number
1770+
* :param frame_number: One-based frame number
17711771
*
17721772
* :return: Frame
17731773
*/
@@ -1776,6 +1776,28 @@ DcmFrame *dcm_filehandle_read_frame(DcmError **error,
17761776
DcmFilehandle *filehandle,
17771777
uint32_t frame_number);
17781778

1779+
/**
1780+
* Get the frame number at a position.
1781+
*
1782+
* Given a tile row and column, get the number of the frame that should be
1783+
* displayed at that position, taking into account any frame-positioning
1784+
* metadata.
1785+
*
1786+
* :param error: Pointer to error object
1787+
* :param filehandle: File
1788+
* :param column: Column number, from 0
1789+
* :param row: Row number, from 0
1790+
* :param frame_number: Return one-based frame number
1791+
*
1792+
* :return: true on success, false for no frame available
1793+
*/
1794+
DCM_EXTERN
1795+
bool dcm_filehandle_get_frame_number(DcmError **error,
1796+
DcmFilehandle *filehandle,
1797+
uint32_t column,
1798+
uint32_t row,
1799+
uint32_t *frame_number);
1800+
17791801
/**
17801802
* Read the frame at a position in a File.
17811803
*

src/dicom-file.c

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ struct _DcmFilehandle {
5151
uint32_t frame_width;
5252
uint32_t frame_height;
5353
uint32_t num_frames;
54+
uint32_t frame_offset;
5455
struct PixelDescription desc;
5556
DcmLayout layout;
5657

@@ -312,6 +313,24 @@ static bool get_num_frames(DcmError **error,
312313
}
313314

314315

316+
static bool get_frame_offset(DcmError **error,
317+
const DcmDataSet *metadata,
318+
uint32_t *frame_offset)
319+
{
320+
// optional, defaults to 0
321+
int64_t value;
322+
if (!get_tag_int(error,
323+
metadata, "ConcatenationFrameOffsetNumber", &value)) {
324+
value = 0;
325+
}
326+
327+
// it's a uint32 in the DICOM file
328+
*frame_offset = (uint32_t) value;
329+
330+
return true;
331+
}
332+
333+
315334
static bool get_frame_size(DcmError **error,
316335
const DcmDataSet *metadata,
317336
uint32_t *frame_width,
@@ -867,6 +886,7 @@ const DcmDataSet *dcm_filehandle_get_metadata_subset(DcmError **error,
867886
&filehandle->frame_width,
868887
&filehandle->frame_height) ||
869888
!get_num_frames(error, meta, &filehandle->num_frames) ||
889+
!get_frame_offset(error, meta, &filehandle->frame_offset) ||
870890
!get_tiles(error, meta,
871891
&filehandle->tiles_across, &filehandle->tiles_down) ||
872892
!set_pixel_description(error, meta, &filehandle->desc)) {
@@ -1344,15 +1364,16 @@ DcmFrame *dcm_filehandle_read_frame(DcmError **error,
13441364
}
13451365

13461366

1347-
DcmFrame *dcm_filehandle_read_frame_position(DcmError **error,
1348-
DcmFilehandle *filehandle,
1349-
uint32_t column,
1350-
uint32_t row)
1367+
bool dcm_filehandle_get_frame_number(DcmError **error,
1368+
DcmFilehandle *filehandle,
1369+
uint32_t column,
1370+
uint32_t row,
1371+
uint32_t *frame_number)
13511372
{
1352-
dcm_log_debug("Read frame position (%u, %u)", column, row);
1373+
dcm_log_debug("Get frame number at (%u, %u)", column, row);
13531374

13541375
if (!dcm_filehandle_prepare_read_frame(error, filehandle)) {
1355-
return NULL;
1376+
return false;
13561377
}
13571378

13581379
if (column >= filehandle->tiles_across ||
@@ -1362,22 +1383,51 @@ DcmFrame *dcm_filehandle_read_frame_position(DcmError **error,
13621383
"column and Row must be less than %u, %u",
13631384
filehandle->tiles_across,
13641385
filehandle->tiles_down);
1365-
return NULL;
1386+
return false;
13661387
}
13671388

1368-
uint32_t index = column + row * filehandle->tiles_across;
1389+
int64_t index = column + row * filehandle->tiles_across;
13691390
if (filehandle->layout == DCM_LAYOUT_SPARSE) {
13701391
index = filehandle->frame_index[index];
13711392
if (index == 0xffffffff) {
13721393
dcm_error_set(error, DCM_ERROR_CODE_MISSING_FRAME,
13731394
"no frame",
13741395
"no frame at position (%u, %u)", column, row);
1375-
return NULL;
1396+
return false;
1397+
}
1398+
} else {
1399+
// subtract the start of this file, for catenation support
1400+
index -= filehandle->frame_offset;
1401+
if (index < 0 || index >= (int64_t) filehandle->num_frames) {
1402+
dcm_error_set(error, DCM_ERROR_CODE_MISSING_FRAME,
1403+
"no frame",
1404+
"no frame at position (%u, %u)", column, row);
1405+
return false;
13761406
}
13771407
}
13781408

1379-
// read_frame() numbers from 1
1380-
return dcm_filehandle_read_frame(error, filehandle, index + 1);
1409+
// frame numbers are from 1, and are always uint32
1410+
if (frame_number)
1411+
*frame_number = (uint32_t) (index + 1);
1412+
1413+
return true;
1414+
}
1415+
1416+
1417+
DcmFrame *dcm_filehandle_read_frame_position(DcmError **error,
1418+
DcmFilehandle *filehandle,
1419+
uint32_t column,
1420+
uint32_t row)
1421+
{
1422+
dcm_log_debug("Read frame position (%u, %u)", column, row);
1423+
1424+
uint32_t frame_number;
1425+
if (!dcm_filehandle_get_frame_number(error,
1426+
filehandle, column, row, &frame_number)) {
1427+
return NULL;
1428+
}
1429+
1430+
return dcm_filehandle_read_frame(error, filehandle, frame_number);
13811431
}
13821432

13831433

0 commit comments

Comments
 (0)