Removed the definition of basename, which confuses an existing system one.
[folded-ctf.git] / rgb_image.cc
1 /*
2  *  folded-ctf is an implementation of the folded hierarchy of
3  *  classifiers for object detection, developed by Francois Fleuret
4  *  and Donald Geman.
5  *
6  *  Copyright (c) 2008 Idiap Research Institute, http://www.idiap.ch/
7  *  Written by Francois Fleuret <francois.fleuret@idiap.ch>
8  *
9  *  This file is part of folded-ctf.
10  *
11  *  folded-ctf is free software: you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License version 3 as
13  *  published by the Free Software Foundation.
14  *
15  *  folded-ctf is distributed in the hope that it will be useful, but
16  *  WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with folded-ctf.  If not, see <http://www.gnu.org/licenses/>.
22  *
23  */
24
25 #include <iostream>
26 #include <stdio.h>
27
28 #include <libpng/png.h>
29 #include "jpeg_misc.h"
30
31 #include "rgb_image.h"
32
33 void RGBImage::allocate() {
34   _bit_plans = new unsigned char **[RGB_DEPTH];
35   _bit_lines = new unsigned char *[RGB_DEPTH * _height];
36   _bit_map = new unsigned char [_width * _height * RGB_DEPTH];
37   for(int k = 0; k < RGB_DEPTH; k++) _bit_plans[k] = _bit_lines + k * _height;
38   for(int k = 0; k < RGB_DEPTH * _height; k++) _bit_lines[k] = _bit_map + k * _width;
39 }
40
41 void RGBImage::deallocate() {
42   delete[] _bit_plans;
43   delete[] _bit_lines;
44   delete[] _bit_map;
45 }
46
47 RGBImage::RGBImage() : _bit_plans(0), _bit_lines(0), _bit_map(0) { }
48
49 RGBImage::RGBImage(int width, int height) : _width(width), _height(height) {
50   allocate();
51   memset(_bit_map, 0, _width * _height * RGB_DEPTH * sizeof(unsigned char));
52 }
53
54 RGBImage::RGBImage(RGBImage *image, scalar_t scale) {
55   _width = int(scale * image->_width);
56   _height = int(scale * image->_height);
57
58   allocate();
59
60   for(int y = 0; y < _height; y++) {
61     for(int x = 0; x < _width; x++) {
62
63       const int delta = 10;
64       int sr = 0, sg = 0, sb = 0, t = 0;
65       int xo, yo;
66
67       for(int yy = y * delta; yy < (y + 1) * delta; yy++) {
68         for(int xx = x * delta; xx < (x + 1) * delta; xx++) {
69           xo = (image->_width * xx)/(_width * delta);
70           yo = (image->_height * yy)/(_height * delta);
71           if(xo >= 0 && xo < image->_width && yo >= 0 && yo < image->_height) {
72             sr += image->_bit_plans[RED][yo][xo];
73             sg += image->_bit_plans[GREEN][yo][xo];
74             sb += image->_bit_plans[BLUE][yo][xo];
75             t++;
76           }
77         }
78       }
79
80       if(t > 0) {
81         _bit_plans[RED][y][x] = sr / t;
82         _bit_plans[GREEN][y][x] = sg / t;
83         _bit_plans[BLUE][y][x] = sb / t;
84       } else {
85         _bit_plans[RED][y][x] = 0;
86         _bit_plans[GREEN][y][x] = 0;
87         _bit_plans[BLUE][y][x] = 0;
88       }
89
90     }
91   }
92 }
93
94 RGBImage::~RGBImage() {
95   deallocate();
96 }
97
98 void RGBImage::read_png(const char *name) {
99   // This is the number of bytes the read_png routine will read to
100   // decide if the file is a PNG or not. According to the png
101   // documentation, it can be 1 to 8 bytes, 8 being the max and the
102   // best.
103
104   const int header_size = 8;
105
106   png_byte header[header_size];
107   png_bytep *row_pointers;
108
109   deallocate();
110
111   // open file
112   FILE *fp = fopen(name, "rb");
113   if (!fp) {
114     cerr << "Unable to open file " << name << " for reading.\n";
115     exit(1);
116   }
117
118   // read header
119   fread(header, 1, header_size, fp);
120   if (png_sig_cmp(header, 0, header_size)) {
121     cerr << "File " << name << " does not look like PNG.\n";
122     fclose(fp);
123     exit(1);
124   }
125
126   // create png pointer
127   png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
128   if (!png_ptr) {
129     cerr << "png_create_read_struct failed\n";
130     fclose(fp);
131     exit(1);
132   }
133
134   // create png info struct
135   png_infop info_ptr = png_create_info_struct(png_ptr);
136   if (!info_ptr) {
137     png_destroy_read_struct(&png_ptr, (png_infopp) 0, (png_infopp) 0);
138     cerr << "png_create_info_struct failed\n";
139     fclose(fp);
140     exit(1);
141   }
142
143   // get image info
144   png_init_io(png_ptr, fp);
145   png_set_sig_bytes(png_ptr, header_size);
146   png_read_info(png_ptr, info_ptr);
147
148   _width = info_ptr->width;
149   _height = info_ptr->height;
150
151   png_byte bit_depth, color_type, channels;
152   color_type = info_ptr->color_type;
153   bit_depth = info_ptr->bit_depth;
154   channels = info_ptr->channels;
155
156   if(bit_depth != 8) {
157     cerr << "Can only read 8-bits PNG images." << endl;
158     exit(1);
159   }
160
161   // allocate image pointer
162   row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * _height);
163   for (int y = 0; y < _height; y++)
164     row_pointers[y] = (png_byte*) malloc(info_ptr->rowbytes);
165
166   allocate();
167
168   // read image
169   png_read_image(png_ptr, row_pointers);
170
171   // send image to red, green and blue buffers
172   switch (color_type) {
173   case PNG_COLOR_TYPE_GRAY:
174     {
175       unsigned char pixel = 0;
176       for (int y = 0; y < _height; y++) for (int x = 0; x < _width; x++) {
177         pixel = row_pointers[y][x];
178         _bit_plans[RED][y][x] = pixel;
179         _bit_plans[GREEN][y][x] = pixel;
180         _bit_plans[BLUE][y][x] = pixel;
181       }
182     }
183     break;
184
185   case PNG_COLOR_TYPE_GRAY_ALPHA:
186     cerr << "PNG type GRAY_ALPHA not supported.\n";
187     exit(1);
188     break;
189
190   case PNG_COLOR_TYPE_PALETTE:
191     cerr << "PNG type PALETTE not supported.\n";
192     exit(1);
193     break;
194
195   case PNG_COLOR_TYPE_RGB:
196     {
197       if(channels != RGB_DEPTH) {
198         cerr << "Unsupported number of channels for RGB type\n";
199         break;
200       }
201       int k;
202       for (int y = 0; y < _height; y++) {
203         k = 0;
204         for (int x = 0; x < _width; x++) {
205           _bit_plans[RED][y][x] = row_pointers[y][k++];
206           _bit_plans[GREEN][y][x] = row_pointers[y][k++];
207           _bit_plans[BLUE][y][x] = row_pointers[y][k++];
208         }
209       }
210     }
211     break;
212
213   case PNG_COLOR_TYPE_RGB_ALPHA:
214     cerr << "PNG type RGB_ALPHA not supported.\n";
215     exit(1);
216     break;
217
218   default:
219     cerr << "Unknown PNG type\n";
220     exit(1);
221   }
222
223   // release memory
224   png_destroy_read_struct(&png_ptr, &info_ptr, 0);
225
226   for (int y = 0; y < _height; y++) free(row_pointers[y]);
227   free(row_pointers);
228
229   fclose(fp);
230 }
231
232 void RGBImage::write_png(const char *name) {
233   png_bytep *row_pointers;
234
235   // create file
236   FILE *fp = fopen(name, "wb");
237
238   if (!fp) {
239     cerr << "Unable to create image '" << name << "'\n";
240     exit(1);
241   }
242
243   png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
244
245   if (!png_ptr) {
246     cerr << "png_create_write_struct failed\n";
247     fclose(fp);
248     exit(1);
249   }
250
251   png_infop info_ptr = png_create_info_struct(png_ptr);
252   if (!info_ptr) {
253     cerr << "png_create_info_struct failed\n";
254     fclose(fp);
255     exit(1);
256   }
257
258   png_init_io(png_ptr, fp);
259
260   png_set_IHDR(png_ptr, info_ptr, _width, _height,
261                8, 2, PNG_INTERLACE_NONE,
262                PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
263
264   png_write_info(png_ptr, info_ptr);
265
266   // allocate memory
267   row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * _height);
268   for (int y = 0; y < _height; y++)
269     row_pointers[y] = (png_byte*) malloc(info_ptr->rowbytes);
270
271   int k;
272   for (int y = 0; y < _height; y++) {
273     k = 0;
274     for (int x = 0; x < _width; x++) {
275       row_pointers[y][k++] = _bit_map[x + _width * (y + _height * RED)];
276       row_pointers[y][k++] = _bit_map[x + _width * (y + _height * GREEN)];
277       row_pointers[y][k++] = _bit_map[x + _width * (y + _height * BLUE)];
278     }
279   }
280
281   png_write_image(png_ptr, row_pointers);
282   png_write_end(png_ptr, 0);
283
284   png_destroy_write_struct(&png_ptr, &info_ptr);
285
286   // cleanup heap allocation
287   for (int y = 0; y < _height; y++) free(row_pointers[y]);
288   free(row_pointers);
289
290   fclose(fp);
291 }
292
293 void RGBImage::write_jpg(const char *filename, int quality) {
294   struct jpeg_compress_struct cinfo;
295   struct my_error_mgr jerr;
296   FILE *outfile;                /* target file */
297   JSAMPARRAY buffer;            /* Output row buffer */
298
299   jpeg_create_compress (&cinfo);
300
301   if ((outfile = fopen (filename, "wb")) == 0) {
302     fprintf (stderr, "Can't open %s\n", filename);
303     exit(1);
304   }
305
306   cinfo.err = jpeg_std_error (&jerr.pub);
307   jerr.pub.error_exit = my_error_exit;
308
309   if (setjmp (jerr.setjmp_buffer)) {
310     jpeg_destroy_compress (&cinfo);
311     fclose (outfile);
312     exit(1);
313   }
314
315   jpeg_stdio_dest (&cinfo, outfile);
316
317   cinfo.image_width = _width;
318   cinfo.image_height = _height;
319   cinfo.input_components = RGB_DEPTH;
320
321   cinfo.in_color_space = JCS_RGB;
322
323   jpeg_set_defaults (&cinfo);
324   jpeg_set_quality (&cinfo, quality, TRUE);
325   jpeg_start_compress (&cinfo, TRUE);
326   int y = 0;
327   buffer =
328     (*cinfo.mem->alloc_sarray) ((j_common_ptr) & cinfo, JPOOL_IMAGE,
329                                 _width * RGB_DEPTH, 1);
330   while (int(cinfo.next_scanline) < _height) {
331     for(int d = 0; d < RGB_DEPTH; d++)
332       for(int x = 0; x < _width; x++)
333         buffer[0][x * RGB_DEPTH + d] =
334           (JSAMPLE) ((_bit_map[x + _width * (y + _height * d)] * (MAXJSAMPLE + 1)) / 255);
335     jpeg_write_scanlines (&cinfo, buffer, 1);
336     y++;
337   }
338
339   jpeg_finish_compress (&cinfo);
340   fclose (outfile);
341
342   jpeg_destroy_compress (&cinfo);
343 }
344
345 void RGBImage::read_jpg(const char *filename) {
346   struct jpeg_decompress_struct cinfo;
347   struct my_error_mgr jerr;
348   FILE *infile;
349   JSAMPARRAY buffer;
350
351   deallocate();
352
353   if ((infile = fopen (filename, "rb")) == 0) {
354     fprintf (stderr, "can't open %s\n", filename);
355     return;
356   }
357
358   cinfo.err = jpeg_std_error (&jerr.pub);
359   jerr.pub.error_exit = my_error_exit;
360
361   if (setjmp (jerr.setjmp_buffer)) {
362     jpeg_destroy_decompress (&cinfo);
363     fclose (infile);
364     delete[] _bit_map;
365     _width = 0;
366     _height = 0;
367     _bit_map = 0;
368     return;
369   }
370
371   jpeg_create_decompress (&cinfo);
372   jpeg_stdio_src (&cinfo, infile);
373   jpeg_read_header (&cinfo, TRUE);
374   jpeg_start_decompress (&cinfo);
375
376   _width = cinfo.output_width;
377   _height = cinfo.output_height;
378   int depth = cinfo.output_components;
379
380   allocate();
381
382   buffer =
383     (*cinfo.mem->alloc_sarray) ((j_common_ptr) & cinfo, JPOOL_IMAGE,
384                                 _width * depth, 1);
385
386   int y = 0;
387   while (cinfo.output_scanline < cinfo.output_height) {
388     jpeg_read_scanlines (&cinfo, buffer, 1);
389     if(depth == 1) {
390       for(int d = 0; d < RGB_DEPTH; d++)
391         for(int x = 0; x < _width; x++)
392           _bit_plans[d][y][x] =
393             (unsigned char) ((buffer[0][x * depth] * 255) / (MAXJSAMPLE + 1));
394     } else {
395       for(int d = 0; d < depth; d++)
396         for(int x = 0; x < _width; x++)
397           _bit_plans[d][y][x] =
398             (unsigned char) ((buffer[0][x * depth + d] * 255) / (MAXJSAMPLE + 1));
399     }
400     y++;
401   }
402
403   jpeg_finish_decompress (&cinfo);
404   jpeg_destroy_decompress (&cinfo);
405
406   fclose (infile);
407 }
408
409 void RGBImage::draw_line(int thickness,
410                          unsigned char r, unsigned char g, unsigned char b,
411                          scalar_t x0, scalar_t y0, scalar_t x1, scalar_t y1) {
412   int l = 0;
413   int dx, dy, h, v;
414   int ix0 = int(x0 + 0.5), iy0 = int(y0 + 0.5), ix1 = int(x1 + 0.5), iy1 = int(y1 + 0.5);
415
416   if(ix0 < ix1) { dx = 1; h = ix1 - ix0; } else { dx = -1; h = ix0 - ix1; }
417   if(iy0 < iy1) { dy = 1; v = iy1 - iy0; } else { dy = -1; v = iy0 - iy1; }
418
419   int x = ix0, y = iy0;
420
421   if(h > v) {
422     for(int i = 0; i < h + 1; i++) {
423       for(int ex = - thickness / 2 - 1; ex < (thickness + 1) / 2 + 1; ex++) {
424         for(int ey = - thickness / 2 - 1; ey < (thickness + 1) / 2 + 1; ey++) {
425           if(ex * ex + ey * ey <= thickness * thickness / 4) {
426             int xx = x + ex, yy = y + ey;
427             if(xx >= 0 && xx < _width && yy >= 0 && yy < _height)
428               set_pixel(xx, yy, r, g, b);
429           }
430         }
431       }
432
433       x += dx; l += v;
434       if(l > 0) { y += dy; l -= h; }
435     }
436
437   } else {
438
439     for(int i = 0; i < v + 1; i++) {
440       for(int ex = - thickness / 2 - 1; ex < (thickness + 1) / 2 + 1; ex++) {
441         for(int ey = - thickness / 2 - 1; ey < (thickness + 1) / 2 + 1; ey++) {
442           if(ex * ex + ey * ey <= thickness * thickness / 4) {
443             int xx = x + ex, yy = y + ey;
444             if(xx >= 0 && xx < _width && yy >= 0 && yy < _height)
445               set_pixel(xx, yy, r, g, b);
446           }
447         }
448       }
449
450       y += dy; l -= h;
451       if(l < 0) { x += dx; l += v; }
452     }
453
454   }
455
456 }
457
458 void RGBImage::draw_ellipse(int thickness,
459                             unsigned char r, unsigned char g, unsigned char b,
460                             scalar_t xc, scalar_t yc, scalar_t radius_1, scalar_t radius_2, scalar_t tilt) {
461   scalar_t ux1 =   cos(tilt) * radius_1, uy1 =   sin(tilt) * radius_1;
462   scalar_t ux2 = - sin(tilt) * radius_2, uy2 =   cos(tilt) * radius_2;
463
464   const int nb_points_to_draw = 80;
465   scalar_t x, y, px = 0, py = 0;
466
467   for(int i = 0; i <= nb_points_to_draw; i++) {
468     scalar_t alpha = (M_PI * 2 * scalar_t(i)) / scalar_t(nb_points_to_draw);
469
470     x = xc + cos(alpha) * ux1 + sin(alpha) * ux2;
471     y = yc + cos(alpha) * uy1 + sin(alpha) * uy2;
472
473     if(i > 0) {
474       draw_line(thickness, r, g, b, px, py, x, y);
475     }
476
477     px = x; py = y;
478   }
479 }