Skip to content

Commit

Permalink
Merge branch 'release'
Browse files Browse the repository at this point in the history
  • Loading branch information
malaterre committed Oct 19, 2022
2 parents 8359f92 + 99cee05 commit 169a467
Show file tree
Hide file tree
Showing 3 changed files with 346 additions and 182 deletions.
276 changes: 200 additions & 76 deletions Source/MediaStorageAndFileFormat/gdcmEquipmentManufacturer.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -16,112 +16,236 @@
#include "gdcmAttribute.h"
#include "gdcmSystem.h"

namespace gdcm
{

static const char *TypeStrings[] = {
"UNKNOWN",
"FUJI",
"GEMS",
"HITACHI",
"KODAK",
"MARCONI",
"PMS",
"SIEMENS",
"TOSHIBA"
};
namespace gdcm {

// FIXME: fuji and hitachi are the same now
static const char* TypeStrings[] = {"UNKNOWN", "FUJI", "GEMS", "HITACHI",
"KODAK", "MARCONI", "PMS", "SIEMENS",
"TOSHIBA", "AGFA", "SAMSUNG", "UIH"};

const char *EquipmentManufacturer::TypeToString( Type type )
{
const char* EquipmentManufacturer::TypeToString(Type type) {
return TypeStrings[type];
}

struct Mapping
{
struct Mapping {
EquipmentManufacturer::Type type;
size_t nstrings;
const char* const *strings;
const char* const* strings;
};

static const char* const fuji[] = {"FUJI", "FUJI PHOTO FILM Co., ltd.","FUJIFILM Corporation","FUJI PHOTO FILM CO. LTD."};
static const char* const gems[] = {"GE MEDICAL SYSTEMS", "GE_MEDICAL_SYSTEMS", "GE Healthcare", "G.E. Medical Systems","GE Vingmed Ultrasound","\"GE Healthcare\""/*sigh*/};
static const char* const hitachi[] = {"Hitachi Medical Corporation","ALOKA CO., LTD."};
static const char* const agfa[] = {"Agfa"};
static const char* const fuji[] = {"FUJI",
"FUJI PHOTO FILM Co., ltd.",
"FUJIFILM Healthcare Corporation",
"FUJIFILM SonoSite",
"FUJIFILM Corporation",
"FUJI PHOTO FILM CO. LTD."};
static const char* const gems[] = {"GE MEDICAL SYSTEMS",
"GE_MEDICAL_SYSTEMS",
"GE Healthcare",
"G.E. Medical Systems",
"GE Healthcare IT Cardiology",
"GE Healthcare Austria GmbH & Co OG",
"GE Healthcare Ultrasound",
"GE MEDICAL SYSTEMS, NUCLEAR",
"GEMS Ultrasound",
"GE OEC Medical Systems GmbH",
"GE Vingmed Ultrasound",
"\"GE Healthcare\"" /*sigh*/};
static const char* const hitachi[] = {"Hitachi Medical Corporation",
"Hitachi, Ltd.", "ALOKA CO., LTD."};
static const char* const kodak[] = {"Kodak"};
static const char* const pms[] = { "Philips Medical Systems", "Philips Healthcare", "Philips Medical Systems, Inc.","Philips","Picker International, Inc." };
static const char* const siemens[] = { "Siemens Healthineers", "SIEMENS", "Siemens HealthCare GmbH", "Siemens Health Services","Acuson" };
static const char* const marconi[] = { "Marconi Medical Systems, Inc." };
static const char* const toshiba[] = { "TOSHIBA_MEC", "Toshiba" };

#define ARRAY_SIZE( X ) \
sizeof(X) / sizeof(*X)
static const char* const pms[] = {
"Philips Medical Systems", "Philips Healthcare",
"Philips Medical Systems, Inc.", "Philips", "Picker International, Inc."};
static const char* const siemens[] = {"Siemens Healthineers",
"SIEMENS",
"SIEMENS NM",
"Siemens HealthCare GmbH",
"Siemens Health Services",
"Acuson"};
static const char* const marconi[] = {"Marconi Medical Systems, Inc."};
static const char* const samsung[] = {"SAMSUNG MEDISON CO., LTD.",
"SAMSUNG MEDISON CO.,LTD." /*sigh*/};
static const char* const toshiba[] = {"TOSHIBA_MEC", "CANON_MEC",
"TOSHIBA_MEC_US",
"Toshiba"}; // must include canon
static const char* const uih[] = {"UIH"}; // United Imaging Healthcare

#define ARRAY_SIZE(X) sizeof(X) / sizeof(*X)

#define MAPPING(X, Y) \
{ X, ARRAY_SIZE(Y), Y }

static const Mapping mappings[] = {
MAPPING( EquipmentManufacturer::FUJI, fuji ),
MAPPING( EquipmentManufacturer::GEMS, gems ),
MAPPING( EquipmentManufacturer::HITACHI, hitachi ),
MAPPING( EquipmentManufacturer::KODAK, kodak ),
MAPPING( EquipmentManufacturer::PMS, pms ),
MAPPING( EquipmentManufacturer::SIEMENS, siemens ),
MAPPING( EquipmentManufacturer::MARCONI, marconi ),
MAPPING( EquipmentManufacturer::TOSHIBA, toshiba )
};
MAPPING(EquipmentManufacturer::AGFA, agfa),
MAPPING(EquipmentManufacturer::FUJI, fuji),
MAPPING(EquipmentManufacturer::GEMS, gems),
MAPPING(EquipmentManufacturer::HITACHI, hitachi),
MAPPING(EquipmentManufacturer::KODAK, kodak),
MAPPING(EquipmentManufacturer::PMS, pms),
MAPPING(EquipmentManufacturer::SIEMENS, siemens),
MAPPING(EquipmentManufacturer::MARCONI, marconi),
MAPPING(EquipmentManufacturer::SAMSUNG, samsung),
MAPPING(EquipmentManufacturer::TOSHIBA, toshiba),
MAPPING(EquipmentManufacturer::UIH, uih)};

// long story short, private creator could be moved around, what we are trying
// to achieve here is true modality check, so generally they should not have
// been moved in the process.
static bool IsPrivateCreatorFound(DataSet const& ds, Tag const& private_tag,
std::string const& creator_value) {
if (ds.FindDataElement(private_tag)) {
const DataElement& de = ds.GetDataElement(private_tag);
Element<VR::LO, VM::VM1> priv_creator;
priv_creator.SetFromDataElement(de);
if (priv_creator.GetValue().Trim() == creator_value) return true;
}
return false;
}

EquipmentManufacturer::Type EquipmentManufacturer::GuessFromPrivateAttributes( DataSet const & ds )
{
template <long long TVR, int TVM>
static std::string GetPrivateTagValueOrEmpty(DataSet const& ds,
PrivateTag const& pt) {
if (ds.FindDataElement(pt)) {
const DataElement& de = ds.GetDataElement(pt);
Element<TVR, TVM> value = {""};
value.SetFromDataElement(de);
return value.GetValue().Trim();
}
return "";
}

EquipmentManufacturer::Type EquipmentManufacturer::GuessFromPrivateAttributes(
DataSet const& ds) {
// try against with well known private tag:
// watch out for private creator such as ELSCINT1 which can be found in
// GEMS/PEMS and maybe even SIEMENS !
gdcm::Tag gems_iden_01(0x0009,0x0010);
if( ds.FindDataElement( gems_iden_01 ) )
{
const gdcm::DataElement & de = ds.GetDataElement( gems_iden_01 );
gdcm::Element<VR::LO, VM::VM1> priv_creator;
priv_creator.SetFromDataElement( de );
if( priv_creator.GetValue() == "GEMS_IDEN_01" ) return GEMS;
if( priv_creator.GetValue() == "GEMS_PETD_01" ) return GEMS;
}

gdcm::PrivateTag siemens_manu(0x0021,0x0022,"SIEMENS MR SDS 01");
if( ds.FindDataElement( siemens_manu ) )
{
const gdcm::DataElement & de = ds.GetDataElement( siemens_manu );
gdcm::Element<VR::SH, VM::VM1> value;
value.SetFromDataElement( de );
if( value.GetValue().Trim() == "SIEMENS" ) return SIEMENS;
}
// Try to prefer those listed at:
// https://dicom.nema.org/medical/dicom/current/output/chtml/part15/sect_E.3.10.html#table_E.3.10-1
if (ds.FindDataElement(PrivateTag(0x0019, 0x0023, "GEMS_ACQU_01")) ||
ds.FindDataElement(PrivateTag(0x0043, 0x0039, "GEMS_PARM_01")) ||
ds.FindDataElement(PrivateTag(0x0045, 0x0001, "GEMS_HELIOS_01")) ||
ds.FindDataElement(PrivateTag(0x0025, 0x001b, "GEMS_SERS_01"))
/* extra */
|| ds.FindDataElement(
PrivateTag(0x6003, 0x0010, "GEMS_Ultrasound_ImageGroup_001")) ||
ds.FindDataElement(PrivateTag(0x0019, 0x0007, "DLX_SERIE_01")) ||
ds.FindDataElement(PrivateTag(0x0009, 0x0001, "GEMS_GENIE_1")) ||
ds.FindDataElement(PrivateTag(0x0011, 0x0003, "GEMS_GDXE_FALCON_04")))
return GEMS;

#if 0
if (IsPrivateCreatorFound(ds, Tag(0x0025, 0x0010), "GEMS_IDEN_01") ||
IsPrivateCreatorFound(ds, Tag(0x0009, 0x0010), "GEMS_IDEN_01") ||
IsPrivateCreatorFound(ds, Tag(0x0009, 0x0010), "GEMS_GENIE_1") ||
IsPrivateCreatorFound(ds, Tag(0x0009, 0x0010), "GEMS_PETD_01"))
return GEMS;
#endif

// Philips:
if (ds.FindDataElement(
PrivateTag(0x2005, 0x000d, "Philips MR Imaging DD 001")) ||
ds.FindDataElement(
PrivateTag(0x2005, 0x000e, "Philips MR Imaging DD 001")) ||
ds.FindDataElement(
PrivateTag(0x2001, 0x0003, "Philips Imaging DD 001")) ||
ds.FindDataElement(PrivateTag(0x2001, 0x005f, "Philips Imaging DD 001")))
return PMS;
#if 0
if (IsPrivateCreatorFound(ds, Tag(0x2005, 0x0014),
"Philips MR Imaging DD 005"))
return PMS;
#endif
if (IsPrivateCreatorFound(ds, Tag(0x0019, 0x0010), "PHILIPS MR/PART") &&
IsPrivateCreatorFound(ds, Tag(0x0021, 0x0010), "PHILIPS MR/PART"))
return PMS;
if (IsPrivateCreatorFound(ds, Tag(0x0009, 0x0010), "SPI-P Release 1") &&
IsPrivateCreatorFound(ds, Tag(0x0019, 0x0010), "SPI-P Release 1"))
return PMS;

// Siemens:
if (ds.FindDataElement(PrivateTag(0x0029, 0x0010, "SIEMENS CSA HEADER")) ||
ds.FindDataElement(PrivateTag(0x0029, 0x0020, "SIEMENS CSA HEADER")) ||
ds.FindDataElement(PrivateTag(0x0029, 0x0010, "SIEMENS MEDCOM OOG")) ||
ds.FindDataElement(
PrivateTag(0x7fdf, 0x0000, "ACUSON:1.2.840.113680.1.0:7ffe")) ||
ds.FindDataElement(PrivateTag(0x0019, 0x0012, "SIEMENS CM VA0 ACQU")) ||
ds.FindDataElement(PrivateTag(0x0009, 0x0010, "SIEMENS CT VA0 IDE")))
return SIEMENS;
#if 0
if (GetPrivateTagValueOrEmpty<VR::SH, VM::VM1>(
ds, PrivateTag(0x0021, 0x0022, "SIEMENS MR SDS 01")) == "SIEMENS")
return SIEMENS;
// gdcm-MR-SIEMENS-16-2.acr
if (GetPrivateTagValueOrEmpty<VR::LO, VM::VM1>(
ds, PrivateTag(0x0019, 0x0012, "SIEMENS CM VA0 ACQU")) == "SIEMENS")
return SIEMENS;
#endif

// toshiba:
if (ds.FindDataElement(PrivateTag(0x7005, 0x0008, "TOSHIBA_MEC_CT3")) ||
ds.FindDataElement(PrivateTag(0x700d, 0x0008, "TOSHIBA_MEC_MR3")) ||
ds.FindDataElement(PrivateTag(0x0029, 0x0001, "PMTF INFORMATION DATA")) ||
ds.FindDataElement(PrivateTag(0x0029, 0x0001, "CANON_MEC_MR3")) ||
ds.FindDataElement(PrivateTag(0x0029, 0x0001, "TOSHIBA_MEC_MR3")))
return TOSHIBA;
// fuji
if (ds.FindDataElement(PrivateTag(0x0021, 0x0010, "FDMS 1.0"))) return FUJI;
// hitachi
if (ds.FindDataElement(PrivateTag(0x0009, 0x0000, "HMC - CT - ID")) ||
ds.FindDataElement(PrivateTag(0x0009, 0x0003, "MMCPrivate")) ||
ds.FindDataElement(PrivateTag(0x0009, 0x0050, "MMCPrivate")) ||
ds.FindDataElement(PrivateTag(0x0019, 0x000e, "MMCPrivate")) ||
ds.FindDataElement(PrivateTag(0x0019, 0x0021, "MMCPrivate")) ||
ds.FindDataElement(PrivateTag(0x0029, 0x002f, "MMCPrivate")) ||
ds.FindDataElement(PrivateTag(0x0029, 0x00d7, "MMCPrivate")))
return HITACHI;
// UIH
if (ds.FindDataElement(PrivateTag(0x0065, 0x000a, "Image Private Header")))
return UIH;

return UNKNOWN;
}

EquipmentManufacturer::Type EquipmentManufacturer::Compute( DataSet const & ds )
{
EquipmentManufacturer::Type ret = GuessFromPrivateAttributes( ds );
EquipmentManufacturer::Type EquipmentManufacturer::Compute(DataSet const& ds) {
EquipmentManufacturer::Type ret = GuessFromPrivateAttributes(ds);

// proper anonymizer should not touch Manufacturer attribute value:
// http://dicom.nema.org/medical/dicom/current/output/chtml/part15/chapter_E.html#table_E.1-1
gdcm::Attribute<0x0008,0x0070> manu = { "" }; // Manufacturer
manu.SetFromDataSet( ds );
const std::string manufacturer = manu.GetValue().Trim();
for( const Mapping * mapping = mappings; mapping != mappings + ARRAY_SIZE(mappings); ++mapping )
{
for( size_t i = 0; i < mapping->nstrings; ++i )
{
// case insensitive to handle: "GE MEDICAL SYSTEMS" vs "GE Medical Systems"
if( System::StrCaseCmp( mapping->strings[i], manufacturer.c_str() ) == 0 ) {
if( ret != UNKNOWN && ret != mapping->type ) {
gdcmErrorMacro(" Impossible happen: " << ret << " vs " << mapping->type );
return UNKNOWN;
Attribute<0x0008, 0x0070> manu = {""}; // Manufacturer
std::string manufacturer;
if (ds.FindDataElement(manu.GetTag())) {
manu.SetFromDataSet(ds);
manufacturer = manu.GetValue().Trim();
// TODO: contributing equipement ?
} else {
// MFSPLIT export seems to remove the attribute completely:
manufacturer = GetPrivateTagValueOrEmpty<VR::SH, VM::VM1>(
ds, PrivateTag(0x0021, 0x0022, "SIEMENS MR SDS 01"));
}
if (!manufacturer.empty()) {
for (const Mapping* mapping = mappings;
mapping != mappings + ARRAY_SIZE(mappings); ++mapping) {
for (size_t i = 0; i < mapping->nstrings; ++i) {
// case insensitive to handle: "GE MEDICAL SYSTEMS" vs "GE Medical
// Systems"
if (System::StrCaseCmp(mapping->strings[i], manufacturer.c_str()) ==
0) {
if (ret != UNKNOWN && ret != mapping->type) {
gdcmErrorMacro(" Impossible happen: " << ret << " vs "
<< mapping->type);
return UNKNOWN;
}
return mapping->type;
}
return mapping->type;
}
}
}

gdcmWarningMacro( "Unknown Manufacturer [" << manufacturer << "] trying guess." );
gdcmWarningMacro("Unknown Manufacturer [" << manufacturer
<< "] trying guess.");
return ret;
}

} // end namespace gdcm
} // end namespace gdcm
45 changes: 23 additions & 22 deletions Source/MediaStorageAndFileFormat/gdcmEquipmentManufacturer.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,41 +16,42 @@

#include "gdcmTypes.h"

namespace gdcm
{
namespace gdcm {

class DataSet;
/**
* \brief
* \details
* \brief
* \details
* The intent is for private tags handling. This class is not meant to handle
* all possible vendors in the world, simply those well known where we intend
* to read private tags afterwards (typically SIEMENS+CSA, GEMS+PDB ...)
*/
class GDCM_EXPORT EquipmentManufacturer
{
public:

class GDCM_EXPORT EquipmentManufacturer {
public:
typedef enum {
UNKNOWN = 0,
FUJI,
GEMS,
HITACHI,
KODAK,
MARCONI,
PMS,
SIEMENS,
TOSHIBA
FUJI = 1,
GEMS = 2,
HITACHI = 3,
KODAK = 4,
MARCONI = 5,
PMS = 6,
SIEMENS = 7,
TOSHIBA = 8,
AGFA = 9,
SAMSUNG = 10,
UIH = 11
} Type;

static Type Compute( DataSet const & ds );
static Type Compute(DataSet const &ds);

static const char *TypeToString( Type type );
static const char *TypeToString(Type type);

private:
static EquipmentManufacturer::Type GuessFromPrivateAttributes( DataSet const & ds );
private:
static EquipmentManufacturer::Type GuessFromPrivateAttributes(
DataSet const &ds);
};

} // end namespace gdcm
} // end namespace gdcm

#endif // GDCMEQUIPMENTMANUFACTURER_H
#endif // GDCMEQUIPMENTMANUFACTURER_H
Loading

0 comments on commit 169a467

Please sign in to comment.