1 /*
2 * The GNU Octave dicom package is Copyright Andy Buckle 2010
3 * Contact: blondandy using the sf.net system,
4 * <https://sourceforge.net/sendmessage.php?touser=1760416>
5 *
6 * The GNU Octave dicom package is free software: you can redistribute
7 * it and/or modify it under the terms of the GNU General Public
8 * License as published by the Free Software Foundation, either
9 * version 3 of the License, or (at your option) any later version.
10 *
11 * The GNU Octave dicom packag is distributed in the hope that it
12 * will be useful, but WITHOUT ANY WARRANTY; without even the
13 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
15 *
16 * Please see the file, "COPYING" for further details of GNU General
17 * Public License version 3.
18 *
19 */
20
21 #include "octave/oct.h"
22 #include "octave/ov-struct.h"
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "gdcmDictEntry.h"
29 #include "gdcmImage.h"
30 #include "gdcmImageWriter.h"
31 #include "gdcmFileDerivation.h"
32 #include "gdcmUIDGenerator.h"
33 #include "gdcmPhotometricInterpretation.h"
34
35 #include "gdcmAttribute.h"
36
37 #include "dicomdict.h"
38
39 #define DICOM_ERR -1
40 #define DICOM_OK 0
41
42 #define OCT_FN_NAME dicomwrite
43 #define QUOTED_(x) #x
44 #define QUOTED(x) QUOTED_(x)
45
46 // TODO: allow user to specify colourmap
47 // TODO: allow user to specify metadata
48 // TODO: return status, like the help output says
49
50 // TODO all fns here should throw exceptions, not use this "std::string & err" arg
51
52 void struct2metadata (gdcm::ImageWriter *w, gdcm::File *file, const octave_value & ov, bool trial, int sequenceDepth) ;
53 void structarray2sequence (gdcm::SequenceOfItems & sq, octave_map * om, bool trial, int sequenceDepth);
54 void value2element (gdcm::DataElement * de, const octave_value * ov, gdcm::Tag * tag, const std::string & keyword, bool trial, bool * handled, int sequenceDepth);
55 void octaveVal2dicomImage (gdcm::ImageWriter *w, octave_value *pixval) ;
56 void genMinimalMetaData (gdcm::ImageWriter *w, gdcm::File *file);
57
58 DEFUN_DLD (dicomwrite, args, nargout,
59 "-*- texinfo -*- \n\
60 @deftypefn {Loadable Function} {} dicomwrite(@var{im}, @var{filename})\n\
61 @deftypefnx {Loadable Function} {} dicomwrite(@var{im}, @var{filename}, @var{info})\n\
62 \n\
63 Write a DICOM format file to @var{filename}.\n\
64 \n\
65 @var{im} is image data or empty matrix, [], if only metadata save is required\n\
66 @var{filename} is filename to write dicom to. if [], then function runs in verbose trial mode.\n\
67 @var{info} struct, like that produced by dicominfo\n\
68 \n\
69 @seealso{dicomread, dicominfo}\n\
70 @end deftypefn \n\
71 ")
72 {
73 octave_value_list retval; // create object to store return values
74 if (2 > args.length())
75 {
76 error (QUOTED(OCT_FN_NAME)": should have at least 2 arguments");
77 return retval;
78 }
79 if (! args(1).is_string ())
80 {
81 error (QUOTED(OCT_FN_NAME)": second argument should be a string filename");
82 return retval;
83 }
84
85 charMatrix ch = args(1).char_matrix_value ();
86 bool trial = false;
87 if (0 == ch.numel())
88 {
89 trial = true; // more output if null string supplied for filename;
90 }
91 else if (ch.rows() != 1)
92 {
93 error (QUOTED(OCT_FN_NAME)": second arg should be a filename");
94 return retval;
95 }
96 std::string filename = ch.row_as_string(0);
97
98 //bool fn_failed_verbose=trial; //TODO other way of setting this. is it even necessary?
99 bool writing_image = (0 != args(0).numel()) ;
100
101 // prepare writer
102 gdcm::ImageWriter w;
103 if (!trial) w.SetFileName (filename.c_str());
104
105 gdcm::SmartPointer<gdcm::File> file = new gdcm::File; // should delete be used later?
106
107 if (3 > args.length())
108 {
109 // no metadata supplied, need to make some
110 try
111 {
112 genMinimalMetaData(&w, file);
113 }
114 catch (std::exception&)
115 {
116 return retval;
117 }
118 }
119 else
120 {
121 // 3rd arg should be struct to turn into metadata
122 try
123 {
124 struct2metadata (&w, file, args(2), trial, 0 /* depth of indent for SQ nesting */);
125 }
126 catch (std::exception&)
127 {
128 return retval;
129 }
130 }
131
132 if (writing_image)
133 {
134 octave_value pixval = args(0);
135 try
136 {
137 octaveVal2dicomImage(&w, &pixval);
138 }
139 catch (std::exception&)
140 {
141 return retval;
142 }
143 }
144
145 #if 0 /* debug */
146 //NOTE: debug code currently crashes octave - do not use without rework
147 gdcm::File fc=w.GetFile();
148 gdcm::DataSet dsc=fc.GetDataSet();
149 const gdcm::DataElement & sopclass = dsc.GetDataElement( gdcm::Tag(0x0008, 0x0016) ); // SOPClassUID
150 const gdcm::ByteValue *bv = sopclass.GetByteValue();
151 char * bufc=(char *) malloc((bv->GetLength()+1)*sizeof(char));
152 memcpy(bufc, bv->GetPointer(), bv->GetLength());
153 bufc[bv->GetLength()]='\0';
154 octave_stdout << bufc << '\n' ;
155 free(bufc);
156 #endif
157
158 if (!trial)
159 {
160 // if not writing image, use superclass
161 if (writing_image ? !w.ImageWriter::Write() : !w.Writer::Write())
162 {
163 error (QUOTED(OCT_FN_NAME)": unable to save");
164 return retval;
165 }
166 }
167
168 return retval;
169 }
170
struct2metadata(gdcm::ImageWriter * w,gdcm::File * file,const octave_value & ov,bool trial,int sequenceDepth)171 void struct2metadata (gdcm::ImageWriter *w, gdcm::File *file, const octave_value & ov, bool trial, int sequenceDepth)
172 {
173 if (!ov.OV_ISMAP())
174 {
175 error (QUOTED(OCT_FN_NAME)": 3rd arg should be struct holding metadata. it is %s",ov.type_name().c_str());
176 throw std::exception ();
177 }
178 gdcm::DataSet ds;
179 gdcm::FileMetaInformation hds;
180 octave_map om=ov.map_value();
181 uint32_t skipped = 0;
182 for (octave_map::iterator it = om.begin(); it != om.end(); it++)
183 {
184 std::string keyword(om.key(it));
185 Cell cell = om.contents(it);
186 if (!dicom_is_present(keyword))
187 {
188 if ( 0==keyword.compare("FileModDate") || 0==keyword.compare("Filename"))
189 {
190 if (trial)
191 octave_stdout << "from dicominfo, ignoring:" << keyword << ":[" << cell(0).string_value() << "]\n";
192 continue; //dicominfo adds these non-DICOM bits
193 }
194 skipped++;
195 // warning (QUOTED(OCT_FN_NAME)": skipping \"%s\". not in dictionary\n",keyword.c_str());
196 continue;
197 }
198 // PixelData is here becuase it handled by other fn. the others seem to cause bugs
199 if (0 == keyword.compare("PixelData") || 0 == keyword.compare("FileMetaInformationVersion"))
200 {
201 if (trial)
202 octave_stdout << "handled separately:" << keyword << std::endl;
203 continue;
204 }
205 gdcm::DataElement de;
206 gdcm::Tag tag;
207 bool handled;
208 try
209 {
210 value2element(&de, &cell(0), &tag, keyword, trial, &handled, sequenceDepth);
211 }
212 catch (std::exception&)
213 {
214 return;
215 }
216 if (handled)
217 {
218 if (tag.GetGroup() == (uint32_t)0x0002 )
219 {
220 // group 0x2 : header
221 hds.Insert(de);
222 // TODO move this somehow: if (trial) octave_stdout << ':' << "(header)";
223 }
224 else
225 {
226 // everything else is metadata
227 ds.Insert(de);
228 }
229 }
230 }
231 if (skipped > 0)
232 {
233 // TODO: this does not count elements nested in SQs
234 warning (QUOTED(OCT_FN_NAME)": skipped %u keyword-value pairs. not in dictionary\n",skipped);
235 }
236 // TODO are these set functions taking a copy? if they take a reference to objects that are about to go out of scope, we have a problem
237 file->SetDataSet ((gdcm::DataSet &)ds);
238 file->SetHeader (hds);
239 w->SetFile (*file);
240 return;
241 }
242
structarray2sequence(gdcm::SequenceOfItems & sq,octave_map * om,bool trial,int sequenceDepth)243 void structarray2sequence(gdcm::SequenceOfItems & sq, octave_map * om, bool trial, int sequenceDepth)
244 {
245 for (octave_map::iterator it = om->begin(); it != om->end(); it++)
246 {
247 gdcm::Item item;
248 // item.SetVLToUndefined(); //TODO: does VL need to be set for items that contain datasets?
249 gdcm::DataSet &nds = item.GetNestedDataSet();
250 std::string itemname(om->key(it));
251 // TODO: test itemname is something like Item_n.
252 Cell cell = om->contents(it);
253 octave_map subom = cell(0).map_value();
254 // octave_stdout << itemname <<std::endl;
255 for (octave_map::iterator subit = subom.begin(); subit != subom.end(); subit++)
256 {
257 std::string subkeyword(subom.key(subit));
258 gdcm::DataElement de;
259 gdcm::Tag tag;
260 bool handled;
261 try
262 {
263 value2element(&de, &(subom.contents(subit)(0)), &tag, subkeyword, trial, &handled, sequenceDepth);
264 }
265 catch (std::exception&)
266 {
267 return;
268 }
269 if (!handled)
270 {
271 warning (QUOTED(OCT_FN_NAME)": element in sequence not handled %s", subkeyword.c_str());
272 continue;
273 }
274 nds.Insert(de); //insert element into dataset
275 }
276 sq.AddItem(item); // add dataset to sequence
277 }
278 return;
279 }
280
value2element(gdcm::DataElement * de,const octave_value * ov,gdcm::Tag * tag,const std::string & keyword,bool trial,bool * handled,int sequenceDepth)281 void value2element (gdcm::DataElement * de, const octave_value * ov, gdcm::Tag * tag, const std::string & keyword, bool trial, bool * handled, int sequenceDepth)
282 {
283 gdcm::DictEntry entry;
284 if (!dicom_is_present(keyword))
285 {
286 if (trial)
287 {
288 octave_stdout << std::setw(2*sequenceDepth) << "" << std::setw(0);
289 octave_stdout << keyword << ": not in dictionary" << std::endl;
290 }
291 *handled = false;
292 return;
293 }
294 lookup_dicom_tag (*tag, keyword);
295 lookup_dicom_entry (entry, *tag);
296 gdcm::VL len((uint32_t)ov->byte_size());
297 //gdcm::DataElement de(*tag, len, entry.GetVR());
298 de->SetTag (*tag);
299 de->SetVL (len);
300 de->SetVR (entry.GetVR());
301 *handled = true;
302 if (trial)
303 {
304 octave_stdout << std::setw(2*sequenceDepth) << "" << std::setw(0);
305 octave_stdout << *tag << ':' << entry.GetVR() << ':' << keyword << ':';
306 }
307 if (entry.GetVR() & VRSTRING)
308 {
309 // TODO: check even padding requirement
310 // TODO some dicom string types are stored as numbers
311 if (!ov->is_string())
312 {
313 warning (QUOTED(OCT_FN_NAME)": dicomdict gives string VR for %s, octave value is %s", keyword.c_str(), ov->class_name().c_str());
314 }
315 if (trial)
316 octave_stdout << '[' << ov->string_value() << ']' << std::endl ;
317 de->SetByteValue ((const char *)ov->string_value().c_str(),len);
318 }
319 else if (entry.GetVR() & gdcm::VR::US)
320 {
321 // unsigned short
322 if (!ov->is_scalar_type())
323 {
324 // dicominfo turns US into double, this turns it back to uint16
325 warning (QUOTED(OCT_FN_NAME)": dicomdict gives VR of US for %s, octave value is %s", keyword.c_str(), ov->class_name().c_str());
326 }
327 if (trial)
328 octave_stdout << '[' << ov->uint16_scalar_value() << ']' << std::endl ;
329 de->SetByteValue ((const char *)ov->uint16_array_value().fortran_vec(), gdcm::VL((uint32_t)2));
330 }
331 else if ( entry.GetVR() & gdcm::VR::OB)
332 {
333 // other byte
334 uint8NDArray obv = ov->uint8_array_value();
335 //if (!ov->is_scalar_type())
336 // {
337 // // dicominfo seems to return string
338 // warning (QUOTED(OCT_FN_NAME)": dicomdict gives VR of OB for %s, octave value is %s", keyword.c_str(), ov->class_name().c_str());
339 // }
340 if (trial)
341 {
342 // TODO surely there is a better way to do this with c++ output stream
343 octave_uint8 * obv_p=obv.fortran_vec();
344 char buf[8];
345 octave_stdout << '[' ;
346 for (octave_idx_type i = 0; i < (octave_idx_type)len; i++)
347 {
348 snprintf (buf, 7, "%02X ", (const uint8_t)obv_p[i]);
349 octave_stdout << buf << " " ;
350 }
351 octave_stdout << ']' << std::endl ;
352 }
353 de->SetByteValue((const char *)obv.fortran_vec(), gdcm::VL((uint32_t)obv.byte_size()));
354 }
355 else if (entry.GetVR() & gdcm::VR::DS)
356 {
357 // double string. sep by '\\'
358 if (!ov->is_double_type())
359 {
360 warning (QUOTED(OCT_FN_NAME)": dicomdict gives VR of DS for %s, expecting double, octave value is %s", keyword.c_str(), ov->class_name().c_str());
361 }
362 std::stringstream ss;
363 Matrix mat=ov->matrix_value() ; //to matrix of doubles
364 double * mat_p=mat.fortran_vec();
365 for (int i = 0; i < mat.numel() ;i++)
366 ss << mat_p[i] << '\\' ;
367 std::string s = ss.str();
368 s = s.substr(0,s.length()-1); // strip last '\\'
369 if (s.length()%2==1)
370 s = s +" "; // ensure even number of chars
371 if (trial)
372 octave_stdout << '[' << s << ']' << std::endl ;
373 de->SetByteValue ( (const char *)s.c_str(), gdcm::VL((uint32_t)s.length()) );
374 }
375 else if ( entry.GetVR() & gdcm::VR::IS)
376 {
377 // integer string. only single values, I think
378 if (!ov->is_scalar_type())
379 {
380 warning(QUOTED(OCT_FN_NAME)": dicomdict gives VR of IS for %s, octave value is %s", keyword.c_str(), ov->class_name().c_str());
381 }
382 int32_t val=ov->int32_scalar_value() ;
383 char buf[16];
384 snprintf (buf, 14, "%i", val);
385 int len = strlen(buf);
386 // ensure even number of chars
387 if (len%2 == 1)
388 {
389 buf[len]=' ';
390 buf[len+1]='\0';
391 }
392 if (trial)
393 octave_stdout << '[' << buf << ']' << std::endl;
394 de->SetByteValue ( buf, gdcm::VL((uint32_t)strlen(buf)) );
395 }
396 else if ( entry.GetVR() & gdcm::VR::SQ)
397 {
398 // sequence
399 if (!ov->OV_ISMAP())
400 {
401 warning (QUOTED(OCT_FN_NAME)": dicomdict gives VR of SQ for %s, octave value is %s", keyword.c_str(), ov->class_name().c_str());
402 }
403 octave_stdout << std::endl;
404 //int nObj = ov->numel();
405 octave_map subom = ov->map_value();
406 gdcm::SmartPointer<gdcm::SequenceOfItems> sq = new gdcm::SequenceOfItems();
407 try
408 {
409 structarray2sequence(*sq, &subom, trial, ++sequenceDepth) ;
410 }
411 catch (std::exception&)
412 {
413 return;
414 }
415 sequenceDepth--;
416 }
417 else
418 {
419 if (trial)
420 octave_stdout << " ### not handled ### " << std::endl;
421 *handled = false;
422 }
423 }
424
425 // TODO set HighBit etc using octave class. or cast pixel data using metadata, or just give error if they don't agree
octaveVal2dicomImage(gdcm::ImageWriter * w,octave_value * pixval)426 void octaveVal2dicomImage(gdcm::ImageWriter *w, octave_value *pixval)
427 {
428 if (pixval->ndims() != 2)
429 {
430 error (QUOTED(OCT_FN_NAME)": image has %i dimensions. not implemented. ", (int)pixval->ndims());
431 throw std::exception ();
432 }
433
434 // get current properties we may have gotten from the input
435 gdcm::DataSet ds = w->GetFile().GetDataSet();
436
437 // make image
438 gdcm::SmartPointer<gdcm::Image> im = new gdcm::Image;
439
440 im->SetNumberOfDimensions (2);
441 im->SetDimension (0, pixval->dims()(0));
442 im->SetDimension (1, pixval->dims()(1));
443
444 gdcm::Attribute<0x0028,0x0004> pi_at;
445 if (ds.FindDataElement (pi_at.GetTag()))
446 {
447 pi_at.SetFromDataElement (ds.GetDataElement (pi_at.GetTag()));
448
449 gdcm::PhotometricInterpretation::PIType type = gdcm::PhotometricInterpretation::GetPIType(pi_at.GetValue());
450 if (type != gdcm::PhotometricInterpretation().GetType())
451 im->SetPhotometricInterpretation( type );
452 else
453 im->SetPhotometricInterpretation( gdcm::PhotometricInterpretation::MONOCHROME1 );
454 }
455 else
456 {
457 im->SetPhotometricInterpretation( gdcm::PhotometricInterpretation::MONOCHROME1 );
458 }
459
460 // prepare to make data from mat available
461 char * matbuf = 0; // to be set to point to octave matrix
462
463 if (pixval->is_uint8_type())
464 {
465 uint8NDArray data = pixval->uint8_array_value().transpose();
466 uint8_t * buf = new uint8_t[data.numel ()];
467 memcpy (buf, data.fortran_vec(), data.numel()*sizeof(uint8_t));
468 matbuf = (char *)buf;
469 im->GetPixelFormat().SetScalarType(gdcm::PixelFormat::UINT8);
470 }
471 else if (pixval->is_uint16_type())
472 {
473 uint16NDArray data = pixval->uint16_array_value().transpose();
474 uint16_t * buf = new uint16_t[data.numel ()];
475 memcpy (buf, data.fortran_vec(), data.numel()*sizeof(uint16_t));
476 matbuf = (char *)buf;
477 im->GetPixelFormat().SetScalarType(gdcm::PixelFormat::UINT16);
478 }
479 else if (pixval->is_uint32_type())
480 {
481 uint32NDArray data = pixval->uint32_array_value().transpose();
482 uint32_t * buf = new uint32_t[data.numel ()];
483 memcpy (buf, data.fortran_vec(), data.numel()*sizeof(uint32_t));
484 matbuf = (char *)buf;
485 im->GetPixelFormat().SetScalarType(gdcm::PixelFormat::UINT32);
486 }
487 else if (pixval->is_int8_type())
488 {
489 int8NDArray data = pixval->int8_array_value().transpose();
490 int8_t * buf = new int8_t[data.numel ()];
491 memcpy (buf, data.fortran_vec(), data.numel()*sizeof(int8_t));
492 matbuf = (char *)buf;
493 im->GetPixelFormat().SetScalarType(gdcm::PixelFormat::INT8);
494 }
495 else if (pixval->is_int16_type())
496 {
497 int16NDArray data = pixval->int16_array_value().transpose();
498 int16_t * buf = new int16_t[data.numel ()];
499 memcpy (buf, data.fortran_vec(), data.numel()*sizeof(int16_t));
500 matbuf = (char *)buf;
501 im->GetPixelFormat().SetScalarType(gdcm::PixelFormat::INT16);
502 }
503 else if (pixval->is_int32_type())
504 {
505 int32NDArray data = pixval->int32_array_value().transpose();
506 int32_t * buf = new int32_t[data.numel ()];
507 memcpy (buf, data.fortran_vec(), data.numel()*sizeof(int32_t));
508 matbuf = (char *)buf;
509 im->GetPixelFormat().SetScalarType(gdcm::PixelFormat::INT32);
510 }
511 else
512 { // TODO: gdcm::PixelFormat has float types too.
513 error(QUOTED(OCT_FN_NAME)": cannot write this type");
514 throw std::exception();
515 }
516
517 unsigned long buflen = im->GetBufferLength();
518 if (buflen != pixval->byte_size())
519 {
520 delete [] matbuf;
521 error (QUOTED(OCT_FN_NAME)": prepared DICOM buffer size(%lu) does not match Octave array buffer size(%i).",buflen, (int)pixval->byte_size());
522 throw std::exception();
523 }
524
525 gdcm::DataElement pixeldata (gdcm::Tag(0x7fe0,0x0010));
526 pixeldata.SetByteValue (matbuf, buflen);
527
528 im->SetDataElement (pixeldata);
529
530 delete [] matbuf;
531
532 w->SetImage (*im);
533
534 // set any image values we already have in our data attributes
535 gdcm::Attribute<0x0028,0x0030> sp_at;
536 if (ds.FindDataElement(sp_at.GetTag()))
537 {
538 sp_at.SetFromDataElement( ds.GetDataElement(sp_at.GetTag()) );
539 im->SetSpacing(0, sp_at.GetValue(0));
540 im->SetSpacing(1, sp_at.GetValue(1));
541 }
542 gdcm::Attribute<0x0020,0x0037> dir_at;
543 if (ds.FindDataElement(dir_at.GetTag()))
544 {
545 dir_at.SetFromDataElement( ds.GetDataElement(dir_at.GetTag()) );
546 im->SetDirectionCosines(0, (double)dir_at.GetValue(0));
547 im->SetDirectionCosines(1, (double)dir_at.GetValue(1));
548 im->SetDirectionCosines(2, (double)dir_at.GetValue(2));
549 im->SetDirectionCosines(3, (double)dir_at.GetValue(3));
550 im->SetDirectionCosines(4, (double)dir_at.GetValue(4));
551 im->SetDirectionCosines(5, (double)dir_at.GetValue(5));
552 }
553 gdcm::Attribute<0x0020,0x0032> or_at;
554 if (ds.FindDataElement(or_at.GetTag()))
555 {
556 or_at.SetFromDataElement( ds.GetDataElement(or_at.GetTag()) );
557 im->SetOrigin(0, or_at.GetValue(0));
558 im->SetOrigin(1, or_at.GetValue(1));
559 im->SetOrigin(2, or_at.GetValue(2));
560 }
561 gdcm::Attribute<0x0028,0x1053> sl_at;
562 if (ds.FindDataElement(sl_at.GetTag()))
563 {
564 sl_at.SetFromDataElement( ds.GetDataElement(sl_at.GetTag()) );
565 im->SetSlope(sl_at.GetValue());
566 }
567 gdcm::Attribute<0x0028,0x1052> int_at;
568 if (ds.FindDataElement(int_at.GetTag()))
569 {
570 int_at.SetFromDataElement( ds.GetDataElement(int_at.GetTag()) );
571 im->SetIntercept(int_at.GetValue());
572 }
573
574 return;
575 }
576
genMinimalMetaData(gdcm::ImageWriter * w,gdcm::File * file)577 void genMinimalMetaData(gdcm::ImageWriter *w, gdcm::File *file)
578 {
579 gdcm::UIDGenerator uid; // helper for uid generation
580
581 // Step 2: DERIVED object
582 gdcm::FileDerivation fd; //TODO: copied this: don't understand it
583 // For the pupose of this execise we will pretend that this image is referencing
584 // two source image (we need to generate fake UID for that).
585 const char ReferencedSOPClassUID[] = "1.2.840.10008.5.1.4.1.1.7"; // Secondary Capture
586 fd.AddReference( ReferencedSOPClassUID, uid.Generate() );
587 fd.AddReference( ReferencedSOPClassUID, uid.Generate() );
588
589 // Again for the purpose of the exercise we will pretend that the image is a
590 // multiplanar reformat (MPR):
591 // CID 7202 Source Image Purposes of Reference
592 // {"DCM",121322,"Source image for image processing operation"},
593 fd.SetPurposeOfReferenceCodeSequenceCodeValue( 121322 );
594 // CID 7203 Image Derivation
595 // { "DCM",113072,"Multiplanar reformatting" },
596 fd.SetDerivationCodeSequenceCodeValue( 113072 );
597 fd.SetFile( *file );
598 // If all Code Value are ok the filter will execute properly
599 if (! fd.Derive())
600 {
601 error(QUOTED(OCT_FN_NAME)": file derivation failed");
602 throw std::exception();
603 }
604
605 w->SetFile (fd.GetFile ());
606
607 return;
608 }
609 /*
610 %!shared testfile1, testfile2
611 %! testfile1 = tempname ();
612 %! testfile2 = tempname ();
613
614 %!test
615 %! wdata = uint8 (10*rand (10,10));
616 %! dicomwrite (wdata, testfile1);
617 %! rdata = dicomread (testfile1);
618 %! assert(wdata, rdata);
619
620 %!fail ("dicominfo", "dicominfo: one arg required: dicom filename");
621 %!fail ("dicominfo ([])");
622 %!fail ("dicominfo ([],1)");
623
624 %!test
625 %! wdata = uint8 (10*rand (10,10));
626 %! dicomwrite (wdata, testfile1);
627 %! info = dicominfo (testfile1);
628 %! dicomwrite (wdata, testfile2, info);
629
630 %!test
631 %! wdata = uint8 (10*rand (10,10));
632 %! s.PatientName = "fred";
633 %! s.PatientID = "1";
634 %! dicomwrite (wdata, testfile2, s);
635 %! p = dicominfo (testfile2);
636 %! assert (p.PatientName, "fred");
637 %! assert (p.PatientID, "1");
638
639 %!test
640 %! # test we have control of image property information
641 %! wdata = uint8 (10*rand (10,10));
642 %! dicomwrite (wdata, testfile2);
643 %! p = dicominfo (testfile2);
644 %! assert (p.PhotometricInterpretation, "MONOCHROME1 ");
645 %! s.PhotometricInterpretation = "MONOCHROME2";
646 %! dicomwrite (wdata, testfile2, s);
647 %! p = dicominfo (testfile2);
648 %! assert (p.PhotometricInterpretation, "MONOCHROME2 ");
649
650 %!test
651 %! if exist (testfile1, 'file')
652 %! delete (testfile1);
653 %! endif
654 %! if exist (testfile2, 'file')
655 %! delete (testfile2);
656 %! endif
657 */
658