ctkDICOMDataset.cpp 29 KB

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