Chan-Vese Segmentation
rgb2ind.c
Go to the documentation of this file.
1 
20 #include <stdio.h>
21 #include <string.h>
22 #include "rgb2ind.h"
23 
25 typedef struct
26 {
27  int Min[3];
28  int Max[3];
29  double Average[3];
30  long NumPixels;
31  long Volume;
32 } bbox;
33 
34 
35 static long BoxVolume(bbox Box);
36 static void MedianSplit(bbox *NewBox, bbox *SplitBox,
37  const unsigned char *RgbImage, long NumPixels);
38 
39 
70 int Rgb2Ind(unsigned char *Dest, unsigned char *Palette, int NumColors,
71  const unsigned char *RgbImage, long NumPixels)
72 {
73  float Merit, MaxMerit;
74  const long NumEl = 3*NumPixels;
75  long i, Dist, MinDist, Diff;
76  int k, BestBox = 0, Channel, NumBoxes = 1;
77  bbox Box[256];
78 
79  if(!Dest || !Palette || NumColors > 256 || !RgbImage || NumPixels <= 0)
80  return 0;
81 
82  /* Determine the smallest box containing all pixels */
83  Box[0].Min[0] = Box[0].Min[1] = Box[0].Min[2] = 255;
84  Box[0].Max[0] = Box[0].Max[1] = Box[0].Max[2] = 0;
85 
86  for(i = 0; i < NumEl; i += 3)
87  for(Channel = 0; Channel < 3; Channel++)
88  {
89  if(Box[0].Min[Channel] > RgbImage[i + Channel])
90  Box[0].Min[Channel] = RgbImage[i + Channel];
91  if(Box[0].Max[Channel] < RgbImage[i + Channel])
92  Box[0].Max[Channel] = RgbImage[i + Channel];
93  }
94 
95  Box[0].NumPixels = NumPixels;
96  Box[0].Volume = BoxVolume(Box[0]);
97 
98  while(NumBoxes < NumColors)
99  {
100  MaxMerit = 0;
101 
102  /* Select a box to split */
103  if(NumBoxes % 4 > 0) /* Split according to NumPixels */
104  {
105  for(k = 0; k < NumBoxes; k++)
106  if(Box[k].Volume > 2
107  && (Merit = (float)Box[k].NumPixels) > MaxMerit)
108  {
109  MaxMerit = Merit;
110  BestBox = k;
111  }
112  }
113  else /* Split according to NumPixels*Volume */
114  for(k = 0; k < NumBoxes; k++)
115  if(Box[k].Volume > 2
116  && (Merit = ((float)Box[k].NumPixels)
117  * ((float)Box[k].Volume)) > MaxMerit)
118  {
119  MaxMerit = Merit;
120  BestBox = k;
121  }
122 
123  /* Split the box */
124  MedianSplit(&Box[NumBoxes], &Box[BestBox], RgbImage, NumPixels);
125  NumBoxes++;
126  }
127 
128  for(k = 0; k < NumBoxes; k++)
129  {
130  Box[k].Average[0] = Box[k].Average[1] = Box[k].Average[2] = 0;
131  Box[k].NumPixels = 0;
132  }
133 
134  /* Compute box averages */
135  for(i = 0; i < NumEl; i += 3)
136  {
137  for(k = 0; k < NumBoxes; k++)
138  if(Box[k].Min[0] <= RgbImage[i + 0]
139  && RgbImage[i + 0] <= Box[k].Max[0]
140  && Box[k].Min[1] <= RgbImage[i + 1]
141  && RgbImage[i + 1] <= Box[k].Max[1]
142  && Box[k].Min[2] <= RgbImage[i + 2]
143  && RgbImage[i + 2] <= Box[k].Max[2])
144  break;
145 
146  if(k == NumBoxes)
147  {
148  fprintf(stderr, "Color (%d,%d,%d) unassigned\n",
149  RgbImage[i + 0], RgbImage[i + 1], RgbImage[i + 2]);
150  k = 0;
151  }
152  else
153  {
154  /* Accumate the average color for each box */
155  Box[k].Average[0] += RgbImage[i + 0];
156  Box[k].Average[1] += RgbImage[i + 1];
157  Box[k].Average[2] += RgbImage[i + 2];
158  }
159 
160  Box[k].NumPixels++;
161  }
162 
163  /* Fill Palette with the box averages */
164  for(k = 0; k < NumBoxes; k++)
165  if(Box[k].NumPixels > 0)
166  for(Channel = 0; Channel < 3; Channel++)
167  {
168  Box[k].Average[Channel] /= Box[k].NumPixels;
169 
170  if(Box[k].Average[Channel] < 0.5)
171  Palette[3*k + Channel] = 0;
172  else if(Box[k].Average[Channel] >= 254.5)
173  Palette[3*k + Channel] = 255;
174  else
175  Palette[3*k + Channel] =
176  (unsigned char)(Box[k].Average[Channel] + 0.5);
177  }
178  else
179  for(Channel = 0; Channel < 3; Channel++)
180  Palette[3*k + Channel] = 0;
181 
182  /* Assign palette indices to quantized pixels */
183  for(i = 0; i < NumEl; i += 3)
184  {
185  /* Find the closest palette color */
186  for(k = 0, MinDist = 1000000; k < NumBoxes; k++)
187  {
188  Diff = ((long)RgbImage[i + 0]) - Palette[3*k + 0];
189  Dist = Diff * Diff;
190  Diff = ((long)RgbImage[i + 1]) - Palette[3*k + 1];
191  Dist += Diff * Diff;
192  Diff = ((long)RgbImage[i + 2]) - Palette[3*k + 2];
193  Dist += Diff * Diff;
194 
195  if(MinDist > Dist)
196  {
197  MinDist = Dist;
198  BestBox = k;
199  }
200  }
201 
202  *Dest = BestBox;
203  Dest++;
204  }
205 
206  return 1;
207 }
208 
209 
211 static long BoxVolume(bbox Box)
212 {
213  return (Box.Max[0] - Box.Min[0] + 1)
214  * (Box.Max[1] - Box.Min[1] + 1)
215  * (Box.Max[2] - Box.Min[2] + 1);
216 }
217 
218 
220 static void MedianSplit(bbox *NewBox, bbox *SplitBox,
221  const unsigned char *RgbImage, long NumPixels)
222 {
223  bbox Box = *SplitBox;
224  const long NumEl = 3*NumPixels;
225  long i, Accum, Hist[256];
226  int Length, MaxLength, MaxDim;
227 
228  /* Determine the longest box dimension */
229  MaxLength = MaxDim = 0;
230 
231  for(i = 0; i < 3; i++)
232  if((Length = Box.Max[i] - Box.Min[i] + 1) > MaxLength)
233  {
234  MaxLength = Length;
235  MaxDim = i;
236  }
237 
238  /* Build a histogram over MaxDim for pixels within Box */
239  memset(Hist, 0, sizeof(long)*256);
240 
241  for(i = 0; i < NumEl; i += 3)
242  if(Box.Min[0] <= RgbImage[i + 0] && RgbImage[i + 0] <= Box.Max[0]
243  && Box.Min[1] <= RgbImage[i + 1] && RgbImage[i + 1] <= Box.Max[1]
244  && Box.Min[2] <= RgbImage[i + 2] && RgbImage[i + 2] <= Box.Max[2])
245  Hist[RgbImage[i + MaxDim]]++;
246 
247  Accum = Hist[i = Box.Min[MaxDim]];
248 
249  /* Set i equal to the median */
250  while(2*Accum < Box.NumPixels && i < 254)
251  Accum += Hist[++i];
252 
253  /* Adjust i so that the median is included with the larger partition */
254  if(i > Box.Min[MaxDim]
255  && ((i - Box.Min[MaxDim]) < (Box.Max[MaxDim] - i - 1)))
256  Accum -= Hist[i--];
257 
258  /* Adjust i to ensure that boxes are not empty */
259  for(; i >= Box.Max[MaxDim]; i--)
260  Accum -= Hist[i];
261 
262  /* Split the boxes */
263  *NewBox = Box;
264  NewBox->Max[MaxDim] = i;
265  NewBox->NumPixels = Accum;
266  NewBox->Volume = BoxVolume(*NewBox);
267 
268  SplitBox->Min[MaxDim] = i + 1;
269  SplitBox->NumPixels = Box.NumPixels - Accum;
270  SplitBox->Volume = BoxVolume(*SplitBox);
271 }