ctkDICOMDataset.cpp 30 KB

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