OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
color_quant.c
Go to the documentation of this file.
1 /*
2  * This was adapted from the code found here.
3  *
4  * https://rosettacode.org/wiki/Color_quantization/C
5  */
6 
7 
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <fcntl.h>
11 #include <unistd.h>
12 #include <ctype.h>
13 #include <string.h>
14 #include <stdint.h>
15 #include <math.h>
16 
17 #include <imageutils.h>
18 
19 #define ON_INHEAP 1
20 
21 
22 
23 typedef struct oct_node_t oct_node_t, *oct_node;
24 
25 struct oct_node_t {
26  int64_t r, g, b; /* sum of all child node colors */
28  uint8_t n_kids, kid_idx, flags, depth;
29  oct_node kids[8], parent;
30 };
31 
32 typedef struct {
33  int alloc, n;
34  oct_node* buf;
35 } node_heap;
36 
37 
38 static oct_node pool = 0;
39 
40 img_rgb_t* img_new(int w, int h) {
41  img_rgb_t* img = malloc(sizeof (img_rgb_t) + h * w * 3);
42  img->w = w;
43  img->h = h;
44  img->pix = (uint8_t *) (img + 1);
45  return img;
46 }
47 
49  // this works since the image structure and pixels memory
50  // were allocated in one big block
51  free(img);
52 }
53 
55  FILE *fp = fopen(filename, "wb");
56  if (!fp) return 0;
57  fprintf(fp, "P6\n%d %d\n255\n", img->w, img->h);
58  fwrite(img->pix, 1, 3 * img->w * img->h, fp);
59  fclose(fp);
60  return 1;
61 }
62 
63 int read_num(FILE *f) {
64  int n;
65  while (!fscanf(f, "%d ", &n)) {
66  if ((n = fgetc(f)) == '#') {
67  while ((n = fgetc(f)) != '\n')
68  if (n == EOF) return 0;
69  } else
70  return 0;
71  }
72  return n;
73 }
74 
76  FILE *fp = fopen(filename, "rb");
77  int w, h, maxval;
78  img_rgb_t* img = 0;
79  if (!fp) return 0;
80 
81  if (fgetc(fp) != 'P' || fgetc(fp) != '6' || !isspace(fgetc(fp)))
82  goto bail;
83 
84  w = read_num(fp);
85  h = read_num(fp);
86  maxval = read_num(fp);
87  if (!w || !h || !maxval) goto bail;
88 
89  img = img_new(w, h);
90  fread(img->pix, 1, 3 * w * h, fp);
91 bail:
92  if (fp) fclose(fp);
93  return img;
94 }
95 
96 /* cmp function that decides the ordering in the heap. This is how we determine
97  which octree node to fold next, the heart of the algorithm. */
98 int cmp_node(oct_node a, oct_node b) {
99  if (a->n_kids < b->n_kids) return -1;
100  if (a->n_kids > b->n_kids) return 1;
101 
102  int ac = a->count >> a->depth;
103  int bc = b->count >> b->depth;
104  return ac < bc ? -1 : ac > bc;
105 }
106 
107 void down_heap(node_heap *h, oct_node p) {
108  int n = p->heap_idx, m;
109  while (1) {
110  m = n * 2;
111  if (m >= h->n) break;
112  if (m + 1 < h->n && cmp_node(h->buf[m], h->buf[m + 1]) > 0) m++;
113 
114  if (cmp_node(p, h->buf[m]) <= 0) break;
115 
116  h->buf[n] = h->buf[m];
117  h->buf[n]->heap_idx = n;
118  n = m;
119  }
120  h->buf[n] = p;
121  p->heap_idx = n;
122 }
123 
124 void up_heap(node_heap *h, oct_node p) {
125  int n = p->heap_idx;
126  oct_node prev;
127 
128  while (n > 1) {
129  prev = h->buf[n / 2];
130  if (cmp_node(p, prev) >= 0) break;
131 
132  h->buf[n] = prev;
133  prev->heap_idx = n;
134  n /= 2;
135  }
136  h->buf[n] = p;
137  p->heap_idx = n;
138 }
139 
140 void heap_add(node_heap *h, oct_node p) {
141  if ((p->flags & ON_INHEAP)) {
142  down_heap(h, p);
143  up_heap(h, p);
144  return;
145  }
146 
147  p->flags |= ON_INHEAP;
148  if (!h->n) h->n = 1;
149  if (h->n >= h->alloc) {
150  while (h->n >= h->alloc) h->alloc += 1024;
151  h->buf = realloc(h->buf, sizeof (oct_node) * h->alloc);
152  }
153 
154  p->heap_idx = h->n;
155  h->buf[h->n++] = p;
156  up_heap(h, p);
157 }
158 
159 oct_node pop_heap(node_heap *h) {
160  if (h->n <= 1) return 0;
161 
162  oct_node ret = h->buf[1];
163  h->buf[1] = h->buf[--h->n];
164 
165  h->buf[h->n] = 0;
166 
167  h->buf[1]->heap_idx = 1;
168  down_heap(h, h->buf[1]);
169 
170  return ret;
171 }
172 
173 oct_node node_new(uint8_t idx, uint8_t depth, oct_node p) {
174  static int len = 0;
175  if (len <= 1) {
176  oct_node p = calloc(sizeof (oct_node_t), 2048);
177  p->parent = pool;
178  pool = p;
179  len = 2047;
180  }
181 
182  oct_node x = pool + len--;
183  x->kid_idx = idx;
184  x->depth = depth;
185  x->parent = p;
186  if (p) p->n_kids++;
187  return x;
188 }
189 
190 void node_free() {
191  oct_node p;
192  while (pool) {
193  p = pool->parent;
194  free(pool);
195  pool = p;
196  }
197 }
198 
199 /* adding a color triple to octree */
200 oct_node node_insert(oct_node root, uint8_t *pix) {
201  uint8_t i, bit, depth = 0;
202 
203  for (bit = 1 << 7; ++depth < 8; bit >>= 1) {
204  i = !!(pix[1] & bit) * 4 + !!(pix[0] & bit) * 2 + !!(pix[2] & bit);
205  if (!root->kids[i])
206  root->kids[i] = node_new(i, depth, root);
207 
208  root = root->kids[i];
209  }
210 
211  root->r += pix[0];
212  root->g += pix[1];
213  root->b += pix[2];
214  root->count++;
215  return root;
216 }
217 
218 /* remove a node in octree and add its count and colors to parent node. */
219 oct_node node_fold(oct_node p) {
220  if (p->n_kids) abort();
221  oct_node q = p->parent;
222  q->count += p->count;
223 
224  q->r += p->r;
225  q->g += p->g;
226  q->b += p->b;
227  q->n_kids--;
228  q->kids[p->kid_idx] = 0;
229  return q;
230 }
231 
232 /* traverse the octree just like construction, but this time we replace the pixel
233  color with color stored in the tree node */
234 void color_replace(oct_node root, uint8_t *pix) {
235  uint8_t i, bit;
236 
237  for (bit = 1 << 7; bit; bit >>= 1) {
238  i = !!(pix[1] & bit) * 4 + !!(pix[0] & bit) * 2 + !!(pix[2] & bit);
239  if (!root->kids[i]) break;
240  root = root->kids[i];
241  }
242 
243  pix[0] = root->r;
244  pix[1] = root->g;
245  pix[2] = root->b;
246 }
247 
248 uint8_t find_color_index(oct_node root, uint8_t* pix) {
249  uint8_t i, bit;
250 
251  for (bit = 1 << 7; bit; bit >>= 1) {
252  i = !!(pix[1] & bit) * 4 + !!(pix[0] & bit) * 2 + !!(pix[2] & bit);
253  if (!root->kids[i]) break;
254  root = root->kids[i];
255  }
256 
257  return root->heap_idx - 1;
258 }
259 
260 /* Building an octree and keep leaf nodes in a bin heap. Afterwards remove first node
261  in heap and fold it into its parent node (which may now be added to heap), until heap
262  contains required number of colors. */
263 void img_color_quant(img_rgb_t* im, int n_colors, int dither) {
264  int i;
265  uint8_t *pix = im->pix;
266  node_heap heap = {0, 0, 0};
267 
268  oct_node root = node_new(0, 0, 0), got;
269  for (i = 0; i < im->w * im->h; i++, pix += 3)
270  heap_add(&heap, node_insert(root, pix));
271 
272  while (heap.n > n_colors + 1)
273  heap_add(&heap, node_fold(pop_heap(&heap)));
274 
275  double c;
276  for (i = 1; i < heap.n; i++) {
277  got = heap.buf[i];
278  c = got->count;
279  got->r = got->r / c + .5;
280  got->g = got->g / c + .5;
281  got->b = got->b / c + .5;
282  }
283 
284  for (i = 0, pix = im->pix; i < im->w * im->h; i++, pix += 3)
285  color_replace(root, pix);
286 
287  node_free();
288  free(heap.buf);
289 }
290 
300 void img_color_palette_quantization(img_rgb_t* in_image, int num_colors,
301  uint8_t* palette, uint8_t* out_image) {
302  int i;
303  uint8_t *pix = in_image->pix;
304  node_heap heap = {0, 0, 0};
305 
306  // load up the oct tree and the heap
307  oct_node root = node_new(0, 0, 0), got;
308  for (i = 0; i < in_image->w * in_image->h; i++, pix += 3)
309  heap_add(&heap, node_insert(root, pix));
310 
311  // reduce the colors to the requested number
312  while (heap.n > num_colors + 1)
313  heap_add(&heap, node_fold(pop_heap(&heap)));
314 
315  // write out the color palette
316  double c;
317  pix = palette;
318  for (i = 1; i < heap.n; i++) {
319  got = heap.buf[i];
320  c = got->count;
321  *(pix++) = got->r / c + .5;
322  *(pix++) = got->g / c + .5;
323  *(pix++) = got->b / c + .5;
324  }
325  for (; i <= num_colors; i++) { // zero out the unused palette entries
326  *(pix++) = 0;
327  *(pix++) = 0;
328  *(pix++) = 0;
329  }
330 
331  // write out the image data
332  for (i = 0, pix = in_image->pix; i < in_image->w * in_image->h; i++, pix += 3) {
333  out_image[i] = find_color_index(root, pix);
334  }
335 
336  node_free();
337  free(heap.buf);
338 }
339 
oct_node node_fold(oct_node p)
Definition: color_quant.c:219
data_t q
Definition: decode_rs.h:74
oct_node node_insert(oct_node root, uint8_t *pix)
Definition: color_quant.c:200
int cmp_node(oct_node a, oct_node b)
Definition: color_quant.c:98
int64_t r
Definition: color_quant.c:26
int heap_idx
Definition: color_quant.c:27
data_t root[NROOTS]
Definition: decode_rs.h:78
void down_heap(node_heap *h, oct_node p)
Definition: color_quant.c:107
oct_node parent
Definition: color_quant.c:29
uint8_t * pix
Definition: imageutils.h:16
img_rgb_t * img_new(int w, int h)
Definition: color_quant.c:40
float h[MODELMAX]
Definition: atrem_corl1.h:131
int64_t g
Definition: color_quant.c:26
oct_node * buf
Definition: color_quant.c:34
void img_free(img_rgb_t *img)
Definition: color_quant.c:48
oct_node pop_heap(node_heap *h)
Definition: color_quant.c:159
int32_t im
Definition: atrem_corl1.h:161
uint8_t find_color_index(oct_node root, uint8_t *pix)
Definition: color_quant.c:248
double precision function f(R1)
Definition: tmd.lp.f:1454
int read_num(FILE *f)
Definition: color_quant.c:63
oct_node kids[8]
Definition: color_quant.c:29
uint8_t flags
Definition: color_quant.c:28
uint8_t kid_idx
Definition: color_quant.c:28
void up_heap(node_heap *h, oct_node p)
Definition: color_quant.c:124
int64_t b
Definition: color_quant.c:26
void img_color_quant(img_rgb_t *im, int n_colors, int dither)
Definition: color_quant.c:263
char filename[FILENAME_MAX]
Definition: atrem_corl1.h:122
float * ac[MAX_BANDS]
oct_node node_new(uint8_t idx, uint8_t depth, oct_node p)
Definition: color_quant.c:173
#define isspace(c)
data_t b[NROOTS+1]
Definition: decode_rs.h:77
void node_free()
Definition: color_quant.c:190
int img_write_ppm(img_rgb_t *img, char *filename)
Definition: color_quant.c:54
img_rgb_t * img_read_ppm(char *filename)
Definition: color_quant.c:75
void color_replace(oct_node root, uint8_t *pix)
Definition: color_quant.c:234
void heap_add(node_heap *h, oct_node p)
Definition: color_quant.c:140
void img_color_palette_quantization(img_rgb_t *in_image, int num_colors, uint8_t *palette, uint8_t *out_image)
Definition: color_quant.c:300
no change in intended resolving MODur00064 Corrected handling of bad ephemeris attitude resolving resolving GSFcd00179 Corrected handling of fill values for[Sensor|Solar][Zenith|Azimuth] resolving MODxl01751 Changed to validate LUT version against a value retrieved from the resolving MODxl02056 Changed to calculate Solar Diffuser angles without adjustment for estimated post launch changes in the MODIS orientation relative to incidentally resolving defects MODxl01766 Also resolves MODxl01947 Changed to ignore fill values in SCI_ABNORM and SCI_STATE rather than treating them as resolving MODxl01780 Changed to use spacecraft ancillary data to recognise when the mirror encoder data is being set by side A or side B and to change calculations accordingly This removes the need for seperate LUTs for Side A and Side B data it makes the new LUTs incompatible with older versions of the and vice versa Also resolves MODxl01685 A more robust GRing algorithm is being which will create a non default GRing anytime there s even a single geolocated pixel in a granule Removed obsolete messages from seed as required for compatibility with version of the SDP toolkit Corrected test output file names to end in per delivery and then split off a new MYD_PR03 pcf file for Aqua Added AssociatedPlatformInstrumentSensor to the inventory metadata in MOD01 mcf and MOD03 mcf Created new versions named MYD01 mcf and MYD03 where AssociatedPlatformShortName is rather than Terra The program itself has been changed to read the Satellite Instrument validate it against the input L1A and LUT and to use it determine the correct files to retrieve the ephemeris and attitude data from Changed to produce a LocalGranuleID starting with MYD03 if run on Aqua data Added the Scan Type file attribute to the Geolocation copied from the L1A and attitude_angels to radians rather than degrees The accumulation of Cumulated gflags was moved from GEO_validate_earth_location c to GEO_locate_one_scan c
Definition: HISTORY.txt:464
uint8_t depth
Definition: color_quant.c:28
int i
Definition: decode_rs.h:71
PGE01 indicating that PGE02 PGE01 V6 for and PGE01 V2 for MOD03 were used to produce the granule By convention adopted in all MODIS Terra PGE02 code versions are The fourth digit of the PGE02 version denotes the LUT version used to produce the granule The source of the metadata environment variable ProcessingCenter was changed from a QA LUT value to the Process Configuration A sign used in error in the second order term was changed to a
Definition: HISTORY.txt:424
float p[MODELMAX]
Definition: atrem_corl1.h:131
#define ON_INHEAP
Definition: color_quant.c:19
uint8_t n_kids
Definition: color_quant.c:28