1 /*
2  *
3  *  Copyright (C) 2002-2016, OFFIS e.V.
4  *  All rights reserved.  See COPYRIGHT file for details.
5  *
6  *  This software and supporting documentation were developed by
7  *
8  *    OFFIS e.V.
9  *    R&D Division Health
10  *    Escherweg 2
11  *    D-26121 Oldenburg, Germany
12  *
13  *
14  *  Module:  dcmimage
15  *
16  *  Author:  Marco Eichelberg
17  *
18  *  Purpose: class DcmQuantColorTable
19  *
20  */
21 
22 
23 #ifndef DIQTCTAB_H
24 #define DIQTCTAB_H
25 
26 #include "dcmtk/config/osconfig.h"
27 #include "dcmtk/ofstd/oftypes.h"      /* for OFBool */
28 #include "dcmtk/ofstd/ofcond.h"       /* for OFCondition */
29 #include "dcmtk/dcmimage/diqtpix.h"   /* for DcmQuantPixel */
30 #include "dcmtk/dcmimage/diqthash.h"  /* for DcmQuantHistogramItem */
31 #include "dcmtk/ofstd/ofstring.h"     /* for class OFString */
32 
33 
34 class DicomImage;
35 class DcmItem;
36 
37 
38 /** this class implements a color table that can either be
39  *  a look-up table or an image color histogram.
40  */
41 class DCMTK_DCMIMAGE_EXPORT DcmQuantColorTable
42 {
43 public:
44 
45   /// constructor
46   DcmQuantColorTable();
47 
48   /// destructor
49   ~DcmQuantColorTable();
50 
51   /// resets the object to default-constructed state
52   void clear();
53 
54   /** returns the number of colors in the color table
55    *  @return number of colors in color table
56    */
getColors()57   inline unsigned long getColors() const
58   {
59     return numColors;
60   }
61 
62   /** creates a description string suitable for use as
63    *  Derivation Description.
64    *  @param str description string returned in this parameter
65    */
66   void setDescriptionString(OFString& str) const;
67 
68   /** creates a color table containing a histogram of the given
69    *  image.  Pixel sample values are downsampled if necessary
70    *  to make sure the histogram fits into the given size limit.
71    *  @param image color image for which a histogram is computed
72    *  @param maxcolors maximum number of colors allowed in histogram.
73    *    If necessary, pixel sample values are down-sampled to enforce
74    *    this maximum.
75    *  @return EC_Normal if successful, an error code otherwise.
76    */
77   OFCondition computeHistogram(DicomImage& image, unsigned long maxcolors);
78 
79   /** after a call to computeHistogram(), this method
80    *  returns the maximum pixel value to which all color samples
81    *  were down-sampled during computation of the histogram.
82    */
getMaxVal()83   inline unsigned long getMaxVal() const
84   {
85     return maxval;
86   }
87 
88   /** returns the color at index idx.
89    *  @param idx index, must be < getColors()
90    *  @return const reference to color
91    */
getPixel(unsigned long idx)92   inline const DcmQuantPixel& getPixel(unsigned long idx) const
93   {
94 #ifdef DEBUG
95     assert(array && idx < numColors);
96 #endif
97     return *(array[idx]);
98   }
99 
100   /** returns the red color component at index idx
101    *  @param idx index, must be < getColors()
102    *  @return red color component
103    */
getRed(unsigned long idx)104   inline DcmQuantComponent getRed(unsigned long idx) const
105   {
106 #ifdef DEBUG
107     assert(array && idx < numColors);
108 #endif
109     return array[idx]->getRed();
110   }
111 
112   /** returns the green color component at index idx
113    *  @param idx index, must be < getColors()
114    *  @return green color component
115    */
getGreen(unsigned long idx)116   inline DcmQuantComponent getGreen(unsigned long idx) const
117   {
118 #ifdef DEBUG
119     assert(array && idx < numColors);
120 #endif
121     return array[idx]->getGreen();
122   }
123 
124   /** returns the blue color component at index idx
125    *  @param idx index, must be < getColors()
126    *  @return blue color component
127    */
getBlue(unsigned long idx)128   inline DcmQuantComponent getBlue(unsigned long idx) const
129   {
130 #ifdef DEBUG
131     assert(array && idx < numColors);
132 #endif
133     return array[idx]->getBlue();
134   }
135 
136   /** computes a color LUT for the given image histogram.
137    *  This median-cut colormap generator is based
138    *  on Paul Heckbert's paper "Color Image Quantization for Frame Buffer
139    *  Display", SIGGRAPH '82 Proceedings, page 297.
140    *  @param histogram image color histogram
141    *  @param sum number of pixels in image (colums * rows * frames)
142    *  @param theMaxval maximum value to which pixels were
143    *    downsampled for histogram computation
144    *  @param numberOfColors desired number of colors in color LUT
145    *  @param largeType algorithm used for determining the largest dimension
146    *    in the Median Cut algorithm
147    *  @param repType algorithm for choosing a representative color for each
148    *  box in the Median Cut algorithm
149    *  @return EC_Normal if successful, an error code otherwise.
150    */
151   OFCondition medianCut(
152     DcmQuantColorTable& histogram,
153     unsigned long sum,
154     unsigned long theMaxval,
155     unsigned long numberOfColors,
156     DcmLargestDimensionType largeType,
157     DcmRepresentativeColorType repType);
158 
159   /** determines for a given color the closest match in the color LUT.
160    *  @param px color to look up in LUT
161    *  @return index of closest match in LUT, -1 if look-up table empty
162    */
computeIndex(const DcmQuantPixel & px)163   inline int computeIndex(const DcmQuantPixel& px) const
164   {
165     int result = -1;
166     int r2, g2, b2;
167     long newdist;
168     int r1 = OFstatic_cast(int, px.getRed());
169     int g1 = OFstatic_cast(int, px.getGreen());
170     int b1 = OFstatic_cast(int, px.getBlue());
171     long dist = 2000000000;
172     for (unsigned long i = 0; i < numColors; ++i)
173     {
174         r2 = r1 - OFstatic_cast(int, array[i]->getRed());
175         g2 = g1 - OFstatic_cast(int, array[i]->getGreen());
176         b2 = b1 - OFstatic_cast(int, array[i]->getBlue());
177         newdist = r2*r2 + g2*g2 + b2*b2;
178         if (newdist < dist)
179         {
180             result = OFstatic_cast(int, i);
181             dist = newdist;
182             if (dist < array[i]->getValue()) i=numColors; // break out of for loop
183         }
184     }
185     return result;
186   }
187 
188   /** writes the current color table into a DICOM object, encoded as
189    *  Red/Green/Blue Palette Color Lookup Table and Data.
190    *  @param target DICOM dataset to write to
191    *  @param writeAsOW if true, LUT data is encoded as OW, otherwise LUT data
192    *    is encoded as US.  A LUT with 64k entries is always encoded as OW since
193    *    a US data element with 64k entries cannot be written in explicit VR.
194    *  @param write16BitEntries if true, LUT data is encoded with 16 bits per entry
195    *  @return EC_Normal if successful, an error code otherwise.
196    */
197   OFCondition write(
198     DcmItem& target,
199     OFBool writeAsOW,
200     OFBool write16BitEntries);
201 
202 
203 private:
204 
205   /** after a call to medianCut(), this method computes for each entry in
206    *  the color map the minimum of the euclidean distances to any other
207    *  of the entries.  Any color which has an euclidean distance of less
208    *  than half of this distance is necessarily mapped to this entry.
209    *  This data is used by computeIndex()
210    */
211   void computeClusters();
212 
213   /// private undefined copy constructor
214   DcmQuantColorTable(const DcmQuantColorTable& src);
215 
216   /// private undefined copy assignment operator
217   DcmQuantColorTable& operator=(const DcmQuantColorTable& src);
218 
219   /// color table data
220   DcmQuantHistogramItemPointer *array;
221 
222   /// number of entries in color table
223   unsigned long numColors;
224 
225   /** maximum pixel value to which all color samples
226    *  were down-sampled during computation of the histogram.
227    */
228   unsigned long maxval;
229 
230 };
231 
232 #endif
233