Skip to content

Commit 3943066

Browse files
committed
COMP: Encapsulate DCMTK metadata loop in DCMTKFileReader
Move the metadata-population loop into DCMTKFileReader::PopulateMetaDataDictionary so DcmDataset never escapes the reader. Per @blowekamp review.
1 parent b99df55 commit 3943066

3 files changed

Lines changed: 74 additions & 71 deletions

File tree

Modules/IO/DCMTK/include/itkDCMTKFileReader.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "dcmtk/dcmdata/dcsequen.h"
3636
#include "itkMacro.h"
3737
#include "itkImageIOBase.h"
38+
#include "itkMetaDataDictionary.h"
3839

3940
class DcmSequenceOfItems;
4041
class DcmFileFormat;
@@ -498,11 +499,8 @@ class ITKIODCMTK_EXPORT DCMTKFileReader
498499
static bool
499500
IsImageFile(const std::string & filename);
500501

501-
DcmDataset *
502-
GetDataset()
503-
{
504-
return m_Dataset;
505-
}
502+
void
503+
PopulateMetaDataDictionary(MetaDataDictionary & dict) const;
506504

507505
private:
508506
std::string m_FileName;

Modules/IO/DCMTK/src/itkDCMTKFileReader.cxx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "dcmtk/dcmdata/dcvrus.h" /* for DcmUnsignedShort */
3434
#include "dcmtk/dcmdata/dcvris.h" /* for DcmIntegerString */
3535
#include "dcmtk/dcmdata/dcvrobow.h" /* for DcmOtherByteOtherWord */
36+
#include "dcmtk/dcmdata/dcelem.h" /* for DcmElement */
3637
#include "dcmtk/dcmdata/dcvrui.h" /* for DcmUniqueIdentifier */
3738
#include "dcmtk/dcmdata/dcfilefo.h" /* for DcmFileFormat */
3839
#include "dcmtk/dcmdata/dcmetinf.h" /* for DcmMetaInfo */
@@ -47,7 +48,10 @@
4748

4849
#include "vnl/vnl_cross.h"
4950
#include "itkIntTypes.h"
51+
#include "itkMetaDataObject.h"
52+
#include "itksys/Base64.h"
5053
#include <algorithm>
54+
#include <memory>
5155

5256
namespace itk
5357
{
@@ -1369,6 +1373,72 @@ DCMTKFileReader::GetFileNumber() const
13691373
return m_FileNumber;
13701374
}
13711375

1376+
void
1377+
DCMTKFileReader::PopulateMetaDataDictionary(MetaDataDictionary & dict) const
1378+
{
1379+
dict.Clear();
1380+
if (m_Dataset == nullptr)
1381+
{
1382+
return;
1383+
}
1384+
const unsigned long numElements = m_Dataset->card();
1385+
for (unsigned long i = 0; i < numElements; ++i)
1386+
{
1387+
DcmElement * element = m_Dataset->getElement(i);
1388+
if (element == nullptr)
1389+
{
1390+
continue;
1391+
}
1392+
const DcmTag & tag = element->getTag();
1393+
// Skip pixel data (7FE0,0010)
1394+
if (tag.getGroup() == 0x7fe0 && tag.getElement() == 0x0010)
1395+
{
1396+
continue;
1397+
}
1398+
// Format key as "GGGG|EEEE" (lowercase hex, matching GDCMImageIO)
1399+
char key[10];
1400+
std::snprintf(key, sizeof(key), "%04x|%04x", tag.getGroup(), tag.getElement());
1401+
1402+
const DcmEVR vr = element->getVR();
1403+
if (vr == EVR_SQ)
1404+
{
1405+
// Sequences are nested datasets, not byte arrays; getUint8Array() does
1406+
// not return their content. Skip rather than emit an empty entry.
1407+
continue;
1408+
}
1409+
if (vr == EVR_OB || vr == EVR_OW || vr == EVR_OF || vr == EVR_OD || vr == EVR_OL || vr == EVR_OV || vr == EVR_UN ||
1410+
vr == EVR_ox || vr == EVR_px)
1411+
{
1412+
// Binary VR — base64-encode the raw bytes
1413+
Uint8 * byteValue = nullptr;
1414+
if (element->getUint8Array(byteValue) == EC_Normal && byteValue != nullptr)
1415+
{
1416+
const Uint32 length = element->getLength();
1417+
if (length > 0)
1418+
{
1419+
int encodedLengthEstimate = 2 * static_cast<int>(length);
1420+
encodedLengthEstimate = ((encodedLengthEstimate / 4) + 1) * 4;
1421+
const auto bin = std::make_unique<char[]>(encodedLengthEstimate);
1422+
const auto encodedLengthActual =
1423+
static_cast<unsigned int>(itksysBase64_Encode(reinterpret_cast<const unsigned char *>(byteValue),
1424+
static_cast<SizeValueType>(length),
1425+
reinterpret_cast<unsigned char *>(bin.get()),
1426+
0));
1427+
EncapsulateMetaData<std::string>(dict, key, std::string(bin.get(), encodedLengthActual));
1428+
}
1429+
}
1430+
}
1431+
else
1432+
{
1433+
OFString value;
1434+
if (element->getOFStringArray(value) == EC_Normal)
1435+
{
1436+
EncapsulateMetaData<std::string>(dict, key, std::string(value.c_str()));
1437+
}
1438+
}
1439+
}
1440+
}
1441+
13721442
void
13731443
DCMTKFileReader::AddDictEntry(DcmDictEntry * entry)
13741444
{

Modules/IO/DCMTK/src/itkDCMTKImageIO.cxx

Lines changed: 1 addition & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,15 @@
2121
#include "itkByteSwapper.h"
2222
#include "itksys/SystemTools.hxx"
2323
#include "itkDCMTKFileReader.h"
24-
#include "itkMetaDataObject.h"
2524
#include <iostream>
2625
#include "vnl/vnl_cross.h"
2726
#include "itkMath.h"
28-
#include "itksys/Base64.h"
2927

3028
#include "dcmtk/dcmimgle/dcmimage.h"
3129
#include "dcmtk/dcmjpeg/djdecode.h"
3230
#include "dcmtk/dcmjpls/djdecode.h"
3331
#include "dcmtk/dcmdata/dcmetinf.h"
3432
#include "dcmtk/dcmdata/dcrledrg.h"
35-
#include "dcmtk/dcmdata/dcelem.h"
3633
#include "dcmtk/oflog/oflog.h"
3734

3835
namespace
@@ -481,69 +478,7 @@ DCMTKImageIO::ReadImageInformation()
481478
this->m_Spacing.push_back(1.0);
482479
}
483480

484-
// Populate the metadata dictionary with all DICOM tag values
485-
MetaDataDictionary & dict = this->GetMetaDataDictionary();
486-
dict.Clear();
487-
DcmDataset * dataset = reader.GetDataset();
488-
if (dataset != nullptr)
489-
{
490-
const unsigned long numElements = dataset->card();
491-
for (unsigned long i = 0; i < numElements; ++i)
492-
{
493-
DcmElement * element = dataset->getElement(i);
494-
if (element == nullptr)
495-
{
496-
continue;
497-
}
498-
const DcmTag & tag = element->getTag();
499-
// Skip pixel data (7FE0,0010)
500-
if (tag.getGroup() == 0x7fe0 && tag.getElement() == 0x0010)
501-
{
502-
continue;
503-
}
504-
// Format key as "GGGG|EEEE" (lowercase hex, matching GDCMImageIO)
505-
char key[10];
506-
std::snprintf(key, sizeof(key), "%04x|%04x", tag.getGroup(), tag.getElement());
507-
508-
const DcmEVR vr = element->getVR();
509-
if (vr == EVR_SQ)
510-
{
511-
// Sequences are nested datasets, not byte arrays; getUint8Array() does
512-
// not return their content. Skip rather than emit an empty entry.
513-
continue;
514-
}
515-
if (vr == EVR_OB || vr == EVR_OW || vr == EVR_OF || vr == EVR_OD || vr == EVR_OL || vr == EVR_OV ||
516-
vr == EVR_UN || vr == EVR_ox || vr == EVR_px)
517-
{
518-
// Binary VR — base64-encode the raw bytes
519-
Uint8 * byteValue = nullptr;
520-
if (element->getUint8Array(byteValue) == EC_Normal && byteValue != nullptr)
521-
{
522-
const Uint32 length = element->getLength();
523-
if (length > 0)
524-
{
525-
int encodedLengthEstimate = 2 * static_cast<int>(length);
526-
encodedLengthEstimate = ((encodedLengthEstimate / 4) + 1) * 4;
527-
const auto bin = std::make_unique<char[]>(encodedLengthEstimate);
528-
const auto encodedLengthActual =
529-
static_cast<unsigned int>(itksysBase64_Encode(reinterpret_cast<const unsigned char *>(byteValue),
530-
static_cast<SizeValueType>(length),
531-
reinterpret_cast<unsigned char *>(bin.get()),
532-
0));
533-
EncapsulateMetaData<std::string>(dict, key, std::string(bin.get(), encodedLengthActual));
534-
}
535-
}
536-
}
537-
else
538-
{
539-
OFString value;
540-
if (element->getOFStringArray(value) == EC_Normal)
541-
{
542-
EncapsulateMetaData<std::string>(dict, key, std::string(value.c_str()));
543-
}
544-
}
545-
}
546-
}
481+
reader.PopulateMetaDataDictionary(this->GetMetaDataDictionary());
547482

548483
this->OpenDicomImage();
549484
const DiPixel * interData = this->m_DImage->getInterData();

0 commit comments

Comments
 (0)