3
* $Id: dicomimg2dcm.cpp 3698 2011-04-14 12:38:49Z carlos $
6
* Copyright 2008-10 MetaEmotion S.L. All rights reserved.
7
* http://ginkgo-cadx.com
9
* This file is licensed under LGPL v3 license.
10
* See License.txt for details
14
//#define _GINKGO_TRACE
15
#include <api/globals.h>
16
#include <main/controllers/controladorlog.h>
17
#include <api/icontextoestudio.h>
19
#define MACRO_QUE_ESTORBA verify
25
#include <dcmtk/config/osconfig.h>
26
#include "libi2d/i2d.h"
27
#include "libi2d/i2djpgs.h"
28
#include "libi2d/i2dles.h"
29
#include "libi2d/i2dplsc.h"
30
#include "libi2d/i2dplvlp.h"
31
#include "libi2d/i2dplnsc.h"
32
#include "dicomimg2dcm.h"
33
#include "dcmtk/dcmdata/dcdict.h"
34
#include "dcmtk/dcmdata/dchashdi.h"
36
#include <dcmtk/dcmsr/dsrdoc.h>
37
#include <dcmtk/dcmdata/dcfilefo.h>
39
#include <main/controllers/controladorpermisos.h>
40
#include <api/internacionalizacion.h>
43
//en este grupo se almacenaran los atributos privados de ginkgo
44
#define GINKGO_GROUP 0x0011
53
DcmElement* DICOMImg2DCM::CrearElementoConValor(const char* s)
55
unsigned int g = 0xffff;
56
unsigned int e = 0xffff;
58
OFString dicName, valStr;
62
size_t pos = str.find('=');
63
if (pos != OFString_npos) {
64
valStr = str.substr(pos + 1, str.length());
65
dicName = str.substr(0, pos);
70
pos = dicName.find("|");
71
if (pos != OFString_npos) {
72
if (2 != sscanf(dicName.c_str(),"%x|%x", &g, &e))
74
LOG_ERROR("Dicomizador", "Error al interpretar el tag " << s);
79
DcmTagKey key(0xffff, 0xffff);
80
const DcmDataDictionary& globalDataDict = dcmDataDict.rdlock();
81
const DcmDictEntry *dicent = globalDataDict.findEntry(dicName.c_str());
84
// found dictionary name, copy group and element number
85
key = dicent->getKey();
89
// not found in dictionary
90
std::cerr << "bad key format or dictionary name not found in dictionary: " << dicName << std::endl;
91
LOG_ERROR("Dicomizador", "Error al obtener la entrada del diccionario para el tag " << dicName.c_str() << ". Cadena: " << s);
97
if (tag.error() != EC_Normal) {
98
std::cerr << "Tag desconocido: (" <<
99
std::hex << std::setw(4) << std::setfill('0') << g << "|" <<
100
std::hex << std::setw(4) << std::setfill('0') << e << ")" << std::endl;
103
DcmElement *elem = newDicomElement(tag);
105
std::cerr << "No se pudo crear el elemento para el tag: (" <<
106
std::hex << std::setw(4) << std::setfill('0') << g << "|" <<
107
std::hex << std::setw(4) << std::setfill('0') << e << ")" << std::endl;
110
if (valStr.length() > 0) {
111
if (elem->putString(valStr.c_str()).bad()) {
112
std::cerr << "No se pudo asignar el valor al elemento: (" <<
113
std::hex << std::setw(4) << std::setfill('0') << g << "|" <<
114
std::hex << std::setw(4) << std::setfill('0') << e << ")=" << valStr.c_str() << std::endl;
126
void DICOMImg2DCM::Inicializar(std::string& inputFile, IInspectCallBack* pICallback, bool debug, TDICOMOutputFormat outputFormat)
130
if (inputFile.length() == 0) {
131
throw I2DException("No se ha especificado el fichero de entrada");
134
GNC::GCS::Permisos::EstadoPermiso mantenerCodificacion = GNC::GCS::ControladorPermisos::Instance()->Get("core.importacion", "mantener");
136
if (mantenerCodificacion.Activo()) {
137
//probamos a ver si nos vale cn un JPEG source
138
I2DJpegSource *jpgSource = new I2DJpegSource();
139
if (jpgSource == NULL) {
140
throw I2DException("No se pudo reservar memoria para realizar la conversion");
143
// Enable/Disable support for progressive JPEG
144
jpgSource->setProgrSupport(OFTrue);
145
// Enable/Disable support for extended sequential JPEG
146
jpgSource->setExtSeqSupport(OFTrue);
147
jpgSource->setImageFile(inputFile.c_str());
148
Inicializar(jpgSource, pICallback, debug, outputFormat);
150
wxString nombreImagen = FROMPATH(inputFile);
151
wxImage img(nombreImagen);
154
throw I2DException(_Std("Image format not supported"));
158
throw I2DException(_Std("Image format not supported"));
161
unsigned char* imgData = img.GetData();
162
Inicializar(imgData, img.GetWidth(), img.GetHeight(), pICallback, debug, outputFormat);
166
void DICOMImg2DCM::Inicializar(unsigned char* pixelData, const int ancho, const int alto, IInspectCallBack* pICallback, bool debug, TDICOMOutputFormat outputFormat)
168
I2DLittleEndianSource* littleEndianSource = new I2DLittleEndianSource();
169
littleEndianSource->setImageData(pixelData, ancho, alto, 3);
170
Inicializar(littleEndianSource, pICallback, debug, outputFormat);
173
void DICOMImg2DCM::Inicializar(I2DImgSource* plugEntrada, IInspectCallBack* pICallback, bool debug, TDICOMOutputFormat outputFormat)
177
inputPlug = plugEntrada;
179
i2d = new Image2Dcm();
181
throw I2DException("No se pudo reservar memoria para realizar la conversion");
184
OFBool dMode = OFFalse;
185
OFBool vMode = OFFalse;
190
i2d->setDebugMode(OFTrue);
191
i2d->setLogStream(&ofConsole);
195
std::cout << inputPlug->inputFormat() << std::endl;
197
if (outputFormat == TDOF_VLP) {
198
outputPlug = new I2DOutputPlugVLP();
199
} else if (outputFormat == TDOF_SC) {
200
outputPlug = new I2DOutputPlugSC();
201
} else if (outputFormat == TDOF_NSC) {
202
outputPlug = new I2DOutputPlugNewSC();
204
throw I2DException("No existe ningun plugin para el formato de salida especificado");
209
std::cout << outputPlug->ident() << std::endl;
211
outputPlug->setDebugMode(dMode);
212
outputPlug->setLogStream(&ofConsole);
214
ActualizarCampos(pICallback);
216
// ISO Latin 1 option
217
// setISOLatin1(OFTrue) => set latin-1 as standard character set
218
// setISOLatin1(OFFalse) => keep 7-bit ASCII as standard character set
219
// i2d->setISOLatin1(OFTrue);
220
i2d->setUTF8(OFTrue);
222
// attribute validity checking
224
OFBool doChecks = OFTrue;
226
// insert missing type 2 attributes (SĆ³lo si doChecks es True)
228
OFBool insertType2 = OFTrue;
230
// invent missing type 1 attributes (SĆ³lo si doChecks es True)
232
OFBool inventType1 = OFTrue;
234
i2d->setValidityChecking(doChecks, insertType2, inventType1);
235
outputPlug->setValidityChecking(doChecks, insertType2, inventType1);
237
inputPlug->setDebugMode(dMode);
238
inputPlug->setLogStream(&ofConsole);
240
// make sure data dictionary is loaded
241
if (!dcmDataDict.isDictionaryLoaded()) {
243
throw I2DException("No se ha cargado el diccionario de datos");
248
cond = i2d->convert(inputPlug, outputPlug, resultObject, writeXfer);
251
//si es un jpegsource se trata de leer la imagen y convertirla desde little endian... (por aqui pasan los png, bmp,... cuando esta activo el mantener)
252
I2DJpegSource* pJPeg = dynamic_cast<I2DJpegSource*>(inputPlug);
254
OFString path = pJPeg->getImageFile();
256
wxString nombreImagen = FROMPATH(path);
257
wxImage img(nombreImagen);
260
throw I2DException(_Std("Image format not supported"));
264
throw I2DException(_Std("Image format not supported"));
267
unsigned char* imgData = img.GetData();
268
Inicializar(imgData, img.GetWidth(), img.GetHeight(), pICallback, debug, outputFormat);
271
throw I2DException(cond.text());
275
if (pICallback != NULL) {
277
// Dumping DICOM Tags...
278
DcmDataDictionary& globalDataDict = dcmDataDict.wrlock();
279
DcmHashDictIterator iter(globalDataDict.normalBegin());
280
DcmHashDictIterator end(globalDataDict.normalEnd());
288
for (; iter != end; ++iter) {
290
DcmTagKey tagkey = (*iter)->getKey();
291
//std::cout << "Checking " << keyStr << std::endl;
292
if (sprintf(keyFormat, "%04X|%04X", tagkey.getGroup(), tagkey.getElement()) > 8) {
293
//std::cout << "Asigning " << keyFormat << std::endl;
294
keyStr.assign(keyFormat);
295
descStr = (*iter)->getTagName();
297
OFCondition c = resultObject->findAndGetOFStringArray(tagkey, val);
299
valStr = val.c_str();
300
pICallback->Inspect(keyStr, descStr, valStr);
304
dcmDataDict.unlock();
309
if (pICallback != NULL) {
314
int DICOMImg2DCM::InsertarTagsPrivados(TipoPrivateTags& tags){
315
return InsertarTagsPrivados(tags,i2d->getOverrideKeys());
318
int DICOMImg2DCM::InsertarTagsPrivados(TipoPrivateTags& tags, DcmDataset* dcmDataSet){
319
//se busca el uid en el rango (GINKGO_GROUP,0010-00FF)
320
unsigned int g=GINKGO_GROUP;
325
e=GetElementIdentifier(tags,dcmDataSet);
327
if(e>0x00FF || e==0){
328
//no deberia llegar aqui
329
std::cerr<<"error al almacenar los tags privados, todos los slots ocupados" <<std::endl;
333
//ya tenemos el e del modulo hay que hacer un desplazamiento de 8 bits a la izquierda para obtener el rango
334
//si el elemento es 00xx el rango sera xx00-xxFF
338
TipoPrivateTags::ListaTags& ListaTags = tags.GetListaTags();
339
for (TipoPrivateTags::ListaTags::iterator it = ListaTags.begin(); it != ListaTags.end(); ++it)
341
eTemp = e | (*it).first;
342
element = (*it).second->ToElement(g,eTemp);
345
cond = dcmDataSet->insert(element, true, false);
347
std::cerr << "error al almacenar los tags privados, error al escribir en el dataset: (" << g << ","<<eTemp<<")" <<std::endl;
351
std::cerr << "error al almacenar los tags privados, error al crear el elemento: (" << g << ","<<eTemp<<")" <<std::endl;
359
int DICOMImg2DCM::InsertarJerarquia(TipoJerarquia& base) {
361
throw I2DException("El conversor no se ha inicializado previamente");
364
return InsertarJerarquia(base,i2d->getOverrideKeys(), NULL, NULL);
367
int DICOMImg2DCM::InsertarJerarquia(TipoJerarquia& base,DcmDataset* dcmDataSet, DcmItem* itemPadre, DcmSequenceOfItems* seqPadre)
370
int numTotalInsertados = 0;
371
int numTagsInsertados = 0;
372
int numItemsInsertados = 0;
373
int numSeqsInsertadas = 0;
377
// Insertamos todos los tags correspondientes a este nivel en la raiz (de haberla, si no: en el dataset).
378
for (TipoJerarquia::ListaTags::iterator it = base.tags.begin(); it != base.tags.end(); it++) {
379
DcmElement* e = this->CrearElementoConValor((*it).first.c_str());
381
e->putString((*it).second.c_str());
384
if (itemPadre == NULL) {
385
cond = dcmDataSet->insert(e, OFTrue);
389
std::cout << "raiz << " << e->getTag().toString() << "=" << str << std::endl;
392
std::cout << "raiz << " << e->getTag().toString() << "=" << std::endl;
396
cond = itemPadre->insert(e, OFTrue);
400
std::cout << itemPadre->getTag().toString().c_str() << " << " << e->getTag().toString() << " = " << str << std::endl;
403
std::cout << itemPadre->getTag().toString().c_str() << " << " << e->getTag().toString() << " = " << std::endl;
409
std::cerr << "No se pudo insertar el elemento: (" << e->getTag().toString().c_str() << "): " << cond.text() << std::endl;
412
numTotalInsertados++;
418
// Insertamos todos los items correspondientes a este nivel en la raiz (de haberla, si no: en el dataset).
420
for (TipoJerarquia::ListaJerarquias::iterator it = base.items.begin(); it != base.items.end(); it++) {
421
DcmItem *item = new DcmItem();
423
int nItems = InsertarJerarquia((*it),dcmDataSet, item, NULL);
429
if (seqPadre == NULL) {
430
//cond = dcmDataSet->insert(item, OFTrue);
431
std::cerr << "No se pudo insertar el item directamente a la raiz. Deben insertarse en secuencias o en otros items. " << nItems << " elementos perdidos: " << cond.text() << std::endl;
435
cond = seqPadre->insert(item, OFTrue);
436
// std::cout << seqPadre->getTag().toString().c_str() << " << " << item->getTag().toString() << std::endl;
440
std::cerr << "No se pudo insertar el item a la raiz. " << nItems << " elementos perdidos: " << cond.text() << std::endl;
444
numTotalInsertados += nItems + 1;
445
numItemsInsertados++;
453
// Insertamos todas las secuencias correspondientes a este nivel en la raiz (de haberla, si no: en el dataset).
454
for (TipoJerarquia::ListaJerarquias::iterator it = base.secuencias.begin(); it != base.secuencias.end(); it++) {
456
std::string claveSecuencia = (*it).tagName;
457
TipoJerarquia& nbase = (*it);
459
unsigned int sg = 0xffff;
460
unsigned int se = 0xffff;
463
sn = sscanf(claveSecuencia.c_str(), "%x|%x", &sg, &se);
465
std::cerr << "Formato invalido (" << claveSecuencia.c_str() << "). Solo se soporta (FFFF|FFFF) como formato de tag para secuencias" << std::endl;
469
if (stag.error() != EC_Normal) {
470
std::cerr << "Tag desconocido: " << claveSecuencia << std::endl;
473
DcmSequenceOfItems* seq = new DcmSequenceOfItems(stag);
475
std::cerr << "No se pudo crear la secuencia para el tag: " << claveSecuencia << std::endl;
479
int nItems = InsertarJerarquia(nbase,dcmDataSet, NULL, seq);
485
if (seqPadre != NULL) {
486
DcmItem* item = new DcmItem();
487
cond = item->insert(seq);
488
seqPadre->insert(item);
489
// std::cout << seqPadre->getTag().toString().c_str() << " << " << seq->getTag().toString() << std::endl;
491
else if (itemPadre != NULL) {
492
cond = itemPadre->insert(seq, OFTrue);
493
// std::cout << itemPadre->getTag().toString().c_str() << " << " << seq->getTag().toString() << std::endl;
497
cond = dcmDataSet->insert(seq, OFTrue);
498
// std::cout << "raiz << " << seq->getTag().toString() << std::endl;
502
std::cerr << "No se pudo insertar el item a la raiz. " << nItems << " elementos perdidos: " << cond.text() << std::endl;
506
numTotalInsertados += nItems + 1;
514
return numTotalInsertados;
517
void DICOMImg2DCM::ActualizarCampos(IInspectCallBack* pICallback) {
519
throw I2DException("El conversor no se ha inicializado previamente");
521
if (pICallback != NULL) {
522
//DcmDataset *overrideKeys = i2d->getOverrideKeys();
524
TipoJerarquia jerarquiaAInsertar;
525
pICallback->ObtenerJerarquiaInserccion(jerarquiaAInsertar);
526
InsertarJerarquia(jerarquiaAInsertar,i2d->getOverrideKeys(), NULL, NULL);
530
bool DICOMImg2DCM::Convertir(std::string& outputFile)
533
if (i2d == NULL || inputPlug == NULL || outputPlug == NULL || resultObject == NULL) {
534
throw I2DException("El conversor no se ha inicializado previamente");
536
if (outputFile.length() == 0) {
537
throw I2DException("No se ha especificado el fichero de salida");
540
// Group length encoding mode for output DICOM file
541
E_GrpLenEncoding grpLengthEnc = EGL_recalcGL;
542
// Item and Sequence encoding mode for output DICOM file
543
E_EncodingType lengthEnc = EET_ExplicitLength;
544
// Padding mode for output DICOM file
545
E_PaddingEncoding padEnc = EPD_noChange;
546
// File pad length for output DICOM file
547
unsigned int filepad = 0;
548
// Item pad length for output DICOM file
549
unsigned int itempad = 0;
551
// Group Length Encoding:
552
// EGL_recalcGL => recalculate group lengths if present
553
// EGL_withGL => always write with group length elements
554
// EGL_withoutGL => always write without group length elements
556
grpLengthEnc = EGL_recalcGL;
558
// Length Encoding in Sequences and Items:
559
// EET_ExplicitLength => write with explicit lengths
560
// EET_UndefinedLength => write with undefined lengths
561
lengthEnc = EET_ExplicitLength;
568
i2d->updateOverrideKeys(resultObject);
570
DcmFileFormat dcmff(resultObject);
571
cond = dcmff.saveFile(outputFile.c_str(), writeXfer, lengthEnc, grpLengthEnc, padEnc, filepad, itempad);
573
throw I2DException(cond.text());
578
std::cout << "----DUMPING DCM----------------------------------------" << std::endl;
579
// Dumping DICOM Tags...
580
const DcmDataDictionary& globalDataDict = dcmDataDict.rdlock();
581
DcmHashDictIterator iter(globalDataDict.normalBegin());
582
DcmHashDictIterator end(globalDataDict.normalEnd());
590
for (; iter != end; ++iter) {
592
DcmTagKey tagkey = (*iter)->getKey();
593
//std::cout << "Checking " << keyStr << std::endl;
594
if (sprintf(keyFormat, "%04X|%04X", tagkey.getGroup(), tagkey.getElement()) > 8) {
595
//std::cout << "Asigning " << keyFormat << std::endl;
596
keyStr.assign(keyFormat);
597
descStr = (*iter)->getTagName();
599
OFCondition c = resultObject->findAndGetOFStringArray(tagkey, val);
601
valStr = val.c_str();
602
std::cout << "[" << keyStr.c_str() << "] " << valStr.c_str() << " // " << descStr.c_str() << std::endl;
606
dcmDataDict.unlock();
607
std::cout << "-------------------------------------------------------" << std::endl;
613
//devuelve el element identifier donde el modulo puede escribir sus tags privados
614
unsigned int DICOMImg2DCM::GetElementIdentifier(GIL::DICOM::TipoPrivateTags& tags, DcmDataset* dataset){
617
unsigned int g=GINKGO_GROUP;
618
unsigned int e=0x0010;
622
cond = dataset->findAndGetElement(key,element,false);
625
//se introduce el uid
628
//tipo short string!!!!
631
if (tag.error() != EC_Normal) {
632
std::cerr << "error al almacenar los tags privados, tag desconocido: (" << g << ","<<e<<")" <<std::endl;
636
element = newDicomElement(tag);
639
std::cerr << "error al almacenar los tags privados, error al crear el elemento uid: (" << g << ","<<e<<")" <<std::endl;
643
cond=element->putString(tags.UIDModulo.c_str());
646
std::cerr << "error al almacenar los tags privados, error al escribir el uid: (" << g << ","<<e<<")" <<std::endl;
650
cond = dataset->insert(element, true, false);
658
cond = element->getString(cadena);
660
ov = std::string(cadena);
665
if(ov==tags.UIDModulo){
666
//estamos en el g y e del modulo deseado
675
bool DICOMImg2DCM::CrearSRDoc(std::string& outputFile, TipoJerarquia& base, std::list<GnkPtr<TipoPrivateTags> >& tagsPrivados) {
676
DSRDocument *doc = new DSRDocument();
680
doc->createNewDocument(DSRTypes::DT_BasicTextSR);
681
doc->setSpecificCharacterSetType(DSRTypes::CS_UTF8);
682
doc->setSpecificCharacterSet("ISO_IR 192");
684
doc->setManufacturer("Ginkgo");
685
doc->setPatientName("Last Name^First Name");
686
doc->setPatientSex("O");
687
doc->setReferringPhysicianName("Last Name^First Name");
689
doc->getTree().addContentItem(DSRTypes::RT_isRoot, DSRTypes::VT_Container);
691
DcmFileFormat *fileformat = new DcmFileFormat();
693
DcmDataset *dataset = NULL;
697
if (fileformat != NULL)
698
dataset = fileformat->getDataset();
701
cond = doc->write(*dataset);
704
if( dataset->putAndInsertOFStringArray(DCM_SpecificCharacterSet, "ISO_IR 192").bad()) {
705
LOG_ERROR("Dicomizacion", "Error al establecer la codificacion en el fichero DICOM");
707
InsertarJerarquia(base,dataset,NULL,NULL);
708
for(std::list<GnkPtr<TipoPrivateTags> >::iterator it = tagsPrivados.begin(); it!= tagsPrivados.end(); it++){
709
InsertarTagsPrivados(*(*it),dataset);
711
//SE ESCRIBE LOS TAGS DEL SR...
713
cond = fileformat->saveFile(outputFile.c_str(), EXS_LittleEndianExplicit);