ctkDICOMDataset.cpp 30 KB

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