From: malaterre Date: Tue, 11 Sep 2007 12:54:49 +0000 (+0000) Subject: ENH: add another version of Extract CSA Tag X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=commitdiff_plain;h=7c3ae7deb1ccc2107e93bd026b9477d23923b0c2;p=gdcm.git ENH: add another version of Extract CSA Tag --- diff --git a/Example/exExtractCSATag.cxx b/Example/exExtractCSATag.cxx new file mode 100644 index 00000000..9cca0580 --- /dev/null +++ b/Example/exExtractCSATag.cxx @@ -0,0 +1,493 @@ +/*========================================================================= + + Program: gdcm + Module: $RCSfile: exExtractCSATag.cxx,v $ + Language: C++ + Date: $Date: 2007/09/11 12:54:49 $ + Version: $Revision: 1.1 $ + + Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de + l'Image). All rights reserved. See Doc/License.txt or + http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#include "gdcmFile.h" +#include "gdcmFileHelper.h" +#include "gdcmCommon.h" +#include "gdcmDebug.h" +#include "gdcmDocEntry.h" +#include "gdcmDataEntry.h" +#include "gdcmSeqEntry.h" +#include "gdcmSQItem.h" + +#include + +namespace gdcm +{ + + static const char *lookupTable1[] = { + "UsedPatientWeight", + "NumberOfPrescans", + "TransmitterCalibration", + "PhaseGradientAmplitude", + "ReadoutGradientAmplitude", + "SelectionGradientAmplitude", + "GradientDelayTime", + "RfWatchdogMask", + "RfPowerErrorIndicator", + "SarWholeBody", + "Sed", + "SequenceFileOwner", + "Stim_mon_mode", + "Operation_mode_flag", + "dBdt_max", + "t_puls_max", + "dBdt_thresh", + "dBdt_limit", + "SW_korr_faktor", + "Stim_max_online", + "Stim_max_ges_norm_online", + "Stim_lim", + "Stim_faktor", + "CoilForGradient", + "CoilTuningReflection", + "CoilId", + "MiscSequenceParam", + "MrProtocolVersion", + "MrProtocol", + "DataFileName", + "RepresentativeImage", + "PositivePCSDirections", + "RelTablePosition", + "ReadoutOS", + "LongModelName", + "SliceArrayConcatenations", + "SliceResolution", + "MrEvaProtocol", + "AbsTablePosition", + "AutoAlignMatrix", + "MeasurementIndex", + "CoilString", + "PATModeText", + "PatReinPattern", + NULL + }; + + static const char *lookupTable2[] = { + "EchoLinePosition", + "EchoColumnPosition", + "EchoPartitionPosition", + "UsedChannelMask", + "Actual3DImaPartNumber", + "ICE_Dims", + "B_value", + "Filter1", + "Filter2", + "ProtocolSliceNumber", + "RealDwellTime", + "PixelFile", + "PixelFileName", + "SliceMeasurementDuration", + "SequenceMask", + "AcquisitionMatrixText", + "MeasuredFourierLines", + "FlowEncodingDirection", + "FlowVenc", + "PhaseEncodingDirectionPositive", + "NumberOfImagesInMosaic", + "DiffusionGradientDirection", + "ImageGroup", + "SliceNormalVector", + "DiffusionDirectionality", + "TimeAfterStart", + "FlipAngle", + "SequenceName", + "RepetitionTime", + "EchoTime", + "NumberOfAverages", + "VoxelThickness", + "VoxelPhaseFOV", + "VoxelReadoutFOV", + "VoxelPositionSag", + "VoxelPositionCor", + "VoxelPositionTra", + "VoxelNormalSag", + "VoxelNormalCor", + "VoxelNormalTra", + "VoxelInPlaneRot", + "ImagePositionPatient", + "ImageOrientationPatient", + "PixelSpacing", + "SliceLocation", + "SliceThickness", + "SpectrumTextRegionLabel", + "Comp_Algorithm", + "Comp_Blended", + "Comp_ManualAdjusted", + "Comp_AutoParam", + "Comp_AdjustedParam", + "Comp_JobID", + "FMRIStimulInfo", + "FlowEncodingDirectionString", + "RepetitionTimeEffective", + NULL + }; + + static const char *lookupTable3[] = { + "ImageNumber", + "ImageComments", + "ReferencedImageSequence", + "PatientOrientation", + "ScanningSequence", + "SequenceName", + "RepetitionTime", + "EchoTime", + "InversionTime", + "NumberOfAverages", + "ImagingFrequency", + "ImagedNucleus", + "EchoNumbers", + "MagneticFieldStrength", + "NumberOfPhaseEncodingSteps", + "EchoTrainLength", + "PercentSampling", + "PercentPhaseFieldOfView", + "TriggerTime", + "ReceivingCoil", + "TransmittingCoil", + "AcqusitionMatrix", + "PhaseEncodingDirection", + "FlipAngle", + "VariableFlipAngleFlag", + "SAR", + "dBdt", + "Rows", + "Columns", + "SliceThickness", + "ImagePositionPatient", + "ImageOrientationPatient", + "SliceLocation", + "EchoLinePosition", + "EchoColumnPosition", + "EchoPartitionPosition", + "Actual3DImaPartNumber", + "RealDwellTime", + "ProtocolSliceNumber", + "DataFile", + "DataFileName", + "ICE_Dims", + "PixelSpacing", + "SourceImageSequence", + "PixelBandwidth", + "SliceMeasurementDuration", + "SequenceMask", + "AcquisitionMatrixText", + "MeasuredFourierLines", + "CsiGridshiftVector", + NULL + }; + + /* + * What if SIEMENS decide to add another entry in this table, all the offsets are completely off ... + * Also we do not respect the ordering anymore. + * TODO: Need to check if elements always comes in the same order + */ + unsigned int GetLookupCSAIndex(const char *csa_name, const char **lookuptable = lookupTable1) + { + const char **p = lookuptable; + while( *p && strcmp(*p, csa_name) != 0 ) + { + ++p; + } + assert( strcmp(*p, csa_name) == 0 ); + return p - lookuptable + 1; // Start at 1 to avoid being on the 0000 position + } + + // Looks like there is mapping in between syngodt and vr... + // O <=> UN + // 3 <=> DS + // 4 <=> FD + // 5 <=> FL + // 6 <=> IS + // 9 <=> UL + // 10 <=> US + // 16 <=> CS + // 19 <=> LO + // 20 <=> LT + // 22 <=> SH + // 25 <=> UI + +struct equ +{ + uint32_t syngodt; + char vr[2+1]; +}; + +static equ mapping[] = { + { 0 , "UN" }, + { 3 , "DS" }, + { 4 , "FD" }, + { 5 , "FL" }, + { 6 , "IS" }, + { 7 , "SL" }, + { 8 , "SS" }, + { 9 , "UL" }, + { 10 , "US" }, + { 16 , "CS" }, + { 19 , "LO" }, + { 20 , "LT" }, + { 22 , "SH" }, + { 23 , "ST" }, + { 25 , "UI" }, + { 27 , "UT" }, +}; + +bool check_mapping(uint32_t syngodt, const char *vr) +{ + static const unsigned int max = sizeof(mapping) / sizeof(equ); + unsigned int s = 0; + const equ *p = mapping; + assert( syngodt <= mapping[max-1].syngodt ); + while(p->syngodt < syngodt ) + { + ++p; + } + assert( p->syngodt == syngodt ); // or else need to update mapping + const char* lvr = p->vr; + int check = strcmp(vr, lvr) == 0; + assert( check ); + return true; +} + +uint32_t readCSAHeader(std::istream &is) +{ + char dummy[4+1]; + dummy[4] = 0; + is.read(dummy, 4); + std::cout << dummy << std::endl; + if( strcmp( dummy, "SV10" ) ) + { + std::cerr << "Either not a SV10 header or filled with 0..." << std::endl; + return 1; + } + // wotsit ? + is.read(dummy, 4); + if( strcmp( dummy, "\4\3\2\1" ) ) + { + std::cerr << "Either not a SV10 header or filled with 0..." << std::endl; + return 1; + } + std::cout << dummy << std::endl; + uint32_t n; + is.read((char*)&n, sizeof(n)); + std::cout << n << std::endl; + uint32_t unused; + is.read((char*)&unused, sizeof(unused)); + std::cout << unused << std::endl; + assert( unused == 77 ); // 'M' character... + + return n; +} + +DataEntry *readCSAElement(std::istream &is) +{ + char name[64+1]; + name[64] = 0; // security + //std::cout << "Pos 0x" << std::hex << is.tellg() << std::dec << std::endl; + is.read(name, 64); + std::cout << "Name=" << name << std::endl; + unsigned int element = GetLookupCSAIndex(name,lookupTable2); + uint32_t vm; + is.read((char*)&vm, sizeof(vm)); + std::cout << "vm=" << vm << std::endl; + char vr[4]; + is.read(vr, 4); + assert( vr[2] == vr[3] && vr[2] == 0 ); + std::cout << "vr=" << vr << std::endl; + + DataEntry *de = DataEntry::New(0x0029, element, vr); + + uint32_t syngodt; + is.read((char*)&syngodt, sizeof(syngodt)); + check_mapping(syngodt, vr); + + std::cout << "syngodt=" << syngodt << std::endl; + uint32_t nitems; + is.read((char*)&nitems, sizeof(nitems)); + std::cout << "nitems=" << nitems<< std::endl; + uint32_t xx; + is.read((char*)&xx, sizeof(xx)); + //std::cout << "xx=" << xx<< std::endl; + assert( xx == 77 || xx == 205 ); + for( uint32_t j = 0; j < nitems; ++j) + { + uint32_t item_xx[4]; + is.read((char*)&item_xx, 4*sizeof(uint32_t)); + std::cout << "item_xx=" << item_xx[0] << " " << item_xx[1] << " " << item_xx[2] << " " << item_xx[3] << std::endl; + //std::cout << "0x" << std::hex << is.tellg() << std::dec << std::endl; + assert( item_xx[2] == 77 || item_xx[2] == 205 ); + uint32_t len = item_xx[1]; // 2nd element + std::cout << "len=" << len << std::endl; + assert( item_xx[0] == item_xx[1] && item_xx[1] == item_xx[3] ); + char *val = new char[len+1]; + val[len] = 0; // security + is.read(val,len); + // WARNING vr does not means anything AFAIK, simply print the value as if it was IS/DS or LO (ASCII) + std::cout << "val=" << val << std::endl; + if( !j ) + de->SetString( std::string(val,len) ); + delete[] val; + char dummy[4]; + uint32_t dummy_len = (4 - len % 4) % 4; + is.read(dummy, dummy_len ); + for(uint32_t d= 0; d < dummy_len; ++d) + { + // I think dummy should always be 0 + if( dummy[d] ) + { + std::cout << "dummy=" << (int)dummy[d] << std::endl; + } + } + } + + + return de; +} + +int convertCSA(std::istream &is, File *f) +{ + f->RemoveEntry( f->GetDataEntry(0X0029,0x1010) ); + + SeqEntry *sq = SeqEntry::New(0x0029,0x1010); + SQItem *sqi = SQItem::New(1); +// DataEntry *e_0008_1150 = DataEntry::New(0x0008, 0x1150, "UI"); +// e_0008_1150->SetString( "coucou" ); +// sqi->AddEntry(e_0008_1150); +// e_0008_1150->Delete(); +// +// DataEntry *e_0008_1155 = DataEntry::New(0x0008, 0x1155, "UI"); +// e_0008_1155->SetString( "mathieu" ); +// sqi->AddEntry(e_0008_1155); +// e_0008_1155->Delete(); + + + + uint32_t n = readCSAHeader(is); + + for(uint32_t i = 0; i < n; ++i) + { + DataEntry *de = readCSAElement(is); + sqi->AddEntry(de); + de->Delete(); + } + + sq->AddSQItem(sqi,1); + sqi->Delete(); + //sq->Print( std::cout ); + f->AddEntry( sq ); + sq->Delete(); + f->Print( std::cout ); + + gdcm::FileHelper *fh = gdcm::FileHelper::New(f); + fh->SetWriteTypeToDcmExplVR(); + fh->Write("/tmp/csa2.dcm"); + + return 0; +} +} // end namespace gdcm + +int main(int argc, char *argv[]) +{ + gdcm::File *f; + + if( argc < 5 ) + { + std::cerr << "Usage :" << argv[0] << " input.dcm group element outputfile" << std::endl; + std::cerr << " Ex: " << argv[0] << " /tmp/bla.dcm 0029 2110 /tmp/out.raw" << std::endl; + return 1; + } + std::string fileName = argv[1]; + + std::cout << fileName << std::endl; +// ============================================================ +// Read the input image. +// ============================================================ + + f = gdcm::File::New( ); + + //f->SetLoadMode(gdcm::LD_NOSEQ | gdcm::LD_NOSHADOW); + f->SetFileName( fileName ); + f->SetMaxSizeLoadEntry(0xffff); + bool res = f->Load(); + + if( gdcm::Debug::GetDebugFlag()) + { + std::cout << "---------------------------------------------" << std::endl; + f->Print(); + std::cout << "---------------------------------------------" << std::endl; + } + if (!res) { + std::cerr << "Sorry, " << fileName << " not a gdcm-readable " + << "DICOM / ACR File" + << std::endl; + f->Delete(); + return 1; + } + std::cout << " ... is readable " << std::endl; + + // Find the dicom tag, and extract the string + uint16_t group, elem; + std::istringstream convert; + convert.str( argv[2] ); + convert >> std::hex >> group; + convert.clear(); //important + convert.str( argv[3] ); + convert >> std::hex >> elem; + std::cout << "Extracting tag: (0x" << std::hex << std::setw(4) << std::setfill('0') + << group << ",0x" << std::setw(4) << std::setfill('0') << elem << ")" << std::endl; + std::string dicom_tag_value = f->GetEntryString(group, elem); + if (dicom_tag_value == gdcm::GDCM_UNFOUND) + { + gdcm::DictEntry *dictEntry = f->GetPubDict()->GetEntry( group, elem); + std::cerr << "Image doesn't contain any tag: " << dictEntry->GetName() << std::endl; + f->Delete(); + return 1; + } + + gdcm::DocEntry *dicom_tag_doc = f->GetDocEntry(group, elem); + gdcm::DataEntry *dicom_tag = dynamic_cast(dicom_tag_doc); + if( !dicom_tag ) + { + std::cerr << "Sorry DataEntry only please" << std::endl; + f->Delete(); + return 1; + } + + // Write out the data as a file: + std::ofstream o(argv[4]); + if( !o ) + { + std::cerr << "Problem opening file: " << argv[4] << std::endl; + f->Delete(); + return 1; + } + o.write((char*)dicom_tag->GetBinArea(), dicom_tag->GetLength()); + o.close(); + + std::istringstream is; + is.str( std::string( (char*)dicom_tag->GetBinArea(), dicom_tag->GetLength()) ); + + gdcm::convertCSA(is, f); + + f->Delete(); + + + + return 0; +} + +