image_u8.c
Go to the documentation of this file.
1 /* Copyright (C) 2013-2016, The Regents of The University of Michigan.
2 All rights reserved.
3 This software was developed in the APRIL Robotics Lab under the
4 direction of Edwin Olson, ebolson@umich.edu. This software may be
5 available under alternative licensing terms; contact the address above.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8 1. Redistributions of source code must retain the above copyright notice, this
9  list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright notice,
11  this list of conditions and the following disclaimer in the documentation
12  and/or other materials provided with the distribution.
13 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 The views and conclusions contained in the software and documentation are those
24 of the authors and should not be interpreted as representing official policies,
25 either expressed or implied, of the Regents of The University of Michigan.
26 */
27 
28 #include <assert.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <math.h>
33 
34 #include "common/image_u8.h"
35 #include "common/pnm.h"
36 #include "common/math_util.h"
37 
38 // least common multiple of 64 (sandy bridge cache line) and 24 (stride
39 // needed for RGB in 8-wide vector processing)
40 #define DEFAULT_ALIGNMENT_U8 96
41 
42 image_u8_t *image_u8_create_stride(unsigned int width, unsigned int height, unsigned int stride)
43 {
44  uint8_t *buf = calloc(height*stride, sizeof(uint8_t));
45 
46  // const initializer
47  image_u8_t tmp = { .width = width, .height = height, .stride = stride, .buf = buf };
48 
49  image_u8_t *im = calloc(1, sizeof(image_u8_t));
50  memcpy(im, &tmp, sizeof(image_u8_t));
51  return im;
52 }
53 
54 image_u8_t *image_u8_create(unsigned int width, unsigned int height)
55 {
56  return image_u8_create_alignment(width, height, DEFAULT_ALIGNMENT_U8);
57 }
58 
59 image_u8_t *image_u8_create_alignment(unsigned int width, unsigned int height, unsigned int alignment)
60 {
61  int stride = width;
62 
63  if ((stride % alignment) != 0)
64  stride += alignment - (stride % alignment);
65 
66  return image_u8_create_stride(width, height, stride);
67 }
68 
70 {
71  uint8_t *buf = malloc(in->height*in->stride*sizeof(uint8_t));
72  memcpy(buf, in->buf, in->height*in->stride*sizeof(uint8_t));
73 
74  // const initializer
75  image_u8_t tmp = { .width = in->width, .height = in->height, .stride = in->stride, .buf = buf };
76 
77  image_u8_t *copy = calloc(1, sizeof(image_u8_t));
78  memcpy(copy, &tmp, sizeof(image_u8_t));
79  return copy;
80 }
81 
83 {
84  if (!im)
85  return;
86 
87  free(im->buf);
88  free(im);
89 }
90 
92 // PNM file i/o
94 {
96 }
97 
98 image_u8_t *image_u8_create_from_pnm_alignment(const char *path, int alignment)
99 {
100  pnm_t *pnm = pnm_create_from_file(path);
101  if (pnm == NULL)
102  return NULL;
103 
104  image_u8_t *im = NULL;
105 
106  switch (pnm->format) {
107  case PNM_FORMAT_GRAY: {
108  im = image_u8_create_alignment(pnm->width, pnm->height, alignment);
109 
110  if (pnm->max == 255) {
111  for (int y = 0; y < im->height; y++)
112  memcpy(&im->buf[y*im->stride], &pnm->buf[y*im->width], im->width);
113  } else if (pnm->max == 65535) {
114  for (int y = 0; y < im->height; y++)
115  for (int x = 0; x < im->width; x++)
116  im->buf[y*im->stride + x] = pnm->buf[2*(y*im->width + x)];
117  } else {
118  assert(0);
119  }
120 
121  break;
122  }
123 
124  case PNM_FORMAT_RGB: {
125  im = image_u8_create_alignment(pnm->width, pnm->height, alignment);
126 
127  if (pnm->max == 255) {
128  // Gray conversion for RGB is gray = (r + g + g + b)/4
129  for (int y = 0; y < im->height; y++) {
130  for (int x = 0; x < im->width; x++) {
131  uint8_t gray = (pnm->buf[y*im->width*3 + 3*x+0] + // r
132  pnm->buf[y*im->width*3 + 3*x+1] + // g
133  pnm->buf[y*im->width*3 + 3*x+1] + // g
134  pnm->buf[y*im->width*3 + 3*x+2]) // b
135  / 4;
136 
137  im->buf[y*im->stride + x] = gray;
138  }
139  }
140  } else if (pnm->max == 65535) {
141  for (int y = 0; y < im->height; y++) {
142  for (int x = 0; x < im->width; x++) {
143  int r = pnm->buf[6*(y*im->width + x) + 0];
144  int g = pnm->buf[6*(y*im->width + x) + 2];
145  int b = pnm->buf[6*(y*im->width + x) + 4];
146 
147  im->buf[y*im->stride + x] = (r + g + g + b) / 4;
148  }
149  }
150  } else {
151  assert(0);
152  }
153 
154  break;
155  }
156 
157  case PNM_FORMAT_BINARY: {
158  im = image_u8_create_alignment(pnm->width, pnm->height, alignment);
159 
160  // image is padded to be whole bytes on each row.
161 
162  // how many bytes per row on the input?
163  int pbmstride = (im->width + 7) / 8;
164 
165  for (int y = 0; y < im->height; y++) {
166  for (int x = 0; x < im->width; x++) {
167  int byteidx = y * pbmstride + x / 8;
168  int bitidx = 7 - (x & 7);
169 
170  // ack, black is one according to pbm docs!
171  if ((pnm->buf[byteidx] >> bitidx) & 1)
172  im->buf[y*im->stride + x] = 0;
173  else
174  im->buf[y*im->stride + x] = 255;
175  }
176  }
177  break;
178  }
179  }
180 
181  pnm_destroy(pnm);
182  return im;
183 }
184 
186 {
187  image_u8_t *im = image_u8_create(fim->width, fim->height);
188 
189  for (int y = 0; y < fim->height; y++) {
190  for (int x = 0; x < fim->width; x++) {
191  float v = fim->buf[y*fim->stride + x];
192  im->buf[y*im->stride + x] = (int) (255 * v);
193  }
194  }
195 
196  return im;
197 }
198 
199 
200 int image_u8_write_pnm(const image_u8_t *im, const char *path)
201 {
202  FILE *f = fopen(path, "wb");
203  int res = 0;
204 
205  if (f == NULL) {
206  res = -1;
207  goto finish;
208  }
209 
210  // Only outputs to grayscale
211  fprintf(f, "P5\n%d %d\n255\n", im->width, im->height);
212 
213  for (int y = 0; y < im->height; y++) {
214  if (im->width != fwrite(&im->buf[y*im->stride], 1, im->width, f)) {
215  res = -2;
216  goto finish;
217  }
218  }
219 
220  finish:
221  if (f != NULL)
222  fclose(f);
223 
224  return res;
225 }
226 
227 void image_u8_draw_circle(image_u8_t *im, float x0, float y0, float r, int v)
228 {
229  r = r*r;
230 
231  for (int y = y0-r; y <= y0+r; y++) {
232  for (int x = x0-r; x <= x0+r; x++) {
233  float d = (x-x0)*(x-x0) + (y-y0)*(y-y0);
234  if (d > r)
235  continue;
236 
237  if (x >= 0 && x < im->width && y >= 0 && y < im->height) {
238  int idx = y*im->stride + x;
239  im->buf[idx] = v;
240  }
241  }
242  }
243 }
244 
245 void image_u8_draw_annulus(image_u8_t *im, float x0, float y0, float r0, float r1, int v)
246 {
247  r0 = r0*r0;
248  r1 = r1*r1;
249 
250  assert(r0 < r1);
251 
252  for (int y = y0-r1; y <= y0+r1; y++) {
253  for (int x = x0-r1; x <= x0+r1; x++) {
254  float d = (x-x0)*(x-x0) + (y-y0)*(y-y0);
255  if (d < r0 || d > r1)
256  continue;
257 
258  int idx = y*im->stride + x;
259  im->buf[idx] = v;
260  }
261  }
262 }
263 
264 // only widths 1 and 3 supported (and 3 only badly)
265 void image_u8_draw_line(image_u8_t *im, float x0, float y0, float x1, float y1, int v, int width)
266 {
267  double dist = sqrtf((y1-y0)*(y1-y0) + (x1-x0)*(x1-x0));
268  double delta = 0.5 / dist;
269 
270  // terrible line drawing code
271  for (float f = 0; f <= 1; f += delta) {
272  int x = ((int) (x1 + (x0 - x1) * f));
273  int y = ((int) (y1 + (y0 - y1) * f));
274 
275  if (x < 0 || y < 0 || x >= im->width || y >= im->height)
276  continue;
277 
278  int idx = y*im->stride + x;
279  im->buf[idx] = v;
280  if (width > 1) {
281  im->buf[idx+1] = v;
282  im->buf[idx+im->stride] = v;
283  im->buf[idx+1+im->stride] = v;
284  }
285  }
286 }
287 
289 {
290  for (int y = 0; y < im->height; y++) {
291  for (int x = 0; x < im->width; x++) {
292  im->buf[im->stride*y+x] /= 2;
293  }
294  }
295 }
296 
297 static void convolve(const uint8_t *x, uint8_t *y, int sz, const uint8_t *k, int ksz)
298 {
299  assert((ksz&1)==1);
300 
301  for (int i = 0; i < ksz/2 && i < sz; i++)
302  y[i] = x[i];
303 
304  for (int i = 0; i < sz - ksz; i++) {
305  uint32_t acc = 0;
306 
307  for (int j = 0; j < ksz; j++)
308  acc += k[j]*x[i+j];
309 
310  y[ksz/2 + i] = acc >> 8;
311  }
312 
313  for (int i = sz - ksz + ksz/2; i < sz; i++)
314  y[i] = x[i];
315 }
316 
317 void image_u8_convolve_2D(image_u8_t *im, const uint8_t *k, int ksz)
318 {
319  assert((ksz & 1) == 1); // ksz must be odd.
320 
321  for (int y = 0; y < im->height; y++) {
322 
323  uint8_t *x = malloc(sizeof(uint8_t)*im->stride);
324  memcpy(x, &im->buf[y*im->stride], im->stride);
325 
326  convolve(x, &im->buf[y*im->stride], im->width, k, ksz);
327  free(x);
328  }
329 
330  for (int x = 0; x < im->width; x++) {
331  uint8_t *xb = malloc(sizeof(uint8_t)*im->height);
332  uint8_t *yb = malloc(sizeof(uint8_t)*im->height);
333 
334  for (int y = 0; y < im->height; y++)
335  xb[y] = im->buf[y*im->stride + x];
336 
337  convolve(xb, yb, im->height, k, ksz);
338  free(xb);
339 
340  for (int y = 0; y < im->height; y++)
341  im->buf[y*im->stride + x] = yb[y];
342  free(yb);
343  }
344 }
345 
346 void image_u8_gaussian_blur(image_u8_t *im, double sigma, int ksz)
347 {
348  if (sigma == 0)
349  return;
350 
351  assert((ksz & 1) == 1); // ksz must be odd.
352 
353  // build the kernel.
354  double *dk = malloc(sizeof(double)*ksz);
355 
356  // for kernel of length 5:
357  // dk[0] = f(-2), dk[1] = f(-1), dk[2] = f(0), dk[3] = f(1), dk[4] = f(2)
358  for (int i = 0; i < ksz; i++) {
359  int x = -ksz/2 + i;
360  double v = exp(-.5*sq(x / sigma));
361  dk[i] = v;
362  }
363 
364  // normalize
365  double acc = 0;
366  for (int i = 0; i < ksz; i++)
367  acc += dk[i];
368 
369  for (int i = 0; i < ksz; i++)
370  dk[i] /= acc;
371 
372  uint8_t *k = malloc(sizeof(uint8_t)*ksz);
373  for (int i = 0; i < ksz; i++)
374  k[i] = dk[i]*255;
375 
376  if (0) {
377  for (int i = 0; i < ksz; i++)
378  printf("%d %15f %5d\n", i, dk[i], k[i]);
379  }
380  free(dk);
381 
382  image_u8_convolve_2D(im, k, ksz);
383  free(k);
384 }
385 
386 image_u8_t *image_u8_rotate(const image_u8_t *in, double rad, uint8_t pad)
387 {
388  int iwidth = in->width, iheight = in->height;
389  rad = -rad; // interpret y as being "down"
390 
391  float c = cos(rad), s = sin(rad);
392 
393  float p[][2] = { { 0, 0}, { iwidth, 0 }, { iwidth, iheight }, { 0, iheight} };
394 
395  float xmin = HUGE_VALF, xmax = -HUGE_VALF, ymin = HUGE_VALF, ymax = -HUGE_VALF;
396  float icx = iwidth / 2.0, icy = iheight / 2.0;
397 
398  for (int i = 0; i < 4; i++) {
399  float px = p[i][0] - icx;
400  float py = p[i][1] - icy;
401 
402  float nx = px*c - py*s;
403  float ny = px*s + py*c;
404 
405  xmin = fmin(xmin, nx);
406  xmax = fmax(xmax, nx);
407  ymin = fmin(ymin, ny);
408  ymax = fmax(ymax, ny);
409  }
410 
411  int owidth = ceil(xmax-xmin), oheight = ceil(ymax - ymin);
412  image_u8_t *out = image_u8_create(owidth, oheight);
413 
414  // iterate over output pixels.
415  for (int oy = 0; oy < oheight; oy++) {
416  for (int ox = 0; ox < owidth; ox++) {
417  // work backwards from destination coordinates...
418  // sample pixel centers.
419  float sx = ox - owidth / 2.0 + .5;
420  float sy = oy - oheight / 2.0 + .5;
421 
422  // project into input-image space
423  int ix = floor(sx*c + sy*s + icx);
424  int iy = floor(-sx*s + sy*c + icy);
425 
426  if (ix >= 0 && iy >= 0 && ix < iwidth && iy < iheight)
427  out->buf[oy*out->stride+ox] = in->buf[iy*in->stride + ix];
428  else
429  out->buf[oy*out->stride+ox] = pad;
430  }
431  }
432 
433  return out;
434 }
435 
437 {
438  int width = im->width, height = im->height;
439 
440  if (ffactor == 1.5) {
441  int swidth = width / 3 * 2, sheight = height / 3 * 2;
442 
443  image_u8_t *decim = image_u8_create(swidth, sheight);
444 
445  int y = 0, sy = 0;
446  while (sy < sheight) {
447  int x = 0, sx = 0;
448  while (sx < swidth) {
449 
450  // a b c
451  // d e f
452  // g h i
453  uint8_t a = im->buf[(y+0)*im->stride + (x+0)];
454  uint8_t b = im->buf[(y+0)*im->stride + (x+1)];
455  uint8_t c = im->buf[(y+0)*im->stride + (x+2)];
456 
457  uint8_t d = im->buf[(y+1)*im->stride + (x+0)];
458  uint8_t e = im->buf[(y+1)*im->stride + (x+1)];
459  uint8_t f = im->buf[(y+1)*im->stride + (x+2)];
460 
461  uint8_t g = im->buf[(y+2)*im->stride + (x+0)];
462  uint8_t h = im->buf[(y+2)*im->stride + (x+1)];
463  uint8_t i = im->buf[(y+2)*im->stride + (x+2)];
464 
465  decim->buf[(sy+0)*decim->stride + (sx + 0)] =
466  (4*a+2*b+2*d+e)/9;
467  decim->buf[(sy+0)*decim->stride + (sx + 1)] =
468  (4*c+2*b+2*f+e)/9;
469 
470  decim->buf[(sy+1)*decim->stride + (sx + 0)] =
471  (4*g+2*d+2*h+e)/9;
472  decim->buf[(sy+1)*decim->stride + (sx + 1)] =
473  (4*i+2*f+2*h+e)/9;
474 
475  x += 3;
476  sx += 2;
477  }
478 
479  y += 3;
480  sy += 2;
481  }
482 
483  return decim;
484  }
485 
486  int factor = (int) ffactor;
487 
488  int swidth = 1 + (width - 1)/factor;
489  int sheight = 1 + (height - 1)/factor;
490  image_u8_t *decim = image_u8_create(swidth, sheight);
491  int sy = 0;
492  for (int y = 0; y < height; y += factor) {
493  int sx = 0;
494  for (int x = 0; x < width; x += factor) {
495  decim->buf[sy*decim->stride + sx] = im->buf[y*im->stride + x];
496  sx++;
497  }
498  sy++;
499  }
500  return decim;
501 }
502 
503 void image_u8_fill_line_max(image_u8_t *im, const image_u8_lut_t *lut, const float *xy0, const float *xy1)
504 {
505  // what is the maximum distance that will result in drawing into our LUT?
506  float max_dist2 = (lut->nvalues-1)/lut->scale;
507  float max_dist = sqrt(max_dist2);
508 
509  // the orientation of the line
510  double theta = atan2(xy1[1]-xy0[1], xy1[0]-xy0[0]);
511  double v = sin(theta), u = cos(theta);
512 
513  int ix0 = iclamp(fmin(xy0[0], xy1[0]) - max_dist, 0, im->width-1);
514  int ix1 = iclamp(fmax(xy0[0], xy1[0]) + max_dist, 0, im->width-1);
515 
516  int iy0 = iclamp(fmin(xy0[1], xy1[1]) - max_dist, 0, im->height-1);
517  int iy1 = iclamp(fmax(xy0[1], xy1[1]) + max_dist, 0, im->height-1);
518 
519  // the line segment xy0---xy1 can be parameterized in terms of line coordinates.
520  // We fix xy0 to be at line coordinate 0.
521  float xy1_line_coord = (xy1[0]-xy0[0])*u + (xy1[1]-xy0[1])*v;
522 
523  float min_line_coord = fmin(0, xy1_line_coord);
524  float max_line_coord = fmax(0, xy1_line_coord);
525 
526  for (int iy = iy0; iy <= iy1; iy++) {
527  float y = iy+.5;
528 
529  for (int ix = ix0; ix <= ix1; ix++) {
530  float x = ix+.5;
531 
532  // compute line coordinate of this pixel.
533  float line_coord = (x - xy0[0])*u + (y - xy0[1])*v;
534 
535  // find point on line segment closest to our current pixel.
536  if (line_coord < min_line_coord)
537  line_coord = min_line_coord;
538  else if (line_coord > max_line_coord)
539  line_coord = max_line_coord;
540 
541  float px = xy0[0] + line_coord*u;
542  float py = xy0[1] + line_coord*v;
543 
544  double dist2 = (x-px)*(x-px) + (y-py)*(y-py);
545 
546  // not in our LUT?
547  int idx = dist2 * lut->scale;
548  if (idx >= lut->nvalues)
549  continue;
550 
551  uint8_t lut_value = lut->values[idx];
552  uint8_t old_value = im->buf[iy*im->stride + ix];
553  if (lut_value > old_value)
554  im->buf[iy*im->stride + ix] = lut_value;
555  }
556  }
557 }
int height
Definition: pnm.h:45
static int iclamp(int v, int minv, int maxv)
Definition: math_util.h:178
void image_u8_draw_circle(image_u8_t *im, float x0, float y0, float r, int v)
Definition: image_u8.c:227
static void convolve(const uint8_t *x, uint8_t *y, int sz, const uint8_t *k, int ksz)
Definition: image_u8.c:297
void image_u8_darken(image_u8_t *im)
Definition: image_u8.c:288
int format
Definition: pnm.h:46
image_u8_t * image_u8_decimate(image_u8_t *im, float ffactor)
Definition: image_u8.c:436
#define PNM_FORMAT_RGB
Definition: pnm.h:38
const int32_t height
Definition: image_types.h:70
#define PNM_FORMAT_BINARY
Definition: pnm.h:36
image_u8_t * image_u8_create_from_f32(image_f32_t *fim)
Definition: image_u8.c:185
uint8_t * buf
Definition: pnm.h:50
image_u8_t * image_u8_create_stride(unsigned int width, unsigned int height, unsigned int stride)
Definition: image_u8.c:42
image_u8_t * image_u8_rotate(const image_u8_t *in, double rad, uint8_t pad)
Definition: image_u8.c:386
float scale
Definition: image_u8.h:44
image_u8_t * image_u8_copy(const image_u8_t *in)
Definition: image_u8.c:69
const int32_t height
Definition: image_types.h:40
Definition: pnm.h:43
image_u8_t * image_u8_create_alignment(unsigned int width, unsigned int height, unsigned int alignment)
Definition: image_u8.c:59
const int32_t width
Definition: image_types.h:39
const int32_t stride
Definition: image_types.h:41
int width
Definition: pnm.h:45
int max
Definition: pnm.h:47
int image_u8_write_pnm(const image_u8_t *im, const char *path)
Definition: image_u8.c:200
static double sq(double v)
Definition: math_util.h:78
image_u8_t * image_u8_create_from_pnm(const char *path)
Definition: image_u8.c:93
void image_u8_gaussian_blur(image_u8_t *im, double sigma, int ksz)
Definition: image_u8.c:346
uint8_t * values
Definition: image_u8.h:47
void image_u8_convolve_2D(image_u8_t *im, const uint8_t *k, int ksz)
Definition: image_u8.c:317
void image_u8_draw_annulus(image_u8_t *im, float x0, float y0, float r0, float r1, int v)
Definition: image_u8.c:245
uint8_t * buf
Definition: image_types.h:43
#define DEFAULT_ALIGNMENT_U8
Definition: image_u8.c:40
image_u8_t * image_u8_create(unsigned int width, unsigned int height)
Definition: image_u8.c:54
void image_u8_destroy(image_u8_t *im)
Definition: image_u8.c:82
float * buf
Definition: image_types.h:73
int nvalues
Definition: image_u8.h:46
image_u8_t * image_u8_create_from_pnm_alignment(const char *path, int alignment)
Definition: image_u8.c:98
const int32_t stride
Definition: image_types.h:71
pnm_t * pnm_create_from_file(const char *path)
Definition: pnm.c:34
void image_u8_fill_line_max(image_u8_t *im, const image_u8_lut_t *lut, const float *xy0, const float *xy1)
Definition: image_u8.c:503
#define PNM_FORMAT_GRAY
Definition: pnm.h:37
const int32_t width
Definition: image_types.h:69
void image_u8_draw_line(image_u8_t *im, float x0, float y0, float x1, float y1, int v, int width)
Definition: image_u8.c:265
void pnm_destroy(pnm_t *pnm)
Definition: pnm.c:147


apriltag
Author(s): Edwin Olson , Max Krogius
autogenerated on Mon Jun 26 2023 02:26:12