]> Creatis software - gdcm.git/blob - src/gdcmPixelWriteConvert.cxx
avoid Doxygen Warnings
[gdcm.git] / src / gdcmPixelWriteConvert.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmPixelWriteConvert.cxx,v $
5   Language:  C++
6   Date:      $Date: 2007/09/15 11:10:37 $
7   Version:   $Revision: 1.24 $
8                                                                                 
9   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10   l'Image). All rights reserved. See Doc/License.txt or
11   http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
12                                                                                 
13      This software is distributed WITHOUT ANY WARRANTY; without even
14      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15      PURPOSE.  See the above copyright notices for more information.
16                                                                                 
17 =========================================================================*/
18
19 #include "gdcmDebug.h"
20 #include "gdcmPixelWriteConvert.h"
21 #include "gdcmFile.h"
22 #include "gdcmUtil.h"
23
24 #include <vector>
25
26 #define WITHOFFSETTABLE 1
27
28 namespace GDCM_NAME_SPACE
29 {
30 //-----------------------------------------------------------------------------
31 // Public
32
33 //-----------------------------------------------------------------------------
34 // Protected
35
36 //-----------------------------------------------------------------------------
37 // Private
38
39 // Constructor / Destructor
40 /**
41  * \brief Constructor
42  */
43 PixelWriteConvert::PixelWriteConvert() 
44 {
45    ReadData     = 0;
46    ReadDataSize = 0;
47
48    UserData     = 0;
49    UserDataSize = 0;
50    Compressed   = false;
51 }
52
53 /**
54  * \brief Destructor
55  */
56 PixelWriteConvert::~PixelWriteConvert() 
57 {
58    gdcmDebugMacro("PixelWriteConvert::~PixelWriteConvert()" );
59    if( Compressed )
60      {
61      delete[] UserData;
62      }
63 }
64
65 /**
66  * \brief   sets Read Data (and size)
67  * @param   data data (uint8_t is for prototyping. if your data is not uint8_t
68  *                     just cast the pointer for calling the method)
69  * @param   size data size, in bytes
70  */
71 void PixelWriteConvert::SetReadData(uint8_t *data, size_t size)
72 {
73    ReadData = data;
74    ReadDataSize = size;
75 }
76
77 /**
78  * \brief   Sets the internal pointer to the caller's inData
79  *          image representation, WITHOUT COPYING THE DATA.
80  *          - 'image' Pixels are presented as C-like 2D arrays : line per line.
81  *          - 'volume'Pixels are presented as C-like 3D arrays : plane per plane 
82  * \warning Since the pixels are not copied, it is the caller's responsability
83  *          not to deallocate its data before gdcm uses them (e.g. with
84  *          the Write() method )
85  * @param   data data (uint8_t is for prototyping. if your data is not uint8_t
86  *                     just cast the pointer for calling the method)
87  * @param   size size, in bytes.
88  */
89 void PixelWriteConvert::SetUserData(uint8_t *data, size_t size)
90 {
91    UserData = data;
92    UserDataSize = size;
93 }
94
95 /**
96  * \brief   Get Data (UserData or ReadData)
97  * @return  data (uint8_t is for prototyping. if your data is *not* uint8_t
98  *                just cast the returned pointer)
99  */
100 uint8_t *PixelWriteConvert::GetData()
101 {
102    if ( UserData )
103    {
104       return UserData;
105    }
106    else
107    {
108       return ReadData;
109    }
110 }
111
112 /**
113  * \brief   Get Data Size (UserData or ReadData)
114  * @return  size, in bytes.
115  */
116 size_t PixelWriteConvert::GetDataSize()
117 {
118    if ( UserData )
119    {
120       return UserDataSize;
121    }
122    else
123    {
124       return ReadDataSize;
125    }
126 }
127
128
129 typedef std::pair<size_t, uint32_t> JpegPair; //offset, jpeg size
130 typedef std::vector<JpegPair> JpegVector;
131
132 bool gdcm_write_JPEG2000_file (std::ostream *of, char *inputdata, size_t inputlength, 
133   int image_width, int image_height, int numZ, int sample_pixel, int bitsallocated,
134   int sign, int quality);
135
136
137 void WriteDICOMItems(std::ostream *fp, JpegVector &v)
138 {
139   // Item tag:
140   uint16_t group = 0xfffe;
141   uint16_t elem  = 0xe000;
142   GDCM_NAME_SPACE::binary_write(*fp, group);
143   GDCM_NAME_SPACE::binary_write(*fp, elem);
144   // Item Length
145   uint32_t dummy = 0x12345678;
146   size_t offset = fp->tellp();
147   JpegPair jp;
148   jp.first = offset;
149   v.push_back(jp);
150   GDCM_NAME_SPACE::binary_write(*fp, dummy);
151 }
152
153 // PS 3.5, page 66
154 void EncodeWithoutBasicOffsetTable(std::ostream *fp, int numFrag)// JpegVector& v) //, uint32_t length)
155 {
156   assert( numFrag == 1);
157
158   // Item tag:
159   uint16_t group = 0xfffe;
160   uint16_t elem  = 0xe000;
161   GDCM_NAME_SPACE::binary_write(*fp, group);
162   GDCM_NAME_SPACE::binary_write(*fp, elem);
163   // Item Length
164   uint32_t item_length = 0x0000;
165   GDCM_NAME_SPACE::binary_write(*fp, item_length);
166
167 }
168
169 // PS 3.5, page 67
170 void EncodeWithBasicOffsetTable(std::ostream *fp, int numFrag, size_t &start)
171 {
172   // Item tag:
173   uint16_t group = 0xfffe;
174   uint16_t elem  = 0xe000;
175   GDCM_NAME_SPACE::binary_write(*fp, group);
176   GDCM_NAME_SPACE::binary_write(*fp, elem);
177   // Item Length
178   uint32_t item_length = numFrag*4; // sizeof(uint32_t)
179   GDCM_NAME_SPACE::binary_write(*fp, item_length);
180
181   // Just prepare the space
182   start = fp->tellp(); //to be able to rewind
183   for(int i=0; i<numFrag;++i)
184     {
185     uint32_t dummy = 0x0000;
186     GDCM_NAME_SPACE::binary_write(*fp, dummy);
187     }
188 }
189
190 void UpdateBasicOffsetTable(std::ostream *fp, JpegVector const &v, size_t pos)
191 {
192   JpegVector::const_iterator i;
193   fp->seekp( pos );
194   const JpegPair &first = v[0];
195   for(i=v.begin(); i!=v.end(); ++i)
196     {
197     const JpegPair &jp = *i;
198     if(i == v.begin() ){ assert( jp.first - first.first == 0); }
199     uint32_t offset = (uint32_t)(jp.first - first.first);
200     GDCM_NAME_SPACE::binary_write(*fp, offset);
201     //std::cerr << "Updating Table:" << jp.first - first.first << std::endl;
202     }
203 }
204
205 void UpdateJpegFragmentSize(std::ostream *fp, JpegVector const &v)
206 {
207   JpegVector::const_iterator i;
208   for(i= v.begin(); i!=v.end(); ++i)
209     {
210     const JpegPair &jp = *i;
211     fp->seekp( jp.first );
212     uint32_t length = jp.second;
213     GDCM_NAME_SPACE::binary_write(*fp, length);
214     //std::cerr << "Updating:" << jp.first << "," << jp.second << std::endl;
215     }
216 }
217
218 void CloseJpeg(std::ostream *fp, JpegVector &v)
219 {
220   // sequence terminator
221   uint16_t group = 0xfffe;
222   uint16_t elem  = 0xe0dd;
223   GDCM_NAME_SPACE::binary_write(*fp, group);
224   GDCM_NAME_SPACE::binary_write(*fp, elem);
225
226   uint32_t length = 0x0;
227   GDCM_NAME_SPACE::binary_write(*fp, length);
228
229   // Jpeg is done, now update the frag length
230   UpdateJpegFragmentSize(fp, v);
231 }
232
233 // I need to pass the File*. I do not understand how PixelWriteConvert is supposed
234 // to access this information otherwise
235 // size can now be computed from File attributes (what an API...)
236 void PixelWriteConvert::SetCompressJPEG2000UserData(uint8_t *data, size_t size, File *image)
237 {
238   Compressed = true;
239   //char * userData = reinterpret_cast<char*>(UserData);
240
241    std::ostringstream *of = new std::ostringstream();
242     int xsize = image->GetXSize();
243     int ysize = image->GetYSize();
244     int zsize = image->GetZSize();
245     int samplesPerPixel = image->GetSamplesPerPixel();
246    //std::cout << "X: " << xsize << std::endl;
247    //std::cout << "Y: " << ysize << std::endl;
248    //std::cout << "Sample: " << samplesPerPixel << std::endl;
249     int bitsallocated = image->GetBitsAllocated();
250     int sign = image->IsSignedPixelData();
251     unsigned int fragment_size = xsize*ysize*samplesPerPixel * (bitsallocated / 8);
252     //assert( fragment_size*zsize == size );
253     
254     gdcmDebugMacro("fragment_size " << fragment_size << " zsize " << zsize << " size " << size);    
255     assert( abs((long)(fragment_size*zsize-size)) <=1 );
256     
257    JpegVector JpegFragmentSize;
258       gdcmDebugMacro("Call Encode..BasicOffsetTable " );
259 #if WITHOFFSETTABLE
260    size_t bots; //basic offset table start
261    EncodeWithBasicOffsetTable(of, zsize, bots);
262 #else
263    EncodeWithoutBasicOffsetTable(of, 1);
264 #endif
265
266    gdcmDebugMacro("Out of Encode..BasicOffsetTable " );
267    
268    uint8_t *pImageData = data;
269    for(int i=0; i<zsize;i++)
270      {
271      gdcmDebugMacro("Write fragment no " << i );
272      WriteDICOMItems(of, JpegFragmentSize);
273      size_t beg = of->tellp();
274      gdcm_write_JPEG2000_file(of, (char*)pImageData,size, 
275        image->GetXSize(), image->GetYSize(), image->GetZSize(), image->GetSamplesPerPixel(),
276        image->GetBitsAllocated(), sign, 100);
277      //userData, UserDataSize);
278      //     CreateOneFrame(of, pImageData, fragment_size, xsize, ysize, zsize, 
279      //       samplesPerPixel, quality, JpegFragmentSize);
280      //assert( !(fragment_size % 2) );
281      // Update the JpegVector with offset
282      size_t end = of->tellp();
283      //static int i = 0;
284      JpegPair &jp = JpegFragmentSize[i];
285      jp.second = (uint32_t)(end-beg);
286      if( ((end-beg) % 2) )
287        {
288        of->put( '\0' );
289        jp.second += 1;
290        }
291      assert( !(jp.second % 2) );
292      //std::cerr << "DIFF: " << i <<" -> " << jp.second << std::endl;
293      //++i;
294      pImageData += fragment_size;
295      }
296    CloseJpeg(of, JpegFragmentSize);
297 #if WITHOFFSETTABLE
298    UpdateBasicOffsetTable(of, JpegFragmentSize, bots);
299 #endif
300
301
302    size_t of_size = of->str().size();
303    UserData = new uint8_t[of_size];
304    memcpy(UserData, of->str().c_str(), of_size);
305    UserDataSize = of_size;
306    delete of;   
307 }
308
309 bool gdcm_write_JPEG_file8 (std::ostream *fp, char *inputdata, size_t inputlength,
310                            int image_width, int image_height, int numZ,
311                            int sample_pixel, int bitsallocated, int quality);
312 bool gdcm_write_JPEG_file12 (std::ostream *fp, char *inputdata, size_t inputlength,
313                            int image_width, int image_height, int numZ,
314                            int sample_pixel, int bitsallocated, int quality);
315 bool gdcm_write_JPEG_file16 (std::ostream *fp, char *inputdata, size_t inputlength,
316                            int image_width, int image_height, int numZ,
317                            int sample_pixel, int bitsallocated, int quality);
318
319 void PixelWriteConvert::SetCompressJPEGUserData(uint8_t *data, size_t size, File *image)
320 {
321   (void)data;
322   (void)size;
323   (void)image;
324   Compressed = true;
325   //char * userData = reinterpret_cast<char*>(UserData);
326
327    std::ostringstream *of = new std::ostringstream();
328     int xsize = image->GetXSize();
329     int ysize = image->GetYSize();
330     int zsize = image->GetZSize();
331     
332     int samplesPerPixel = image->GetSamplesPerPixel();
333     int bitsAllocated   = image->GetBitsAllocated();
334     int bitsStored      = image->GetBitsStored();
335         
336    //std::cout << "X: " << xsize << std::endl;
337    //std::cout << "Y: " << ysize << std::endl;
338    //std::cout << "Sample: " << samplesPerPixel << std::endl;
339    
340     gdcmDebugMacro( "bitsAllocated " << bitsAllocated << " bitsStored " <<bitsStored <<
341                     std::endl);
342    
343
344     unsigned int fragment_size = xsize*ysize*samplesPerPixel * (bitsAllocated / 8);
345     gdcmDebugMacro("fragment_size " << fragment_size << " zsize " << zsize << " size " << size);
346     assert( abs((long)(fragment_size*zsize-size)) <=1 );
347
348    JpegVector JpegFragmentSize;
349 #if WITHOFFSETTABLE
350    size_t bots; //basic offset table start
351    EncodeWithBasicOffsetTable(of, zsize, bots);
352 #else
353    EncodeWithoutBasicOffsetTable(of, 1);
354 #endif
355
356       // to avoid major troubles when BitsStored == 8 && BitsAllocated==16 !
357
358    if (bitsStored == 8 && bitsAllocated == 16)
359       bitsStored = 16;
360
361    uint8_t *pImageData = data;
362    for(int i=0; i<zsize;i++)
363      {
364      gdcmDebugMacro("Write fragment no " << i );
365      WriteDICOMItems(of, JpegFragmentSize);
366      size_t beg = of->tellp();
367      if( bitsStored == 8 )
368        {
369        gdcm_write_JPEG_file8(of, (char*)pImageData,size, 
370          xsize, ysize, zsize, samplesPerPixel,
371          bitsAllocated, 100 );
372        }
373      else if (bitsStored <= 12)
374        {
375        assert( bitsStored >= 8 );
376        gdcm_write_JPEG_file12(of, (char*)pImageData, size,
377          xsize, ysize, zsize, samplesPerPixel,
378          bitsAllocated, 100);
379        }
380      else if (bitsStored <= 16)
381        {
382        assert( bitsStored >= 12 );
383        gdcm_write_JPEG_file16(of, (char*)pImageData,size, 
384          xsize, ysize, zsize, samplesPerPixel,
385          bitsAllocated, 100);
386        }
387      else if (bitsStored <= 32)  // if we are lucky (?), it will compress as a 2 bytes stream
388        {                            // (Actually it doesn't !)
389        // Just to allow ctest not to abort on 32bits per pixel image RTDOSE.dcm
390        assert( bitsStored >= 16 );
391        gdcmDebugMacro( "Warning : bitsStored>16 not supported by JPEG !" );
392        gdcm_write_JPEG_file16(of, (char*)pImageData, size, 
393          xsize, ysize, zsize, samplesPerPixel,
394          bitsAllocated, 100);
395        }       
396      else
397        {
398        std::cerr << "Major pb : bitsStored =" << bitsStored << std::endl;
399        abort();
400        }
401     size_t end = of->tellp();
402     //static int i = 0;
403     JpegPair &jp = JpegFragmentSize[i];
404     jp.second = (uint32_t)(end-beg);
405     if( ((end-beg) % 2) )
406       {
407       of->put( '\0' );
408       jp.second += 1;
409       }
410     assert( !(jp.second % 2) );
411     //std::cerr << "DIFF: " << i <<" -> " << jp.second << std::endl;
412     //++i;
413
414   //JpegPair &jp = v[0];
415   //jp.second = 15328;
416
417      //userData, UserDataSize);
418      //     CreateOneFrame(of, pImageData, fragment_size, xsize, ysize, zsize, 
419      //       samplesPerPixel, quality, JpegFragmentSize);
420      //assert( !(fragment_size % 2) );
421      pImageData += fragment_size;
422      }
423    CloseJpeg(of, JpegFragmentSize);
424 #if WITHOFFSETTABLE
425    UpdateBasicOffsetTable(of, JpegFragmentSize, bots);
426 #endif
427
428    size_t of_size = of->str().size();
429    UserData = new uint8_t[of_size];
430    memcpy(UserData, of->str().c_str(), of_size);
431    UserDataSize = of_size;
432    delete of;
433 }
434
435
436 //-----------------------------------------------------------------------------
437 // Protected
438 //bool PixelWriteConvert::CompressJPEG2000(uint8_t *data, size_t size)
439 //{
440 //}
441
442 //-----------------------------------------------------------------------------
443 // Private
444
445 //-----------------------------------------------------------------------------
446 // Print
447
448 //-----------------------------------------------------------------------------
449 } // end namespace gdcm