[Gdcm-hackers] gdcm-git:Grassroots DICOM branch release updated. 10ca9d3468a95d1b6d9786c5693d59c215
Cross-platform DICOM implementation
Brought to you by:
malat
|
From: Mathieu M. <ma...@us...> - 2022-11-15 07:53:26
|
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Grassroots DICOM".
The branch, release has been updated
via 10ca9d3468a95d1b6d9786c5693d59c21510a4e3 (commit)
via 13780759a34cf25afc3b5e0917397941f5112fd3 (commit)
via 8fe824c39bc18ffe57c5b6c1305b87463b4e2d99 (commit)
via c897cb249306c6530a1e0ad16e798b945bbaeca9 (commit)
via 84c8c3c058cce1a65f3b263fa44a8ab2e5ae179f (commit)
from e5a5d1ba4b9c7024c803020a636c7682e91a9e48 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
https://sourceforge.net/p/gdcm/gdcm/ci/10ca9d3468a95d1b6d9786c5693d59c21510a4e3/
commit 10ca9d3468a95d1b6d9786c5693d59c21510a4e3
Author: Mathieu Malaterre <mat...@gm...>
Date: Mon Nov 14 23:52:48 2022 -0800
Make code future proof with possibly new extension
diff --git a/Source/MediaStorageAndFileFormat/gdcmCleaner.cxx b/Source/MediaStorageAndFileFormat/gdcmCleaner.cxx
index f7131b57f..5f185dd90 100644
--- a/Source/MediaStorageAndFileFormat/gdcmCleaner.cxx
+++ b/Source/MediaStorageAndFileFormat/gdcmCleaner.cxx
@@ -754,8 +754,8 @@ enum CSAImageHeaderType {
static const char *CSAImageHeaderTypeStrings[]{ //
"HG IRECORD", //
"IMAGE NUM 4", //
- "IMAGE_MR", //
- "NUC_FLOOD", //
+ "MR", //
+ "NUC FLOOD", //
"PET NUM 4", //
"SOM 5"};
@@ -856,7 +856,7 @@ static inline bool bs_is_signature_end(const ByteValue *bv, const char *str) {
return false;
}
-bool isSV10Legacy(const ByteValue *bv) {
+static inline bool isSV10Legacy(const ByteValue *bv) {
// 4 bytes aligned:
if (bv->GetLength() % 4 == 0) {
uint32_t n;
@@ -877,60 +877,19 @@ static bool CleanCSAImage(DataSet &ds, const DataElement &de) {
if (!bv) return true;
CSAImageHeaderType image_type = IMAGE_UNK;
+ std::string ref;
{
const PrivateTag ihtTag(0x0029, 0x08, "SIEMENS CSA HEADER");
- std::string ref;
image_type = GetCSAType<CSAImageHeaderType>(ref, ds, ihtTag,
CSAImageHeaderTypeStrings);
- // handle unknown cases first:
- if (image_type == IMAGE_UNK) {
- // not implemented, assume no PHI for now:
- gdcmDebugMacro("NotImplemented CSA: " << ref);
- return true;
- }
- }
-
- bool isSV104321 = false;
- CSAImageHeaderType image_guesstype = IMAGE_UNK;
- static const char sv10[] = "SV10\4\3\2\1"; // 8
- static const char ini2[] = "label:=Standar"; // 14
- static const char end[] = "END! "; // 10
- if (is_signature(bv, sv10) || isSV10Legacy(bv)) {
- assert(image_type == IMAGE_NUM_4 // MR Image Storage / NUMARIS/4
- || image_type ==
- PET_NUM_4 // Positron Emission Tomography Image Storage
- || image_type == IMAGE_MR); // Enhanced SR Storage
- image_guesstype = image_type;
- isSV104321 = true;
- } else if (bs_is_signature(bv, sv10)) {
- assert(image_type == IMAGE_NUM_4 // MR Image Storage / NUMARIS/4
- || image_type ==
- PET_NUM_4 // Positron Emission Tomography Image Storage
- || image_type == IMAGE_MR); // Enhanced SR Storage
- image_guesstype = image_type;
- gdcmWarningMacro("Found byte-swapped SV10");
- } else if (is_signature(bv, ini2) || bs_is_signature(bv, ini2)) {
- // some kind of INI style structured with base64 thumbnail (zlib)
- image_guesstype = HG_IRECORD;
- } else if (is_signature_end(bv, end) || bs_is_signature_end(bv, end)) {
- image_guesstype = SOM_5;
- } else {
- const bool zero = isAllZero(bv->GetPointer(), bv->GetLength());
- if (zero) {
- gdcmWarningMacro("Zero out SV10");
- image_guesstype = image_type;
- }
}
+ static const char sv10[] = "SV10\4\3\2\1"; // 8
- // this is an implementation error must return error
- if (image_guesstype == IMAGE_UNK || image_guesstype != image_type) {
- gdcmErrorMacro("Implementation error CSA: " << image_type);
- return false;
- }
-
- // else
- assert(image_guesstype == image_type);
- if (isSV104321) {
+ // easy case: recognized keywords:
+ if (image_type == IMAGE_NUM_4 // MR Image Storage / NUMARIS/4
+ || image_type == PET_NUM_4 // Positron Emission Tomography Image Storage
+ || image_type == IMAGE_MR) // Enhanced SR Storage
+ {
DataElement clean(de.GetTag());
clean.SetVR(de.GetVR());
std::vector<char> v;
@@ -940,13 +899,23 @@ static bool CleanCSAImage(DataSet &ds, const DataElement &de) {
ds.Replace(clean);
return true;
}
- gdcmErrorMacro("Failure to call CleanCSA");
+ // we failed to clean CSA, let's check possible well known errors
+ if (bs_is_signature(bv, sv10)) {
+ gdcmWarningMacro("Found byte-swapped SV10. Skipping.");
+ return true;
+ } else if (isAllZero(bv->GetPointer(), bv->GetLength())) {
+ gdcmDebugMacro("Zero-out CSA header");
+ return true;
+ }
+ gdcmErrorMacro("Failure to call CleanCSAImage");
return false;
- } else {
- gdcmDebugMacro("Scrubbing is no-op for "
- << CSAImageHeaderTypeStrings[image_type]);
- return true;
}
+ // else
+ // add a dummy check for SV10 signature
+ if (is_signature(bv, sv10)) {
+ gdcmWarningMacro("Please report. SV10 Header found for new type: " << ref);
+ }
+ return true;
}
static bool CleanCSASeries(DataSet &ds, const DataElement &de) {
@@ -955,61 +924,16 @@ static bool CleanCSASeries(DataSet &ds, const DataElement &de) {
if (!bv) return true;
CSASeriesHeaderType series_type = SERIES_UNK;
+ std::string ref;
{
const PrivateTag shtTag(0x0029, 0x18, "SIEMENS CSA HEADER");
- std::string ref;
series_type = GetCSAType<CSASeriesHeaderType>(ref, ds, shtTag,
CSASeriesHeaderTypeStrings);
- // handle unknown cases first:
- if (series_type == SERIES_UNK) {
- // not implemented, assume no PHI for now:
- gdcmWarningMacro("NotImplemented CSA: " << ref);
- return true;
- }
}
+ static const char sv10[] = "SV10\4\3\2\1"; // 8
- bool isSV104321 = false;
- CSASeriesHeaderType series_guesstype = SERIES_UNK;
- static const char sv10[] = "SV10\4\3\2\1"; // 8
- static const char xml_root[] = "<?xml version=\"1.0\" ?>"; // 22
- static const char pds_com[] = "<pds><com>"; // 10
- static const char som7dev[] = "ORIGINALSERIES"; // 14
- if (is_signature(bv, sv10) || isSV10Legacy(bv)) {
- assert(series_type == PT ||
- series_type == SERIES_MR); // Enhanced SR Storage
- series_guesstype = series_type;
- isSV104321 = true;
- } else if (bs_is_signature(bv, sv10)) {
- assert(series_type == PT ||
- series_type == SERIES_MR); // Enhanced SR Storage
- series_guesstype = series_type;
- gdcmWarningMacro("Found byte-swapped SV10");
- } else if (is_signature(bv, xml_root) || bs_is_signature(bv, xml_root)) {
- // ParameterBlock case:
- series_guesstype = ParameterBlock;
- } else if (is_signature(bv, pds_com) || bs_is_signature(bv, pds_com)) {
- // PET_REPLAY_PARAM case:
- series_guesstype = PET_REPLAY_PARAM;
- } else if (is_signature(bv, som7dev) || bs_is_signature(bv, som7dev)) {
- // SOM 7 DEV
- series_guesstype = SOM_7_DEV;
- } else {
- const bool zero = isAllZero(bv->GetPointer(), bv->GetLength());
- if (zero) {
- gdcmWarningMacro("Zero out SV10");
- series_guesstype = series_type;
- }
- }
-
- // this is an implementation error must return error
- if (series_guesstype == SERIES_UNK || series_guesstype != series_type) {
- gdcmErrorMacro("Implementation error CSA: " << series_type);
- return false;
- }
-
- // else
- assert(series_guesstype == series_type);
- if (isSV104321) {
+ // easy case: recognized keywords:
+ if (series_type == PT || series_type == SERIES_MR) {
DataElement clean(de.GetTag());
clean.SetVR(de.GetVR());
std::vector<char> v;
@@ -1019,66 +943,24 @@ static bool CleanCSASeries(DataSet &ds, const DataElement &de) {
ds.Replace(clean);
return true;
}
- gdcmErrorMacro("Failure to call CleanCSA");
+ // we failed to clean CSA, let's check possible well known errors
+ if (bs_is_signature(bv, sv10)) {
+ gdcmWarningMacro("Found byte-swapped SV10. Skipping.");
+ return true;
+ } else if (isAllZero(bv->GetPointer(), bv->GetLength())) {
+ gdcmDebugMacro("Zero-out CSA header");
+ return true;
+ }
+ gdcmErrorMacro("Failure to call CleanCSASeries");
return false;
- } else {
- gdcmDebugMacro("Scrubbing is no-op for "
- << CSASeriesHeaderTypeStrings[series_type]);
- return true;
- }
-}
-
-static bool CleanCSA(DataSet &ds, const DataElement &de) {
- const ByteValue *bv = de.GetByteValue();
- // fast path:
- if (!bv) return true;
- static const char vs01[] = "VS01";
- // bogus big-endian conversion
- if (bv->GetLength() >= 4 && memcmp(bv->GetPointer(), vs01, 4) == 0) {
- // technically there is digital trash, but since it is written in
- // byte-swap mode, it cannot be detected easily.
- return true;
- }
- static const char pds_com[] = "<pds><com>";
- // PET_REPLAY_PARAM case:
- if (bv->GetLength() >= 10 && memcmp(bv->GetPointer(), pds_com, 10) == 0) {
- return true;
- }
- static const char psd_ocm[] = "p<sd<>oc>m";
- // byte-swap PET_REPLAY_PARAM case:
- if (bv->GetLength() >= 10 && memcmp(bv->GetPointer(), psd_ocm, 10) == 0) {
- return true;
}
- // ANGIOHEAD case. This is a DICOM Explicit with a odd ending:
- static const char end[] = "END! ";
- // const char *debug = bv->GetPointer() + bv->GetLength() - 10;
- // size_t d = strlen(end);
- // int i1 = memcmp(bv->GetPointer() + bv->GetLength() - 10, end, 10) ;
- // int i2 = memcmp(debug, end, 10) ;
- if (bv->GetLength() >= 10 &&
- memcmp(bv->GetPointer() + bv->GetLength() - 10, end, 10) == 0) {
- return true;
- }
- // byte-swapped ANGIOHEAD
- static const char ned[] = "NE!D ";
- if (bv->GetLength() >= 10 &&
- memcmp(bv->GetPointer() + bv->GetLength() - 10, ned, 10) == 0) {
- return true;
- }
- const bool zero = isAllZero(bv->GetPointer(), bv->GetLength());
- if (zero) return true;
-
- DataElement clean(de.GetTag());
- clean.SetVR(de.GetVR());
- std::vector<char> v;
- v.resize(bv->GetLength());
- if (csa_memcpy(&v[0], bv->GetPointer(), bv->GetLength())) {
- clean.SetByteValue(&v[0], v.size());
- ds.Replace(clean);
- return true;
+ // else
+ // add a dummy check for SV10 signature
+ if (is_signature(bv, sv10)) {
+ gdcmWarningMacro("SV10 Header found for new type: " << ref);
+ assert(0);
}
- gdcmErrorMacro("Failure to call CleanCSA");
- return false;
+ return true;
}
static bool CleanMEC_MR3(DataSet &ds, const DataElement &de) {
https://sourceforge.net/p/gdcm/gdcm/ci/13780759a34cf25afc3b5e0917397941f5112fd3/
commit 13780759a34cf25afc3b5e0917397941f5112fd3
Author: Mathieu Malaterre <mat...@gm...>
Date: Mon Nov 14 23:52:33 2022 -0800
Simlify code logic
diff --git a/Utilities/gdcmext/csa.c b/Utilities/gdcmext/csa.c
index e0a0eb40c..ad95a6eca 100644
--- a/Utilities/gdcmext/csa.c
+++ b/Utilities/gdcmext/csa.c
@@ -148,19 +148,9 @@ static bool read_magic(struct app *self) {
} else if (n < 0x100 && unused == 0x4d) { // 'M'
// SIEMENS_Sonata-16-MONO2-Value_Multiplicity.dcm
magic = NOMAGIC;
- } else if (n == 0x7364703c && unused == 0x6f633c3e) { // aka '<pds><co'
- // 'PET_REPLAY_PARAM'
- assert(0);
- return false;
- } else if (n == 0x31305356 && unused == 0x2010403) { // aka 'VS01' ...
- // technically could be reserved; should not happen in the wild
- assert(0);
- return false;
} else {
- assert(0);
return false;
}
- assert(n > 0 && n < 128);
self->nelements = n;
self->csa_type = magic;
return true;
https://sourceforge.net/p/gdcm/gdcm/ci/8fe824c39bc18ffe57c5b6c1305b87463b4e2d99/
commit 8fe824c39bc18ffe57c5b6c1305b87463b4e2d99
Author: Mathieu Malaterre <mat...@gm...>
Date: Mon Nov 14 07:49:40 2022 -0800
Reduce verbosity for now
diff --git a/Source/DataStructureAndEncodingDefinition/gdcmPrivateTag.cxx b/Source/DataStructureAndEncodingDefinition/gdcmPrivateTag.cxx
index 809ecab2a..00d457a29 100644
--- a/Source/DataStructureAndEncodingDefinition/gdcmPrivateTag.cxx
+++ b/Source/DataStructureAndEncodingDefinition/gdcmPrivateTag.cxx
@@ -32,7 +32,8 @@ namespace gdcm_ns
|| group % 2 == 0
/*|| strlen(owner.c_str()) == 0*/ ) // can't use owner.empty()
{
- gdcmDebugMacro( "Problem reading Private Tag: " << str );
+ // comment out the following since too verbose
+ //gdcmDebugMacro( "Problem reading Private Tag: " << str );
return false;
}
SetGroup( (uint16_t)group );
diff --git a/Source/DataStructureAndEncodingDefinition/gdcmTag.cxx b/Source/DataStructureAndEncodingDefinition/gdcmTag.cxx
index b55879fd9..a4f5d40f2 100644
--- a/Source/DataStructureAndEncodingDefinition/gdcmTag.cxx
+++ b/Source/DataStructureAndEncodingDefinition/gdcmTag.cxx
@@ -23,7 +23,7 @@ namespace gdcm
unsigned int group = 0, element = 0;
if( !str || sscanf(str, "%04x,%04x", &group , &element) != 2 )
{
- gdcmDebugMacro( "Problem reading Tag: " << str );
+ //gdcmDebugMacro( "Problem reading Tag: " << str );
return false;
}
SetGroup( (uint16_t)group );
https://sourceforge.net/p/gdcm/gdcm/ci/c897cb249306c6530a1e0ad16e798b945bbaeca9/
commit c897cb249306c6530a1e0ad16e798b945bbaeca9
Author: Mathieu Malaterre <mat...@gm...>
Date: Mon Nov 14 07:48:49 2022 -0800
Another round of fix for siemens/csa
diff --git a/Source/MediaStorageAndFileFormat/gdcmCleaner.cxx b/Source/MediaStorageAndFileFormat/gdcmCleaner.cxx
index e2e658e86..f7131b57f 100644
--- a/Source/MediaStorageAndFileFormat/gdcmCleaner.cxx
+++ b/Source/MediaStorageAndFileFormat/gdcmCleaner.cxx
@@ -751,12 +751,13 @@ enum CSAImageHeaderType {
SOM_5, // SOM 5
};
-static const char *CSAImageHeaderTypeStrings[]{
- "HG IRECORD", //
- "IMAGE NUM 4", //
- "IMAGE_MR", "NUC_FLOOD", "PET_NUM_4", "SOM_5",
- nullptr // sentinel
-};
+static const char *CSAImageHeaderTypeStrings[]{ //
+ "HG IRECORD", //
+ "IMAGE NUM 4", //
+ "IMAGE_MR", //
+ "NUC_FLOOD", //
+ "PET NUM 4", //
+ "SOM 5"};
enum CSASeriesHeaderType {
SERIES_UNK = -1,
@@ -778,7 +779,7 @@ static const char *CSASeriesHeaderTypeStrings[]{
};
template <typename T, int N>
-static T GetCSAType(CSComp &ref, const DataSet &ds, const PrivateTag &pt,
+static T GetCSAType(std::string &ref, const DataSet &ds, const PrivateTag &pt,
const char *(&array)[N]) {
T series_type = (T)-1; // UNK
ref = "";
@@ -786,7 +787,7 @@ static T GetCSAType(CSComp &ref, const DataSet &ds, const PrivateTag &pt,
const gdcm::DataElement &de1 = ds.GetDataElement(pt);
Element<VR::CS, VM::VM1> el = {};
el.SetFromDataElement(de1);
- ref = el.GetValue();
+ ref = el.GetValue().Trim();
for (int i = 0; i < N; i++) {
if (strcmp(array[i], ref.c_str()) == 0) {
series_type = (T)(i);
@@ -796,6 +797,80 @@ static T GetCSAType(CSComp &ref, const DataSet &ds, const PrivateTag &pt,
return series_type;
}
+// byte-swapped memcmp implementation:
+static inline int bs_memcmp(const void *s1, const void *s2, size_t n) {
+ size_t i;
+ const unsigned char *us1 = (const unsigned char *)s1;
+ const unsigned char *us2 = (const unsigned char *)s2;
+ assert(n % 2 == 0);
+
+ for (i = 0; i < n; i += 2, us1 += 2, us2 += 2) {
+ if (*us1 < *(us2 + 1)) {
+ return -1;
+ } else if (*us1 > *(us2 + 1)) {
+ return 1;
+ }
+
+ if (*(us1 + 1) < *us2) {
+ return -1;
+ } else if (*(us1 + 1) > *us2) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static inline bool is_signature(const ByteValue *bv, const char *str) {
+ const size_t len = strlen(str);
+ if (bv->GetLength() >= len && memcmp(bv->GetPointer(), str, len) == 0) {
+ return true;
+ }
+ return false;
+}
+
+static inline bool bs_is_signature(const ByteValue *bv, const char *str) {
+ const size_t len = strlen(str);
+ if (bv->GetLength() >= len && bs_memcmp(bv->GetPointer(), str, len) == 0) {
+ return true;
+ }
+ return false;
+}
+
+static inline bool is_signature_end(const ByteValue *bv, const char *str) {
+ const size_t len = strlen(str);
+ if (bv->GetLength() >= len &&
+ memcmp(bv->GetPointer() + bv->GetLength() - len, str, len) == 0) {
+ return true;
+ }
+
+ return false;
+}
+
+static inline bool bs_is_signature_end(const ByteValue *bv, const char *str) {
+ const size_t len = strlen(str);
+ if (bv->GetLength() >= len &&
+ bs_memcmp(bv->GetPointer() + bv->GetLength() - len, str, len) == 0) {
+ return true;
+ }
+
+ return false;
+}
+
+bool isSV10Legacy(const ByteValue *bv) {
+ // 4 bytes aligned:
+ if (bv->GetLength() % 4 == 0) {
+ uint32_t n;
+ uint32_t unused;
+ if (bv->GetLength() >= 8) {
+ const char *buffer = bv->GetPointer();
+ memcpy(&n, buffer, 4);
+ memcpy(&unused, buffer + 4, 4);
+ if (n < 0x100 && unused == 0x4d) return true;
+ }
+ }
+ return false;
+}
+
static bool CleanCSAImage(DataSet &ds, const DataElement &de) {
const ByteValue *bv = de.GetByteValue();
// fast path:
@@ -804,7 +879,7 @@ static bool CleanCSAImage(DataSet &ds, const DataElement &de) {
CSAImageHeaderType image_type = IMAGE_UNK;
{
const PrivateTag ihtTag(0x0029, 0x08, "SIEMENS CSA HEADER");
- CSComp ref;
+ std::string ref;
image_type = GetCSAType<CSAImageHeaderType>(ref, ds, ihtTag,
CSAImageHeaderTypeStrings);
// handle unknown cases first:
@@ -815,11 +890,38 @@ static bool CleanCSAImage(DataSet &ds, const DataElement &de) {
}
}
+ bool isSV104321 = false;
CSAImageHeaderType image_guesstype = IMAGE_UNK;
- static const char xml_root[] = "<?xml version=\"1.0\" ?>";
- // ParameterBlock case:
- if (bv->GetLength() >= 22 && memcmp(bv->GetPointer(), xml_root, 22) == 0) {
+ static const char sv10[] = "SV10\4\3\2\1"; // 8
+ static const char ini2[] = "label:=Standar"; // 14
+ static const char end[] = "END! "; // 10
+ if (is_signature(bv, sv10) || isSV10Legacy(bv)) {
+ assert(image_type == IMAGE_NUM_4 // MR Image Storage / NUMARIS/4
+ || image_type ==
+ PET_NUM_4 // Positron Emission Tomography Image Storage
+ || image_type == IMAGE_MR); // Enhanced SR Storage
+ image_guesstype = image_type;
+ isSV104321 = true;
+ } else if (bs_is_signature(bv, sv10)) {
+ assert(image_type == IMAGE_NUM_4 // MR Image Storage / NUMARIS/4
+ || image_type ==
+ PET_NUM_4 // Positron Emission Tomography Image Storage
+ || image_type == IMAGE_MR); // Enhanced SR Storage
+ image_guesstype = image_type;
+ gdcmWarningMacro("Found byte-swapped SV10");
+ } else if (is_signature(bv, ini2) || bs_is_signature(bv, ini2)) {
+ // some kind of INI style structured with base64 thumbnail (zlib)
+ image_guesstype = HG_IRECORD;
+ } else if (is_signature_end(bv, end) || bs_is_signature_end(bv, end)) {
+ image_guesstype = SOM_5;
+ } else {
+ const bool zero = isAllZero(bv->GetPointer(), bv->GetLength());
+ if (zero) {
+ gdcmWarningMacro("Zero out SV10");
+ image_guesstype = image_type;
+ }
}
+
// this is an implementation error must return error
if (image_guesstype == IMAGE_UNK || image_guesstype != image_type) {
gdcmErrorMacro("Implementation error CSA: " << image_type);
@@ -828,7 +930,7 @@ static bool CleanCSAImage(DataSet &ds, const DataElement &de) {
// else
assert(image_guesstype == image_type);
- if (image_type == IMAGE_MR) {
+ if (isSV104321) {
DataElement clean(de.GetTag());
clean.SetVR(de.GetVR());
std::vector<char> v;
@@ -841,7 +943,8 @@ static bool CleanCSAImage(DataSet &ds, const DataElement &de) {
gdcmErrorMacro("Failure to call CleanCSA");
return false;
} else {
- gdcmDebugMacro("Scrubbing is no-op for " << image_type);
+ gdcmDebugMacro("Scrubbing is no-op for "
+ << CSAImageHeaderTypeStrings[image_type]);
return true;
}
}
@@ -854,23 +957,50 @@ static bool CleanCSASeries(DataSet &ds, const DataElement &de) {
CSASeriesHeaderType series_type = SERIES_UNK;
{
const PrivateTag shtTag(0x0029, 0x18, "SIEMENS CSA HEADER");
- CSComp ref;
+ std::string ref;
series_type = GetCSAType<CSASeriesHeaderType>(ref, ds, shtTag,
CSASeriesHeaderTypeStrings);
// handle unknown cases first:
if (series_type == SERIES_UNK) {
// not implemented, assume no PHI for now:
- gdcmDebugMacro("NotImplemented CSA: " << ref);
+ gdcmWarningMacro("NotImplemented CSA: " << ref);
return true;
}
}
+ bool isSV104321 = false;
CSASeriesHeaderType series_guesstype = SERIES_UNK;
- static const char xml_root[] = "<?xml version=\"1.0\" ?>";
- // ParameterBlock case:
- if (bv->GetLength() >= 22 && memcmp(bv->GetPointer(), xml_root, 22) == 0) {
+ static const char sv10[] = "SV10\4\3\2\1"; // 8
+ static const char xml_root[] = "<?xml version=\"1.0\" ?>"; // 22
+ static const char pds_com[] = "<pds><com>"; // 10
+ static const char som7dev[] = "ORIGINALSERIES"; // 14
+ if (is_signature(bv, sv10) || isSV10Legacy(bv)) {
+ assert(series_type == PT ||
+ series_type == SERIES_MR); // Enhanced SR Storage
+ series_guesstype = series_type;
+ isSV104321 = true;
+ } else if (bs_is_signature(bv, sv10)) {
+ assert(series_type == PT ||
+ series_type == SERIES_MR); // Enhanced SR Storage
+ series_guesstype = series_type;
+ gdcmWarningMacro("Found byte-swapped SV10");
+ } else if (is_signature(bv, xml_root) || bs_is_signature(bv, xml_root)) {
+ // ParameterBlock case:
series_guesstype = ParameterBlock;
+ } else if (is_signature(bv, pds_com) || bs_is_signature(bv, pds_com)) {
+ // PET_REPLAY_PARAM case:
+ series_guesstype = PET_REPLAY_PARAM;
+ } else if (is_signature(bv, som7dev) || bs_is_signature(bv, som7dev)) {
+ // SOM 7 DEV
+ series_guesstype = SOM_7_DEV;
+ } else {
+ const bool zero = isAllZero(bv->GetPointer(), bv->GetLength());
+ if (zero) {
+ gdcmWarningMacro("Zero out SV10");
+ series_guesstype = series_type;
+ }
}
+
// this is an implementation error must return error
if (series_guesstype == SERIES_UNK || series_guesstype != series_type) {
gdcmErrorMacro("Implementation error CSA: " << series_type);
@@ -879,7 +1009,7 @@ static bool CleanCSASeries(DataSet &ds, const DataElement &de) {
// else
assert(series_guesstype == series_type);
- if (series_type == SERIES_MR) {
+ if (isSV104321) {
DataElement clean(de.GetTag());
clean.SetVR(de.GetVR());
std::vector<char> v;
@@ -892,7 +1022,8 @@ static bool CleanCSASeries(DataSet &ds, const DataElement &de) {
gdcmErrorMacro("Failure to call CleanCSA");
return false;
} else {
- gdcmDebugMacro("Scrubbing is no-op for " << series_type);
+ gdcmDebugMacro("Scrubbing is no-op for "
+ << CSASeriesHeaderTypeStrings[series_type]);
return true;
}
}
https://sourceforge.net/p/gdcm/gdcm/ci/84c8c3c058cce1a65f3b263fa44a8ab2e5ae179f/
commit 84c8c3c058cce1a65f3b263fa44a8ab2e5ae179f
Author: Mathieu Malaterre <mat...@gm...>
Date: Mon Nov 14 05:20:28 2022 -0800
Rework the SIEMENS/CSA handling
diff --git a/Source/MediaStorageAndFileFormat/gdcmCleaner.cxx b/Source/MediaStorageAndFileFormat/gdcmCleaner.cxx
index 437154750..e2e658e86 100644
--- a/Source/MediaStorageAndFileFormat/gdcmCleaner.cxx
+++ b/Source/MediaStorageAndFileFormat/gdcmCleaner.cxx
@@ -741,6 +741,162 @@ static bool isAllZero(const char *buffer, size_t len) {
return true;
}
+enum CSAImageHeaderType {
+ IMAGE_UNK = -1,
+ HG_IRECORD = 0, // HG IRECORD,
+ IMAGE_NUM_4, // IMAGE NUM 4 ,
+ IMAGE_MR,
+ NUC_FLOOD,
+ PET_NUM_4,
+ SOM_5, // SOM 5
+};
+
+static const char *CSAImageHeaderTypeStrings[]{
+ "HG IRECORD", //
+ "IMAGE NUM 4", //
+ "IMAGE_MR", "NUC_FLOOD", "PET_NUM_4", "SOM_5",
+ nullptr // sentinel
+};
+
+enum CSASeriesHeaderType {
+ SERIES_UNK = -1,
+ HG_RECORD_SERIES = 0, // HG RECORD SERIES,
+ SERIES_MR,
+ ParameterBlock,
+ PET_REPLAY_PARAM,
+ PT,
+ SOM_7_DEV, // SOM 7 DEV
+};
+
+static const char *CSASeriesHeaderTypeStrings[]{
+ "HG RECORD SERIES", // HG_RECORD_SERIES
+ "MR", // SERIES_MR
+ "ParameterBlock", // ParameterBlock
+ "PET_REPLAY_PARAM", //
+ "PT", //
+ "SOM 7 DEV", // SOM_7_DEV
+};
+
+template <typename T, int N>
+static T GetCSAType(CSComp &ref, const DataSet &ds, const PrivateTag &pt,
+ const char *(&array)[N]) {
+ T series_type = (T)-1; // UNK
+ ref = "";
+ if (ds.FindDataElement(pt)) {
+ const gdcm::DataElement &de1 = ds.GetDataElement(pt);
+ Element<VR::CS, VM::VM1> el = {};
+ el.SetFromDataElement(de1);
+ ref = el.GetValue();
+ for (int i = 0; i < N; i++) {
+ if (strcmp(array[i], ref.c_str()) == 0) {
+ series_type = (T)(i);
+ }
+ }
+ }
+ return series_type;
+}
+
+static bool CleanCSAImage(DataSet &ds, const DataElement &de) {
+ const ByteValue *bv = de.GetByteValue();
+ // fast path:
+ if (!bv) return true;
+
+ CSAImageHeaderType image_type = IMAGE_UNK;
+ {
+ const PrivateTag ihtTag(0x0029, 0x08, "SIEMENS CSA HEADER");
+ CSComp ref;
+ image_type = GetCSAType<CSAImageHeaderType>(ref, ds, ihtTag,
+ CSAImageHeaderTypeStrings);
+ // handle unknown cases first:
+ if (image_type == IMAGE_UNK) {
+ // not implemented, assume no PHI for now:
+ gdcmDebugMacro("NotImplemented CSA: " << ref);
+ return true;
+ }
+ }
+
+ CSAImageHeaderType image_guesstype = IMAGE_UNK;
+ static const char xml_root[] = "<?xml version=\"1.0\" ?>";
+ // ParameterBlock case:
+ if (bv->GetLength() >= 22 && memcmp(bv->GetPointer(), xml_root, 22) == 0) {
+ }
+ // this is an implementation error must return error
+ if (image_guesstype == IMAGE_UNK || image_guesstype != image_type) {
+ gdcmErrorMacro("Implementation error CSA: " << image_type);
+ return false;
+ }
+
+ // else
+ assert(image_guesstype == image_type);
+ if (image_type == IMAGE_MR) {
+ DataElement clean(de.GetTag());
+ clean.SetVR(de.GetVR());
+ std::vector<char> v;
+ v.resize(bv->GetLength());
+ if (csa_memcpy(&v[0], bv->GetPointer(), bv->GetLength())) {
+ clean.SetByteValue(&v[0], v.size());
+ ds.Replace(clean);
+ return true;
+ }
+ gdcmErrorMacro("Failure to call CleanCSA");
+ return false;
+ } else {
+ gdcmDebugMacro("Scrubbing is no-op for " << image_type);
+ return true;
+ }
+}
+
+static bool CleanCSASeries(DataSet &ds, const DataElement &de) {
+ const ByteValue *bv = de.GetByteValue();
+ // fast path:
+ if (!bv) return true;
+
+ CSASeriesHeaderType series_type = SERIES_UNK;
+ {
+ const PrivateTag shtTag(0x0029, 0x18, "SIEMENS CSA HEADER");
+ CSComp ref;
+ series_type = GetCSAType<CSASeriesHeaderType>(ref, ds, shtTag,
+ CSASeriesHeaderTypeStrings);
+ // handle unknown cases first:
+ if (series_type == SERIES_UNK) {
+ // not implemented, assume no PHI for now:
+ gdcmDebugMacro("NotImplemented CSA: " << ref);
+ return true;
+ }
+ }
+
+ CSASeriesHeaderType series_guesstype = SERIES_UNK;
+ static const char xml_root[] = "<?xml version=\"1.0\" ?>";
+ // ParameterBlock case:
+ if (bv->GetLength() >= 22 && memcmp(bv->GetPointer(), xml_root, 22) == 0) {
+ series_guesstype = ParameterBlock;
+ }
+ // this is an implementation error must return error
+ if (series_guesstype == SERIES_UNK || series_guesstype != series_type) {
+ gdcmErrorMacro("Implementation error CSA: " << series_type);
+ return false;
+ }
+
+ // else
+ assert(series_guesstype == series_type);
+ if (series_type == SERIES_MR) {
+ DataElement clean(de.GetTag());
+ clean.SetVR(de.GetVR());
+ std::vector<char> v;
+ v.resize(bv->GetLength());
+ if (csa_memcpy(&v[0], bv->GetPointer(), bv->GetLength())) {
+ clean.SetByteValue(&v[0], v.size());
+ ds.Replace(clean);
+ return true;
+ }
+ gdcmErrorMacro("Failure to call CleanCSA");
+ return false;
+ } else {
+ gdcmDebugMacro("Scrubbing is no-op for " << series_type);
+ return true;
+ }
+}
+
static bool CleanCSA(DataSet &ds, const DataElement &de) {
const ByteValue *bv = de.GetByteValue();
// fast path:
@@ -748,8 +904,8 @@ static bool CleanCSA(DataSet &ds, const DataElement &de) {
static const char vs01[] = "VS01";
// bogus big-endian conversion
if (bv->GetLength() >= 4 && memcmp(bv->GetPointer(), vs01, 4) == 0) {
- // technically there is digital trash, but since it is written in byte-swap
- // mode, it cannot be detected easily.
+ // technically there is digital trash, but since it is written in
+ // byte-swap mode, it cannot be detected easily.
return true;
}
static const char pds_com[] = "<pds><com>";
@@ -764,6 +920,10 @@ static bool CleanCSA(DataSet &ds, const DataElement &de) {
}
// ANGIOHEAD case. This is a DICOM Explicit with a odd ending:
static const char end[] = "END! ";
+ // const char *debug = bv->GetPointer() + bv->GetLength() - 10;
+ // size_t d = strlen(end);
+ // int i1 = memcmp(bv->GetPointer() + bv->GetLength() - 10, end, 10) ;
+ // int i2 = memcmp(debug, end, 10) ;
if (bv->GetLength() >= 10 &&
memcmp(bv->GetPointer() + bv->GetLength() - 10, end, 10) == 0) {
return true;
@@ -965,8 +1125,8 @@ Cleaner::impl::ACTION Cleaner::impl::ComputeAction(
if (!empty_vrs.empty() || !remove_vrs.empty()) {
VR vr = de.GetVR();
assert(ref_dict_vr != VR::INVALID);
- // be careful with vr handling since we must always prefer the one from the
- // dict in case of attribute written as 'OB' but dict states 'PN':
+ // be careful with vr handling since we must always prefer the one from
+ // the dict in case of attribute written as 'OB' but dict states 'PN':
if (ref_dict_vr != VR::UN /*&& ref_dict_vr != VR::INVALID*/) {
// we want to clean VR==PN; but this is a problem for implicit transfer
// syntax, so let's be nice to the user and prefer dict_vr. however for
@@ -1048,7 +1208,8 @@ bool Cleaner::impl::ProcessDataSet(Subject &subject, File &file, DataSet &ds,
// SmartPointer<SequenceOfItems> sqi = de.GetValueAsSQ();
if (!de.IsEmpty()) {
gdcmWarningMacro(
- "Please report. Dictionary states this should be a SQ. But we "
+ "Please report. Dictionary states this should be a SQ. But "
+ "we "
"failed to load it as such. Passing-through as-is"
<< de);
}
@@ -1074,10 +1235,10 @@ bool Cleaner::impl::ProcessDataSet(Subject &subject, File &file, DataSet &ds,
static const PrivateTag &pmtf3 = gdcm::MEC_MR3::GetCanonMECMR3Tag();
if (pt == csa1) {
- const bool ret = CleanCSA(ds, de);
+ const bool ret = CleanCSAImage(ds, de);
if (!ret) return false;
} else if (pt == csa2) {
- const bool ret = CleanCSA(ds, de);
+ const bool ret = CleanCSASeries(ds, de);
if (!ret) return false;
} else if (pt == mec_mr3) {
const bool ret = CleanMEC_MR3(ds, de);
-----------------------------------------------------------------------
Summary of changes:
.../gdcmPrivateTag.cxx | 3 +-
.../DataStructureAndEncodingDefinition/gdcmTag.cxx | 2 +-
Source/MediaStorageAndFileFormat/gdcmCleaner.cxx | 254 +++++++++++++++++----
Utilities/gdcmext/csa.c | 10 -
4 files changed, 217 insertions(+), 52 deletions(-)
hooks/post-receive
--
Grassroots DICOM
|