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