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