ctkDICOMDataset.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946
  1. #include "ctkDICOMDataset.h"
  2. #include <dctk.h>
  3. #include <dcostrmb.h>
  4. #include <dcistrmb.h>
  5. #include <stdexcept>
  6. class ctkDICOMDatasetPrivate
  7. {
  8. public:
  9. ctkDICOMDatasetPrivate() {}
  10. QString m_SpecificCharacterSet;
  11. bool m_DICOMDataSetInitialized;
  12. static DcmDataDictionary* s_Dictionary;
  13. };
  14. DcmDataDictionary* ctkDICOMDatasetPrivate::s_Dictionary = new DcmDataDictionary(OFTrue, OFTrue);
  15. // ------------------------------------------- QPersonName -----------------------------------------------------------
  16. class QPersonNamePrivate
  17. {
  18. public:
  19. QPersonNamePrivate() {}
  20. QString m_LastName;
  21. QString m_FirstName;
  22. QString m_MiddleName;
  23. QString m_NamePrefix;
  24. QString m_NameSuffix;
  25. };
  26. QPersonName::QPersonName(const QString& lastName,
  27. const QString& firstName,
  28. const QString& middleName,
  29. const QString& namePrefix,
  30. const QString& nameSuffix)
  31. :d_ptr(new QPersonNamePrivate)
  32. {
  33. Q_D(QPersonName);
  34. d->m_LastName = lastName;
  35. d->m_FirstName = firstName;
  36. d->m_MiddleName = middleName;
  37. d->m_NamePrefix = namePrefix;
  38. d->m_NameSuffix = nameSuffix;
  39. }
  40. QString QPersonName::GetFormattedName() const
  41. {
  42. Q_D(const QPersonName);
  43. QString result("");
  44. /* not sortable
  45. if (!m_NamePrefix.isEmpty()) result += QString("%1 ").arg(m_NamePrefix);
  46. if (!m_FirstName.isEmpty()) result += QString("%1 " ).arg(m_FirstName);
  47. if (!m_MiddleName.isEmpty()) result += QString("%1 ").arg(m_MiddleName);
  48. if (!m_LastName.isEmpty()) result += QString("%1").arg(m_LastName);
  49. if (!m_NameSuffix.isEmpty()) result += QString(", %1").arg(m_NameSuffix); // this might be unclean if last name is empty
  50. */
  51. if (!d->m_LastName.isEmpty()) result += QString("%1").arg(d->m_LastName);
  52. if (!d->m_FirstName.isEmpty()) result += QString(", %1" ).arg(d->m_FirstName);
  53. if (!d->m_MiddleName.isEmpty()) result += QString(" %1").arg(d->m_MiddleName);
  54. if (!d->m_NameSuffix.isEmpty()) result += QString(", %1").arg(d->m_NameSuffix); // this might be unclean if last name is empty
  55. return result;
  56. }
  57. QString QPersonName::GetLastName() const
  58. {
  59. Q_D(const QPersonName);
  60. return d->m_LastName;
  61. }
  62. QString QPersonName::GetFirstName() const
  63. {
  64. Q_D(const QPersonName);
  65. return d->m_FirstName;
  66. }
  67. QString QPersonName::GetMiddleName() const
  68. {
  69. Q_D(const QPersonName);
  70. return d->m_MiddleName;
  71. }
  72. QString QPersonName::GetNamePrefix() const
  73. {
  74. Q_D(const QPersonName);
  75. return d->m_NamePrefix;
  76. }
  77. QString QPersonName::GetNameSuffix() const
  78. {
  79. Q_D(const QPersonName);
  80. return d->m_NameSuffix;
  81. }
  82. QPersonName::operator QString() const
  83. {
  84. return this->GetFormattedName();
  85. }
  86. std::string QPersonName::toStdString() const
  87. {
  88. // the complicated looking .toLocal8Bit().constData() is on purpose.
  89. // if we'd use .toStdString(), the string would be converted to ASCII
  90. // instead of the local 8bit character encoding.
  91. // changes for correctly looking output of the strings are higher with .toLocal8Bit()
  92. return this->GetFormattedName().toLocal8Bit().constData();
  93. }
  94. ctkDICOMDataset::ctkDICOMDataset()
  95. :d_ptr(new ctkDICOMDatasetPrivate)
  96. {
  97. Q_D(ctkDICOMDataset);
  98. d->m_DICOMDataSetInitialized = false;
  99. }
  100. ctkDICOMDataset::~ctkDICOMDataset()
  101. {
  102. }
  103. void ctkDICOMDataset::InitializeFromDataset(DcmDataset* dataset)
  104. {
  105. Q_D(ctkDICOMDataset);
  106. if (dataset)
  107. {
  108. if (!d->m_DICOMDataSetInitialized)
  109. {
  110. d->m_DICOMDataSetInitialized = true;
  111. // remember "specific character set" tag for conversion of strings to the right encoding
  112. //std::cerr << "** " << (void*)this << " ctkDICOMDataset: Initialized DcmDataset" << std::endl;
  113. if ( CopyElement( dataset, DCM_SpecificCharacterSet, 3 ) )
  114. {
  115. OFString encoding;
  116. if ( CheckCondition( findAndGetOFString(DCM_SpecificCharacterSet, encoding) ) )
  117. {
  118. d->m_SpecificCharacterSet = encoding.c_str();
  119. }
  120. else
  121. {
  122. std::cerr << "Some implementation error in DCMTK. We put something into a box and now the box is empty. This is not ok." << std::endl;
  123. //throw std::logic_error("Some implementation error in DCMTK. We put something into a box and now the box is empty. This is not ok.");
  124. }
  125. }
  126. if (d->m_SpecificCharacterSet.isEmpty())
  127. {
  128. /**
  129. see Bug # 6458:
  130. There are cases, where different studies of the same person get encoded both with and without the SpecificCharacterSet attribute set.
  131. DICOM says: default is ASCII / ISO_IR 6 / ISO 646
  132. Since we experienced such mixed data, we supplement missing characterset information with the ISO_IR 100 / Latin1 character set.
  133. Since Latin1 is a superset of ASCII, this will not cause problems. PLUS in most cases (Europe) we will guess right and suppress
  134. "double patients" in applications.
  135. */
  136. SetElementAsString( DCM_SpecificCharacterSet, "ISO_IR 100" );
  137. }
  138. }
  139. }
  140. }
  141. void ctkDICOMDataset::Serialize()
  142. {
  143. // store content of current DcmDataset (our parent) as QByteArray into m_ctkDICOMDataset
  144. Uint32 buffersize = 1024*1024; // reserve 1MB
  145. char* writebuffer = new char[buffersize];
  146. // write into buffer
  147. DcmOutputBufferStream dcmbuffer(writebuffer, buffersize);
  148. DcmDataset::transferInit();
  149. OFCondition condition = DcmDataset::write(dcmbuffer, EXS_LittleEndianImplicit, EET_UndefinedLength, NULL );
  150. DcmDataset::transferEnd();
  151. if ( condition.bad() )
  152. {
  153. std::cerr << "Could not DcmDataset::write(..): " << condition.text() << std::endl;
  154. }
  155. // get written contents of buffer
  156. offile_off_t datasetsize = 0;
  157. void* readbuffer = NULL;
  158. dcmbuffer.flushBuffer(readbuffer, datasetsize);
  159. //std::cerr << "** " << (void*)this << " ctkDICOMDataset: Serializing Dataset into " << datasetsize << " bytes" << std::endl;
  160. // construct Qt type from that contents
  161. QByteArray qtArray = QByteArray::fromRawData( static_cast<const char*>(readbuffer), datasetsize );
  162. //std::cerr << "** Buffer size: " << qtArray.size() << std::endl;
  163. QString stringbuffer = QString::fromAscii(qtArray.toBase64());
  164. //std::cerr << "** String of size " << stringbuffer.size() << " looks like this:\n" << stringbuffer.toStdString() << std::endl << std::endl;
  165. this->SetStoredSerialization( stringbuffer );
  166. delete[] writebuffer;
  167. }
  168. void ctkDICOMDataset::MarkForInitialization()
  169. {
  170. Q_D(ctkDICOMDataset);
  171. d->m_DICOMDataSetInitialized = false;
  172. }
  173. void ctkDICOMDataset::EnsureDcmDataSetIsInitialized() const
  174. {
  175. const_cast<ctkDICOMDataset*>(this)->Deserialize();
  176. }
  177. void ctkDICOMDataset::Deserialize()
  178. {
  179. Q_D(ctkDICOMDataset);
  180. // read attribute m_ctkDICOMDataset
  181. // construct a DcmDataset from it
  182. // calls InitializeData(DcmDataset*)
  183. // this method can be called both from sub-classes when they get the InitializeData signal from the persistence framework
  184. // and from EnsureDcmDataSetIsInitialized() when a GetElement.. or SetElement.. method is called.
  185. if (d->m_DICOMDataSetInitialized) return; // only need to do this once
  186. QString stringbuffer = this->GetStoredSerialization();
  187. if ( stringbuffer.isEmpty() )
  188. {
  189. d->m_DICOMDataSetInitialized = true;
  190. return; // TODO nicer: hold three states: newly created / loaded but not initialized / restored from DB
  191. }
  192. //std::cerr << "** " << (void*)this << " ctkDICOMDataset: Deserialize Dataset from string of size " << stringbuffer.size() << "\n" << stringbuffer.toStdString() << std::endl;
  193. QByteArray qtArray = QByteArray::fromBase64( stringbuffer.toAscii() );
  194. //std::cerr << "** " << (void*)this << " ctkDICOMDataset: Deserialize Dataset from byte array of size " << qtArray.size() << std::endl;
  195. DcmInputBufferStream dcmbuffer;
  196. dcmbuffer.setBuffer( qtArray.data(), qtArray.size() );
  197. //std::cerr << "** Buffer state: " << dcmbuffer.status().code() << " " << dcmbuffer.good() << " " << dcmbuffer.eos() << " tell " << dcmbuffer.tell() << " avail " << dcmbuffer.avail() << std::endl;
  198. DcmDataset dataset;
  199. dataset.transferInit();
  200. //std::cerr << "** Dataset state: " << dataset.transferState() << std::endl << std::endl;
  201. OFCondition condition = dataset.read( dcmbuffer, EXS_LittleEndianImplicit );
  202. dataset.transferEnd();
  203. // do this in all cases, even when reading reported an error
  204. this->InitializeFromDataset(&dataset);
  205. if ( condition.bad() )
  206. {
  207. std::cerr << "** Condition code of Dataset::read() is " << condition.code() << std::endl;
  208. std::cerr << "** Buffer state: " << dcmbuffer.status().code() << " " << dcmbuffer.good() << " " << dcmbuffer.eos() << " tell " << dcmbuffer.tell() << " avail " << dcmbuffer.avail() << std::endl;
  209. std::cerr << "** Dataset state: " << dataset.transferState() << std::endl;
  210. std::cerr << std::string("Could not DcmDataset::read(..): ") + condition.text() << std::endl;
  211. //throw std::invalid_argument( std::string("Could not DcmDataset::read(..): ") + condition.text() );
  212. }
  213. }
  214. OFCondition ctkDICOMDataset::findAndGetElement(const DcmTag& tag, DcmElement*& element, const OFBool searchIntoSub) const
  215. {
  216. // this one const_cast allows us to declare quite a lot of methods nicely with const
  217. return ((DcmDataset&)(const_cast<ctkDICOMDataset&>(*this))).findAndGetElement(tag, element, searchIntoSub);
  218. }
  219. OFCondition ctkDICOMDataset::findAndGetOFString(const DcmTag& tag, OFString& value, const unsigned long pos, const OFBool searchIntoSub) const
  220. {
  221. // this second const_cast allows us to declare quite a lot of methods nicely with const
  222. return ((DcmDataset&)(const_cast<ctkDICOMDataset&>(*this))).findAndGetOFString(tag, value, pos, searchIntoSub);
  223. }
  224. bool ctkDICOMDataset::CheckCondition(const OFCondition& condition)
  225. {
  226. if ( condition.bad() )
  227. {
  228. //std::cerr << "Bad return code (" << condition.code() << "): " << condition.text() << std::endl;
  229. }
  230. return condition.good();
  231. }
  232. bool ctkDICOMDataset::CopyElement( DcmDataset* dataset, const DcmTagKey& tag, int type )
  233. {
  234. switch (type)
  235. {
  236. case 0x1:
  237. case 0x1C:
  238. case 0x2:
  239. case 0x2C:
  240. case 0x3:
  241. // these are ok
  242. break;
  243. default:
  244. // nothing else is ok
  245. std::cerr << "Unknown attribute type. Cannot process call to ExtractElement " << TagKey(tag).toStdString() << std::endl;
  246. return false;
  247. }
  248. bool missing(false);
  249. bool copied(true);
  250. if (!dataset) return false;
  251. // type 1 or 1C must exist AND have a value
  252. if (!dataset->tagExistsWithValue( tag ))
  253. {
  254. if (type == 0x1 || type == 0x1C) missing = true;
  255. }
  256. // type 2 or 2C must exist but may have an empty value
  257. if (!dataset->tagExists( tag ))
  258. {
  259. if (type == 0x1 || type == 0x1C) missing = true;
  260. if (type == 0x2 || type == 0x2C) missing = true;
  261. }
  262. else
  263. {
  264. // we found this tag
  265. DcmElement* element(NULL);
  266. dataset->findAndGetElement( tag, element, OFFalse, OFTrue ); // OFTrue is important (copies element), DcmDataset takes ownership and deletes elements on its own destruction
  267. if (element)
  268. {
  269. copied = CheckCondition( DcmDataset::insert(element) );
  270. }
  271. }
  272. if (missing)
  273. {
  274. std::cerr << "Tag " << TagKey(tag).toStdString() << " [" << TagDescription(tag).toStdString() << "] of type " << QString("%1").arg(type,0,16).toStdString() << " missing or empty." << std::endl;
  275. }
  276. if (!copied)
  277. {
  278. std::cerr << "Tag " << TagKey(tag).toStdString() << " [" << TagDescription(tag).toStdString() << "] not copied successfully" << std::endl;
  279. }
  280. return !missing && copied;
  281. }
  282. QString ctkDICOMDataset::Decode( const DcmTag& tag, const OFString& raw ) const
  283. {
  284. Q_D(const ctkDICOMDataset);
  285. // decode for types LO, LT, PN, SH, ST, UT
  286. QString vr = TagVR(tag);
  287. if ( !d->m_SpecificCharacterSet.isEmpty()
  288. && (vr == "LO" ||
  289. vr == "LT" ||
  290. vr == "PN" ||
  291. vr == "SH" ||
  292. vr == "ST" ||
  293. vr == "UT" ) )
  294. {
  295. //std::cout << "Decode from encoding " << d->m_SpecificCharacterSet.toStdString() << std::endl;
  296. static QMap<QString, QTextDecoder*> decoders;
  297. static QMap<QString, QString> qtEncodingNamesForDICOMEncodingNames;
  298. if (qtEncodingNamesForDICOMEncodingNames.isEmpty())
  299. {
  300. // runs only once and fills up a map of encoding names that might be named in DICOM files.
  301. // for each encoding we store the name that Qt uses for the same encoding.
  302. // This is because there is not yet a standard naming scheme but lots of aliases
  303. // out in the real world: e.g. http://www.openi18n.org/subgroups/sa/locnameguide/final/CodesetAliasTable.html
  304. // DICOM Qt
  305. qtEncodingNamesForDICOMEncodingNames.insert("ISO_IR 192", "UTF-8");
  306. qtEncodingNamesForDICOMEncodingNames.insert("ISO_IR 100", "ISO-8859-1");
  307. qtEncodingNamesForDICOMEncodingNames.insert("ISO_IR 101", "ISO-8859-2");
  308. qtEncodingNamesForDICOMEncodingNames.insert("ISO_IR 109", "ISO-8859-3");
  309. qtEncodingNamesForDICOMEncodingNames.insert("ISO_IR 110", "ISO-8859-4");
  310. qtEncodingNamesForDICOMEncodingNames.insert("ISO_IR 144", "ISO-8859-5");
  311. qtEncodingNamesForDICOMEncodingNames.insert("ISO_IR 127", "ISO-8859-6");
  312. qtEncodingNamesForDICOMEncodingNames.insert("ISO_IR 126", "ISO-8859-7");
  313. qtEncodingNamesForDICOMEncodingNames.insert("ISO_IR 138", "ISO-8859-8");
  314. qtEncodingNamesForDICOMEncodingNames.insert("ISO_IR 148", "ISO-8859-9");
  315. qtEncodingNamesForDICOMEncodingNames.insert("ISO_IR 179", "ISO-8859-13");
  316. qtEncodingNamesForDICOMEncodingNames.insert("ISO 2022 IR 13", "jisx0201*-0");
  317. // use all names that Qt knows by itself
  318. foreach( QByteArray c, QTextCodec::availableCodecs() )
  319. {
  320. qtEncodingNamesForDICOMEncodingNames.insert( c.constData(), c.constData() );
  321. }
  322. }
  323. if ( qtEncodingNamesForDICOMEncodingNames.contains(d->m_SpecificCharacterSet) )
  324. {
  325. QString encodingName( qtEncodingNamesForDICOMEncodingNames[d->m_SpecificCharacterSet] );
  326. if ( !decoders.contains( encodingName ) )
  327. {
  328. QTextCodec* codec = QTextCodec::codecForName( encodingName.toAscii() );
  329. if (!codec)
  330. {
  331. std::cerr << "Could not create QTextCodec object for '" << encodingName.toStdString() << "'. Using default encoding instead." << std::endl;
  332. decoders.insert( encodingName, QTextCodec::codecForCStrings()->makeDecoder() ); // uses Latin1
  333. }
  334. else
  335. {
  336. // initialize a QTextDecoder for given encoding
  337. decoders.insert( encodingName, codec->makeDecoder() );
  338. // We are responsible for deleting the QTextDecoder objects
  339. // created by makeDecoder(). BUT as these objects are stored
  340. // in a static map that lives until application end AND
  341. // nothing application relevant happens during their
  342. // destruction, we just let them be destructed by C++ on
  343. // application exit.
  344. // Any potential leaks that are found by this behavior can
  345. // be suppressed.
  346. }
  347. }
  348. //std::cout << "Decode '" << raw.c_str() << "' to '" << decoders[encodingName]->toUnicode( raw.c_str() ).toLocal8Bit().constData() << "'" << std::endl;
  349. return decoders[encodingName]->toUnicode( raw.c_str() );
  350. }
  351. else
  352. {
  353. std::cerr << "DICOM dataset contains some encoding that we never thought we would see(" << d->m_SpecificCharacterSet.toStdString() << "). Using default encoding." << std::endl;
  354. }
  355. }
  356. return QString::fromLatin1(raw.c_str()); // Latin1 is ISO 8859, which is the default character set of DICOM (PS 3.5-2008, Page 18)
  357. }
  358. OFString ctkDICOMDataset::Encode( const DcmTag& tag, const QString& qstring ) const
  359. {
  360. // TODO: respect given character-set when encoding; see Decode()
  361. return OFString( qstring.toLatin1().data() ); // Latin1 is ISO 8859, which is the default character set of DICOM (PS 3.5-2008, Page 18)
  362. }
  363. QString ctkDICOMDataset::GetAllElementValuesAsString( const DcmTag& tag ) const
  364. {
  365. this->EnsureDcmDataSetIsInitialized();
  366. QStringList qsl;
  367. DcmElement* element(NULL);
  368. findAndGetElement(tag, element);
  369. if (!element) return QString::null;
  370. const unsigned long count = element->getVM(); // value multiplicity
  371. for (unsigned long i = 0; i < count; ++i)
  372. {
  373. OFString s;
  374. if ( CheckCondition( const_cast<ctkDICOMDataset*>(this)->findAndGetOFString(tag, s, i) ) )
  375. {
  376. qsl << Decode( tag, s );
  377. }
  378. }
  379. return qsl.join("|");
  380. }
  381. QString ctkDICOMDataset::GetElementAsString( const DcmTag& tag, unsigned long pos ) const
  382. {
  383. this->EnsureDcmDataSetIsInitialized();
  384. OFString s;
  385. if ( CheckCondition( findAndGetOFString(tag, s, pos) ) )
  386. {
  387. return Decode( tag, s );
  388. }
  389. else
  390. {
  391. return QString::null;
  392. }
  393. }
  394. QStringList ctkDICOMDataset::GetElementAsStringList( const DcmTag& tag ) const
  395. {
  396. this->EnsureDcmDataSetIsInitialized();
  397. QStringList qsl;
  398. DcmElement* element(NULL);
  399. findAndGetElement(tag, element);
  400. if (!element) return qsl;
  401. const unsigned long count = element->getVM(); // value multiplicity
  402. for (unsigned long i = 0; i < count; ++i)
  403. {
  404. qsl << GetElementAsString(tag, i);
  405. }
  406. return qsl;
  407. }
  408. QPersonName ctkDICOMDataset::GetElementAsPersonName( const DcmTag& tag, unsigned long pos ) const
  409. {
  410. this->EnsureDcmDataSetIsInitialized();
  411. DcmElement* element(NULL);
  412. findAndGetElement(tag, element);
  413. DcmPersonName* name = dynamic_cast<DcmPersonName*>(element);
  414. if (!name) return QPersonName(); // invalid
  415. OFString lastName;
  416. OFString firstName;
  417. OFString middleName;
  418. OFString namePrefix;
  419. OFString nameSuffix;
  420. if (CheckCondition( name->getNameComponents(lastName, firstName, middleName, namePrefix, nameSuffix, pos) ) )
  421. {
  422. return QPersonName(
  423. Decode(tag, lastName),
  424. Decode(tag, firstName),
  425. Decode(tag, middleName),
  426. Decode(tag, namePrefix),
  427. Decode(tag, nameSuffix) );
  428. }
  429. else
  430. {
  431. return QPersonName();
  432. }
  433. }
  434. QPersonNameList ctkDICOMDataset::GetElementAsPersonNameList( const DcmTag& tag ) const
  435. {
  436. this->EnsureDcmDataSetIsInitialized();
  437. QPersonNameList qpnl;
  438. DcmElement* element(NULL);
  439. findAndGetElement(tag, element);
  440. if (!element) return qpnl;
  441. const unsigned long count = element->getVM(); // value multiplicity
  442. for (unsigned long i = 0; i < count; ++i)
  443. {
  444. qpnl << GetElementAsPersonName(tag, i);
  445. }
  446. return qpnl;
  447. }
  448. QDate ctkDICOMDataset::GetElementAsDate( const DcmTag& tag, unsigned long pos ) const
  449. {
  450. this->EnsureDcmDataSetIsInitialized();
  451. DcmElement* element(NULL);
  452. findAndGetElement(tag, element);
  453. DcmDate* date = dynamic_cast<DcmDate*>(element);
  454. if (!date) return QDate(); // invalid
  455. OFString ofs;
  456. if (CheckCondition( date->getISOFormattedDate(ofs, pos) ) )
  457. {
  458. QString qs(ofs.c_str());
  459. return QDate::fromString(qs, "yyyy-MM-dd");
  460. }
  461. else
  462. {
  463. return QDate();
  464. }
  465. }
  466. QTime ctkDICOMDataset::GetElementAsTime( const DcmTag& tag, unsigned long pos ) const
  467. {
  468. this->EnsureDcmDataSetIsInitialized();
  469. DcmElement* element(NULL);
  470. findAndGetElement(tag, element);
  471. DcmTime* time = dynamic_cast<DcmTime*>(element);
  472. if (!time) return QTime(); // invalid
  473. OFString ofs;
  474. if (CheckCondition( time->getISOFormattedTime(ofs, pos, OFTrue, OFFalse) ) ) // true (seconds), false (fraction of a second)
  475. {
  476. QString qs(ofs.c_str());
  477. return QTime::fromString(qs, "hh:mm:ss");
  478. }
  479. else
  480. {
  481. return QTime();
  482. }
  483. }
  484. QDateTime ctkDICOMDataset::GetElementAsDateTime( const DcmTag& tag, unsigned long pos ) const
  485. {
  486. this->EnsureDcmDataSetIsInitialized();
  487. DcmElement* element(NULL);
  488. findAndGetElement(tag, element);
  489. DcmDateTime* datetime = dynamic_cast<DcmDateTime*>(element);
  490. if (!datetime) return QDateTime(); // invalid
  491. OFString ofs;
  492. if (CheckCondition( datetime->getISOFormattedDateTime(ofs, pos, OFTrue, OFFalse, OFTrue) ) ) // true (seconds), false (fraction of a second), true (time zone)
  493. {
  494. QString qs(ofs.c_str());
  495. return QDateTime::fromString(qs, "dd-MM-yyy hh:mm:ss");
  496. }
  497. else
  498. {
  499. return QDateTime();
  500. }
  501. }
  502. double ctkDICOMDataset::GetElementAsDouble( const DcmTag& tag, unsigned long pos ) const
  503. {
  504. this->EnsureDcmDataSetIsInitialized();
  505. DcmElement* element(NULL);
  506. findAndGetElement(tag, element);
  507. DcmDecimalString* ds = dynamic_cast<DcmDecimalString*>(element);
  508. if (!ds) throw std::logic_error("Element not found or not a decimal number");
  509. Float64 d;
  510. ds->getFloat64(d, pos);
  511. return d;
  512. }
  513. long ctkDICOMDataset::GetElementAsInteger( const DcmTag& tag, unsigned long pos ) const
  514. {
  515. this->EnsureDcmDataSetIsInitialized();
  516. DcmElement* element(NULL);
  517. findAndGetElement(tag, element);
  518. DcmIntegerString* is = dynamic_cast<DcmIntegerString*>(element);
  519. if (!is) throw std::logic_error("Element not found or not an integer");
  520. Sint32 i;
  521. is->getSint32(i, pos);
  522. return i;
  523. }
  524. int ctkDICOMDataset::GetElementAsSignedShort( const DcmTag& tag, unsigned long pos ) const // type SS
  525. {
  526. this->EnsureDcmDataSetIsInitialized();
  527. DcmElement* element(NULL);
  528. findAndGetElement(tag, element);
  529. DcmSignedShort* ss = dynamic_cast<DcmSignedShort*>(element);
  530. if (!ss) throw std::logic_error("Element not found or not a signed short integer");
  531. Sint16 i;
  532. ss->getSint16(i, pos);
  533. return i;
  534. }
  535. int ctkDICOMDataset::GetElementAsUnsignedShort( const DcmTag& tag, unsigned long pos ) const // type US
  536. {
  537. this->EnsureDcmDataSetIsInitialized();
  538. DcmElement* element(NULL);
  539. findAndGetElement(tag, element);
  540. DcmUnsignedShort* us = dynamic_cast<DcmUnsignedShort*>(element);
  541. if (!us) throw std::logic_error("Element not found or not a unsigned short integer");
  542. Uint16 i;
  543. us->getUint16(i, pos);
  544. return i;
  545. }
  546. bool ctkDICOMDataset::SetElementAsString( const DcmTag& tag, QString string )
  547. {
  548. this->EnsureDcmDataSetIsInitialized();
  549. // TODO: Evaluate DICOM tag for proper encoding (see GetElementAsString())
  550. return CheckCondition( putAndInsertString( tag, string.toLatin1().data() ) );
  551. }
  552. bool ctkDICOMDataset::SetElementAsStringList( const DcmTag& /*tag*/, QStringList /*stringList*/ )
  553. {
  554. this->EnsureDcmDataSetIsInitialized();
  555. // TODO: Find out how this can be implemented with DcmDataset methods; there is no method for
  556. // setting a string at a given position
  557. return false;
  558. }
  559. bool ctkDICOMDataset::SetElementAsPersonName( const DcmTag& tag, QPersonName personName )
  560. {
  561. this->EnsureDcmDataSetIsInitialized();
  562. DcmPersonName* dcmPersonName = new DcmPersonName( tag ); // TODO leak?
  563. if ( CheckCondition( dcmPersonName->putNameComponents(
  564. Encode( tag, personName.GetLastName() ),
  565. Encode( tag, personName.GetFirstName() ),
  566. Encode( tag, personName.GetMiddleName() ),
  567. Encode( tag, personName.GetNamePrefix() ),
  568. Encode( tag, personName.GetNameSuffix() ) ) ) )
  569. {
  570. return CheckCondition( insert( dcmPersonName ) );
  571. }
  572. return false;
  573. }
  574. bool ctkDICOMDataset::SetElementAsPersonNameList( const DcmTag& tag, QPersonNameList personNameList )
  575. {
  576. this->EnsureDcmDataSetIsInitialized();
  577. // TODO: Find out how this can be implemented with DcmDataset methods; there is no method for
  578. // setting an element at a given position
  579. return false;
  580. }
  581. bool ctkDICOMDataset::SetElementAsDate( const DcmTag& tag, QDate date )
  582. {
  583. this->EnsureDcmDataSetIsInitialized();
  584. OFDate ofDate( date.year(), date.month(), date.day() );
  585. DcmDate* dcmDate = new DcmDate( tag ); // TODO leak?
  586. if ( CheckCondition( dcmDate->setOFDate( ofDate ) ) )
  587. {
  588. return CheckCondition( insert( dcmDate ) );
  589. }
  590. return false;
  591. }
  592. bool ctkDICOMDataset::SetElementAsTime( const DcmTag& tag, QTime time )
  593. {
  594. this->EnsureDcmDataSetIsInitialized();
  595. OFTime ofTime( time.hour(), time.minute(), time.second() );
  596. DcmTime* dcmTime = new DcmTime( tag ); // TODO leak?
  597. if ( CheckCondition( dcmTime->setOFTime( ofTime ) ) )
  598. {
  599. return CheckCondition( insert( dcmTime ) );
  600. }
  601. return false;
  602. }
  603. bool ctkDICOMDataset::SetElementAsDateTime( const DcmTag& tag, QDateTime dateTime )
  604. {
  605. this->EnsureDcmDataSetIsInitialized();
  606. QDate date = dateTime.date();
  607. QTime time = dateTime.time();
  608. OFDateTime ofDateTime;
  609. ofDateTime.setDateTime( date.year(), date.month(), date.day(), time.hour(), time.minute(), time.second() );
  610. DcmDateTime* dcmDateTime = new DcmDateTime( tag ); // TODO leak?
  611. if ( CheckCondition( dcmDateTime->setOFDateTime( ofDateTime ) ) )
  612. {
  613. return CheckCondition( insert( dcmDateTime ) );
  614. }
  615. return false;
  616. }
  617. bool ctkDICOMDataset::SetElementAsInteger( const DcmTag& tag, long value, unsigned long pos )
  618. {
  619. this->EnsureDcmDataSetIsInitialized();
  620. //std::cerr << "TagVR: " << TagVR( tag ).toStdString() << std::endl;
  621. return CheckCondition( putAndInsertSint32( tag, value, pos ) );
  622. }
  623. bool ctkDICOMDataset::SetElementAsSignedShort( const DcmTag& tag, int value, unsigned long pos )
  624. {
  625. this->EnsureDcmDataSetIsInitialized();
  626. //std::cerr << "TagVR: " << TagVR( tag ).toStdString() << std::endl;
  627. return CheckCondition( putAndInsertSint16( tag, value, pos ) );
  628. }
  629. bool ctkDICOMDataset::SetElementAsUnsignedShort( const DcmTag& tag, int value, unsigned long pos )
  630. {
  631. this->EnsureDcmDataSetIsInitialized();
  632. //std::cerr << "TagVR: " << TagVR( tag ).toStdString() << std::endl;
  633. return CheckCondition( putAndInsertUint16( tag, value, pos ) );
  634. }
  635. QString ctkDICOMDataset::TranslateDefinedTermPatientPosition( const QString& dt )
  636. {
  637. static bool initialized = false;
  638. static QMap<QString, QString> descriptionOfTerms;
  639. if (!initialized)
  640. {
  641. descriptionOfTerms.insert("HFP", "Head First - Prone");
  642. descriptionOfTerms.insert("HFDR", "Head First - Decubitus Right");
  643. descriptionOfTerms.insert("FFDR", "Feet First - Decubitus Right");
  644. descriptionOfTerms.insert("FFP", "Feet First - Prone");
  645. descriptionOfTerms.insert("HFS", "Head First - Supine");
  646. descriptionOfTerms.insert("HFDL", "Head First - Decubitus Left");
  647. descriptionOfTerms.insert("FFDL", "Feet First - Decubitus Left");
  648. descriptionOfTerms.insert("FFS", "Feet First - Supine");
  649. initialized = true;
  650. }
  651. if ( descriptionOfTerms.contains( dt.toUpper() ) )
  652. {
  653. return descriptionOfTerms.value(dt.toUpper());
  654. }
  655. else
  656. {
  657. std::cerr << "Invalid enum for patient position" << std::endl;
  658. return QString::null;
  659. }
  660. }
  661. QString ctkDICOMDataset::TranslateDefinedTermModality( const QString& dt )
  662. {
  663. static bool initialized = false;
  664. static QMap<QString, QString> descriptionOfTerms;
  665. if (!initialized)
  666. {
  667. descriptionOfTerms.insert("CR", "Computed Radiography");
  668. descriptionOfTerms.insert("CT", "Computed Tomography");
  669. descriptionOfTerms.insert("MR", "Magnetic Resonance");
  670. descriptionOfTerms.insert("NM", "Nuclear Medicine");
  671. descriptionOfTerms.insert("US", "Ultrasound");
  672. descriptionOfTerms.insert("OT", "Other");
  673. descriptionOfTerms.insert("BI", "Biomagnetic imaging");
  674. descriptionOfTerms.insert("CD", "Color flow Doppler");
  675. descriptionOfTerms.insert("DD", "Duplex Doppler");
  676. descriptionOfTerms.insert("ES", "Endoscopy");
  677. descriptionOfTerms.insert("LS", "Laser surface scan");
  678. descriptionOfTerms.insert("PT", "Positron emission tomography (PET)");
  679. descriptionOfTerms.insert("RG", "Radiographic imaging (conventional film/screen)");
  680. descriptionOfTerms.insert("ST", "Single-photon emission computed tomograpy (SPECT)");
  681. descriptionOfTerms.insert("TG", "Thermography");
  682. descriptionOfTerms.insert("XA", "X-Ray Aniography");
  683. descriptionOfTerms.insert("RF", "Radio Fluoroscopy");
  684. descriptionOfTerms.insert("RTIMAGE", "Radiotherapy Image");
  685. descriptionOfTerms.insert("RTDOSE", "Radiotherapy Dose");
  686. descriptionOfTerms.insert("RTSTRUCT", "Radiotherapy Structure Set");
  687. descriptionOfTerms.insert("RTPLAN", "Radiotherapy Plan");
  688. descriptionOfTerms.insert("RTRECORD", "RT Treatment Record");
  689. descriptionOfTerms.insert("HC", "Hard Copy");
  690. descriptionOfTerms.insert("DX", "Digital Radiography");
  691. descriptionOfTerms.insert("MG", "Mammography");
  692. descriptionOfTerms.insert("IO", "Intra-oral Radiography");
  693. descriptionOfTerms.insert("PX", "Panoramic X-Ray");
  694. descriptionOfTerms.insert("GM", "General Microscopy");
  695. descriptionOfTerms.insert("SM", "Slide Microscopy");
  696. descriptionOfTerms.insert("XC", "External-camera Photography");
  697. descriptionOfTerms.insert("PR", "Presentation state");
  698. descriptionOfTerms.insert("AU", "Audio");
  699. descriptionOfTerms.insert("ECG", "Electrocardiography");
  700. descriptionOfTerms.insert("EPS", "Cardiac Electrophysiology");
  701. descriptionOfTerms.insert("HD", "Hemodynamic Waveform");
  702. descriptionOfTerms.insert("SR", "SR Document");
  703. descriptionOfTerms.insert("IVUS", "Intravascular Ultrasound");
  704. descriptionOfTerms.insert("OP", "Ophthalmic Photography");
  705. descriptionOfTerms.insert("SMR", "Stereometric Relationship");
  706. descriptionOfTerms.insert("OCT", "Optical Coherence Tomography (non-Ophthalmic)");
  707. descriptionOfTerms.insert("OPR", "Ophthalmic Refraction");
  708. descriptionOfTerms.insert("OPV", "Ophthalmic Visual Field");
  709. descriptionOfTerms.insert("OPM", "Ophthalmic Mapping");
  710. descriptionOfTerms.insert("KO", "Key Object Selection");
  711. descriptionOfTerms.insert("SEG", "Segmentation");
  712. descriptionOfTerms.insert("REG", "Registration");
  713. descriptionOfTerms.insert("OPT", "Ophthalmic Tomography");
  714. descriptionOfTerms.insert("BDUS", "Bone Densitometry (ultrasound)");
  715. descriptionOfTerms.insert("BMD", "Bone Densitometry (X-Ray)");
  716. descriptionOfTerms.insert("DOC", "Document");
  717. // retired terms (but probably still in use)
  718. descriptionOfTerms.insert("DS", "Digital Subtraction Angiography");
  719. descriptionOfTerms.insert("CF", "Cinefluorography");
  720. descriptionOfTerms.insert("DF", "Digital fluoroscopy");
  721. descriptionOfTerms.insert("VF", "Videofluorography");
  722. descriptionOfTerms.insert("AS", "Angioscopy");
  723. descriptionOfTerms.insert("CS", "Cystoscopy");
  724. descriptionOfTerms.insert("EC", "Echocardiography");
  725. descriptionOfTerms.insert("LP", "Laparoscopy");
  726. descriptionOfTerms.insert("FA", "Fluorescein angiography ");
  727. descriptionOfTerms.insert("CP", "Culposcopy");
  728. descriptionOfTerms.insert("DM", "Digital microscopy");
  729. descriptionOfTerms.insert("FS", "Fundoscopy");
  730. descriptionOfTerms.insert("MA", "Magnetic resonance angiography");
  731. descriptionOfTerms.insert("MS", "Magnetic resonance spectroscopy");
  732. initialized = true;
  733. }
  734. if ( descriptionOfTerms.contains( dt.toUpper() ) )
  735. {
  736. return descriptionOfTerms.value(dt.toUpper());
  737. }
  738. else
  739. {
  740. std::cerr << "Invalid enum for patient position" << std::endl;
  741. return QString::null;
  742. }
  743. }
  744. QString ctkDICOMDataset::TagKey( const DcmTag& tag )
  745. {
  746. return QString("(%1,%2)").arg( tag.getGroup(), 4, 16, QLatin1Char('0')).arg( tag.getElement(), 4, 16, QLatin1Char('0') );
  747. }
  748. QString ctkDICOMDataset::TagDescription( const DcmTag& tag )
  749. {
  750. if (!ctkDICOMDatasetPrivate::s_Dictionary->isDictionaryLoaded()) return QString("<no DICOM dictionary loaded. application broken>");
  751. const DcmDictEntry* entry = ctkDICOMDatasetPrivate::s_Dictionary->findEntry(tag, NULL);
  752. if (entry)
  753. {
  754. return QString(entry->getTagName());
  755. }
  756. else
  757. {
  758. return QString("<unknown>");
  759. }
  760. }
  761. QString ctkDICOMDataset::TagVR( const DcmTag& tag )
  762. {
  763. if (!ctkDICOMDatasetPrivate::s_Dictionary->isDictionaryLoaded()) return QString("<no DICOM dictionary loaded. application broken>");
  764. const DcmDictEntry* entry = ctkDICOMDatasetPrivate::s_Dictionary->findEntry(tag, NULL);
  765. if (entry)
  766. {
  767. return QString(entry->getVR().getVRName());
  768. }
  769. else
  770. {
  771. return QString("UN"); // unknown
  772. }
  773. }
  774. QString ctkDICOMDataset::GetStoredSerialization()
  775. {
  776. throw std::runtime_error("No serialization implemented for this object!");
  777. }
  778. void ctkDICOMDataset::SetStoredSerialization(QString serializedDataset)
  779. {
  780. throw std::runtime_error("No serialization implemented for this object!");
  781. }