1 /*=========================================================================
2
3 Program: GDCM (Grassroots DICOM). A DICOM library
4
5 Copyright (c) 2006-2011 Mathieu Malaterre
6 All rights reserved.
7 See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
8
9 This software is distributed WITHOUT ANY WARRANTY; without even
10 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 PURPOSE. See the above copyright notice for more information.
12
13 =========================================================================*/
14 /*
15 * HISTORY:
16 * In GDCM 1.X the preferred term was 'ReWrite', however one author of GDCM dislike
17 * the term ReWrite since it is associated with the highly associated with the Rewrite
18 * notion in software programming where using reinvent the wheel and rewrite from scratch code
19 * the term convert was preferred
20 *
21 * Tools to conv. Goals being to 'purify' a DICOM file.
22 * For now it will do the minimum:
23 * - If Group Length is present, the length is guarantee to be correct
24 * - If Element with Group Tag 0x1, 0x3, 0x5 or 0x7 are present they are
25 * simply discarded (not written).
26 * - Elements are written in alphabetical order
27 * - 32bits VR have the residue bytes sets to 0x0,0x0
28 * - Same goes from Item Length end delimiter, sets to 0x0,0x0
29 * - All buggy files (wrong length: GE, 13 and Siemens Leonardo) are fixed
30 * - All size are even (no odd length from gdcm 1.x)
31 *
32 * // \todo:
33 * // --preamble: clean preamble
34 * // --meta: clean meta (meta info version...)
35 * // --dicomV3 (use TS unless not supported)
36 * // --recompute group-length
37 * // --undefined sq
38 * // --explicit sq *
39 * \todo in a close future:
40 * - Set appropriate VR from DICOM dict
41 * - Rewrite PMS SQ into DICOM SQ
42 * - Rewrite Implicit SQ with defined length as undefined length
43 * - PixelData with `overlay` in unused bits should be cleanup
44 * - Any broken JPEG file (wrong bits) should be fixed
45 * - DicomObject bug should be fixed
46 * - Meta and Dataset should have a matching UID (more generally File Meta
47 * should be correct (Explicit!) and consistent with DataSet)
48 * - User should be able to specify he wants Group Length (or remove them)
49 * - Media SOP should be correct (deduct from something else or set to
50 * SOP Secondary if all else fail).
51 * - Padding character should be correct
52 *
53 * \todo distant future:
54 * - Later on, it should run through a Validator
55 * which will make sure all field 1, 1C are present and those only
56 * - In a perfect world I should remove private tags and transform them into
57 * public fields.
58 * - DA should be correct, PN should be correct (no space!)
59 * - Enumerated Value should be correct
60 */
61 /*
62 check-meta is ideal for image like:
63
64 gdcmconv -C gdcmData/PICKER-16-MONO2-No_DicomV3_Preamble.dcm bla.dcm
65 */
66 #include "gdcmReader.h"
67 #include "gdcmFileDerivation.h"
68 #include "gdcmAnonymizer.h"
69 #include "gdcmVersion.h"
70 #include "gdcmPixmapReader.h"
71 #include "gdcmPixmapWriter.h"
72 #include "gdcmWriter.h"
73 #include "gdcmSystem.h"
74 #include "gdcmFileMetaInformation.h"
75 #include "gdcmDataSet.h"
76 #include "gdcmIconImageGenerator.h"
77 #include "gdcmAttribute.h"
78 #include "gdcmSequenceOfItems.h"
79 #include "gdcmUIDGenerator.h"
80 #include "gdcmImage.h"
81 #include "gdcmImageChangeTransferSyntax.h"
82 #include "gdcmImageApplyLookupTable.h"
83 #include "gdcmFileDecompressLookupTable.h"
84 #include "gdcmImageFragmentSplitter.h"
85 #include "gdcmImageChangePlanarConfiguration.h"
86 #include "gdcmImageChangePhotometricInterpretation.h"
87 #include "gdcmFileExplicitFilter.h"
88 #include "gdcmJPEG2000Codec.h"
89 #include "gdcmJPEGCodec.h"
90 #include "gdcmJPEGLSCodec.h"
91 #include "gdcmSequenceOfFragments.h"
92
93 #include <string>
94 #include <iostream>
95
96 #include <stdio.h> /* for printf */
97 #include <stdlib.h> /* for exit */
98 #include <getopt.h>
99 #include <string.h>
100
101 struct SetSQToUndefined
102 {
operator ()SetSQToUndefined103 void operator() (gdcm::DataElement &de) {
104 de.SetVLToUndefined();
105 }
106 };
107
PrintVersion()108 static void PrintVersion()
109 {
110 std::cout << "gdcmconv: gdcm " << gdcm::Version::GetVersion() << " ";
111 const char date[] = "$Date$";
112 std::cout << date << std::endl;
113 }
114
PrintLossyWarning()115 static void PrintLossyWarning()
116 {
117 std::cout << "You have selected a lossy compression transfer syntax." << std::endl;
118 std::cout << "This will degrade the quality of your input image, and can." << std::endl;
119 std::cout << "impact professional interpretation of the image." << std::endl;
120 std::cout << "Do not use if you do not understand the risk." << std::endl;
121 std::cout << "WARNING: this mode is very experimental." << std::endl;
122 }
123
PrintHelp()124 static void PrintHelp()
125 {
126 PrintVersion();
127 std::cout << "Usage: gdcmconv [OPTION] input.dcm output.dcm" << std::endl;
128 std::cout << "Convert a DICOM file into another DICOM file.\n";
129 std::cout << "Parameter (required):" << std::endl;
130 std::cout << " -i --input DICOM filename" << std::endl;
131 std::cout << " -o --output DICOM filename" << std::endl;
132 std::cout << "Options:" << std::endl;
133 std::cout << " -X --explicit Change Transfer Syntax to explicit." << std::endl;
134 std::cout << " -M --implicit Change Transfer Syntax to implicit." << std::endl;
135 std::cout << " -U --use-dict Use dict for VR (only public by default)." << std::endl;
136 std::cout << " --with-private-dict Use private dict for VR (advanced user only)." << std::endl;
137 std::cout << " -C --check-meta Check File Meta Information (advanced user only)." << std::endl;
138 std::cout << " --root-uid Root UID." << std::endl;
139 std::cout << " --remove-gl Remove group length (deprecated in DICOM 2008)." << std::endl;
140 std::cout << " --remove-private-tags Remove private tags." << std::endl;
141 std::cout << " --remove-retired Remove retired tags." << std::endl;
142 std::cout << "Image only Options:" << std::endl;
143 std::cout << " -l --apply-lut Apply LUT (non-standard, advanced user only)." << std::endl;
144 std::cout << " -8 --apply-lut8 Apply LUT/RGB8 (non-standard, advanced user only)." << std::endl;
145 std::cout << " --decompress-lut Decompress LUT (linearize segmented LUT)." << std::endl;
146 std::cout << " -P --photometric-interpretation %s Change Photometric Interpretation (when possible)." << std::endl;
147 std::cout << " -w --raw Decompress image." << std::endl;
148 std::cout << " -d --deflated Compress using deflated (gzip)." << std::endl;
149 std::cout << " -J --jpeg Compress image in jpeg." << std::endl;
150 std::cout << " -K --j2k Compress image in j2k." << std::endl;
151 std::cout << " -L --jpegls Compress image in jpeg-ls." << std::endl;
152 std::cout << " -R --rle Compress image in rle (lossless only)." << std::endl;
153 std::cout << " -F --force Force decompression/merging before recompression/splitting." << std::endl;
154 std::cout << " --generate-icon Generate icon." << std::endl;
155 std::cout << " --icon-minmax %d,%d Min/Max value for icon." << std::endl;
156 std::cout << " --icon-auto-minmax Automatically compute best Min/Max values for icon." << std::endl;
157 std::cout << " --compress-icon Decide whether icon follows main TransferSyntax or remains uncompressed." << std::endl;
158 std::cout << " --planar-configuration [01] Change planar configuration." << std::endl;
159 std::cout << " -Y --lossy Use the lossy (if possible) compressor." << std::endl;
160 std::cout << " -S --split %d Write 2D image with multiple fragments (using max size)" << std::endl;
161 std::cout << "General Options:" << std::endl;
162 std::cout << " -V --verbose more verbose (warning+error)." << std::endl;
163 std::cout << " -W --warning print warning info." << std::endl;
164 std::cout << " -D --debug print debug info." << std::endl;
165 std::cout << " -E --error print error info." << std::endl;
166 std::cout << " -h --help print help." << std::endl;
167 std::cout << " -v --version print version." << std::endl;
168 std::cout << " --quiet do not print to stdout." << std::endl;
169 std::cout << "JPEG Options:" << std::endl;
170 std::cout << " -q --quality %*f set quality." << std::endl;
171 std::cout << "JPEG-LS Options:" << std::endl;
172 std::cout << " -e --allowed-error %*i set allowed error." << std::endl;
173 std::cout << "J2K Options:" << std::endl;
174 std::cout << " -r --rate %*f set rate." << std::endl;
175 std::cout << " -q --quality %*f set quality." << std::endl;
176 std::cout << " -t --tile %d,%d set tile size." << std::endl;
177 std::cout << " -n --number-resolution %d set number of resolution." << std::endl;
178 std::cout << " --irreversible set irreversible." << std::endl;
179 std::cout << "Special Options:" << std::endl;
180 std::cout << " -I --ignore-errors convert even if file is corrupted (advanced users only, see disclaimers)." << std::endl;
181 std::cout << "Env var:" << std::endl;
182 std::cout << " GDCM_ROOT_UID Root UID" << std::endl;
183 /*
184 * Default behavior for root UID is:
185 * By default the GDCM one is used
186 * If GDCM_ROOT_UID is set, then use this one instead
187 * If --root-uid is explicitly set on the command line, it will override any other defined behavior
188 */
189 }
190
191 template <typename T>
readvector(std::vector<T> & v,const char * str)192 static size_t readvector(std::vector<T> &v, const char *str)
193 {
194 if( !str ) return 0;
195 std::istringstream os( str );
196 T f;
197 while( os >> f )
198 {
199 v.push_back( f );
200 os.get(); // == ","
201 }
202 return v.size();
203 }
204
205 namespace gdcm
206 {
derives(File & file,const Pixmap & compressed_image)207 static bool derives( File & file, const Pixmap& compressed_image )
208 {
209 #if 1
210 DataSet &ds = file.GetDataSet();
211
212 if( !ds.FindDataElement( Tag(0x0008,0x0016) )
213 || ds.GetDataElement( Tag(0x0008,0x0016) ).IsEmpty() )
214 {
215 return false;
216 }
217 if( !ds.FindDataElement( Tag(0x0008,0x0018) )
218 || ds.GetDataElement( Tag(0x0008,0x0018) ).IsEmpty() )
219 {
220 return false;
221 }
222 const DataElement &sopclassuid = ds.GetDataElement( Tag(0x0008,0x0016) );
223 const DataElement &sopinstanceuid = ds.GetDataElement( Tag(0x0008,0x0018) );
224 // Make sure that const char* pointer will be properly padded with \0 char:
225 std::string sopclassuid_str( sopclassuid.GetByteValue()->GetPointer(), sopclassuid.GetByteValue()->GetLength() );
226 std::string sopinstanceuid_str( sopinstanceuid.GetByteValue()->GetPointer(), sopinstanceuid.GetByteValue()->GetLength() );
227 ds.Remove( Tag(0x8,0x18) );
228
229 FileDerivation fd;
230 fd.SetFile( file );
231 fd.AddReference( sopclassuid_str.c_str(), sopinstanceuid_str.c_str() );
232
233 // CID 7202 Source Image Purposes of Reference
234 // {"DCM",121320,"Uncompressed predecessor"},
235 fd.SetPurposeOfReferenceCodeSequenceCodeValue( 121320 );
236
237 // CID 7203 Image Derivation
238 // { "DCM",113040,"Lossy Compression" },
239 fd.SetDerivationCodeSequenceCodeValue( 113040 );
240 fd.SetDerivationDescription( "lossy conversion" );
241 if( !fd.Derive() )
242 {
243 std::cerr << "Sorry could not derive using input info" << std::endl;
244 return false;
245 }
246
247
248 #else
249 /*
250 (0008,2111) ST [Lossy compression with JPEG extended sequential 8 bit, IJG quality... # 102, 1 DerivationDescription
251 (0008,2112) SQ (Sequence with explicit length #=1) # 188, 1 SourceImageSequence
252 (fffe,e000) na (Item with explicit length #=3) # 180, 1 Item
253 (0008,1150) UI =UltrasoundImageStorage # 28, 1 ReferencedSOPClassUID
254 (0008,1155) UI [1.2.840.1136190195280574824680000700.3.0.1.19970424140438] # 58, 1 ReferencedSOPInstanceUID
255 (0040,a170) SQ (Sequence with explicit length #=1) # 66, 1 PurposeOfReferenceCodeSequence
256 (fffe,e000) na (Item with explicit length #=3) # 58, 1 Item
257 (0008,0100) SH [121320] # 6, 1 CodeValue
258 (0008,0102) SH [DCM] # 4, 1 CodingSchemeDesignator
259 (0008,0104) LO [Uncompressed predecessor] # 24, 1 CodeMeaning
260 (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem
261 (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem
262 (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem
263 (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem
264 */
265 const Tag sisq(0x8,0x2112);
266 SequenceOfItems * sqi;
267 sqi = new SequenceOfItems;
268 DataElement de( sisq);
269 de.SetVR( VR::SQ );
270 de.SetValue( *sqi );
271 de.SetVLToUndefined();
272
273 DataSet &ds = file.GetDataSet();
274 ds.Insert( de );
275 {
276 // (0008,0008) CS [ORIGINAL\SECONDARY] # 18, 2 ImageType
277 gdcm::Attribute<0x0008,0x0008> at3;
278 static const gdcm::CSComp values[] = {"DERIVED","SECONDARY"};
279 at3.SetValues( values, 2, true ); // true => copy data !
280 if( ds.FindDataElement( at3.GetTag() ) )
281 {
282 const gdcm::DataElement &de = ds.GetDataElement( at3.GetTag() );
283 at3.SetFromDataElement( de );
284 // Make sure that value #1 is at least 'DERIVED', so override in all cases:
285 at3.SetValue( 0, values[0] );
286 }
287 ds.Replace( at3.GetAsDataElement() );
288
289 }
290 {
291 Attribute<0x0008,0x2111> at1;
292 at1.SetValue( "lossy conversion" );
293 ds.Replace( at1.GetAsDataElement() );
294 }
295
296 sqi = (SequenceOfItems*)ds.GetDataElement( sisq ).GetSequenceOfItems();
297 sqi->SetLengthToUndefined();
298
299 if( !sqi->GetNumberOfItems() )
300 {
301 Item item; //( Tag(0xfffe,0xe000) );
302 item.SetVLToUndefined();
303 sqi->AddItem( item );
304 }
305
306 Item &item1 = sqi->GetItem(1);
307 DataSet &subds = item1.GetNestedDataSet();
308 /*
309 (0008,1150) UI =UltrasoundImageStorage # 28, 1 ReferencedSOPClassUID
310 (0008,1155) UI [1.2.840.1136190195280574824680000700.3.0.1.19970424140438] # 58, 1 ReferencedSOPInstanceUID
311 */
312 {
313 DataElement sopinstanceuid = ds.GetDataElement( Tag(0x0008,0x0016) );
314 sopinstanceuid.SetTag( Tag(0x8,0x1150 ) );
315 subds.Replace( sopinstanceuid );
316 DataElement sopclassuid = ds.GetDataElement( Tag(0x0008,0x0018) );
317 sopclassuid.SetTag( Tag(0x8,0x1155 ) );
318 subds.Replace( sopclassuid );
319 ds.Remove( Tag(0x8,0x18) );
320 }
321
322 const Tag prcs(0x0040,0xa170);
323 if( !subds.FindDataElement( prcs) )
324 {
325 SequenceOfItems *sqi2 = new SequenceOfItems;
326 DataElement de( prcs );
327 de.SetVR( VR::SQ );
328 de.SetValue( *sqi2 );
329 de.SetVLToUndefined();
330 subds.Insert( de );
331 }
332
333 sqi = (SequenceOfItems*)subds.GetDataElement( prcs ).GetSequenceOfItems();
334 sqi->SetLengthToUndefined();
335
336 if( !sqi->GetNumberOfItems() )
337 {
338 Item item; //( Tag(0xfffe,0xe000) );
339 item.SetVLToUndefined();
340 sqi->AddItem( item );
341 }
342 Item &item2 = sqi->GetItem(1);
343 DataSet &subds2 = item2.GetNestedDataSet();
344
345 /*
346 (0008,0100) SH [121320] # 6, 1 CodeValue
347 (0008,0102) SH [DCM] # 4, 1 CodingSchemeDesignator
348 (0008,0104) LO [Uncompressed predecessor] # 24, 1 CodeMeaning
349 */
350
351 Attribute<0x0008,0x0100> at1;
352 at1.SetValue( "121320" );
353 subds2.Replace( at1.GetAsDataElement() );
354 Attribute<0x0008,0x0102> at2;
355 at2.SetValue( "DCM" );
356 subds2.Replace( at2.GetAsDataElement() );
357 Attribute<0x0008,0x0104> at3;
358 at3.SetValue( "Uncompressed predecessor" );
359 subds2.Replace( at3.GetAsDataElement() );
360
361 /*
362 (0008,9215) SQ (Sequence with explicit length #=1) # 98, 1 DerivationCodeSequence
363 (fffe,e000) na (Item with explicit length #=3) # 90, 1 Item
364 (0008,0100) SH [121327] # 6, 1 CodeValue
365 (0008,0102) SH [DCM] # 4, 1 CodingSchemeDesignator
366 (0008,0104) LO [Full fidelity image, uncompressed or lossless compressed] # 56, 1 CodeMeaning
367 (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem
368 (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem
369 */
370 {
371 const Tag sisq(0x8,0x9215);
372 SequenceOfItems * sqi;
373 sqi = new SequenceOfItems;
374 DataElement de( sisq );
375 de.SetVR( VR::SQ );
376 de.SetValue( *sqi );
377 de.SetVLToUndefined();
378 ds.Insert( de );
379 sqi = (SequenceOfItems*)ds.GetDataElement( sisq ).GetSequenceOfItems();
380 sqi->SetLengthToUndefined();
381
382 if( !sqi->GetNumberOfItems() )
383 {
384 Item item; //( Tag(0xfffe,0xe000) );
385 item.SetVLToUndefined();
386 sqi->AddItem( item );
387 }
388
389 Item &item1 = sqi->GetItem(1);
390 DataSet &subds3 = item1.GetNestedDataSet();
391
392 Attribute<0x0008,0x0100> at1;
393 at1.SetValue( "121327" );
394 subds3.Replace( at1.GetAsDataElement() );
395 Attribute<0x0008,0x0102> at2;
396 at2.SetValue( "DCM" );
397 subds3.Replace( at2.GetAsDataElement() );
398 Attribute<0x0008,0x0104> at3;
399 at3.SetValue( "Full fidelity image, uncompressed or lossless compressed" );
400 subds3.Replace( at3.GetAsDataElement() );
401 }
402 #endif
403
404 {
405 /*
406 (0028,2110) CS [01] # 2, 1 LossyImageCompression
407 (0028,2112) DS [15.95] # 6, 1 LossyImageCompressionRatio
408 (0028,2114) CS [ISO_10918_1] # 12, 1 LossyImageCompressionMethod
409 */
410 const DataElement & pixeldata = compressed_image.GetDataElement();
411 size_t len = pixeldata.GetSequenceOfFragments()->ComputeByteLength();
412 size_t reflen = compressed_image.GetBufferLength();
413 double ratio = (double)reflen / (double)len;
414 Attribute<0x0028,0x2110> at1;
415 at1.SetValue( "01" );
416 ds.Replace( at1.GetAsDataElement() );
417 Attribute<0x0028,0x2112> at2;
418 at2.SetValues( &ratio, 1);
419 ds.Replace( at2.GetAsDataElement() );
420 Attribute<0x0028,0x2114> at3;
421
422 // ImageWriter will properly set attribute 0028,2114 (Lossy Image Compression Method)
423 }
424
425 return true;
426
427 }
428 } // end namespace gdcm
429
main(int argc,char * argv[])430 int main (int argc, char *argv[])
431 {
432 int c;
433 //int digit_optind = 0;
434
435 std::string filename;
436 std::string outfilename;
437 std::string root;
438 int explicitts = 0; // explicit is a reserved keyword
439 int implicit = 0;
440 int quiet = 0;
441 int lut = 0;
442 int lut8 = 0;
443 int decompress_lut = 0;
444 int raw = 0;
445 int deflated = 0;
446 int rootuid = 0;
447 int checkmeta = 0;
448 int jpeg = 0;
449 int jpegls = 0;
450 int j2k = 0;
451 int lossy = 0;
452 int split = 0;
453 int fragmentsize = 0;
454 int rle = 0;
455 int force = 0;
456 int planarconf = 0;
457 int planarconfval = 0;
458 double iconmin = 0;
459 double iconmax = 0;
460 int usedict = 0;
461 int compressicon = 0;
462 int generateicon = 0;
463 int iconminmax = 0;
464 int iconautominmax = 0;
465 int removegrouplength = 0;
466 int removeprivate = 0;
467 int removeretired = 0;
468 int photometricinterpretation = 0;
469 std::string photometricinterpretation_str;
470 int quality = 0;
471 int rate = 0;
472 int tile = 0;
473 int nres = 0;
474 int nresvalue = 6; // ??
475 std::vector<float> qualities;
476 std::vector<float> rates;
477 std::vector<unsigned int> tilesize;
478 int irreversible = 0;
479 int changeprivatetags = 0;
480
481 int verbose = 0;
482 int warning = 0;
483 int debug = 0;
484 int error = 0;
485 int help = 0;
486 int version = 0;
487 int ignoreerrors = 0;
488 int jpeglserror = 0;
489 int jpeglserror_value = 0;
490
491 while (true) {
492 //int this_option_optind = optind ? optind : 1;
493 int option_index = 0;
494 static struct option long_options[] = {
495 {"input", 1, nullptr, 0},
496 {"output", 1, nullptr, 0},
497 {"group-length", 1, nullptr, 0}, // valid / create / remove
498 {"preamble", 1, nullptr, 0}, // valid / create / remove
499 {"padding", 1, nullptr, 0}, // valid (\0 -> space) / optimize (at most 1 byte of padding)
500 {"vr", 1, nullptr, 0}, // valid
501 {"sop", 1, nullptr, 0}, // default to SC...
502 {"iod", 1, nullptr, 0}, // valid
503 {"meta", 1, nullptr, 0}, // valid / create / remove
504 {"dataset", 1, nullptr, 0}, // valid / create / remove?
505 {"sequence", 1, nullptr, 0}, // defined / undefined
506 {"deflate", 1, nullptr, 0}, // 1 - 9 / best = 9 / fast = 1
507 {"tag", 1, nullptr, 0}, // need to specify a tag xxxx,yyyy = value to override default
508 {"name", 1, nullptr, 0}, // same as tag but explicit use of name
509 {"root-uid", 1, &rootuid, 1}, // specific Root (not GDCM)
510 {"check-meta", 0, &checkmeta, 1}, // specific Root (not GDCM)
511 // Image specific options:
512 {"pixeldata", 1, nullptr, 0}, // valid
513 {"apply-lut", 0, &lut, 1}, // default (implicit VR, LE) / Explicit LE / Explicit BE
514 {"raw", 0, &raw, 1}, // default (implicit VR, LE) / Explicit LE / Explicit BE
515 {"deflated", 0, &deflated, 1}, // DeflatedExplicitVRLittleEndian
516 {"lossy", 0, &lossy, 1}, // Specify lossy comp
517 {"force", 0, &force, 1}, // force decompression even if target compression is identical
518 {"jpeg", 0, &jpeg, 1}, // JPEG lossy / lossless
519 {"jpegls", 0, &jpegls, 1}, // JPEG-LS: lossy / lossless
520 {"j2k", 0, &j2k, 1}, // J2K: lossy / lossless
521 {"rle", 0, &rle, 1}, // lossless !
522 {"mpeg2", 0, nullptr, 0}, // lossy !
523 {"jpip", 0, nullptr, 0}, // ??
524 {"split", 1, &split, 1}, // split fragments
525 {"planar-configuration", 1, &planarconf, 1}, // Planar Configuration
526 {"explicit", 0, &explicitts, 1}, //
527 {"implicit", 0, &implicit, 1}, //
528 {"use-dict", 0, &usedict, 1}, //
529 {"generate-icon", 0, &generateicon, 1}, //
530 {"icon-minmax", 1, &iconminmax, 1}, //
531 {"icon-auto-minmax", 0, &iconautominmax, 1}, //
532 {"compress-icon", 0, &compressicon, 1}, //
533 {"remove-gl", 0, &removegrouplength, 1}, //
534 {"remove-private-tags", 0, &removeprivate, 1}, //
535 {"remove-retired", 0, &removeretired, 1}, //
536 {"photometric-interpretation", 1, &photometricinterpretation, 1}, //
537 {"with-private-dict", 0, &changeprivatetags, 1}, //
538 {"decompress-lut", 0, &decompress_lut, 1}, // linearized segmented LUT
539 {"apply-lut8", 0, &lut8, 1},
540 // j2k :
541 {"rate", 1, &rate, 1}, //
542 {"quality", 1, &quality, 1}, // will also work for regular jpeg compressor
543 {"tile", 1, &tile, 1}, //
544 {"number-resolution", 1, &nres, 1}, //
545 {"irreversible", 0, &irreversible, 1}, //
546 {"allowed-error", 1, &jpeglserror, 1}, //
547
548 // General options !
549 {"verbose", 0, &verbose, 1},
550 {"warning", 0, &warning, 1},
551 {"debug", 0, &debug, 1},
552 {"error", 0, &error, 1},
553 {"help", 0, &help, 1},
554 {"version", 0, &version, 1},
555 {"ignore-errors", 0, &ignoreerrors, 1},
556 {"quiet", 0, &quiet, 1},
557
558 {nullptr, 0, nullptr, 0}
559 };
560
561 c = getopt_long (argc, argv, "i:o:XMUCl8wdJKLRFYS:P:VWDEhvIr:q:t:n:e:",
562 long_options, &option_index);
563 if (c == -1)
564 {
565 break;
566 }
567
568 switch (c)
569 {
570 case 0:
571 {
572 const char *s = long_options[option_index].name; (void)s;
573 //printf ("option %s", s);
574 if (optarg)
575 {
576 if( option_index == 0 ) /* input */
577 {
578 assert( strcmp(s, "input") == 0 );
579 assert( filename.empty() );
580 filename = optarg;
581 }
582 else if( option_index == 14 ) /* root-uid */
583 {
584 assert( strcmp(s, "root-uid") == 0 );
585 assert( root.empty() );
586 root = optarg;
587 }
588 else if( option_index == 28 ) /* split */
589 {
590 assert( strcmp(s, "split") == 0 );
591 fragmentsize = atoi(optarg);
592 }
593 else if( option_index == 29 ) /* planar conf*/
594 {
595 assert( strcmp(s, "planar-configuration") == 0 );
596 planarconfval = atoi(optarg);
597 }
598 else if( option_index == 34 ) /* icon minmax*/
599 {
600 assert( strcmp(s, "icon-minmax") == 0 );
601 std::stringstream ss;
602 ss.str( optarg );
603 ss >> iconmin;
604 char comma;
605 ss >> comma;
606 ss >> iconmax;
607 }
608 else if( option_index == 40 ) /* photometricinterpretation */
609 {
610 assert( strcmp(s, "photometric-interpretation") == 0 );
611 photometricinterpretation_str = optarg;
612 }
613 else if( option_index == 42 ) /* rate */
614 {
615 assert( strcmp(s, "rate") == 0 );
616 readvector(rates, optarg);
617 }
618 else if( option_index == 43 ) /* quality */
619 {
620 assert( strcmp(s, "quality") == 0 );
621 readvector(qualities, optarg);
622 }
623 else if( option_index == 44 ) /* tile */
624 {
625 assert( strcmp(s, "tile") == 0 );
626 size_t n = readvector(tilesize, optarg);
627 assert( n == 2 ); (void)n;
628 }
629 else if( option_index == 45 ) /* number of resolution */
630 {
631 assert( strcmp(s, "number-resolution") == 0 );
632 nresvalue = atoi(optarg);
633 }
634 else if( option_index == 47 ) /* JPEG-LS error */
635 {
636 assert( strcmp(s, "allowed-error") == 0 );
637 jpeglserror_value = atoi(optarg);
638 }
639 //printf (" with arg %s, index = %d", optarg, option_index);
640 }
641 //printf ("\n");
642 }
643 break;
644
645 case 'i':
646 //printf ("option i with value '%s'\n", optarg);
647 assert( filename.empty() );
648 filename = optarg;
649 break;
650
651 case 'o':
652 //printf ("option o with value '%s'\n", optarg);
653 assert( outfilename.empty() );
654 outfilename = optarg;
655 break;
656
657 case 'X':
658 explicitts = 1;
659 break;
660
661 case 'M':
662 implicit = 1;
663 break;
664
665 case 'U':
666 usedict = 1;
667 break;
668
669 case 'C':
670 checkmeta = 1;
671 break;
672
673 // root-uid
674
675 case 'l':
676 lut = 1;
677 break;
678
679 case '8':
680 lut8 = 1;
681 break;
682
683
684 case 'w':
685 raw = 1;
686 break;
687
688 case 'e':
689 jpeglserror = 1;
690 jpeglserror_value = atoi(optarg);
691 break;
692
693 case 'd':
694 deflated = 1;
695 break;
696
697 case 'J':
698 jpeg = 1;
699 break;
700
701 case 'K':
702 j2k = 1;
703 break;
704
705 case 'L':
706 jpegls = 1;
707 break;
708
709 case 'R':
710 rle = 1;
711 break;
712
713 case 'F':
714 force = 1;
715 break;
716
717 case 'Y':
718 lossy = 1;
719 break;
720
721 case 'S':
722 split = 1;
723 fragmentsize = atoi(optarg);
724 break;
725
726 case 'P':
727 photometricinterpretation = 1;
728 photometricinterpretation_str = optarg;
729 break;
730
731 case 'r':
732 rate = 1;
733 readvector(rates, optarg);
734 break;
735
736 case 'q':
737 quality = 1;
738 readvector(qualities, optarg);
739 break;
740
741 case 't':
742 tile = 1;
743 readvector(tilesize, optarg);
744 break;
745
746 case 'n':
747 nres = 1;
748 nresvalue = atoi(optarg);
749 break;
750
751 // General option
752 case 'V':
753 verbose = 1;
754 break;
755
756 case 'W':
757 warning = 1;
758 break;
759
760 case 'D':
761 debug = 1;
762 break;
763
764 case 'E':
765 error = 1;
766 break;
767
768 case 'h':
769 help = 1;
770 break;
771
772 case 'v':
773 version = 1;
774 break;
775
776 case 'I':
777 ignoreerrors = 1;
778 break;
779
780 case '?':
781 break;
782
783 default:
784 printf ("?? getopt returned character code 0%o ??\n", c);
785 }
786 }
787
788 // For now only support one input / one output
789 if (optind < argc)
790 {
791 //printf ("non-option ARGV-elements: ");
792 std::vector<std::string> files;
793 while (optind < argc)
794 {
795 //printf ("%s\n", argv[optind++]);
796 files.emplace_back(argv[optind++] );
797 }
798 //printf ("\n");
799 if( files.size() == 2
800 && filename.empty()
801 && outfilename.empty()
802 )
803 {
804 filename = files[0];
805 outfilename = files[1];
806 }
807 else
808 {
809 PrintHelp();
810 return 1;
811 }
812 }
813
814 if( version )
815 {
816 //std::cout << "version" << std::endl;
817 PrintVersion();
818 return 0;
819 }
820
821 if( help )
822 {
823 //std::cout << "help" << std::endl;
824 PrintHelp();
825 return 0;
826 }
827
828 if( filename.empty() )
829 {
830 //std::cerr << "Need input file (-i)\n";
831 PrintHelp();
832 return 1;
833 }
834 if( outfilename.empty() )
835 {
836 //std::cerr << "Need output file (-o)\n";
837 PrintHelp();
838 return 1;
839 }
840
841 // Debug is a little too verbose
842 gdcm::Trace::SetDebug( (debug > 0 ? true : false));
843 gdcm::Trace::SetWarning( (warning > 0 ? true : false));
844 gdcm::Trace::SetError( (error > 0 ? true : false));
845 // when verbose is true, make sure warning+error are turned on:
846 if( verbose )
847 {
848 gdcm::Trace::SetWarning( (verbose > 0 ? true : false) );
849 gdcm::Trace::SetError( (verbose > 0 ? true : false) );
850 }
851
852 gdcm::FileMetaInformation::SetSourceApplicationEntityTitle( "gdcmconv" );
853 if( !rootuid )
854 {
855 // only read the env var is no explicit cmd line option
856 // maybe there is an env var defined... let's check
857 const char *rootuid_env = getenv("GDCM_ROOT_UID");
858 if( rootuid_env )
859 {
860 rootuid = 1;
861 root = rootuid_env;
862 }
863 }
864 if( rootuid )
865 {
866 // root is set either by the cmd line option or the env var
867 if( !gdcm::UIDGenerator::IsValid( root.c_str() ) )
868 {
869 std::cerr << "specified Root UID is not valid: " << root << std::endl;
870 return 1;
871 }
872 gdcm::UIDGenerator::SetRoot( root.c_str() );
873 }
874
875 if( removegrouplength || removeprivate || removeretired )
876 {
877 gdcm::Reader reader;
878 reader.SetFileName( filename.c_str() );
879 if( !reader.Read() )
880 {
881 std::cerr << "Could not read: " << filename << std::endl;
882 return 1;
883 }
884 gdcm::MediaStorage ms;
885 ms.SetFromFile( reader.GetFile() );
886 if( ms == gdcm::MediaStorage::MediaStorageDirectoryStorage )
887 {
888 std::cerr << "Sorry DICOMDIR is not supported" << std::endl;
889 return 1;
890 }
891
892 gdcm::Anonymizer ano;
893 ano.SetFile( reader.GetFile() );
894 if( removegrouplength )
895 {
896 if( !ano.RemoveGroupLength() )
897 {
898 std::cerr << "Could not remove group length" << std::endl;
899 }
900 }
901 if( removeretired )
902 {
903 if( !ano.RemoveRetired() )
904 {
905 std::cerr << "Could not remove retired tags" << std::endl;
906 }
907 }
908 if( removeprivate )
909 {
910 if( !ano.RemovePrivateTags() )
911 {
912 std::cerr << "Could not remove private tags" << std::endl;
913 }
914 }
915
916 gdcm::Writer writer;
917 writer.SetFileName( outfilename.c_str() );
918 writer.SetFile( ano.GetFile() );
919 if( !writer.Write() )
920 {
921 std::cerr << "Failed to write: " << outfilename << std::endl;
922 return 1;
923 }
924
925 return 0;
926 }
927
928 // Handle here the general file (not required to be image)
929 if ( !raw && (explicitts || implicit || deflated) )
930 {
931 if( explicitts && implicit ) return 1; // guard
932 if( explicitts && deflated ) return 1; // guard
933 if( implicit && deflated ) return 1; // guard
934 gdcm::Reader reader;
935 reader.SetFileName( filename.c_str() );
936 if( !reader.Read() )
937 {
938 std::cerr << "Could not read: " << filename << std::endl;
939 return 1;
940 }
941 gdcm::MediaStorage ms;
942 ms.SetFromFile( reader.GetFile() );
943 if( ms == gdcm::MediaStorage::MediaStorageDirectoryStorage )
944 {
945 std::cerr << "Sorry DICOMDIR is not supported" << std::endl;
946 return 1;
947 }
948
949 gdcm::Writer writer;
950 writer.SetFileName( outfilename.c_str() );
951 writer.SetFile( reader.GetFile() );
952 gdcm::File & file = writer.GetFile();
953 gdcm::FileMetaInformation &fmi = file.GetHeader();
954
955 const gdcm::TransferSyntax &orits = fmi.GetDataSetTransferSyntax();
956 if( orits != gdcm::TransferSyntax::ExplicitVRLittleEndian
957 && orits != gdcm::TransferSyntax::ImplicitVRLittleEndian
958 && orits != gdcm::TransferSyntax::DeflatedExplicitVRLittleEndian )
959 {
960 std::cerr << "Sorry input Transfer Syntax not supported for this conversion: " << orits << std::endl;
961 return 1;
962 }
963
964 gdcm::TransferSyntax ts = gdcm::TransferSyntax::ImplicitVRLittleEndian;
965 if( explicitts )
966 {
967 ts = gdcm::TransferSyntax::ExplicitVRLittleEndian;
968 }
969 else if( deflated )
970 {
971 ts = gdcm::TransferSyntax::DeflatedExplicitVRLittleEndian;
972 }
973 std::string tsuid = gdcm::TransferSyntax::GetTSString( ts );
974 if( tsuid.size() % 2 == 1 )
975 {
976 tsuid.push_back( 0 ); // 0 padding
977 }
978 gdcm::DataElement de( gdcm::Tag(0x0002,0x0010) );
979 de.SetByteValue( &tsuid[0], (uint32_t)tsuid.size() );
980 de.SetVR( gdcm::Attribute<0x0002, 0x0010>::GetVR() );
981 fmi.Clear();
982 fmi.Replace( de );
983
984 fmi.SetDataSetTransferSyntax(ts);
985
986 if( explicitts || deflated )
987 {
988 gdcm::FileExplicitFilter fef;
989 fef.SetChangePrivateTags( (changeprivatetags > 0 ? true: false));
990 fef.SetFile( reader.GetFile() );
991 if( !fef.Change() )
992 {
993 std::cerr << "Failed to change: " << filename << std::endl;
994 return 1;
995 }
996 }
997
998 if( !writer.Write() )
999 {
1000 std::cerr << "Failed to write: " << outfilename << std::endl;
1001 return 1;
1002 }
1003
1004 return 0;
1005 }
1006
1007 // split fragments
1008 if( split )
1009 {
1010 gdcm::PixmapReader reader;
1011 reader.SetFileName( filename.c_str() );
1012 if( !reader.Read() )
1013 {
1014 std::cerr << "Could not read (pixmap): " << filename << std::endl;
1015 return 1;
1016 }
1017 const gdcm::Pixmap &image = reader.GetPixmap();
1018
1019 gdcm::ImageFragmentSplitter splitter;
1020 splitter.SetInput( image );
1021 splitter.SetFragmentSizeMax( fragmentsize );
1022 splitter.SetForce( (force > 0 ? true: false));
1023 bool b = splitter.Split();
1024 if( !b )
1025 {
1026 std::cerr << "Could not split: " << filename << std::endl;
1027 return 1;
1028 }
1029 gdcm::PixmapWriter writer;
1030 writer.SetFileName( outfilename.c_str() );
1031 writer.SetFile( reader.GetFile() );
1032 writer.SetPixmap( splitter.PixmapToPixmapFilter::GetOutput() );
1033 if( !writer.Write() )
1034 {
1035 std::cerr << "Failed to write: " << outfilename << std::endl;
1036 return 1;
1037 }
1038 }
1039 else if( photometricinterpretation )
1040 {
1041 gdcm::PixmapReader reader;
1042 reader.SetFileName( filename.c_str() );
1043 if( !reader.Read() )
1044 {
1045 std::cerr << "Could not read (pixmap): " << filename << std::endl;
1046 return 1;
1047 }
1048 const gdcm::Pixmap &image = reader.GetPixmap();
1049
1050 // Just in case:
1051 if( gdcm::PhotometricInterpretation::GetPIType(photometricinterpretation_str.c_str())
1052 == gdcm::PhotometricInterpretation::PI_END )
1053 {
1054 std::cerr << "Do not handle PhotometricInterpretation: " << photometricinterpretation_str << std::endl;
1055 return 1;
1056 }
1057 gdcm::PhotometricInterpretation pi (
1058 gdcm::PhotometricInterpretation::GetPIType(photometricinterpretation_str.c_str()) );
1059 gdcm::ImageChangePhotometricInterpretation pifilt;
1060 pifilt.SetInput( image );
1061 pifilt.SetPhotometricInterpretation( pi );
1062 bool b = pifilt.Change();
1063 if( !b )
1064 {
1065 std::cerr << "Could not apply PhotometricInterpretation: " << filename << std::endl;
1066 return 1;
1067 }
1068 gdcm::PixmapWriter writer;
1069 writer.SetFileName( outfilename.c_str() );
1070 writer.SetFile( reader.GetFile() );
1071 writer.SetPixmap( pifilt.PixmapToPixmapFilter::GetOutput() );
1072 if( !writer.Write() )
1073 {
1074 std::cerr << "Failed to write: " << outfilename << std::endl;
1075 return 1;
1076 }
1077 }
1078 else if( decompress_lut )
1079 {
1080 gdcm::PixmapReader reader;
1081 reader.SetFileName( filename.c_str() );
1082 if( !reader.Read() )
1083 {
1084 std::cerr << "Could not read (pixmap): " << filename << std::endl;
1085 return 1;
1086 }
1087 const gdcm::Pixmap &image = reader.GetPixmap();
1088
1089 gdcm::FileDecompressLookupTable lutfilt;
1090 lutfilt.SetFile( reader.GetFile() );
1091 lutfilt.SetPixmap( image );
1092 bool b = lutfilt.Change();
1093 if( !b )
1094 {
1095 std::cerr << "Could not decompress LUT: " << filename << std::endl;
1096 return 1;
1097 }
1098 gdcm::PixmapWriter writer;
1099 writer.SetFileName( outfilename.c_str() );
1100 writer.SetFile( reader.GetFile() );
1101 writer.SetPixmap( lutfilt.GetPixmap() );
1102 if( !writer.Write() )
1103 {
1104 std::cerr << "Failed to write: " << outfilename << std::endl;
1105 return 1;
1106 }
1107 }
1108 else if( lut || lut8 )
1109 {
1110 gdcm::PixmapReader reader;
1111 reader.SetFileName( filename.c_str() );
1112 if( !reader.Read() )
1113 {
1114 std::cerr << "Could not read (pixmap): " << filename << std::endl;
1115 return 1;
1116 }
1117 const gdcm::Pixmap &image = reader.GetPixmap();
1118
1119 gdcm::ImageApplyLookupTable lutfilt;
1120 lutfilt.SetInput( image );
1121 lutfilt.SetRGB8( lut8 != 0 );
1122 bool b = lutfilt.Apply();
1123 if( !b )
1124 {
1125 std::cerr << "Could not apply LUT: " << filename << std::endl;
1126 return 1;
1127 }
1128 gdcm::PixmapWriter writer;
1129 writer.SetFileName( outfilename.c_str() );
1130 writer.SetFile( reader.GetFile() );
1131 writer.SetPixmap( lutfilt.PixmapToPixmapFilter::GetOutput() );
1132 if( !writer.Write() )
1133 {
1134 std::cerr << "Failed to write: " << outfilename << std::endl;
1135 return 1;
1136 }
1137 }
1138 else if( jpeg || j2k || jpegls || rle || raw || force /*|| deflated*/ /*|| planarconf*/ )
1139 {
1140 gdcm::PixmapReader reader;
1141 reader.SetFileName( filename.c_str() );
1142 if( !reader.Read() )
1143 {
1144 std::cerr << "Could not read (pixmap): " << filename << std::endl;
1145 return 1;
1146 }
1147 gdcm::Pixmap &image = reader.GetPixmap();
1148 //const gdcm::IconImage &icon = image.GetIconImage();
1149 //if( !icon.IsEmpty() )
1150 // {
1151 // std::cerr << "Icons are not supported" << std::endl;
1152 // return 1;
1153 // }
1154 if( generateicon )
1155 {
1156 gdcm::IconImageGenerator iig;
1157 iig.SetPixmap( image );
1158 const unsigned int idims[2] = { 64, 64 };
1159 iig.SetOutputDimensions( idims );
1160 if( iconminmax )
1161 {
1162 iig.SetPixelMinMax( iconmin, iconmax );
1163 }
1164 iig.AutoPixelMinMax( iconautominmax ? true : false );
1165 bool b = iig.Generate();
1166 if( !b ) return 1;
1167 const gdcm::IconImage &icon = iig.GetIconImage();
1168 image.SetIconImage( icon );
1169 }
1170
1171 gdcm::JPEG2000Codec j2kcodec;
1172 gdcm::JPEGCodec jpegcodec;
1173 gdcm::JPEGLSCodec jpeglscodec;
1174 gdcm::ImageChangeTransferSyntax change;
1175 change.SetForce( (force > 0 ? true: false));
1176 change.SetCompressIconImage( (compressicon > 0 ? true: false));
1177 if( jpeg )
1178 {
1179 if( lossy )
1180 {
1181 const gdcm::PixelFormat &pf = image.GetPixelFormat();
1182 if( pf.GetBitsAllocated() > 8 )
1183 change.SetTransferSyntax(gdcm::TransferSyntax::JPEGExtendedProcess2_4);
1184 else
1185 change.SetTransferSyntax( gdcm::TransferSyntax::JPEGBaselineProcess1 );
1186 jpegcodec.SetLossless( false );
1187 if( quality )
1188 {
1189 assert( qualities.size() == 1 );
1190 jpegcodec.SetQuality( static_cast<double>(qualities[0]) );
1191 }
1192 change.SetUserCodec( &jpegcodec );
1193 }
1194 else
1195 {
1196 change.SetTransferSyntax( gdcm::TransferSyntax::JPEGLosslessProcess14_1 );
1197 }
1198 }
1199 else if( jpegls )
1200 {
1201 if( lossy )
1202 {
1203 change.SetTransferSyntax( gdcm::TransferSyntax::JPEGLSNearLossless );
1204 jpeglscodec.SetLossless( false );
1205 if( jpeglserror )
1206 {
1207 jpeglscodec.SetLossyError( jpeglserror_value );
1208 }
1209 change.SetUserCodec( &jpeglscodec );
1210 }
1211 else
1212 {
1213 change.SetTransferSyntax( gdcm::TransferSyntax::JPEGLSLossless );
1214 }
1215 }
1216 else if( j2k )
1217 {
1218 if( lossy )
1219 {
1220 change.SetTransferSyntax( gdcm::TransferSyntax::JPEG2000 );
1221 if( rate )
1222 {
1223 int i = 0;
1224 for(std::vector<float>::const_iterator it = rates.begin(); it != rates.end(); ++it )
1225 {
1226 j2kcodec.SetRate(i++, static_cast<double>(*it) );
1227 }
1228 }
1229 if( quality )
1230 {
1231 int i = 0;
1232 for(std::vector<float>::const_iterator it = qualities.begin(); it != qualities.end(); ++it )
1233 {
1234 j2kcodec.SetQuality( i++, static_cast<double>(*it) );
1235 }
1236 }
1237 if( tile )
1238 {
1239 j2kcodec.SetTileSize( tilesize[0], tilesize[1] );
1240 }
1241 if( nres )
1242 {
1243 j2kcodec.SetNumberOfResolutions( nresvalue );
1244 }
1245 j2kcodec.SetReversible( !irreversible );
1246 change.SetUserCodec( &j2kcodec );
1247 }
1248 else
1249 {
1250 change.SetTransferSyntax( gdcm::TransferSyntax::JPEG2000Lossless );
1251 }
1252 }
1253 else if( raw )
1254 {
1255 if( lossy )
1256 {
1257 std::cerr << "no such thing as raw & lossy" << std::endl;
1258 return 1;
1259 }
1260 const gdcm::TransferSyntax &ts = image.GetTransferSyntax();
1261 #ifdef GDCM_WORDS_BIGENDIAN
1262 (void)ts;
1263 change.SetTransferSyntax( gdcm::TransferSyntax::ExplicitVRBigEndian );
1264 #else
1265 if( ts.IsExplicit() )
1266 {
1267 change.SetTransferSyntax( gdcm::TransferSyntax::ExplicitVRLittleEndian );
1268 if( implicit )
1269 change.SetTransferSyntax( gdcm::TransferSyntax::ImplicitVRLittleEndian );
1270 }
1271 else
1272 {
1273 assert( ts.IsImplicit() );
1274 change.SetTransferSyntax( gdcm::TransferSyntax::ImplicitVRLittleEndian );
1275 if( explicitts )
1276 change.SetTransferSyntax( gdcm::TransferSyntax::ExplicitVRLittleEndian );
1277 }
1278 #endif
1279 }
1280 else if( rle )
1281 {
1282 if( lossy )
1283 {
1284 std::cerr << "no such thing as rle & lossy" << std::endl;
1285 return 1;
1286 }
1287 change.SetTransferSyntax( gdcm::TransferSyntax::RLELossless );
1288 }
1289 else if( deflated )
1290 {
1291 if( lossy )
1292 {
1293 std::cerr << "no such thing as deflated & lossy" << std::endl;
1294 return 1;
1295 }
1296 change.SetTransferSyntax( gdcm::TransferSyntax::DeflatedExplicitVRLittleEndian );
1297 }
1298 else if( force )
1299 {
1300 // If image is encapsulated it will check some attribute (col/row/pi/pf) and
1301 // some attributes...
1302 }
1303 else
1304 {
1305 std::cerr << "unhandled action" << std::endl;
1306 return 1;
1307 }
1308 if( raw && planarconf )
1309 {
1310 gdcm::ImageChangePlanarConfiguration icpc;
1311 icpc.SetPlanarConfiguration( planarconfval );
1312 icpc.SetInput( image );
1313 bool b = icpc.Change();
1314 if( !b )
1315 {
1316 std::cerr << "Could not change the Planar Configuration: " << filename << std::endl;
1317 return 1;
1318 }
1319 change.SetInput( icpc.PixmapToPixmapFilter::GetOutput() );
1320 }
1321 else
1322 {
1323 change.SetInput( image );
1324 }
1325 bool b = change.Change();
1326 if( !b )
1327 {
1328 std::cerr << "Could not change the Transfer Syntax: " << filename << std::endl;
1329 return 1;
1330 }
1331 if( lossy )
1332 {
1333 if(!quiet)
1334 PrintLossyWarning();
1335 if( !gdcm::derives( reader.GetFile(), change.PixmapToPixmapFilter::GetOutput() ) )
1336 {
1337 std::cerr << "Failed to derives: " << filename << std::endl;
1338 return 1;
1339 }
1340 }
1341 if( usedict /*ts.IsImplicit()*/ )
1342 {
1343 gdcm::FileExplicitFilter fef;
1344 fef.SetChangePrivateTags( (changeprivatetags > 0 ? true : false));
1345 fef.SetFile( reader.GetFile() );
1346 if(!fef.Change())
1347 {
1348 std::cerr << "Failed to change: " << filename << std::endl;
1349 return 1;
1350 }
1351 }
1352
1353 gdcm::PixmapWriter writer;
1354 writer.SetFileName( outfilename.c_str() );
1355 writer.SetFile( reader.GetFile() );
1356 //writer.SetFile( fef.GetFile() );
1357
1358 gdcm::File & file = writer.GetFile();
1359 gdcm::FileMetaInformation &fmi = file.GetHeader();
1360 fmi.Remove( gdcm::Tag(0x0002,0x0100) ); // ' ' ' // PrivateInformationCreatorUID
1361 fmi.Remove( gdcm::Tag(0x0002,0x0102) ); // ' ' ' // PrivateInformation
1362
1363 const gdcm::Pixmap &pixout = change.PixmapToPixmapFilter::GetOutput();
1364 writer.SetPixmap( pixout );
1365 if( !writer.Write() )
1366 {
1367 std::cerr << "Failed to write: " << outfilename << std::endl;
1368 return 1;
1369 }
1370
1371 }
1372 else if( raw && false )
1373 {
1374 gdcm::PixmapReader reader;
1375 reader.SetFileName( filename.c_str() );
1376 if( !reader.Read() )
1377 {
1378 std::cerr << "Could not read (pixmap): " << filename << std::endl;
1379 return 1;
1380 }
1381
1382 const gdcm::Pixmap &ir = reader.GetPixmap();
1383
1384 gdcm::Pixmap image( ir );
1385 const gdcm::TransferSyntax &ts = ir.GetTransferSyntax();
1386 if( ts.IsExplicit() )
1387 {
1388 image.SetTransferSyntax( gdcm::TransferSyntax::ExplicitVRLittleEndian );
1389 }
1390 else
1391 {
1392 assert( ts.IsImplicit() );
1393 image.SetTransferSyntax( gdcm::TransferSyntax::ImplicitVRLittleEndian );
1394 }
1395
1396 /*
1397 image.SetNumberOfDimensions( ir.GetNumberOfDimensions() );
1398
1399 const unsigned int *dims = ir.GetDimensions();
1400 image.SetDimension(0, dims[0] );
1401 image.SetDimension(1, dims[1] );
1402
1403 const gdcm::PixelFormat &pixeltype = ir.GetPixelFormat();
1404 image.SetPixelFormat( pixeltype );
1405
1406 const gdcm::PhotometricInterpretation &pi = ir.GetPhotometricInterpretation();
1407 image.SetPhotometricInterpretation( pi );
1408 */
1409
1410 unsigned long len = ir.GetBufferLength();
1411 //assert( len = ir.GetBufferLength() );
1412 std::vector<char> buffer;
1413 buffer.resize(len); // black image
1414
1415 ir.GetBuffer( &buffer[0] );
1416 gdcm::ByteValue *bv = new gdcm::ByteValue(buffer);
1417 gdcm::DataElement pixeldata( gdcm::Tag(0x7fe0,0x0010) );
1418 pixeldata.SetValue( *bv );
1419 image.SetDataElement( pixeldata );
1420
1421 gdcm::PixmapWriter writer;
1422 writer.SetFile( reader.GetFile() );
1423 writer.SetPixmap( image );
1424 writer.SetFileName( outfilename.c_str() );
1425
1426 if( !writer.Write() )
1427 {
1428 std::cerr << "could not write: " << outfilename << std::endl;
1429 return 1;
1430 }
1431 }
1432 else
1433 {
1434 gdcm::Reader reader;
1435 reader.SetFileName( filename.c_str() );
1436 if( !reader.Read() )
1437 {
1438 if( ignoreerrors )
1439 {
1440 std::cerr << "WARNING: an error was found during the reading of your DICOM file." << std::endl;
1441 std::cerr << "gdcmconv will still try to continue and rewrite your DICOM file." << std::endl;
1442 std::cerr << "There is absolutely no guarantee that your output file will be valid." << std::endl;
1443 }
1444 else
1445 {
1446 std::cerr << "Failed to read: " << filename << std::endl;
1447 return 1;
1448 }
1449 }
1450 gdcm::MediaStorage ms;
1451 ms.SetFromFile( reader.GetFile() );
1452 if( ms == gdcm::MediaStorage::MediaStorageDirectoryStorage )
1453 {
1454 std::cerr << "Sorry DICOMDIR is not supported" << std::endl;
1455 return 1;
1456 }
1457
1458 #if 0
1459 // if preamble create:
1460 gdcm::File f(reader.GetFile());
1461 gdcm::Preamble p;
1462 p.Create();
1463 f.SetPreamble(p);
1464 gdcm::DataSet ds = reader.GetFile().GetDataSet();
1465 SetSQToUndefined undef;
1466 ds.ExecuteOperation(undef);
1467
1468 gdcm::File f(reader.GetFile());
1469 f.SetDataSet(ds);
1470 #endif
1471
1472 #if 0
1473 gdcm::DataSet& ds = reader.GetFile().GetDataSet();
1474 gdcm::DataElement de = ds.GetDataElement( gdcm::Tag(0x0010,0x0010) );
1475 const char patname[] = "John^Doe";
1476 de.SetByteValue(patname, strlen(patname));
1477 std::cout << de << std::endl;
1478
1479 ds.Replace( de );
1480 std::cout << ds.GetDataElement( gdcm::Tag(0x0010,0x0010) ) << std::endl;
1481 #endif
1482
1483 /*
1484 //(0020,0032) DS [-158.135803\-179.035797\-75.699997] # 34, 3 ImagePositionPatient
1485 //(0020,0037) DS [1.000000\0.000000\0.000000\0.000000\1.000000\0.000000] # 54, 6 ImageOrientationPatient
1486 gdcm::Attribute<0x0020,0x0032> at = { -158.135803, -179.035797, -75.699997 };
1487 gdcm::DataElement ipp = at.GetAsDataElement();
1488 ds.Remove( at.GetTag() );
1489 ds.Remove( ipp.GetTag() );
1490 ds.Replace( ipp );
1491 */
1492
1493 gdcm::Writer writer;
1494 writer.SetFileName( outfilename.c_str() );
1495 writer.SetCheckFileMetaInformation( (checkmeta > 0 ? true : false));
1496 //writer.SetFile( f );
1497 writer.SetFile( reader.GetFile() );
1498 if( !writer.Write() )
1499 {
1500 std::cerr << "Failed to write: " << outfilename << std::endl;
1501 // remove file to avoid any temptation
1502 if( filename != outfilename )
1503 {
1504 gdcm::System::RemoveFile( outfilename.c_str() );
1505 }
1506 else
1507 {
1508 std::cerr << "gdcmconv just corrupted: " << filename << " for you (data lost)." << std::endl;
1509 }
1510 return 1;
1511 }
1512 }
1513
1514 return 0;
1515 }
1516