IVT
ImageProcessor.cpp
Go to the documentation of this file.
1 // ****************************************************************************
2 // This file is part of the Integrating Vision Toolkit (IVT).
3 //
4 // The IVT is maintained by the Karlsruhe Institute of Technology (KIT)
5 // (www.kit.edu) in cooperation with the company Keyetech (www.keyetech.de).
6 //
7 // Copyright (C) 2014 Karlsruhe Institute of Technology (KIT).
8 // All rights reserved.
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are met:
12 //
13 // 1. Redistributions of source code must retain the above copyright
14 // notice, this list of conditions and the following disclaimer.
15 //
16 // 2. Redistributions in binary form must reproduce the above copyright
17 // notice, this list of conditions and the following disclaimer in the
18 // documentation and/or other materials provided with the distribution.
19 //
20 // 3. Neither the name of the KIT nor the names of its contributors may be
21 // used to endorse or promote products derived from this software
22 // without specific prior written permission.
23 //
24 // THIS SOFTWARE IS PROVIDED BY THE KIT AND CONTRIBUTORS “AS IS” AND ANY
25 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 // DISCLAIMED. IN NO EVENT SHALL THE KIT OR CONTRIBUTORS BE LIABLE FOR ANY
28 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 // ****************************************************************************
35 // ****************************************************************************
36 // Filename: ImageProcessor.cpp
37 // Author: Pedram Azad
38 // Date: 2004
39 // ****************************************************************************
40 // Changes: 20.05.2008, Florian Hecht
41 // * Added Summed Area Table functions
42 //
43 // 07.01.2009, Moritz Hassert
44 // * Added new functions:
45 // Add, AddAndSaturate, Subtract, AbsoluteDifference,
46 // Average, Min, Max
47 // ****************************************************************************
48 
49 
50 // ****************************************************************************
51 // Includes
52 // ****************************************************************************
53 
54 #include <new> // for explicitly using correct new/delete operators on VC DSPs
55 
56 #include "ImageProcessor.h"
57 
58 #include "ByteImage.h"
59 #include "ShortImage.h"
60 #include "IntImage.h"
61 #include "FloatImage.h"
62 #include "PrimitivesDrawer.h"
63 #include "Color/RGBColorModel.h"
64 #include "Math/LinearAlgebra.h"
65 #include "Math/FloatMatrix.h"
66 #include "Math/DoubleMatrix.h"
67 #include "Math/Constants.h"
68 #include "Math/Math3d.h"
69 #include "Math/Matd.h"
70 #include "Math/Vecd.h"
71 #include "Helpers/helpers.h"
74 
75 #include <stdio.h>
76 #include <stdlib.h>
77 #include <string.h>
78 #include <math.h>
79 #include <stddef.h>
80 #include <algorithm>
81 #include <limits.h>
82 #include <float.h>
83 
84 
85 
86 // ****************************************************************************
87 // Static variables and functions
88 // ****************************************************************************
89 
90 // (1 << 20) / i
91 static const int division_table[] =
92 {
93  0, 1048576, 524288, 349525, 262144, 209715, 174762, 149796,
94  131072, 116508, 104857, 95325, 87381, 80659, 74898, 69905,
95  65536, 61680, 58254, 55188, 52428, 49932, 47662, 45590,
96  43690, 41943, 40329, 38836, 37449, 36157, 34952, 33825,
97  32768, 31775, 30840, 29959, 29127, 28339, 27594, 26886,
98  26214, 25575, 24966, 24385, 23831, 23301, 22795, 22310,
99  21845, 21399, 20971, 20560, 20164, 19784, 19418, 19065,
100  18724, 18396, 18078, 17772, 17476, 17189, 16912, 16644,
101  16384, 16131, 15887, 15650, 15420, 15196, 14979, 14768,
102  14563, 14364, 14169, 13981, 13797, 13617, 13443, 13273,
103  13107, 12945, 12787, 12633, 12483, 12336, 12192, 12052,
104  11915, 11781, 11650, 11522, 11397, 11275, 11155, 11037,
105  10922, 10810, 10699, 10591, 10485, 10381, 10280, 10180,
106  10082, 9986, 9892, 9799, 9709, 9619, 9532, 9446,
107  9362, 9279, 9198, 9118, 9039, 8962, 8886, 8811,
108  8738, 8665, 8594, 8525, 8456, 8388, 8322, 8256,
109  8192, 8128, 8065, 8004, 7943, 7884, 7825, 7767,
110  7710, 7653, 7598, 7543, 7489, 7436, 7384, 7332,
111  7281, 7231, 7182, 7133, 7084, 7037, 6990, 6944,
112  6898, 6853, 6808, 6765, 6721, 6678, 6636, 6594,
113  6553, 6512, 6472, 6432, 6393, 6355, 6316, 6278,
114  6241, 6204, 6168, 6132, 6096, 6061, 6026, 5991,
115  5957, 5924, 5890, 5857, 5825, 5793, 5761, 5729,
116  5698, 5667, 5637, 5607, 5577, 5548, 5518, 5489,
117  5461, 5433, 5405, 5377, 5349, 5322, 5295, 5269,
118  5242, 5216, 5190, 5165, 5140, 5115, 5090, 5065,
119  5041, 5017, 4993, 4969, 4946, 4922, 4899, 4877,
120  4854, 4832, 4809, 4788, 4766, 4744, 4723, 4702,
121  4681, 4660, 4639, 4619, 4599, 4578, 4559, 4539,
122  4519, 4500, 4481, 4462, 4443, 4424, 4405, 4387,
123  4369, 4350, 4332, 4315, 4297, 4279, 4262, 4245,
124  4228, 4211, 4194, 4177, 4161, 4144, 4128, 4112
125 };
126 
127 static const int MatrixGaussian5x5[25] =
128 {
129  1, 5, 7, 5, 1,
130  5, 20, 33, 20, 5,
131  7, 33, 55, 33, 7,
132  5, 20, 33, 20, 5,
133  1, 5, 7, 5, 1
134 };
135 
136 static float sin_table_[380];
137 static float cos_table_[380];
138 static float *sin_table = sin_table_ + 10;
139 static float *cos_table = cos_table_ + 10;
140 
141 static void InitSinCosTables()
142 {
143  static bool bSinCosTablesInitialized = false;
144 
145  if (bSinCosTablesInitialized)
146  return;
147 
148  for (int i = -10; i < 370; i++)
149  {
150  const float theta = i * FLOAT_DEG2RAD;
151  sin_table[i] = sinf(theta);
152  cos_table[i] = cosf(theta);
153  }
154 
155  bSinCosTablesInitialized = true;
156 }
157 
158 
159 
160 // ****************************************************************************
161 // Functions
162 // ****************************************************************************
163 
164 bool ImageProcessor::GeneralFilter(const CByteImage *pInputImage, CByteImage *pOutputImage, const int *pKernel, int nMaskSize, int nDivider, bool bAbsoluteValue)
165 {
166  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
167  pInputImage->type != CByteImage::eGrayScale || pOutputImage->type != CByteImage::eGrayScale)
168  {
169  printf("error: input and output image do not match for ImageProcessor::GeneralFilter\n");
170  return false;
171  }
172 
173  if (nMaskSize < 3 || (nMaskSize + 1) % 2 != 0)
174  {
175  printf("error: nMaskSize must be 2 * k + 1 with k >= 1 for ImageProcessor::GeneralFilter\n");
176  return false;
177  }
178 
179  CByteImage *pSaveOutputImage = 0;
180  if (pInputImage->pixels == pOutputImage->pixels)
181  {
182  pSaveOutputImage = pOutputImage;
183  pOutputImage = new CByteImage(pInputImage);
184  }
185 
186  ImageProcessor::Zero(pOutputImage);
187 
188  const int umax = pInputImage->width - nMaskSize + 1;
189  const int vmax = pInputImage->height - nMaskSize + 1;
190 
191  const unsigned char *input = pInputImage->pixels;
192  unsigned char *output = pOutputImage->pixels;
193 
194  const int diff = (nMaskSize - 1) * (pInputImage->width + 1) / 2;
195 
196  if (nDivider == 1)
197  {
198  if (bAbsoluteValue)
199  {
200  for (int v = 0, offset = diff; v < vmax; v++, offset += nMaskSize - 1)
201  {
202  for (int u = 0; u < umax; u++, offset++)
203  {
204  int sum = 0;
205 
206  for (int i = 0, offset2 = offset - diff, offset3 = 0; i < nMaskSize; i++, offset2 += umax - 1)
207  {
208  for (int j = 0; j < nMaskSize; j++, offset2++, offset3++)
209  sum += input[offset2] * pKernel[offset3];
210  }
211 
212  output[offset] = (unsigned char) abs(sum);
213  }
214  }
215  }
216  else
217  {
218  for (int v = 0, offset = diff; v < vmax; v++, offset += nMaskSize - 1)
219  {
220  for (int u = 0; u < umax; u++, offset++)
221  {
222  int sum = 0;
223 
224  for (int i = 0, offset2 = offset - diff, offset3 = 0; i < nMaskSize; i++, offset2 += umax - 1)
225  {
226  for (int j = 0; j < nMaskSize; j++, offset2++, offset3++)
227  sum += input[offset2] * pKernel[offset3];
228  }
229 
230  output[offset] = (unsigned char) sum;
231  }
232  }
233  }
234  }
235  else
236  {
237  if (bAbsoluteValue)
238  {
239  for (int v = 0, offset = diff; v < vmax; v++, offset += nMaskSize - 1)
240  {
241  for (int u = 0; u < umax; u++, offset++)
242  {
243  int sum = 0;
244 
245  for (int i = 0, offset2 = offset - diff, offset3 = 0; i < nMaskSize; i++, offset2 += umax - 1)
246  {
247  for (int j = 0; j < nMaskSize; j++, offset2++, offset3++)
248  sum += input[offset2] * pKernel[offset3];
249  }
250 
251  output[offset] = (unsigned char) (abs(sum) / nDivider);
252  }
253  }
254  }
255  else
256  {
257  for (int v = 0, offset = diff; v < vmax; v++, offset += nMaskSize - 1)
258  {
259  for (int u = 0; u < umax; u++, offset++)
260  {
261  int sum = 0;
262 
263  for (int i = 0, offset2 = offset - diff, offset3 = 0; i < nMaskSize; i++, offset2 += umax - 1)
264  {
265  for (int j = 0; j < nMaskSize; j++, offset2++, offset3++)
266  sum += input[offset2] * pKernel[offset3];
267  }
268 
269  output[offset] = (unsigned char) (sum / nDivider);
270  }
271  }
272  }
273  }
274 
275  if (pSaveOutputImage)
276  {
277  CopyImage(pOutputImage, pSaveOutputImage);
278  delete pOutputImage;
279  }
280 
281  return true;
282 }
283 
284 bool ImageProcessor::GeneralFilter(const CByteImage *pInputImage, CShortImage *pOutputImage, const int *pKernel, int nMaskSize, int nDivider, bool bAbsoluteValue)
285 {
286  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
287  pInputImage->type != CByteImage::eGrayScale)
288  {
289  printf("error: input and output image do not match for ImageProcessor::GeneralFilter\n");
290  return false;
291  }
292 
293  if (nMaskSize < 3 || (nMaskSize + 1) % 2 != 0)
294  {
295  printf("error: nMaskSize must be 2 * k + 1 with k >= 1 for ImageProcessor::GeneralFilter\n");
296  return false;
297  }
298 
299  ImageProcessor::Zero(pOutputImage);
300 
301  const int width = pInputImage->width;
302  const int height = pInputImage->height;
303  const int umax = width - nMaskSize + 1;
304  const int vmax = height - nMaskSize + 1;
305 
306  const unsigned char *input = pInputImage->pixels;
307  short *output = pOutputImage->pixels;
308 
309  const int diff = (nMaskSize - 1) * (width + 1) / 2;
310 
311  if (nDivider == 1)
312  {
313  if (bAbsoluteValue)
314  {
315  for (int v = 0, offset = diff; v < vmax; v++, offset += nMaskSize - 1)
316  {
317  for (int u = 0; u < umax; u++, offset++)
318  {
319  int sum = 0;
320 
321  for (int i = 0, offset2 = offset - diff, offset3 = 0; i < nMaskSize; i++, offset2 += umax - 1)
322  {
323  for (int j = 0; j < nMaskSize; j++, offset2++, offset3++)
324  sum += input[offset2] * pKernel[offset3];
325  }
326 
327  output[offset] = (short) abs(sum);
328  }
329  }
330  }
331  else
332  {
333  for (int v = 0, offset = diff; v < vmax; v++, offset += nMaskSize - 1)
334  {
335  for (int u = 0; u < umax; u++, offset++)
336  {
337  int sum = 0;
338 
339  for (int i = 0, offset2 = offset - diff, offset3 = 0; i < nMaskSize; i++, offset2 += umax - 1)
340  {
341  for (int j = 0; j < nMaskSize; j++, offset2++, offset3++)
342  sum += input[offset2] * pKernel[offset3];
343  }
344 
345  output[offset] = (short) sum;
346  }
347  }
348  }
349  }
350  else
351  {
352  if (bAbsoluteValue)
353  {
354  for (int v = 0, offset = diff; v < vmax; v++, offset += nMaskSize - 1)
355  {
356  for (int u = 0; u < umax; u++, offset++)
357  {
358  int sum = 0;
359 
360  for (int i = 0, offset2 = offset - diff, offset3 = 0; i < nMaskSize; i++, offset2 += umax - 1)
361  {
362  for (int j = 0; j < nMaskSize; j++, offset2++, offset3++)
363  sum += input[offset2] * pKernel[offset3];
364  }
365 
366  output[offset] = (short) (abs(sum) / nDivider);
367  }
368  }
369  }
370  else
371  {
372  for (int v = 0, offset = diff; v < vmax; v++, offset += nMaskSize - 1)
373  {
374  for (int u = 0; u < umax; u++, offset++)
375  {
376  int sum = 0;
377 
378  for (int i = 0, offset2 = offset - diff, offset3 = 0; i < nMaskSize; i++, offset2 += umax - 1)
379  {
380  for (int j = 0; j < nMaskSize; j++, offset2++, offset3++)
381  sum += input[offset2] * pKernel[offset3];
382  }
383 
384  output[offset] = (short) (sum / nDivider);
385  }
386  }
387  }
388  }
389 
390  return true;
391 }
392 
393 bool ImageProcessor::GeneralFilter(const CByteImage *pInputImage, CFloatMatrix *pOutputMatrix, const int *pKernel, int nMaskSize, int nDivider, bool bAbsoluteValue)
394 {
395  if (pInputImage->width != pOutputMatrix->columns || pInputImage->height != pOutputMatrix->rows ||
396  pInputImage->type != CByteImage::eGrayScale)
397  {
398  printf("error: input image and output matrix do not match for ImageProcessor::GeneralFilter\n");
399  return false;
400  }
401 
402  if (nMaskSize < 3 || (nMaskSize + 1) % 2 != 0)
403  {
404  printf("error: nMaskSize must be 2 * k + 1 with k >= 1 for ImageProcessor::GeneralFilter\n");
405  return false;
406  }
407 
408  ImageProcessor::Zero(pOutputMatrix);
409 
410  const int width = pInputImage->width;
411  const int height = pInputImage->height;
412  const int umax = width - nMaskSize + 1;
413  const int vmax = height - nMaskSize + 1;
414 
415  const unsigned char *input = pInputImage->pixels;
416  float *output = pOutputMatrix->data;
417 
418 #if 0
419 
420  const int k = (nMaskSize - 1) / 2;
421 
422  for (int v = 0; v < vmax; v++)
423  {
424  for (int u = 0; u < umax; u++)
425  {
426  int sum = 0;
427 
428  for (int i = 0; i < nMaskSize; i++)
429  {
430  for (int j = 0; j < nMaskSize; j++)
431  sum += input[(v + i) * width + (u + j)] * pKernel[i * nMaskSize + j];
432  }
433 
434  output[(v + k) * width + (u + k)] = float(abs(sum)) / nDivider;
435  }
436  }
437 
438 #else
439 
440  const int diff = (nMaskSize - 1) * (width + 1) / 2;
441 
442  if (nDivider == 1)
443  {
444  if (bAbsoluteValue)
445  {
446  for (int v = 0, offset = diff; v < vmax; v++, offset += nMaskSize - 1)
447  {
448  for (int u = 0; u < umax; u++, offset++)
449  {
450  int sum = 0;
451 
452  for (int i = 0, offset2 = offset - diff, offset3 = 0; i < nMaskSize; i++, offset2 += umax - 1)
453  {
454  for (int j = 0; j < nMaskSize; j++, offset2++, offset3++)
455  sum += input[offset2] * pKernel[offset3];
456  }
457 
458  output[offset] = (float) abs(sum);
459  }
460  }
461  }
462  else
463  {
464  for (int v = 0, offset = diff; v < vmax; v++, offset += nMaskSize - 1)
465  {
466  for (int u = 0; u < umax; u++, offset++)
467  {
468  int sum = 0;
469 
470  for (int i = 0, offset2 = offset - diff, offset3 = 0; i < nMaskSize; i++, offset2 += umax - 1)
471  {
472  for (int j = 0; j < nMaskSize; j++, offset2++, offset3++)
473  sum += input[offset2] * pKernel[offset3];
474  }
475 
476  output[offset] = (float) sum;
477  }
478  }
479  }
480  }
481  else
482  {
483  if (bAbsoluteValue)
484  {
485  for (int v = 0, offset = diff; v < vmax; v++, offset += nMaskSize - 1)
486  {
487  for (int u = 0; u < umax; u++, offset++)
488  {
489  int sum = 0;
490 
491  for (int i = 0, offset2 = offset - diff, offset3 = 0; i < nMaskSize; i++, offset2 += umax - 1)
492  {
493  for (int j = 0; j < nMaskSize; j++, offset2++, offset3++)
494  sum += input[offset2] * pKernel[offset3];
495  }
496 
497  output[offset] = float(abs(sum)) / nDivider;
498  }
499  }
500  }
501  else
502  {
503  for (int v = 0, offset = diff; v < vmax; v++, offset += nMaskSize - 1)
504  {
505  for (int u = 0; u < umax; u++, offset++)
506  {
507  int sum = 0;
508 
509  for (int i = 0, offset2 = offset - diff, offset3 = 0; i < nMaskSize; i++, offset2 += umax - 1)
510  {
511  for (int j = 0; j < nMaskSize; j++, offset2++, offset3++)
512  sum += input[offset2] * pKernel[offset3];
513  }
514 
515  output[offset] = float(sum) / nDivider;
516  }
517  }
518  }
519  }
520 #endif
521 
522  return true;
523 }
524 
525 
526 // Separation:
527 //
528 // ( 1 4 6 4 1 ) ( 1 )
529 // ( 4 16 24 16 4 ) ( 4 )
530 // ( 6 24 36 24 6 ) = ( 6 ) * ( 1 4 6 4 1 )
531 // ( 4 16 24 16 4 ) ( 4 )
532 // ( 1 4 6 4 1 ) ( 1 )
533 
534 bool ImageProcessor::GaussianSmooth5x5(const CByteImage *pInputImage, CByteImage *pOutputImage)
535 {
536  OPTIMIZED_FUNCTION_HEADER_2(GaussianSmooth5x5, pInputImage, pOutputImage)
537 
538  if (!pInputImage->IsCompatible(pOutputImage))
539  {
540  printf("error: input and output image do not match for ImageProcessor::GaussianSmooth5x5\n");
541  return false;
542  }
543 
544  if (pInputImage->width < 5 || pInputImage->height < 5)
545  {
546  printf("error: image must be at least of size 5x5 for ImageProcessor::GaussianSmooth5x5\n");
547  return false;
548  }
549 
550  const int width = pInputImage->width;
551  const int height = pInputImage->height;
552 
553  if (pInputImage->type == CByteImage::eRGB24)
554  {
555  // slow but better than nothing
556  CByteImage inputImageRGB24Split(width, height, CByteImage::eRGB24Split);
557  CByteImage outputImageRGB24Split(width, height, CByteImage::eRGB24Split);
558 
559  ImageProcessor::ConvertImage(pInputImage, &inputImageRGB24Split);
560 
561  GaussianSmooth5x5(&inputImageRGB24Split, &outputImageRGB24Split);
562 
563  ConvertImage(&outputImageRGB24Split, pOutputImage);
564 
565  return true;
566  }
567  else if (pInputImage->type == CByteImage::eRGB24Split)
568  {
569  const int nPixels = width * height;
570 
571  CByteImage imageHeaderInputGrayscale(width, height, CByteImage::eGrayScale, true);
572  CByteImage imageHeaderOutputGrayscale(width, height, CByteImage::eGrayScale, true);
573 
574  // red channel
575  imageHeaderInputGrayscale.pixels = pInputImage->pixels;
576  imageHeaderOutputGrayscale.pixels = pOutputImage->pixels;
577  GaussianSmooth5x5(&imageHeaderInputGrayscale, &imageHeaderOutputGrayscale);
578 
579  // green channel
580  imageHeaderInputGrayscale.pixels = pInputImage->pixels + nPixels;
581  imageHeaderOutputGrayscale.pixels = pOutputImage->pixels + nPixels;
582  GaussianSmooth5x5(&imageHeaderInputGrayscale, &imageHeaderOutputGrayscale);
583 
584  // blue channel
585  imageHeaderInputGrayscale.pixels = pInputImage->pixels + 2 * nPixels;
586  imageHeaderOutputGrayscale.pixels = pOutputImage->pixels + 2 * nPixels;
587  GaussianSmooth5x5(&imageHeaderInputGrayscale, &imageHeaderOutputGrayscale);
588 
589  return true;
590  }
591 
592  // create temp image
593  CByteImage *pTempImage = new CByteImage(pInputImage);
594 
595  const int width2 = width << 1;
596 
597  const unsigned char *input = pInputImage->pixels;
598  unsigned char *temp = pTempImage->pixels;
599  unsigned char *output = pOutputImage->pixels;
600 
601  int x, y, offset;
602 
603  // x direction
604  for (y = 0, offset = 0; y < height; y++)
605  {
606  temp[offset] = (11 * input[offset] + (input[offset + 1] << 2) + input[offset + 2] + 8) >> 4;
607  temp[offset + 1] = (5 * input[offset] + 6 * input[offset + 1] + (input[offset + 2] << 2) + input[offset + 3] + 8) >> 4;
608  offset += 2;
609 
610  // core loop
611  for (x = 4; x < width; x++, offset++)
612  temp[offset] = (input[offset - 2] + (input[offset - 1] << 2) + 6 * input[offset] + (input[offset + 1] << 2) + input[offset + 2] + 8) >> 4;
613 
614  temp[offset] = (input[offset - 2] + (input[offset - 1] << 2) + 6 * input[offset] + 5 * input[offset + 1] + 8) >> 4;
615  temp[offset + 1] = (input[offset - 1] + (input[offset] << 2) + 11 * input[offset + 1] + 8) >> 4;
616  offset += 2;
617  }
618 
619  // y direction
620  for (x = 0, offset = 0; x < width; x++, offset++)
621  {
622  output[offset] = (11 * temp[offset] + (temp[offset + width] << 2) + temp[offset + width2] + 8) >> 4;
623  output[offset + width] = (5 * temp[offset] + 6 * temp[offset + width] + (temp[offset + width2] << 2) + temp[offset + 3 * width] + 8) >> 4;
624  }
625 
626  // core loop
627  for (y = 4, offset = width << 1; y < height; y++)
628  for (x = 0; x < width; x++, offset++)
629  output[offset] = (temp[offset - width2] + (temp[offset - width] << 2) + 6 * temp[offset] + (temp[offset + width] << 2) + temp[offset + width2] + 8) >> 4;
630 
631  for (x = 0, offset = (height - 2) * width; x < width; x++, offset++)
632  {
633  output[offset] = (temp[offset - width2] + (temp[offset - width] << 2) + 6 * temp[offset] + 5 * temp[offset + width] + 8) >> 4;
634  output[offset + width] = (temp[offset - width] + (temp[offset] << 2) + 11 * temp[offset + width] + 8) >> 4;
635  }
636 
637  // free memory
638  delete pTempImage;
639 
641 
642  return true;
643 }
644 
645 
646 static bool AverageFilter3x3(const CByteImage *pInputImage, CByteImage *pOutputImage)
647 {
648  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
649  pInputImage->type != CByteImage::eGrayScale || pOutputImage->type != CByteImage::eGrayScale)
650  {
651  printf("error: input and output image do not match for ImageProcessor::AverageFilter\n");
652  return false;
653  }
654 
655  CByteImage *pSaveOutputImage = 0;
656  if (pInputImage->pixels == pOutputImage->pixels)
657  {
658  pSaveOutputImage = pOutputImage;
659  pOutputImage = new CByteImage(pInputImage);
660  }
661 
662  ImageProcessor::ZeroFrame(pOutputImage);
663 
664  const int maxj = pInputImage->width - 1, maxi = pInputImage->height - 1;
665  const int width = pInputImage->width;
666  const unsigned char *input = pInputImage->pixels;
667  unsigned char *output = pOutputImage->pixels;
668 
669  for (int i = 1, offset = width + 1; i < maxi; i++, offset += 2)
670  {
671  for (int j = 1; j < maxj; j++, offset++)
672  {
673  output[offset] = (
674  input[offset - width - 1] + input[offset - width] + input[offset - width + 1] +
675  input[offset - 1] + input[offset] + input[offset + 1] +
676  input[offset + width - 1] + input[offset + width] + input[offset + width + 1]
677  ) / 9;
678  }
679  }
680 
681  if (pSaveOutputImage)
682  {
683  ImageProcessor::CopyImage(pOutputImage, pSaveOutputImage);
684  delete pOutputImage;
685  }
686 
687  return true;
688 }
689 
690 bool ImageProcessor::AverageFilter(const CByteImage *pInputImage, CByteImage *pOutputImage, int nMaskSize)
691 {
692  if (nMaskSize == 3)
693  {
694  AverageFilter3x3(pInputImage, pOutputImage);
695  return false;
696  }
697 
698  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
699  pInputImage->type != CByteImage::eGrayScale || pOutputImage->type != CByteImage::eGrayScale)
700  {
701  printf("error: input and output image do not match for ImageProcessor::AverageFilter\n");
702  return false;
703  }
704 
705  if (nMaskSize < 3 || (nMaskSize + 1) % 2 != 0)
706  {
707  printf("error: nMaskSize must be 2 * k + 1 with k >= 1 for ImageProcessor::AverageFilter\n");
708  return false;
709  }
710 
711  const int width = pInputImage->width;
712  const int height = pInputImage->height;
713 
714  ImageProcessor::Zero(pOutputImage);
715 
716  CIntImage sat(width, height);
717 
718  ImageProcessor::CalculateSummedAreaTable(pInputImage, &sat);
719 
720  const int frame = (nMaskSize - 1) / 2;
721  const int area_size = nMaskSize*nMaskSize;
722 
723  const int maxx = width - 2*frame;
724  const int maxy = height - 2*frame;
725  const int offset = nMaskSize * width;
726 
727  const int *input = sat.pixels;
728  unsigned char *output = pOutputImage->pixels;
729 
730  for (int y = 1; y < maxy; y++)
731  {
732  const int line_offset = (y + frame)*width + frame;
733  for (int x = 1; x < maxx; x++)
734  {
735  int p1 = (y-1)*width + x - 1;
736  int p3 = p1 + offset;
737 
738  output[line_offset + x] = (input[p3 + nMaskSize] - input[p3] - input[p1 + nMaskSize] + input[p1]) / area_size;
739  }
740  }
741 
742  return true;
743 }
744 
745 
746 bool ImageProcessor::GaussianSmooth3x3(const CByteImage *pInputImage, CByteImage *pOutputImage)
747 {
748  OPTIMIZED_FUNCTION_HEADER_2(GaussianSmooth3x3, pInputImage, pOutputImage)
749 
750  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
751  pInputImage->type != pOutputImage->type)
752  {
753  printf("error: input and output image do not match for ImageProcessor::GaussianSmooth3x3\n");
754  return false;
755  }
756 
757  if (pInputImage->width < 3 || pInputImage->height < 3)
758  {
759  printf("error: image must be at least of size 3x3 for ImageProcessor::GaussianSmooth3x3\n");
760  return false;
761  }
762 
763  CByteImage *pSaveOutputImage = 0;
764  if (pInputImage->pixels == pOutputImage->pixels)
765  {
766  pSaveOutputImage = pOutputImage;
767  pOutputImage = new CByteImage(pInputImage);
768  }
769 
770  const int maxj = pInputImage->width - 1, maxi = pInputImage->height - 1;
771  const unsigned char *p = pInputImage->pixels;
772  unsigned char *output = pOutputImage->pixels;
773 
774  if (pInputImage->type == CByteImage::eGrayScale)
775  {
776  const int width = pInputImage->width;
777 
778  for (int i = 1, offset = width + 1; i < maxi; i++, offset += 2)
779  {
780  for (int j = 1; j < maxj; j++, offset++)
781  {
782  output[offset] = (
783  p[offset - width - 1] + (p[offset - width] << 1) + p[offset - width + 1] +
784  (p[offset - 1] << 1) + (p[offset] << 2) + (p[offset + 1] << 1) +
785  p[offset + width - 1] + (p[offset + width] << 1) + p[offset + width + 1] + 8
786  ) >> 4;
787  }
788  }
789  }
790  else if (pInputImage->type == CByteImage::eRGB24)
791  {
792  const int width = 3 * pInputImage->width;
793  int offset = width + 3;
794 
795  for (int i = 1; i < maxi; i++)
796  {
797  for (int j = 1; j < maxj; j++)
798  {
799  output[offset] = (
800  p[offset - width - 3] + (p[offset - width] << 1) + p[offset - width + 3] +
801  (p[offset - 3] << 1) + (p[offset] << 2) + (p[offset + 3] << 1) +
802  p[offset + width - 3] + (p[offset + width] << 1) + p[offset + width + 3] + 8
803  ) >> 4;
804 
805  offset++;
806 
807  output[offset] = (
808  p[offset - width - 3] + (p[offset - width] << 1) + p[offset - width + 3] +
809  (p[offset - 3] << 1) + (p[offset] << 2) + (p[offset + 3] << 1) +
810  p[offset + width - 3] + (p[offset + width] << 1) + p[offset + width + 3] + 8
811  ) >> 4;
812 
813  offset++;
814 
815  output[offset] = (
816  p[offset - width - 3] + (p[offset - width] << 1) + p[offset - width + 3] +
817  (p[offset - 3] << 1) + (p[offset] << 2) + (p[offset + 3] << 1) +
818  p[offset + width - 3] + (p[offset + width] << 1) + p[offset + width + 3] + 8
819  ) >> 4;
820 
821  offset++;
822  }
823 
824  offset += 6;
825  }
826  }
827 
828  // copy the border pixels from the original, since the border is not calculated
829  if (pSaveOutputImage)
830  CopyFrame(pSaveOutputImage, pOutputImage);
831  else
832  CopyFrame(pInputImage, pOutputImage);
833 
834  if (pSaveOutputImage)
835  {
836  CopyImage(pOutputImage, pSaveOutputImage);
837  delete pOutputImage;
838  }
839 
841 
842  return true;
843 }
844 
845 
846 bool ImageProcessor::SobelX(const CByteImage *pInputImage, CByteImage *pOutputImage, bool bAbsoluteValue)
847 {
848  CShortImage image(pInputImage->width, pInputImage->height);
849 
850  if (!SobelX(pInputImage, &image, bAbsoluteValue))
851  return false;
852 
853  return ConvertImage(&image, pOutputImage);
854 }
855 
856 bool ImageProcessor::SobelY(const CByteImage *pInputImage, CByteImage *pOutputImage, bool bAbsoluteValue)
857 {
858  CShortImage image(pInputImage->width, pInputImage->height);
859 
860  if (!SobelY(pInputImage, &image, bAbsoluteValue))
861  return false;
862 
863  return ConvertImage(&image, pOutputImage);
864 }
865 
866 bool ImageProcessor::SobelY(const CByteImage *pInputImage, CShortImage *pOutputImage, bool bAbsoluteValue)
867 {
868  OPTIMIZED_FUNCTION_HEADER_3(SobelY, pInputImage, pOutputImage, bAbsoluteValue)
869 
870  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
871  pInputImage->type != CByteImage::eGrayScale)
872  {
873  printf("error: input and output image do not match for ImageProcessor::SobelY\n");
874  return false;
875  }
876 
877  ZeroFrame(pOutputImage);
878 
879  const int maxj = pInputImage->width - 1, maxi = pInputImage->height - 1;
880  const int width = pInputImage->width;
881  const unsigned char *input = pInputImage->pixels;
882  short *output = pOutputImage->pixels;
883 
884  if (bAbsoluteValue)
885  {
886  for (int i = 1, offset = width + 1; i < maxi; i++, offset += 2)
887  {
888  for (int j = 1; j < maxj; j++, offset++)
889  {
890  output[offset] =
891  abs(input[offset + width - 1] + (input[offset + width] << 1) + input[offset + width + 1] -
892  input[offset - width - 1] - (input[offset - width] << 1) - input[offset - width + 1]);
893  }
894  }
895  }
896  else
897  {
898  for (int i = 1, offset = width + 1; i < maxi; i++, offset += 2)
899  {
900  for (int j = 1; j < maxj; j++, offset++)
901  {
902  output[offset] =
903  input[offset + width - 1] + (input[offset + width] << 1) + input[offset + width + 1] -
904  input[offset - width - 1] - (input[offset - width] << 1) - input[offset - width + 1];
905  }
906  }
907  }
908 
910 
911  return true;
912 }
913 
914 bool ImageProcessor::SobelX(const CByteImage *pInputImage, CShortImage *pOutputImage, bool bAbsoluteValue)
915 {
916  OPTIMIZED_FUNCTION_HEADER_3(SobelX, pInputImage, pOutputImage, bAbsoluteValue)
917 
918  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
919  pInputImage->type != CByteImage::eGrayScale)
920  {
921  printf("error: input and output image do not match for ImageProcessor::SobelX\n");
922  return false;
923  }
924 
925  ZeroFrame(pOutputImage);
926 
927  const int maxj = pInputImage->width - 1, maxi = pInputImage->height - 1;
928  const int width = pInputImage->width;
929  const unsigned char *input = pInputImage->pixels;
930  short *output = pOutputImage->pixels;
931 
932  if (bAbsoluteValue)
933  {
934  for (int i = 1, offset = width + 1; i < maxi; i++, offset += 2)
935  {
936  for (int j = 1; j < maxj; j++, offset++)
937  {
938  output[offset] =
939  abs(input[offset - width + 1] + (input[offset + 1] << 1) + input[offset + width + 1] -
940  input[offset - width - 1] - (input[offset - 1] << 1) - input[offset + width - 1]);
941  }
942  }
943  }
944  else
945  {
946  for (int i = 1, offset = width + 1; i < maxi; i++, offset += 2)
947  {
948  for (int j = 1; j < maxj; j++, offset++)
949  {
950  output[offset] =
951  input[offset - width + 1] + (input[offset + 1] << 1) + input[offset + width + 1] -
952  input[offset - width - 1] - (input[offset - 1] << 1) - input[offset + width - 1];
953  }
954  }
955  }
956 
958 
959  return true;
960 }
961 
962 
963 bool ImageProcessor::PrewittX(const CByteImage *pInputImage, CByteImage *pOutputImage, bool bAbsoluteValue)
964 {
965  CShortImage image(pInputImage->width, pInputImage->height);
966 
967  if (!PrewittX(pInputImage, &image, bAbsoluteValue))
968  return false;
969 
970  return ConvertImage(&image, pOutputImage);
971 }
972 
973 bool ImageProcessor::PrewittY(const CByteImage *pInputImage, CByteImage *pOutputImage, bool bAbsoluteValue)
974 {
975  CShortImage image(pInputImage->width, pInputImage->height);
976 
977  if (!PrewittY(pInputImage, &image, bAbsoluteValue))
978  return false;
979 
980  return ConvertImage(&image, pOutputImage);
981 }
982 
983 bool ImageProcessor::PrewittY(const CByteImage *pInputImage, CShortImage *pOutputImage, bool bAbsoluteValue)
984 {
985  OPTIMIZED_FUNCTION_HEADER_3(PrewittY, pInputImage, pOutputImage, bAbsoluteValue)
986 
987  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
988  pInputImage->type != CByteImage::eGrayScale)
989  {
990  printf("error: input and output image do not match for ImageProcessor::PrewittY\n");
991  return false;
992  }
993 
994  ZeroFrame(pOutputImage);
995 
996  const int maxj = pInputImage->width - 1, maxi = pInputImage->height - 1;
997  const int width = pInputImage->width;
998  const unsigned char *input = pInputImage->pixels;
999  short *output = pOutputImage->pixels;
1000 
1001  if (bAbsoluteValue)
1002  {
1003  for (int i = 1, offset = width + 1; i < maxi; i++, offset += 2)
1004  {
1005  for (int j = 1; j < maxj; j++, offset++)
1006  {
1007  output[offset] =
1008  abs(input[offset + width - 1] + input[offset + width] + input[offset + width + 1] -
1009  input[offset - width - 1] - input[offset - width] - input[offset - width + 1]);
1010  }
1011  }
1012  }
1013  else
1014  {
1015  for (int i = 1, offset = width + 1; i < maxi; i++, offset += 2)
1016  {
1017  for (int j = 1; j < maxj; j++, offset++)
1018  {
1019  output[offset] =
1020  input[offset + width - 1] + input[offset + width] + input[offset + width + 1] -
1021  input[offset - width - 1] - input[offset - width] - input[offset - width + 1];
1022  }
1023  }
1024  }
1025 
1027 
1028  return true;
1029 }
1030 
1031 bool ImageProcessor::PrewittX(const CByteImage *pInputImage, CShortImage *pOutputImage, bool bAbsoluteValue)
1032 {
1033  OPTIMIZED_FUNCTION_HEADER_3(PrewittX, pInputImage, pOutputImage, bAbsoluteValue)
1034 
1035  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
1036  pInputImage->type != CByteImage::eGrayScale)
1037  {
1038  printf("error: input and output image do not match for ImageProcessor::PrewittX\n");
1039  return false;
1040  }
1041 
1042  ZeroFrame(pOutputImage);
1043 
1044  const int maxj = pInputImage->width - 1, maxi = pInputImage->height - 1;
1045  const int width = pInputImage->width;
1046  const unsigned char *input = pInputImage->pixels;
1047  short *output = pOutputImage->pixels;
1048 
1049  if (bAbsoluteValue)
1050  {
1051  for (int i = 1; i < maxi; ++i)
1052  {
1053  int offset = width * i + 1;
1054  for (int j = 1; j < maxj; ++j, ++offset)
1055  {
1056  output[offset] =
1057  abs(input[offset - width + 1] + input[offset + 1] + input[offset + width + 1] -
1058  input[offset - width - 1] - input[offset - 1] - input[offset + width - 1]);
1059  }
1060  }
1061  }
1062  else
1063  {
1064  for (int i = 1; i < maxi; ++i)
1065  {
1066  int offset = width * i + 1;
1067  for (int j = 1; j < maxj; ++j, ++offset)
1068  {
1069  output[offset] =
1070  input[offset - width + 1] + input[offset + 1] + input[offset + width + 1] -
1071  input[offset - width - 1] - input[offset - 1] - input[offset + width - 1];
1072  }
1073  }
1074  }
1075 
1077 
1078  return true;
1079 }
1080 
1081 
1082 bool ImageProcessor::Laplace1(const CByteImage *pInputImage, CByteImage *pOutputImage, bool bAbsoluteValue)
1083 {
1084  CShortImage image(pInputImage->width, pInputImage->height);
1085 
1086  if (!Laplace1(pInputImage, &image, bAbsoluteValue))
1087  return false;
1088 
1089  ConvertImage(&image, pOutputImage);
1090 
1091  return true;
1092 }
1093 
1094 bool ImageProcessor::Laplace2(const CByteImage *pInputImage, CByteImage *pOutputImage, bool bAbsoluteValue)
1095 {
1096  CShortImage image(pInputImage->width, pInputImage->height);
1097 
1098  if (!Laplace2(pInputImage, &image, bAbsoluteValue))
1099  return false;
1100 
1101  ConvertImage(&image, pOutputImage);
1102 
1103  return true;
1104 }
1105 
1106 bool ImageProcessor::Laplace1(const CByteImage *pInputImage, CShortImage *pOutputImage, bool bAbsoluteValue)
1107 {
1108  OPTIMIZED_FUNCTION_HEADER_3(Laplace1, pInputImage, pOutputImage, bAbsoluteValue)
1109 
1110  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
1111  pInputImage->type != CByteImage::eGrayScale)
1112  {
1113  printf("error: input and output image do not match for ImageProcessor::Laplace1\n");
1114  return false;
1115  }
1116 
1117  ZeroFrame(pOutputImage);
1118 
1119  const int maxj = pInputImage->width - 1, maxi = pInputImage->height - 1;
1120  const int width = pInputImage->width;
1121  const unsigned char *input = pInputImage->pixels;
1122  short *output = pOutputImage->pixels;
1123 
1124  if (bAbsoluteValue)
1125  {
1126  for (int i = 1, offset = width + 1; i < maxi; i++, offset += 2)
1127  {
1128  for (int j = 1; j < maxj; j++, offset++)
1129  {
1130  output[offset] =
1131  abs(input[offset - width] +
1132  input[offset - 1] - (input[offset] << 2) + input[offset + 1] +
1133  input[offset + width]);
1134  }
1135  }
1136  }
1137  else
1138  {
1139  for (int i = 1, offset = width + 1; i < maxi; i++, offset += 2)
1140  {
1141  for (int j = 1; j < maxj; j++, offset++)
1142  {
1143  output[offset] =
1144  input[offset - width] +
1145  input[offset - 1] - (input[offset] << 2) + input[offset + 1] +
1146  input[offset + width];
1147  }
1148  }
1149  }
1150 
1152 
1153  return true;
1154 }
1155 
1156 bool ImageProcessor::Laplace2(const CByteImage *pInputImage, CShortImage *pOutputImage, bool bAbsoluteValue)
1157 {
1158  OPTIMIZED_FUNCTION_HEADER_3(Laplace2, pInputImage, pOutputImage, bAbsoluteValue)
1159 
1160  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
1161  pInputImage->type != CByteImage::eGrayScale)
1162  {
1163  printf("error: input and output image do not match for ImageProcessor::Laplace2\n");
1164  return false;
1165  }
1166 
1167  ZeroFrame(pOutputImage);
1168 
1169  const int maxj = pInputImage->width - 1, maxi = pInputImage->height - 1;
1170  const int width = pInputImage->width;
1171  const unsigned char *input = pInputImage->pixels;
1172  short *output = pOutputImage->pixels;
1173 
1174  if (bAbsoluteValue)
1175  {
1176  for (int i = 1, offset = width + 1; i < maxi; i++, offset += 2)
1177  {
1178  for (int j = 1; j < maxj; j++, offset++)
1179  {
1180  output[offset] =
1181  abs(input[offset - width - 1] + input[offset - width] + input[offset - width + 1] +
1182  input[offset - 1] - (input[offset] << 3) + input[offset + 1] +
1183  input[offset + width - 1] + input[offset + width] + input[offset + width + 1]);
1184  }
1185  }
1186  }
1187  else
1188  {
1189  for (int i = 1, offset = width + 1; i < maxi; i++, offset += 2)
1190  {
1191  for (int j = 1; j < maxj; j++, offset++)
1192  {
1193  output[offset] =
1194  input[offset - width - 1] + input[offset - width] + input[offset - width + 1] +
1195  input[offset - 1] - (input[offset] << 3) + input[offset + 1] +
1196  input[offset + width - 1] + input[offset + width] + input[offset + width + 1];
1197  }
1198  }
1199  }
1200 
1202 
1203  return true;
1204 }
1205 
1206 
1208 {
1210 
1211  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
1212  pInputImage->type != CByteImage::eGrayScale)
1213  {
1214  printf("error: input and output image do not match for ImageProcessor::CalculateGradientImagePrewitt\n");
1215  return false;
1216  }
1217 
1218  CByteImage *pSaveOutputImage = 0;
1219  if (pInputImage->pixels == pOutputImage->pixels)
1220  {
1221  pSaveOutputImage = pOutputImage;
1222  pOutputImage = new CByteImage(pInputImage);
1223  }
1224 
1225  ZeroFrame(pOutputImage);
1226 
1227  const int maxu = pInputImage->width - 1, maxv = pInputImage->height - 1;
1228  const int width = pInputImage->width;
1229  const unsigned char *input = pInputImage->pixels;
1230  unsigned char *output = pOutputImage->pixels;
1231 
1232  for (int v = 1, offset = width + 1; v < maxv; v++, offset += 2)
1233  {
1234  for (int u = 1; u < maxu; u++, offset++)
1235  {
1236  const int value_x =
1237  abs(input[offset + width - 1] + input[offset + width] + input[offset + width + 1] -
1238  input[offset - width - 1] - input[offset - width] - input[offset - width + 1]);
1239 
1240  const int value_y =
1241  abs(input[offset - width + 1] + input[offset + 1] + input[offset + width + 1] -
1242  input[offset - width - 1] - input[offset - 1] - input[offset + width - 1]);
1243 
1244  const int value = value_x > value_y ? value_x : value_y;
1245 
1246  output[offset] = value > 255 ? 255 : value;
1247  }
1248  }
1249 
1250  if (pSaveOutputImage)
1251  {
1252  CopyImage(pOutputImage, pSaveOutputImage);
1253  delete pOutputImage;
1254  }
1255 
1257 
1258  return true;
1259 }
1260 
1262 {
1263  OPTIMIZED_FUNCTION_HEADER_2(CalculateGradientImageSobel, pInputImage, pOutputImage)
1264 
1265  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
1266  pInputImage->type != CByteImage::eGrayScale)
1267  {
1268  printf("error: input and output image do not match for ImageProcessor::CalculateGradientImageSobel\n");
1269  return false;
1270  }
1271 
1272  CByteImage *pSaveOutputImage = 0;
1273  if (pInputImage->pixels == pOutputImage->pixels)
1274  {
1275  pSaveOutputImage = pOutputImage;
1276  pOutputImage = new CByteImage(pInputImage);
1277  }
1278 
1279  ZeroFrame(pOutputImage);
1280 
1281  const int maxu = pInputImage->width - 1, maxv = pInputImage->height - 1;
1282  const int width = pInputImage->width;
1283  const unsigned char *input = pInputImage->pixels;
1284  unsigned char *output = pOutputImage->pixels;
1285 
1286  for (int v = 1, offset = width + 1; v < maxv; v++, offset += 2)
1287  {
1288  for (int u = 1; u < maxu; u++, offset++)
1289  {
1290  const int value_x =
1291  abs(input[offset + width - 1] + (input[offset + width] << 1) + input[offset + width + 1] -
1292  input[offset - width - 1] - (input[offset - width] << 1) - input[offset - width + 1]);
1293 
1294  const int value_y =
1295  abs(input[offset - width + 1] + (input[offset + 1] << 1) + input[offset + width + 1] -
1296  input[offset - width - 1] - (input[offset - 1] << 1) - input[offset + width - 1]);
1297 
1298  const int value = value_x > value_y ? value_x : value_y;
1299 
1300  output[offset] = value > 255 ? 255 : value;
1301  }
1302  }
1303 
1304  if (pSaveOutputImage)
1305  {
1306  CopyImage(pOutputImage, pSaveOutputImage);
1307  delete pOutputImage;
1308  }
1309 
1311 
1312  return true;
1313 }
1314 
1315 bool ImageProcessor::CalculateGradientImage(const CByteImage *pInputImage, CByteImage *pOutputImage)
1316 {
1317  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height)
1318  {
1319  printf("error: input and output image do not match for ImageProcessor::CalculateGradientImage\n");
1320  return false;
1321  }
1322 
1323  CByteImage *pSaveOutputImage = 0;
1324  if (pInputImage->pixels == pOutputImage->pixels)
1325  {
1326  pSaveOutputImage = pOutputImage;
1327  pOutputImage = new CByteImage(pInputImage);
1328  }
1329 
1330  ZeroFrame(pOutputImage);
1331 
1332  const int maxu = pInputImage->width - 1, maxv = pInputImage->height - 1;
1333  const int width = pInputImage->width;
1334  const unsigned char *input = pInputImage->pixels;
1335  unsigned char *output = pOutputImage->pixels;
1336 
1337  if (pInputImage->type == CByteImage::eGrayScale)
1338  {
1339  for (int v = 1, offset = width + 1; v < maxv; v++, offset += 2)
1340  {
1341  for (int u = 1; u < maxu; u++, offset++)
1342  {
1343  const int value_x = abs(input[offset + width] - input[offset - width]);
1344  const int value_y = abs(input[offset + 1] - input[offset - 1]);
1345  const int value = value_x > value_y ? value_x : value_y;
1346  output[offset] = value > 255 ? 255 : value;
1347  }
1348  }
1349  }
1350  else if (pInputImage->type == CByteImage::eRGB24)
1351  {
1352  const unsigned char *input_r = pInputImage->pixels;
1353  const unsigned char *input_g = pInputImage->pixels + 1;
1354  const unsigned char *input_b = pInputImage->pixels + 2;
1355  const int width3 = 3 * width;
1356 
1357  for (int v = 1, offset = width3 + 3, output_offset = 0; v < maxv; v++, offset += 6, output_offset += 2)
1358  {
1359  for (int u = 1; u < maxu; u++, offset += 3, output_offset++)
1360  {
1361  const int r1 = abs(input_r[offset + 3] - input_r[offset - 3]);
1362  const int r2 = abs(input_r[offset + width3] - input_r[offset - width3]);
1363  const int g1 = abs(input_g[offset + 3] - input_g[offset - 3]);
1364  const int g2 = abs(input_g[offset + width3] - input_g[offset - width3]);
1365  const int b1 = abs(input_b[offset + 3] - input_b[offset - 3]);
1366  const int b2 = abs(input_b[offset + width3] - input_b[offset - width3]);
1367  const int r = MY_MAX(r1, r2);
1368  const int g = MY_MAX(g1, g2);
1369  const int b = MY_MAX(b1, b2);
1370  const int value = r > g ? (r > b ? r : b) : (g > b ? g : b);
1371  output[output_offset] = value > 255 ? 255 : value;
1372  }
1373  }
1374  }
1375 
1376  if (pSaveOutputImage)
1377  {
1378  CopyImage(pOutputImage, pSaveOutputImage);
1379  delete pOutputImage;
1380  }
1381 
1382  return true;
1383 }
1384 
1386 {
1387  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
1388  pInputImage->type != CByteImage::eGrayScale || pOutputImage->type != CByteImage::eGrayScale)
1389  {
1390  printf("error: input and output image do not match for ImageProcessor::CalculateGradientImageBinary\n");
1391  return false;
1392  }
1393 
1394  CByteImage *pSaveOutputImage = 0;
1395  if (pInputImage->pixels == pOutputImage->pixels)
1396  {
1397  pSaveOutputImage = pOutputImage;
1398  pOutputImage = new CByteImage(pInputImage);
1399  }
1400 
1401  ZeroFrame(pOutputImage);
1402 
1403  const int maxj = pInputImage->width - 1, maxi = pInputImage->height - 1;
1404  const int width = pInputImage->width;
1405  const unsigned char *input = pInputImage->pixels;
1406  unsigned char *output = pOutputImage->pixels;
1407 
1408  for (int i = 0, offset = 0; i < maxi; i++, offset++)
1409  {
1410  for (int j = 0; j < maxj; j++, offset++)
1411  {
1412  output[offset] = (input[offset + 1] ^ input[offset]) | (input[offset] ^ input[offset + width]);
1413  }
1414  }
1415 
1416  if (pSaveOutputImage)
1417  {
1418  CopyImage(pOutputImage, pSaveOutputImage);
1419  delete pOutputImage;
1420  }
1421 
1422  return true;
1423 }
1424 
1425 
1426 static bool Dilate3x3(const CByteImage *pInputImage, CByteImage *pOutputImage, const MyRegion *pROI)
1427 {
1428  OPTIMIZED_FUNCTION_HEADER_2_ROI(Dilate3x3, pInputImage, pOutputImage, pROI)
1429 
1430  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
1431  pInputImage->type != pOutputImage->type || pInputImage->type != CByteImage::eGrayScale)
1432  {
1433  printf("error: input and output image do not match for Dilate3x3\n");
1434  return false;
1435  }
1436 
1437  CByteImage *pSaveOutputImage = 0;
1438  if (pInputImage->pixels == pOutputImage->pixels)
1439  {
1440  pSaveOutputImage = pOutputImage;
1441  pOutputImage = new CByteImage(pInputImage);
1442  }
1443 
1444  ImageProcessor::Zero(pOutputImage, pROI);
1445 
1446  const int width = pInputImage->width;
1447  const int height = pInputImage->height;
1448 
1449  const unsigned char *input = pInputImage->pixels;
1450  unsigned char *output = pOutputImage->pixels;
1451 
1452  int min_u, max_u, min_v, max_v;
1453 
1454  if (pROI)
1455  {
1456  min_u = pROI->min_x + 1;
1457  max_u = pROI->max_x - 1;
1458  min_v = pROI->min_y + 1;
1459  max_v = pROI->max_y - 1;
1460 
1461  if (min_u < 1) min_u = 1;
1462  if (min_u > width - 2) min_u = width - 2;
1463  if (max_u < 1) max_u = 1;
1464  if (max_u > width - 2) max_u = width - 2;
1465  if (min_v < 1) min_v = 1;
1466  if (min_v > height - 2) min_v = height - 2;
1467  if (max_v < 1) max_v = 1;
1468  if (max_v > height - 2) max_v = height - 2;
1469  }
1470  else
1471  {
1472  min_u = 1;
1473  max_u = width - 2;
1474  min_v = 1;
1475  max_v = height - 2;
1476  }
1477 
1478  const int diff = width - (max_u - min_u + 1);
1479 
1480  bool *bLastY = new bool[width];
1481  memset(bLastY, false, width * sizeof(bool));
1482 
1483  for (int v = min_v, offset = min_v * width + min_u; v <= max_v; v++, offset += diff)
1484  {
1485  bool bLastX = false;
1486 
1487  for (int u = min_u; u <= max_u; u++, offset++)
1488  {
1489  if (input[offset])
1490  {
1491  if (bLastX)
1492  {
1493  if (bLastY[u])
1494  {
1495  output[offset + width + 1] = 255;
1496  }
1497  else
1498  {
1499  output[offset - width + 1] = 255;
1500  output[offset + 1] = 255;
1501  output[offset + width + 1] = 255;
1502 
1503  bLastY[u] = true;
1504  }
1505  }
1506  else
1507  {
1508  if (bLastY[u])
1509  {
1510  output[offset + width - 1] = 255;
1511  output[offset + width] = 255;
1512  output[offset + width + 1] = 255;
1513  }
1514  else
1515  {
1516  output[offset - width - 1] = 255;
1517  output[offset - width] = 255;
1518  output[offset - width + 1] = 255;
1519  output[offset - 1] = 255;
1520  output[offset] = 255;
1521  output[offset + 1] = 255;
1522  output[offset + width - 1] = 255;
1523  output[offset + width] = 255;
1524  output[offset + width + 1] = 255;
1525 
1526  bLastY[u] = true;
1527  }
1528 
1529  bLastX = true;
1530  }
1531  }
1532  else
1533  {
1534  bLastX = false;
1535  bLastY[u] = false;
1536  }
1537  }
1538  }
1539 
1540  delete [] bLastY;
1541 
1542  if (pSaveOutputImage)
1543  {
1544  ImageProcessor::CopyImage(pOutputImage, pSaveOutputImage, pROI, true);
1545  delete pOutputImage;
1546  }
1547 
1549 
1550  return true;
1551 }
1552 
1553 static bool Erode3x3(const CByteImage *pInputImage, CByteImage *pOutputImage, const MyRegion *pROI)
1554 {
1555  OPTIMIZED_FUNCTION_HEADER_2_ROI(Erode3x3, pInputImage, pOutputImage, pROI)
1556 
1557  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
1558  pInputImage->type != pOutputImage->type || pInputImage->type != CByteImage::eGrayScale)
1559  {
1560  printf("error: input and output image do not match for Erode3x3\n");
1561  return false;
1562  }
1563 
1564  CByteImage *pSaveOutputImage = 0;
1565  if (pInputImage->pixels == pOutputImage->pixels)
1566  {
1567  pSaveOutputImage = pOutputImage;
1568  pOutputImage = new CByteImage(pInputImage);
1569  }
1570 
1571  ImageProcessor::Zero(pOutputImage, pROI);
1572 
1573  const int width = pInputImage->width;
1574  const int height = pInputImage->height;
1575 
1576  const unsigned char *input = pInputImage->pixels;
1577  unsigned char *output = pOutputImage->pixels;
1578 
1579  int min_u, max_u, min_v, max_v;
1580 
1581  if (pROI)
1582  {
1583  min_u = pROI->min_x + 1;
1584  max_u = pROI->max_x - 1;
1585  min_v = pROI->min_y + 1;
1586  max_v = pROI->max_y - 1;
1587 
1588  if (min_u < 1) min_u = 1;
1589  if (min_u > width - 2) min_u = width - 2;
1590  if (max_u < 1) max_u = 1;
1591  if (max_u > width - 2) max_u = width - 2;
1592  if (min_v < 1) min_v = 1;
1593  if (min_v > height - 2) min_v = height - 2;
1594  if (max_v < 1) max_v = 1;
1595  if (max_v > height - 2) max_v = height - 2;
1596  }
1597  else
1598  {
1599  min_u = 1;
1600  max_u = width - 2;
1601  min_v = 1;
1602  max_v = height - 2;
1603  }
1604 
1605  const int diff = width - (max_u - min_u + 1);
1606 
1607  bool *bLastY = new bool[width];
1608  memset(bLastY, false, width * sizeof(bool));
1609 
1610  for (int v = min_v, offset = min_v * width + min_u; v <= max_v; v++, offset += diff)
1611  {
1612  bool bLastX = false;
1613 
1614  for (int u = min_u; u <= max_u; u++, offset++)
1615  {
1616  if (bLastX)
1617  {
1618  if (bLastY[u])
1619  {
1620  if (!input[offset + width + 1])
1621  {
1622  bLastX = false;
1623  bLastY[u] = false;
1624  continue;
1625  }
1626  }
1627  else
1628  {
1629  if (!input[offset - width + 1] || !input[offset + 1] || !input[offset + width + 1])
1630  {
1631  bLastX = false;
1632  continue;
1633  }
1634  else
1635  {
1636  bLastY[u] = true;
1637  }
1638  }
1639  }
1640  else
1641  {
1642  if (bLastY[u])
1643  {
1644  if (!input[offset + width - 1] || !input[offset + width] || !input[offset + width + 1])
1645  {
1646  bLastY[u] = false;
1647  continue;
1648  }
1649  else
1650  {
1651  bLastX = true;
1652  }
1653  }
1654  else
1655  {
1656  if (!input[offset - width - 1] || !input[offset - width] || !input[offset - width + 1] ||
1657  !input[offset - 1] || !input[offset] || !input[offset + 1] ||
1658  !input[offset + width - 1] || !input[offset + width] || !input[offset + width + 1])
1659  {
1660  continue;
1661  }
1662  else
1663  {
1664  bLastX = true;
1665  bLastY[u] = true;
1666  }
1667  }
1668  }
1669 
1670  output[offset] = 255;
1671  }
1672  }
1673 
1674  delete [] bLastY;
1675 
1676  if (pSaveOutputImage)
1677  {
1678  ImageProcessor::CopyImage(pOutputImage, pSaveOutputImage, pROI, true);
1679  delete pOutputImage;
1680  }
1681 
1683 
1684  return true;
1685 }
1686 
1687 bool ImageProcessor::Dilate(const CByteImage *pInputImage, CByteImage *pOutputImage, int nMaskSize, const MyRegion *pROI)
1688 {
1689  if (nMaskSize == 3)
1690  {
1691  Dilate3x3(pInputImage, pOutputImage, pROI);
1692  return false;
1693  }
1694 
1695  if (nMaskSize < 2)
1696  {
1697  printf("error: mask size is too small for ImageProcessor::Dilate\n");
1698  return false;
1699  }
1700 
1701  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
1702  pInputImage->type != pOutputImage->type || pInputImage->type != CByteImage::eGrayScale)
1703  {
1704  printf("error: input and output image do not match for ImageProcessor::Dilate\n");
1705  return false;
1706  }
1707 
1708  const int k = (nMaskSize - 1) / 2;
1709 
1710  CByteImage *pSaveOutputImage = 0;
1711  if (pInputImage->pixels == pOutputImage->pixels)
1712  {
1713  pSaveOutputImage = pOutputImage;
1714  pOutputImage = new CByteImage(pInputImage);
1715  }
1716 
1717  ImageProcessor::Zero(pOutputImage, pROI);
1718 
1719  const int width = pInputImage->width;
1720  const int height = pInputImage->height;
1721 
1722  const unsigned char *input = pInputImage->pixels;
1723  unsigned char *output = pOutputImage->pixels;
1724 
1725  int min_u, max_u, min_v, max_v;
1726 
1727  if (pROI)
1728  {
1729  min_u = pROI->min_x + 1;
1730  max_u = pROI->max_x - 1;
1731  min_v = pROI->min_y + 1;
1732  max_v = pROI->max_y - 1;
1733 
1734  if (min_u < k) min_u = k;
1735  if (min_u > width - 1 - k) min_u = width - 1 - k;
1736  if (max_u < k) max_u = k;
1737  if (max_u > width - 1 - k) max_u = width - 1 - k;
1738  if (min_v < k) min_v = k;
1739  if (min_v > height - 1 - k) min_v = height - 1 - k;
1740  if (max_v < k) max_v = k;
1741  if (max_v > height - 1 - k) max_v = height - 1 - k;
1742  }
1743  else
1744  {
1745  min_u = k;
1746  max_u = width - 1 - k;
1747  min_v = k;
1748  max_v = height - 1 - k;
1749  }
1750 
1751  const int diff = width - (max_u - min_u + 1);
1752 
1753  bool *bLastY = new bool[width];
1754  memset(bLastY, false, width * sizeof(bool));
1755 
1756  for (int v = min_v, offset = min_v * width + min_u; v <= max_v; v++, offset += diff)
1757  {
1758  bool bLastX = false;
1759 
1760  for (int u = min_u; u <= max_u; u++, offset++)
1761  {
1762  if (input[offset])
1763  {
1764  if (bLastX)
1765  {
1766  if (bLastY[u])
1767  {
1768  output[offset + k * width + k] = 255;
1769  }
1770  else
1771  {
1772  for (int i = -k; i <= k; i++)
1773  output[offset + i * width + k] = 255;
1774 
1775  bLastY[u] = true;
1776  }
1777  }
1778  else
1779  {
1780  if (bLastY[u])
1781  {
1782  for (int i = -k; i <= k; i++)
1783  output[offset + k * width + i] = 255;
1784  }
1785  else
1786  {
1787  for (int i = -k; i <= k; i++)
1788  for (int j = -k; j <= k; j++)
1789  output[offset + i * width + j] = 255;
1790 
1791  bLastY[u] = true;
1792  }
1793 
1794  bLastX = true;
1795  }
1796  }
1797  else
1798  {
1799  bLastX = false;
1800  bLastY[u] = false;
1801  }
1802  }
1803  }
1804 
1805  delete [] bLastY;
1806 
1807  if (pSaveOutputImage)
1808  {
1809  CopyImage(pOutputImage, pSaveOutputImage, pROI, true);
1810  delete pOutputImage;
1811  }
1812 
1813  return true;
1814 }
1815 
1816 bool ImageProcessor::Erode(const CByteImage *pInputImage, CByteImage *pOutputImage, int nMaskSize, const MyRegion *pROI)
1817 {
1818  if (nMaskSize == 3)
1819  {
1820  Erode3x3(pInputImage, pOutputImage, pROI);
1821  return false;
1822  }
1823 
1824  if (nMaskSize < 2)
1825  {
1826  printf("error: mask size is too small for ImageProcessor::Erode\n");
1827  return false;
1828  }
1829 
1830  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
1831  pInputImage->type != pOutputImage->type || pInputImage->type != CByteImage::eGrayScale)
1832  {
1833  printf("error: input and output image do not match for ImageProcessor::Erode\n");
1834  return false;
1835  }
1836 
1837  const int k = (nMaskSize - 1) / 2;
1838 
1839  CByteImage *pSaveOutputImage = 0;
1840  if (pInputImage->pixels == pOutputImage->pixels)
1841  {
1842  pSaveOutputImage = pOutputImage;
1843  pOutputImage = new CByteImage(pInputImage);
1844  }
1845 
1846  ImageProcessor::Zero(pOutputImage, pROI);
1847 
1848  const int width = pInputImage->width;
1849  const int height = pInputImage->height;
1850 
1851  const unsigned char *input = pInputImage->pixels;
1852  unsigned char *output = pOutputImage->pixels;
1853 
1854  int min_u, max_u, min_v, max_v;
1855 
1856  if (pROI)
1857  {
1858  min_u = pROI->min_x + 1;
1859  max_u = pROI->max_x - 1;
1860  min_v = pROI->min_y + 1;
1861  max_v = pROI->max_y - 1;
1862 
1863  if (min_u < k) min_u = k;
1864  if (min_u > width - 1 - k) min_u = width - 1 - k;
1865  if (max_u < k) max_u = k;
1866  if (max_u > width - 1 - k) max_u = width - 1 - k;
1867  if (min_v < k) min_v = k;
1868  if (min_v > height - 1 - k) min_v = height - 1 - k;
1869  if (max_v < k) max_v = k;
1870  if (max_v > height - 1 - k) max_v = height - 1 - k;
1871  }
1872  else
1873  {
1874  min_u = k;
1875  max_u = width - 1 - k;
1876  min_v = k;
1877  max_v = height - 1 - k;
1878  }
1879 
1880  const int diff = width - (max_u - min_u + 1);
1881 
1882  bool *bLastY = new bool[width];
1883  memset(bLastY, false, width * sizeof(bool));
1884 
1885  for (int v = min_v, offset = min_v * width + min_u; v <= max_v; v++, offset += diff)
1886  {
1887  bool bLastX = false;
1888 
1889  for (int u = min_u; u <= max_u; u++)
1890  {
1891  if (bLastX)
1892  {
1893  if (bLastY[u])
1894  {
1895  if (!input[offset + k * width + k])
1896  {
1897  bLastX = false;
1898  bLastY[u] = false;
1899  goto next;
1900  }
1901  }
1902  else
1903  {
1904  for (int i = -k; i <= k; i++)
1905  if (!input[offset + i * width + k])
1906  {
1907  bLastX = false;
1908  goto next;
1909  }
1910 
1911  bLastY[u] = true;
1912  }
1913  }
1914  else
1915  {
1916  if (bLastY[u])
1917  {
1918  for (int i = -k; i <= k; i++)
1919  if (!input[offset + k * width + i])
1920  {
1921  bLastY[u] = false;
1922  goto next;
1923  }
1924  }
1925  else
1926  {
1927  for (int i = -k; i <= k; i++)
1928  for (int j = -k; j <= k; j++)
1929  if (!input[offset + i * width + j])
1930  goto next;
1931 
1932  bLastY[u] = true;
1933  }
1934 
1935  bLastX = true;
1936  }
1937 
1938  output[offset] = 255;
1939 
1940  next:
1941 
1942  offset++;
1943  }
1944  }
1945 
1946  delete [] bLastY;
1947 
1948  if (pSaveOutputImage)
1949  {
1950  CopyImage(pOutputImage, pSaveOutputImage, pROI, true);
1951  delete pOutputImage;
1952  }
1953 
1954  return true;
1955 }
1956 
1957 
1958 bool ImageProcessor::HistogramEqualization(const CByteImage *pInputImage, CByteImage *pOutputImage)
1959 {
1960  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
1961  pInputImage->type != CByteImage::eGrayScale || pOutputImage->type != CByteImage::eGrayScale)
1962  {
1963  printf("error: input and output image do not match for ImageProcessor::HistogramEqualization\n");
1964  return false;
1965  }
1966 
1967  const int nPixels = pInputImage->width * pInputImage->height;
1968  const unsigned char *input = pInputImage->pixels;
1969  unsigned char *output = pOutputImage->pixels;
1970  const float fNormalizeConstant = 255.0f / nPixels;
1971  int histogram[256] = { 0 };
1972  int i;
1973 
1974  // calculate histogram
1975  for (i = 0; i < nPixels; i++)
1976  histogram[input[i]]++;
1977 
1978  // calculate normalized accumulated histogram
1979  for (i = 1; i < 256; i++)
1980  histogram[i] += histogram[i - 1];
1981 
1982  for (i = 0; i < 256; i++)
1983  histogram[i] = int(histogram[i] * fNormalizeConstant + 0.5f);
1984 
1985  // apply normalization
1986  for (i = 0; i < nPixels; i++)
1987  output[i] = histogram[input[i]];
1988 
1989  return true;
1990 }
1991 
1992 
1993 bool ImageProcessor::ApplyAffinePointOperation(const CByteImage *pInputImage, CByteImage *pOutputImage, float a, float b)
1994 {
1995  OPTIMIZED_FUNCTION_HEADER_4(ApplyAffinePointOperation, pInputImage, pOutputImage, a, b)
1996 
1997  if (!pInputImage->IsCompatible(pOutputImage))
1998  {
1999  printf("error: input and output image do not match for ImageProcessor::ApplyAffinePointOperation\n");
2000  return false;
2001  }
2002 
2003  const int nBytes = pInputImage->width * pInputImage->height * pInputImage->bytesPerPixel;
2004  const unsigned char *input = pInputImage->pixels;
2005  unsigned char *output = pOutputImage->pixels;
2006 
2007  b += 0.5f;
2008 
2009  for (int i = 0; i < nBytes; i++)
2010  {
2011  const int v = int(a * input[i] + b);
2012  output[i] = v < 0 ? 0 : (v > 255 ? 255 : (unsigned char) v);
2013  }
2014 
2016 
2017  return true;
2018 }
2019 
2020 
2021 bool ImageProcessor::Spread(const CByteImage *pInputImage, CByteImage *pOutputImage)
2022 {
2023  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
2024  pInputImage->type != CByteImage::eGrayScale || pOutputImage->type != CByteImage::eGrayScale)
2025  {
2026  printf("error: input and output image do not match for ImageProcessor::Spread\n");
2027  return false;
2028  }
2029 
2030  const int nPixels = pInputImage->width * pInputImage->height;
2031  const unsigned char *input = pInputImage->pixels;
2032  unsigned char min = 255, max = 0;
2033 
2034  // calculate histogram
2035  for (int i = 0; i < nPixels; i++)
2036  {
2037  if (input[i] < min)
2038  min = input[i];
2039 
2040  if (input[i] > max)
2041  max = input[i];
2042  }
2043 
2044  if (min == max)
2045  {
2046  printf("error: input image is homogenous with gray value %i\n", min);
2047  return false;
2048  }
2049 
2050  const float a = 255.0f / (max - min);
2051  const float b = -(min * 255.0f) / (max - min);
2052 
2053  ApplyAffinePointOperation(pInputImage, pOutputImage, a, b);
2054 
2055  return true;
2056 }
2057 
2058 bool ImageProcessor::HistogramStretching(const CByteImage *pInputImage, CByteImage *pOutputImage, float p_min, float p_max)
2059 {
2060  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
2061  pInputImage->type != CByteImage::eGrayScale || pOutputImage->type != CByteImage::eGrayScale)
2062  {
2063  printf("error: input and output image do not match for ImageProcessor::HistogramStretching\n");
2064  return false;
2065  }
2066 
2067  const int nPixels = pInputImage->width * pInputImage->height;
2068  const unsigned char *input = pInputImage->pixels;
2069  int histogram[256] = { 0 };
2070  int i;
2071 
2072  // calculate histogram
2073  for (i = 0; i < nPixels; i++)
2074  histogram[input[i]]++;
2075 
2076  // calculate accumulated histogram
2077  for (i = 1; i < 256; i++)
2078  histogram[i] += histogram[i - 1];
2079 
2080  const float nMinValue = p_min * histogram[255];
2081  const float nMaxValue = p_max * histogram[255];
2082  int min = 0, max = 0;
2083 
2084  for (i = 0; i < 256; i++)
2085  {
2086  if (histogram[i] >= nMinValue)
2087  {
2088  min = i;
2089  break;
2090  }
2091  }
2092 
2093  for (i = 0; i < 256; i++)
2094  {
2095  if (histogram[i] >= nMaxValue)
2096  {
2097  max = i;
2098  break;
2099  }
2100  }
2101 
2102  if (min == max)
2103  {
2104  printf("error: input image is homogenous with gray value %i\n", min);
2105  return false;
2106  }
2107 
2108  const float a = 255.0f / (max - min);
2109  const float b = -(min * 255.0f) / (max - min);
2110 
2111  ApplyAffinePointOperation(pInputImage, pOutputImage, a, b);
2112 
2113  return true;
2114 }
2115 
2116 bool ImageProcessor::Invert(const CByteImage *pInputImage, CByteImage *pOutputImage)
2117 {
2118  OPTIMIZED_FUNCTION_HEADER_2(Invert, pInputImage, pOutputImage)
2119 
2120  if (!pInputImage->IsCompatible(pOutputImage))
2121  {
2122  printf("error: input and output image do not match for ImageProcessor::Invert\n");
2123  return false;
2124  }
2125 
2126  const int nBytes = pInputImage->width * pInputImage->height * pInputImage->bytesPerPixel;
2127  const unsigned char *input = pInputImage->pixels;
2128  unsigned char *output = pOutputImage->pixels;
2129 
2130  for (int i = 0; i < nBytes; i++)
2131  output[i] = 255 - input[i];
2132 
2134 
2135  return true;
2136 }
2137 
2138 
2139 bool ImageProcessor::ConvertImage(const CByteImage *pInputImage, CByteImage *pOutputImage, bool bFast, const MyRegion *pROI)
2140 {
2141  OPTIMIZED_FUNCTION_HEADER_3_ROI(ConvertImage, pInputImage, pOutputImage, bFast, pROI)
2142 
2143  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height)
2144  {
2145  printf("error: input and output image do not match for ImageProcessor::ConvertImage\n");
2146  return false;
2147  }
2148 
2149  if (pInputImage->type == pOutputImage->type)
2150  {
2151  CopyImage(pInputImage, pOutputImage, pROI, true);
2152  return true;
2153  }
2154 
2155  const int nPixels = pInputImage->width * pInputImage->height;
2156 
2157  const unsigned char *input = pInputImage->pixels;
2158  unsigned char *output = pOutputImage->pixels;
2159 
2160  if (pROI)
2161  {
2162  const int width = pInputImage->width;
2163  const int height = pInputImage->height;
2164 
2165  int min_x = pROI->min_x;
2166  int max_x = pROI->max_x;
2167  int min_y = pROI->min_y;
2168  int max_y = pROI->max_y;
2169 
2170  if (min_x < 0) min_x = 0;
2171  if (min_x > width - 1) min_x = width - 1;
2172  if (max_x < 0) max_x = 0;
2173  if (max_x > width - 1) max_x = width - 1;
2174  if (min_y < 0) min_y = 0;
2175  if (min_y > height - 1) min_y = height - 1;
2176  if (max_y < 0) max_y = 0;
2177  if (max_y > height - 1) max_y = height - 1;
2178 
2179  const int diff = width - (max_x - min_x + 1);
2180  const int diff_rgb = 3 * diff;
2181 
2182  if (pInputImage->type == CByteImage::eRGB24)
2183  {
2184  if (pOutputImage->type == CByteImage::eGrayScale)
2185  {
2186  if (bFast)
2187  {
2188  for (int y = min_y, offset_output = width * min_y + min_x, offset_input = 3 * offset_output; y <= max_y; y++, offset_output += diff, offset_input += diff_rgb)
2189  for (int x = min_x; x <= max_x; x++, offset_output++, offset_input += 3)
2190  output[offset_output] = (input[offset_input] + (input[offset_input + 1] << 1) + input[offset_input + 2] + 2) >> 2;
2191  }
2192  else
2193  {
2194  for (int y = min_y, offset_output = width * min_y + min_x, offset_input = 3 * offset_output; y <= max_y; y++, offset_output += diff, offset_input += diff_rgb)
2195  for (int x = min_x; x <= max_x; x++, offset_output++, offset_input += 3)
2196  output[offset_output] = (9797 * input[offset_input] + 19235 * input[offset_input + 1] + 3736 * input[offset_input + 2] + 16384) >> 15;
2197  }
2198  }
2199  else if (pOutputImage->type == CByteImage::eRGB24Split)
2200  {
2201  unsigned char *pHelperR = output;
2202  unsigned char *pHelperG = pHelperR + nPixels;
2203  unsigned char *pHelperB = pHelperG + nPixels;
2204 
2205  for (int y = min_y, offset_output = width * min_y + min_x, offset_input = 3 * offset_output; y <= max_y; y++, offset_output += diff, offset_input += diff_rgb)
2206  for (int x = min_x; x <= max_x; x++, offset_output++, offset_input += 3)
2207  {
2208  pHelperR[offset_output] = input[offset_input];
2209  pHelperG[offset_output] = input[offset_input + 1];
2210  pHelperB[offset_output] = input[offset_input + 2];
2211  }
2212  }
2213  }
2214  if (pInputImage->type == CByteImage::eRGB24Split)
2215  {
2216  const unsigned char *pHelperR = input;
2217  const unsigned char *pHelperG = pHelperR + nPixels;
2218  const unsigned char *pHelperB = pHelperG + nPixels;
2219 
2220  if (pOutputImage->type == CByteImage::eGrayScale)
2221  {
2222  if (bFast)
2223  {
2224  for (int y = min_y, offset = width * min_y + min_x; y <= max_y; y++, offset += diff)
2225  for (int x = min_x; x <= max_x; x++, offset++)
2226  output[offset] = (pHelperR[offset] + (pHelperG[offset] << 1) + pHelperB[offset] + 2) >> 2;
2227  }
2228  else
2229  {
2230  for (int y = min_y, offset = width * min_y + min_x; y <= max_y; y++, offset += diff)
2231  for (int x = min_x; x <= max_x; x++, offset++)
2232  output[offset] = (9797 * pHelperR[offset] + 19235 * pHelperG[offset] + 3736 * pHelperB[offset] + 16384) >> 15;
2233  }
2234  }
2235  else if (pOutputImage->type == CByteImage::eRGB24)
2236  {
2237  for (int y = min_y, input_offset = width * min_y + min_x, output_offset = 3 * input_offset; y <= max_y; y++, input_offset += diff, output_offset += diff_rgb)
2238  for (int x = min_x; x <= max_x; x++, input_offset++, output_offset += 3)
2239  {
2240  output[output_offset] = pHelperR[input_offset];
2241  output[output_offset + 1] = pHelperG[input_offset];
2242  output[output_offset + 2] = pHelperB[input_offset];
2243  }
2244  }
2245  }
2246  else if (pInputImage->type == CByteImage::eGrayScale)
2247  {
2248  if (pOutputImage->type == CByteImage::eRGB24)
2249  {
2250  for (int y = min_y, offset_input = width * min_y + min_x, offset_output = 3 * offset_input; y <= max_y; y++, offset_output += diff_rgb, offset_input += diff)
2251  for (int x = min_x; x <= max_x; x++, offset_output += 3, offset_input++)
2252  {
2253  output[offset_output + 2] = input[offset_input];
2254  output[offset_output + 1] = input[offset_input];
2255  output[offset_output] = input[offset_input];
2256  }
2257  }
2258  else if (pOutputImage->type == CByteImage::eRGB24Split)
2259  {
2260  unsigned char *pHelperR = output;
2261  unsigned char *pHelperG = pHelperR + nPixels;
2262  unsigned char *pHelperB = pHelperG + nPixels;
2263 
2264  for (int y = min_y, offset = width * min_y + min_x; y <= max_y; y++, offset += diff)
2265  for (int x = min_x; x <= max_x; x++, offset++)
2266  pHelperR[offset] = pHelperG[offset] = pHelperB[offset] = input[offset];
2267  }
2268  }
2269  }
2270  else
2271  {
2272  if (pInputImage->type == CByteImage::eRGB24)
2273  {
2274  if (pOutputImage->type == CByteImage::eGrayScale)
2275  {
2276  if (bFast)
2277  {
2278  for (int offset = 0, i = 0; i < nPixels; i++, offset += 3)
2279  output[i] = (input[offset] + (input[offset + 1] << 1) + input[offset + 2] + 2) >> 2;
2280  }
2281  else
2282  {
2283  for (int offset = 0, i = 0; i < nPixels; i++, offset += 3)
2284  output[i] = (9797 * input[offset] + 19235 * input[offset + 1] + 3736 * input[offset + 2] + 16384) >> 15;
2285  }
2286  }
2287  else if (pOutputImage->type == CByteImage::eRGB24Split)
2288  {
2289  unsigned char *pHelperR = output;
2290  unsigned char *pHelperG = pHelperR + nPixels;
2291  unsigned char *pHelperB = pHelperG + nPixels;
2292 
2293  for (int offset = 0, i = 0; i < nPixels; i++, offset += 3)
2294  {
2295  pHelperR[i] = input[offset];
2296  pHelperG[i] = input[offset + 1];
2297  pHelperB[i] = input[offset + 2];
2298  }
2299  }
2300  }
2301  else if (pInputImage->type == CByteImage::eRGB24Split)
2302  {
2303  const unsigned char *pHelperR = input;
2304  const unsigned char *pHelperG = pHelperR + nPixels;
2305  const unsigned char *pHelperB = pHelperG + nPixels;
2306 
2307  if (pOutputImage->type == CByteImage::eGrayScale)
2308  {
2309  if (bFast)
2310  {
2311  for (int i = 0; i < nPixels; i++)
2312  output[i] = (pHelperR[i] + (pHelperG[i] << 1) + pHelperB[i] + 2) >> 2;
2313  }
2314  else
2315  {
2316  for (int i = 0; i < nPixels; i++)
2317  output[i] = (9797 * pHelperR[i] + 19235 * pHelperG[i] + 3736 * pHelperB[i] + 16384) >> 15;
2318  }
2319  }
2320  else if (pOutputImage->type == CByteImage::eRGB24)
2321  {
2322  for (int offset = 0, i = 0; i < nPixels; i++, offset += 3)
2323  {
2324  output[offset] = pHelperR[i];
2325  output[offset + 1] = pHelperG[i];
2326  output[offset + 2] = pHelperB[i];
2327  }
2328  }
2329  }
2330  else if (pInputImage->type == CByteImage::eGrayScale)
2331  {
2332  if (pOutputImage->type == CByteImage::eRGB24)
2333  {
2334  for (int offset = 0, i = 0; i < nPixels; i++, offset += 3)
2335  {
2336  output[offset + 2] = input[i];
2337  output[offset + 1] = input[i];
2338  output[offset] = input[i];
2339  }
2340  }
2341  else if (pOutputImage->type == CByteImage::eRGB24Split)
2342  {
2343  unsigned char *pHelperR = output;
2344  unsigned char *pHelperG = pHelperR + nPixels;
2345  unsigned char *pHelperB = pHelperG + nPixels;
2346 
2347  for (int i = 0; i < nPixels; i++)
2348  pHelperR[i] = pHelperG[i] = pHelperB[i] = input[i];
2349  }
2350  }
2351  }
2352 
2354 
2355  return true;
2356 }
2357 
2358 
2359 bool ImageProcessor::ConvertImage(const CFloatImage *pInputImage, CByteImage *pOutputImage, bool equalize)
2360 {
2361  if(pInputImage->numberOfChannels == 1 && pOutputImage->type != CByteImage::eGrayScale)
2362  {
2363  printf("error: ImageProcessor::ConvertImage cannot convert a single channel float image into mulitple channel byte image\n");
2364  return false;
2365  }
2366 
2367  if(pInputImage->numberOfChannels == 3 && pOutputImage->type == CByteImage::eGrayScale)
2368  {
2369  printf("error: ImageProcessor::ConvertImage cannot convert a 3 channel float image into single channel byte image\n");
2370  return false;
2371  }
2372 
2373  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height)
2374  {
2375  printf("error: input and output image dimensions do not match for ImageProcessor::ConvertImage\n");
2376  return false;
2377  }
2378 
2379  const int nPixels = pInputImage->width * pInputImage->height;
2380  const float* input = pInputImage->pixels;
2381  unsigned char* output = pOutputImage->pixels;
2382  float* min = new float[pInputImage->numberOfChannels];
2383  float* max = new float[pInputImage->numberOfChannels];
2384  float* factor = new float[pInputImage->numberOfChannels];
2385  int i, j;
2386  int pixelChannelIndex;
2387  for (i = 0; i < pInputImage->numberOfChannels; i++)
2388  {
2389  min[i] = FLT_MAX;
2390  max[i] = -FLT_MAX;
2391  }
2392 
2393  for (i = 0; i < nPixels; i++)
2394  {
2395  for (j = 0; j < pInputImage->numberOfChannels; j++)
2396  {
2397  pixelChannelIndex = i * pInputImage->numberOfChannels + j;
2398 
2399  if (input[pixelChannelIndex] > max[j])
2400  {
2401  max[j] = input[pixelChannelIndex];
2402  }
2403 
2404  if (input[pixelChannelIndex] < min[j])
2405  {
2406  min[j] = input[pixelChannelIndex];
2407  }
2408  }
2409  }
2410 
2411  if (equalize)
2412  {
2413 
2414  for (i = 0; i < pInputImage->numberOfChannels; i++)
2415  {
2416  if((max[i] - min[i]) != 0.0f)
2417  {
2418  factor[i] = 255.0f / max[i] - min[i];
2419  }
2420  else
2421  {
2422  factor[i] = 0.0f;
2423  }
2424  }
2425 
2426  for (i = 0; i < nPixels; i++)
2427  {
2428  for (j = 0; j < pInputImage->numberOfChannels; j++)
2429  {
2430  pixelChannelIndex = i * pInputImage->numberOfChannels + j;
2431 
2432  output[pixelChannelIndex] = (unsigned char) ((input[pixelChannelIndex] - min[j]) * factor[j]);
2433  }
2434  }
2435  }
2436  else
2437  {
2438  // verify input value ranges
2439  for (i = 0; i < pInputImage->numberOfChannels; i++)
2440  {
2441  if(min[i] < 0.0f || max[i] > 255.0f)
2442  {
2443  printf("error: float input image values exceed value range of output byte image in ImageProcessor::ConvertImage. Equalization required!\n");
2444  return false;
2445  }
2446  }
2447 
2448  for (i = 0; i < nPixels; i++)
2449  {
2450  for (j = 0; j < pInputImage->numberOfChannels; j++)
2451  {
2452  pixelChannelIndex = i * pInputImage->numberOfChannels + j;
2453 
2454  output[pixelChannelIndex] = (unsigned char) input[pixelChannelIndex];
2455  }
2456  }
2457  }
2458 
2459  return true;
2460 }
2461 
2462 
2463 bool ImageProcessor::ConvertImage(const CByteImage *pInputImage, CFloatImage *pOutputImage)
2464 {
2465  if(pOutputImage->numberOfChannels == 1 && pInputImage->type != CByteImage::eGrayScale)
2466  {
2467  printf("error: ImageProcessor::ConvertImage cannot convert a single channel byte image into single channel byte image\n");
2468  return false;
2469  }
2470 
2471  if(pOutputImage->numberOfChannels == 3 && pInputImage->type == CByteImage::eGrayScale)
2472  {
2473  printf("error: ImageProcessor::ConvertImage cannot convert a single channel byte image into 3-channel float image\n");
2474  return false;
2475  }
2476 
2477  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height)
2478  {
2479  printf("error: input and output image dimensions do not match for ImageProcessor::ConvertImage\n");
2480  return false;
2481  }
2482 
2483  const int nPixels = pInputImage->width * pInputImage->height;
2484  const unsigned char *input = pInputImage->pixels;
2485  float *output = pOutputImage->pixels;
2486  int i, j;
2487  int pixelChannelIndex;
2488 
2489  for (i = 0; i < nPixels; i++)
2490  {
2491  for (j = 0; j < pInputImage->bytesPerPixel; j++)
2492  {
2493  pixelChannelIndex = i * pInputImage->bytesPerPixel + j;
2494 
2495  output[pixelChannelIndex] = (float) input[pixelChannelIndex];
2496  }
2497  }
2498 
2499  return true;
2500 }
2501 
2502 
2503 bool ImageProcessor::ConvertImage(const CFloatMatrix *pInputMatrix, CByteImage *pOutputImage)
2504 {
2505  if (pOutputImage->type != CByteImage::eGrayScale || pInputMatrix->columns != pOutputImage->width || pInputMatrix->rows != pOutputImage->height)
2506  {
2507  printf("error: input matrix and output image do not match for ImageProcessor::ConvertImage\n");
2508  return false;
2509  }
2510 
2511  const int nPixels = pInputMatrix->columns * pInputMatrix->rows;
2512  const float *input = pInputMatrix->data;
2513  unsigned char *output = pOutputImage->pixels;
2514  float min = FLT_MAX, max = -FLT_MAX;
2515  int i;
2516 
2517  for (i = 0; i < nPixels; i++)
2518  {
2519  if (input[i] > max)
2520  max = input[i];
2521 
2522  if (input[i] < min)
2523  min = input[i];
2524  }
2525 
2526  const float divider = max - min;
2527 
2528  if (divider == 0.0f)
2529  {
2530  Zero(pOutputImage);
2531  }
2532  else
2533  {
2534  const float factor = 255.0f / divider;
2535 
2536  for (i = 0; i < nPixels; i++)
2537  output[i] = (unsigned char) ((input[i] - min) * factor);
2538  }
2539 
2540  return true;
2541 }
2542 
2543 bool ImageProcessor::ConvertImage(const CByteImage *pInputImage, CFloatMatrix *pOutputMatrix)
2544 {
2545  if (pInputImage->type != CByteImage::eGrayScale || pOutputMatrix->columns != pInputImage->width || pOutputMatrix->rows != pInputImage->height)
2546  {
2547  printf("error: input image and output matrix do not match for ImageProcessor::ConvertImage\n");
2548  return false;
2549  }
2550 
2551  const int nPixels = pOutputMatrix->columns * pOutputMatrix->rows;
2552  const unsigned char *input = pInputImage->pixels;
2553  float *output = pOutputMatrix->data;
2554 
2555  for (int i = 0; i < nPixels; i++)
2556  output[i] = input[i];
2557 
2558  return true;
2559 }
2560 
2561 bool ImageProcessor::ConvertImage(const CShortImage *pInputImage, CByteImage *pOutputImage)
2562 {
2563  if (pOutputImage->type != CByteImage::eGrayScale || pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height)
2564  {
2565  printf("error: input matrix and output image do not match for ImageProcessor::ConvertImage\n");
2566  return false;
2567  }
2568 
2569  const int nPixels = pInputImage->width * pInputImage->height;
2570  const short *input = pInputImage->pixels;
2571  unsigned char *output = pOutputImage->pixels;
2572  short min = SHRT_MAX, max = SHRT_MIN;
2573  int i;
2574 
2575  for (i = 0; i < nPixels; i++)
2576  {
2577  if (input[i] > max)
2578  max = input[i];
2579 
2580  if (input[i] < min)
2581  min = input[i];
2582  }
2583 
2584  const short divider = max - min;
2585 
2586  if (divider == 0)
2587  {
2588  Zero(pOutputImage);
2589  }
2590  else
2591  {
2592  for (i = 0; i < nPixels; i++)
2593  output[i] = (unsigned char) ((255 * (input[i] - min)) / divider);
2594  }
2595 
2596  return true;
2597 }
2598 
2599 bool ImageProcessor::ConvertImage(const CByteImage *pInputImage, CShortImage *pOutputImage)
2600 {
2601  if (pInputImage->type != CByteImage::eGrayScale || pOutputImage->width != pInputImage->width || pOutputImage->height != pInputImage->height)
2602  {
2603  printf("error: input image and output matrix do not match for ImageProcessor::ConvertImage\n");
2604  return false;
2605  }
2606 
2607  const int nPixels = pOutputImage->width * pOutputImage->height;
2608  const unsigned char *input = pInputImage->pixels;
2609  short *output = pOutputImage->pixels;
2610 
2611  for (int i = 0; i < nPixels; i++)
2612  output[i] = (short) input[i];
2613 
2614  return true;
2615 }
2616 
2617 bool ImageProcessor::ConvertImage(const CByteImage *pInputImage, CIntImage *pOutputImage)
2618 {
2619  if (pInputImage->type != CByteImage::eGrayScale || pOutputImage->width != pInputImage->width || pOutputImage->height != pInputImage->height)
2620  {
2621  printf("error: input image and output matrix do not match for ImageProcessor::ConvertImage\n");
2622  return false;
2623  }
2624 
2625  const int nPixels = pOutputImage->width * pOutputImage->height;
2626  const unsigned char *input = pInputImage->pixels;
2627  int *output = pOutputImage->pixels;
2628 
2629  for (int i = 0; i < nPixels; i++)
2630  output[i] = (int) input[i];
2631 
2632  return true;
2633 }
2634 
2635 bool ImageProcessor::ConvertImage(const CIntImage *pInputImage, CByteImage *pOutputImage)
2636 {
2637  if (pOutputImage->type != CByteImage::eGrayScale || pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height)
2638  {
2639  printf("error: input matrix and output image do not match for ImageProcessor::ConvertImage\n");
2640  return false;
2641  }
2642 
2643  const int nPixels = pInputImage->width * pInputImage->height;
2644  const int *input = pInputImage->pixels;
2645  unsigned char *output = pOutputImage->pixels;
2646  int min = INT_MAX, max = INT_MIN;
2647  int i;
2648 
2649  for (i = 0; i < nPixels; i++)
2650  {
2651  if (input[i] > max)
2652  max = input[i];
2653 
2654  if (input[i] < min)
2655  min = input[i];
2656  }
2657 
2658  const int divider = max - min;
2659 
2660  if (divider == 0)
2661  {
2662  Zero(pOutputImage);
2663  }
2664  else
2665  {
2666  for (i = 0; i < nPixels; i++)
2667  output[i] = (unsigned char) ((255 * (input[i] - min)) / divider);
2668  }
2669 
2670  return true;
2671 }
2672 
2673 
2674 bool ImageProcessor::ConvertMatrix(const CFloatMatrix *pInputMatrix, CDoubleMatrix *pOutputMatrix)
2675 {
2676  if (pInputMatrix->rows != pOutputMatrix->rows || pInputMatrix->columns != pOutputMatrix->columns)
2677  {
2678  printf("error: input image and output matrix do not match for ImageProcessor::ConvertMatrix\n");
2679  return false;
2680  }
2681 
2682  const int nElements = pOutputMatrix->rows * pOutputMatrix->columns;
2683  const float *input = pInputMatrix->data;
2684  double *output = pOutputMatrix->data;
2685 
2686  for (int i = 0; i < nElements; i++)
2687  output[i] = (double) input[i];
2688 
2689  return true;
2690 }
2691 
2692 bool ImageProcessor::ConvertMatrix(const CDoubleMatrix *pInputMatrix, CFloatMatrix *pOutputMatrix)
2693 {
2694  if (pInputMatrix->rows != pOutputMatrix->rows || pInputMatrix->columns != pOutputMatrix->columns)
2695  {
2696  printf("error: input image and output matrix do not match for ImageProcessor::ConvertMatrix\n");
2697  return false;
2698  }
2699 
2700  const int nElements = pOutputMatrix->rows * pOutputMatrix->columns;
2701  const double *input = pInputMatrix->data;
2702  float *output = pOutputMatrix->data;
2703 
2704  for (int i = 0; i < nElements; i++)
2705  output[i] = (float) input[i];
2706 
2707  return true;
2708 }
2709 
2710 
2711 bool ImageProcessor::CopyImage(const CByteImage *pInputImage, CByteImage *pOutputImage, const MyRegion *pROI, bool bUseSameSize)
2712 {
2713  if (!pROI || bUseSameSize)
2714  {
2715  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height || pInputImage->type != pOutputImage->type)
2716  {
2717  printf("error: input and output image do not match for ImageProcessor::CopyImage\n");
2718  return false;
2719  }
2720  }
2721  else if (pROI && !bUseSameSize)
2722  {
2723  const int w = pROI->max_x - pROI->min_x + 1;
2724  const int h = pROI->max_y - pROI->min_y + 1;
2725 
2726  if (w != pOutputImage->width || pOutputImage->height != h || pInputImage->type != pOutputImage->type)
2727  {
2728  printf("error: ROI and output image do not match for ImageProcessor::CopyImage\n");
2729  return false;
2730  }
2731  }
2732 
2733  if (pROI)
2734  {
2735  const int width = pInputImage->width;
2736  const int height = pInputImage->height;
2737 
2738  unsigned char *input = pInputImage->pixels;
2739  unsigned char *output = pOutputImage->pixels;
2740 
2741  int min_x = pROI->min_x;
2742  int max_x = pROI->max_x;
2743  int min_y = pROI->min_y;
2744  int max_y = pROI->max_y;
2745 
2746  if (!bUseSameSize)
2747  {
2748  if (min_x < 0 || min_x > width - 1 || max_x < 0 || max_x > width - 1 ||
2749  min_y < 0 || min_y > height - 1 || max_y < 0 || max_y > height - 1)
2750  {
2751  printf("error: ROI is out of image in ImageProcessor::CopyImage\n");
2752  return false;
2753  }
2754  }
2755 
2756  if (min_x < 0) min_x = 0;
2757  if (min_x > width - 1) min_x = width - 1;
2758  if (max_x < 0) max_x = 0;
2759  if (max_x > width - 1) max_x = width - 1;
2760  if (min_y < 0) min_y = 0;
2761  if (min_y > height - 1) min_y = height - 1;
2762  if (max_y < 0) max_y = 0;
2763  if (max_y > height - 1) max_y = height - 1;
2764 
2765  if (pInputImage->type == CByteImage::eGrayScale)
2766  {
2767  const int size = max_x - min_x + 1;
2768 
2769  if (bUseSameSize)
2770  {
2771  for (int y = min_y, offset = width * min_y + min_x; y <= max_y; y++, offset += width)
2772  memcpy(output + offset, input + offset, size);
2773  }
2774  else
2775  {
2776  for (int y = min_y, offset = width * min_y + min_x, output_offset = 0; y <= max_y; y++, offset += width, output_offset += size)
2777  memcpy(output + output_offset, input + offset, size);
2778  }
2779  }
2780  else if (pInputImage->type == CByteImage::eRGB24)
2781  {
2782  const int size = 3 * (max_x - min_x + 1);
2783  const int width3 = 3 * width;
2784 
2785  if (bUseSameSize)
2786  {
2787  for (int y = min_y, offset = 3 * (width * min_y + min_x); y <= max_y; y++, offset += width3)
2788  memcpy(output + offset, input + offset, size);
2789  }
2790  else
2791  {
2792  for (int y = min_y, offset = 3 * (width * min_y + min_x), output_offset = 0; y <= max_y; y++, offset += width3, output_offset += size)
2793  memcpy(output + output_offset, input + offset, size);
2794  }
2795  }
2796  else if (pInputImage->type == CByteImage::eRGB24Split)
2797  {
2798  const int size = max_x - min_x + 1;
2799 
2800  unsigned char *pHelperR = output;
2801  unsigned char *pHelperG = pHelperR + width * height;
2802  unsigned char *pHelperB = pHelperG + width * height;
2803 
2804  if (bUseSameSize)
2805  {
2806  for (int y = min_y, offset = width * min_y + min_x; y <= max_y; y++, offset += width)
2807  {
2808  memcpy(pHelperR + offset, input + offset, size);
2809  memcpy(pHelperG + offset, input + offset, size);
2810  memcpy(pHelperB + offset, input + offset, size);
2811  }
2812  }
2813  else
2814  {
2815  for (int y = min_y, offset = 3 * (width * min_y + min_x), output_offset = 0; y <= max_y; y++, offset += width, output_offset += size)
2816  {
2817  memcpy(pHelperR + output_offset, input + offset, size);
2818  memcpy(pHelperG + output_offset, input + offset, size);
2819  memcpy(pHelperB + output_offset, input + offset, size);
2820  }
2821  }
2822  }
2823  }
2824  else
2825  {
2826  memcpy(pOutputImage->pixels, pInputImage->pixels, pInputImage->width * pInputImage->height * pInputImage->bytesPerPixel);
2827  }
2828 
2829  return true;
2830 }
2831 
2832 bool ImageProcessor::CopyImage(const CShortImage *pInputImage, CShortImage *pOutputImage, const MyRegion *pROI, bool bUseSameSize)
2833 {
2834  if (!pROI || bUseSameSize)
2835  {
2836  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height)
2837  {
2838  printf("error: input and output image do not match for ImageProcessor::CopyImage\n");
2839  return false;
2840  }
2841  }
2842  else if (pROI && !bUseSameSize)
2843  {
2844  const int w = pROI->max_x - pROI->min_x + 1;
2845  const int h = pROI->max_y - pROI->min_y + 1;
2846 
2847  if (w != pOutputImage->width || pOutputImage->height != h)
2848  {
2849  printf("error: ROI and output image do not match for ImageProcessor::CopyImage\n");
2850  return false;
2851  }
2852  }
2853 
2854  if (pROI)
2855  {
2856  const int width = pInputImage->width;
2857  const int height = pInputImage->height;
2858 
2859  short *input = pInputImage->pixels;
2860  short *output = pOutputImage->pixels;
2861 
2862  int min_x = pROI->min_x;
2863  int max_x = pROI->max_x;
2864  int min_y = pROI->min_y;
2865  int max_y = pROI->max_y;
2866 
2867  if (!bUseSameSize)
2868  {
2869  if (min_x < 0 || min_x > width - 1 || max_x < 0 || max_x > width - 1 ||
2870  min_y < 0 || min_y > height - 1 || max_y < 0 || max_y > height - 1)
2871  {
2872  printf("error: ROI is out of image in ImageProcessor::CopyImage\n");
2873  return false;
2874  }
2875  }
2876 
2877  if (min_x < 0) min_x = 0;
2878  if (min_x > width - 1) min_x = width - 1;
2879  if (max_x < 0) max_x = 0;
2880  if (max_x > width - 1) max_x = width - 1;
2881  if (min_y < 0) min_y = 0;
2882  if (min_y > height - 1) min_y = height - 1;
2883  if (max_y < 0) max_y = 0;
2884  if (max_y > height - 1) max_y = height - 1;
2885 
2886  if (bUseSameSize)
2887  {
2888  const int diff = width - (max_x - min_x + 1);
2889 
2890  for (int y = min_y, offset = width * min_y + min_x; y <= max_y; y++, offset += diff)
2891  for (int x = min_x; x <= max_x; x++, offset++)
2892  output[offset] = input[offset];
2893  }
2894  else
2895  {
2896  const int diff = width - (max_x - min_x + 1);
2897 
2898  for (int y = min_y, offset = width * min_y + min_x, output_offset = 0; y <= max_y; y++, offset += diff)
2899  for (int x = min_x; x <= max_x; x++, offset++, output_offset++)
2900  output[output_offset] = input[offset];
2901  }
2902  }
2903  else
2904  {
2905  memcpy(pOutputImage->pixels, pInputImage->pixels, pInputImage->width * pInputImage->height * sizeof(short));
2906  }
2907 
2908  return true;
2909 }
2910 
2911 
2912 bool ImageProcessor::CopyMatrix(const CFloatMatrix *pInputMatrix, CFloatMatrix *pOutputMatrix)
2913 {
2914  if (pInputMatrix->rows != pOutputMatrix->rows || pInputMatrix->columns != pOutputMatrix->columns)
2915  {
2916  printf("error: input and output matrix do not match for ImageProcessor::CopyMatrix\n");
2917  return false;
2918  }
2919 
2920  memcpy(pOutputMatrix->data, pInputMatrix->data, pInputMatrix->rows * pInputMatrix->columns * sizeof(float));
2921 
2922  return true;
2923 }
2924 
2925 bool ImageProcessor::CopyMatrix(const CDoubleMatrix *pInputMatrix, CDoubleMatrix *pOutputMatrix)
2926 {
2927  if (pInputMatrix->rows != pOutputMatrix->rows || pInputMatrix->columns != pOutputMatrix->columns)
2928  {
2929  printf("error: input and output matrix do not match for ImageProcessor::CopyMatrix\n");
2930  return false;
2931  }
2932 
2933  memcpy(pOutputMatrix->data, pInputMatrix->data, pInputMatrix->rows * pInputMatrix->columns * sizeof(double));
2934 
2935  return true;
2936 }
2937 
2938 
2939 void ImageProcessor::Zero(CByteImage *pImage, const MyRegion *pROI)
2940 {
2941  if (pROI)
2942  {
2943  const int width = pImage->width;
2944  const int height = pImage->height;
2945 
2946  unsigned char *pixels = pImage->pixels;
2947 
2948  int min_x = pROI->min_x;
2949  int max_x = pROI->max_x;
2950  int min_y = pROI->min_y;
2951  int max_y = pROI->max_y;
2952 
2953  if (min_x < 0) min_x = 0;
2954  if (min_x > width - 1) min_x = width - 1;
2955  if (max_x < 0) max_x = 0;
2956  if (max_x > width - 1) max_x = width - 1;
2957  if (min_y < 0) min_y = 0;
2958  if (min_y > height - 1) min_y = height - 1;
2959  if (max_y < 0) max_y = 0;
2960  if (max_y > height - 1) max_y = height - 1;
2961 
2962  if (pImage->type == CByteImage::eGrayScale)
2963  {
2964  const int size = max_x - min_x + 1;
2965 
2966  for (int y = min_y, offset = width * min_y + min_x; y <= max_y; y++, offset += width)
2967  memset(pixels + offset, 0, size);
2968  }
2969  else if (pImage->type == CByteImage::eRGB24)
2970  {
2971  const int size = 3 * (max_x - min_x + 1);
2972  const int width3 = 3 * width;
2973 
2974  for (int y = min_y, offset = 3 * (width * min_y + min_x); y <= max_y; y++, offset += width3)
2975  memset(pixels, 0, size);
2976  }
2977  else if (pImage->type == CByteImage::eRGB24Split)
2978  {
2979  const int size = max_x - min_x + 1;
2980 
2981  unsigned char *pHelperR = pixels;
2982  unsigned char *pHelperG = pHelperR + width * height;
2983  unsigned char *pHelperB = pHelperG + width * height;
2984 
2985  for (int y = min_y, offset = width * min_y + min_x; y <= max_y; y++, offset += width)
2986  {
2987  memset(pHelperR + offset, 0, size);
2988  memset(pHelperG + offset, 0, size);
2989  memset(pHelperB + offset, 0, size);
2990  }
2991  }
2992  }
2993  else
2994  {
2995  memset(pImage->pixels, 0, pImage->width * pImage->height * pImage->bytesPerPixel);
2996  }
2997 }
2998 
3000 {
3001  memset(pImage->pixels, 0, pImage->width * pImage->height * sizeof(*pImage->pixels));
3002 }
3003 
3005 {
3006  memset(pImage->pixels, 0, pImage->width * pImage->height * sizeof(*pImage->pixels));
3007 }
3008 
3010 {
3011  memset(pMatrix->data, 0, pMatrix->columns * pMatrix->rows * sizeof(*pMatrix->data));
3012 }
3013 
3015 {
3016  memset(pMatrix->data, 0, pMatrix->columns * pMatrix->rows * sizeof(*pMatrix->data));
3017 }
3018 
3019 
3021 {
3022  const int width = pImage->width;
3023  const int height = pImage->height;
3024  unsigned char *pixels = pImage->pixels;
3025 
3026  if (pImage->type == CByteImage::eGrayScale)
3027  {
3028  // zero top and bottom row
3029  memset(pixels, 0, width);
3030  memset(pixels + width * (height - 1), 0, width);
3031 
3032  // zero left and right column
3033  for (int i = 0, offset = 0; i < height; i++, offset += width)
3034  {
3035  pixels[offset] = 0;
3036  pixels[offset + width - 1] = 0;
3037  }
3038  }
3039  else
3040  {
3041  const int width3 = 3 * width;
3042 
3043  // zero top and bottom row
3044  memset(pixels, 0, width3);
3045  memset(pixels + width3 * (height - 1), 0, width3);
3046 
3047  // zero left and right column
3048  for (int i = 0, offset = 0; i < height; i++, offset += width3)
3049  {
3050  pixels[offset] = pixels[offset + 1] = pixels[offset + 2] = 0;
3051  pixels[offset + width3 - 3] = pixels[offset + width3 - 2] = pixels[offset + width3 - 1] = 0;
3052  }
3053  }
3054 }
3055 
3057 {
3058  const int width = pImage->width;
3059  const int height = pImage->height;
3060  short *pixels = pImage->pixels;
3061 
3062  // zero top and bottom row
3063  memset(pixels, 0, width * sizeof(short));
3064  memset(pixels + width * (height - 1), 0, width * sizeof(short));
3065 
3066  // zero left and right column
3067  for (int i = 0, offset = 0; i < height; i++, offset += width)
3068  {
3069  pixels[offset] = 0;
3070  pixels[offset + width - 1] = 0;
3071  }
3072 }
3073 
3075 {
3076  const int width = pImage->width;
3077  const int height = pImage->height;
3078  int *pixels = pImage->pixels;
3079 
3080  // zero top and bottom row
3081  memset(pixels, 0, width * sizeof(int));
3082  memset(pixels + width * (height - 1), 0, width * sizeof(int));
3083 
3084  // zero left and right column
3085  for (int i = 0, offset = 0; i < height; i++, offset += width)
3086  {
3087  pixels[offset] = 0;
3088  pixels[offset + width - 1] = 0;
3089  }
3090 }
3091 
3092 bool ImageProcessor::CopyFrame(const CByteImage *pInputImage, CByteImage *pOutputImage)
3093 {
3094  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height || pInputImage->type != pOutputImage->type || pInputImage->type == CByteImage::eRGB24Split)
3095  {
3096  printf("error: input and output images do not match for ImageProcessor::CopyFrame\n");
3097  return false;
3098  }
3099 
3100  // copy is not necessary in this case
3101  if (pInputImage->pixels == pOutputImage->pixels)
3102  return true;
3103 
3104  const unsigned char *input = pInputImage->pixels;
3105  unsigned char *output = pOutputImage->pixels;
3106 
3107  const int width = pInputImage->width;
3108  const int height = pInputImage->height;
3109 
3110  if (pInputImage->type == CByteImage::eGrayScale)
3111  {
3112  memcpy(output, input, width);
3113  memcpy(output + width * (height - 1), input + width * (height - 1), width);
3114 
3115  unsigned int offset = width;
3116  const unsigned int offset2 = width - 1;
3117  for (int y = 1; y < height-1; y++, offset += width)
3118  {
3119  output[offset] = input[offset];
3120  output[offset + offset2] = input[offset + offset2];
3121  }
3122  }
3123  else if (pInputImage->type == CByteImage::eRGB24)
3124  {
3125  memcpy(output, input, 3*width);
3126  memcpy(output + 3 * width * (height - 1), input + 3 * width * (height - 1), 3 * width);
3127 
3128  unsigned int offset = 3 * width;
3129  const unsigned int offset2 = 3 * (width - 1);
3130  for (int y = 1; y < height - 1; y++, offset += 3 * width)
3131  {
3132  output[offset] = input[offset];
3133  output[offset + 1] = input[offset + 1];
3134  output[offset + 2] = input[offset + 2];
3135  output[offset + offset2] = input[offset + offset2];
3136  output[offset + offset2 + 1] = input[offset + offset2 + 1];
3137  output[offset + offset2 + 2] = input[offset + offset2 + 2];
3138  }
3139  }
3140 
3141  return true;
3142 }
3143 
3144 bool ImageProcessor::AdaptFrame(const CByteImage *pInputImage, CByteImage *pOutputImage)
3145 {
3146  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height || pInputImage->type != pOutputImage->type || pInputImage->type == CByteImage::eRGB24Split)
3147  {
3148  printf("error: input and output images do not match for ImageProcessor::CopyFrame\n");
3149  return false;
3150  }
3151 
3152  const unsigned char *input = pInputImage->pixels;
3153  unsigned char *output = pOutputImage->pixels;
3154 
3155  const int width = pInputImage->width;
3156  const int height = pInputImage->height;
3157 
3158  if (pInputImage->type == CByteImage::eGrayScale)
3159  {
3160  // adapt the top and bottom row
3161  memcpy(&output[1], &input[width + 1], width-2);
3162  memcpy(&output[width*(height-1) + 1], &input[width*(height-2) + 1], width-2);
3163 
3164  // adapt the corners
3165  output[0] = input[width + 1];
3166  output[width - 1] = input[2*width - 2];
3167  output[width*(height-1)] = input[width*(height-2) + 1];
3168  output[width*height - 1] = input[width*height - 2];
3169 
3170  // adapt the left and right borders
3171  unsigned int offset = width;
3172  const unsigned int offset2 = width-1;
3173  for (int y = 1; y < height-1; y++, offset += width)
3174  {
3175  output[offset] = input[offset+1];
3176  output[offset + offset2] = input[offset + offset2 - 1];
3177  }
3178  }
3179  else if (pInputImage->type == CByteImage::eRGB24)
3180  {
3181  // adapt the top and bottom row
3182  memcpy(&output[3], &input[3*(width + 1)], 3*(width-2));
3183  memcpy(&output[3*(width*(height-1) + 1)], &input[3*(width*(height-2) + 1)], 3*(width-2));
3184 
3185  // adapt the corners
3186  output[0] = input[3*(width + 1)];
3187  output[1] = input[3*(width + 1) + 1];
3188  output[2] = input[3*(width + 1) + 2];
3189  output[3*(width - 1)] = input[3*(2*width - 2)];
3190  output[3*(width - 1) + 1] = input[3*(2*width - 2) + 1];
3191  output[3*(width - 1) + 2] = input[3*(2*width - 2) + 2];
3192  output[3*(width*(height-1))] = input[3*(width*(height-2) + 1)];
3193  output[3*(width*(height-1)) + 1] = input[3*(width*(height-2) + 1) + 1];
3194  output[3*(width*(height-1)) + 2] = input[3*(width*(height-2) + 1) + 2];
3195  output[3*(width*height - 1)] = input[3*(width*(height-1) - 2)];
3196  output[3*(width*height - 1) + 1] = input[3*(width*(height-1) - 2) + 1];
3197  output[3*(width*height - 1) + 2] = input[3*(width*(height-1) - 2) + 2];
3198 
3199  // adapt the left and right borders
3200  unsigned int offset = 3*width;
3201  const unsigned int offset2 = 3*(width-1);
3202  for (int y = 1; y < height-1; y++, offset += 3*width)
3203  {
3204  output[offset] = input[offset + 3];
3205  output[offset + 1] = input[offset + 3 + 1];
3206  output[offset + 2] = input[offset + 3 + 2];
3207  output[offset + offset2] = input[offset + offset2 - 3];
3208  output[offset + offset2 + 1] = input[offset + offset2 - 3 + 1];
3209  output[offset + offset2 + 2] = input[offset + offset2 - 3 + 2];
3210  }
3211  }
3212 
3213  return true;
3214 }
3215 
3216 
3217 bool ImageProcessor::ApplyHomography(const CByteImage *pInputImage, CByteImage *pOutputImage, float a1, float a2, float a3, float a4, float a5, float a6, float a7, float a8, bool bInterpolation)
3218 {
3219  const Mat3d A = { a1, a2, a3, a4, a5, a6, a7, a8, 1.0f };
3220  return ApplyHomography(pInputImage, pOutputImage, A, bInterpolation);
3221 }
3222 
3223 bool ImageProcessor::ApplyHomography(const CByteImage *pInputImage, CByteImage *pOutputImage, const Mat3d &A, bool bInterpolation)
3224 {
3225  if (pInputImage->type != pOutputImage->type)
3226  {
3227  printf("error: input and output image do not match in ImageProcessor::ApplyHomography\n");
3228  return false;
3229  }
3230 
3231  const float a1 = A.r1;
3232  const float a2 = A.r2;
3233  const float a3 = A.r3;
3234  const float a4 = A.r4;
3235  const float a5 = A.r5;
3236  const float a6 = A.r6;
3237  const float a7 = A.r7;
3238  const float a8 = A.r8;
3239  const float a9 = A.r9;
3240 
3241  CByteImage *pSaveOutputImage = 0;
3242  if (pInputImage->pixels == pOutputImage->pixels)
3243  {
3244  pSaveOutputImage = pOutputImage;
3245  pOutputImage = new CByteImage(pInputImage);
3246  }
3247 
3248  const int width = pInputImage->width;
3249  const int height = pInputImage->height;
3250  const int output_width = pOutputImage->width;
3251  const int output_height = pOutputImage->height;
3252  const unsigned char *input = pInputImage->pixels;
3253  unsigned char *output = pOutputImage->pixels;
3254  const CByteImage::ImageType type = pInputImage->type;
3255 
3256  if (bInterpolation)
3257  {
3258  if (type == CByteImage::eGrayScale)
3259  {
3260  for (int v = 0, offset = 0; v < output_height; v++)
3261  {
3262  for (int u = 0; u < output_width; u++, offset++)
3263  {
3264  const float u_ = (a1 * u + a2 * v + a3) / (a7 * u + a8 * v + a9);
3265  const float v_ = (a4 * u + a5 * v + a6) / (a7 * u + a8 * v + a9);
3266 
3267  const int u1 = int(floor(u_));
3268  const int v1 = int(floor(v_));
3269  const int u2 = u1 + 1;
3270  const int v2 = v1 + 1;
3271 
3272  unsigned char f00 = 0, f10 = 0, f01 = 0, f11 = 0;
3273 
3274  const bool v1_ok = v1 >= 0 && v1 < height;
3275  const bool v2_ok = v2 >= 0 && v2 < height;
3276 
3277  if (u1 >= 0 && u1 < width)
3278  {
3279  if (v1_ok)
3280  f00 = input[v1 * width + u1];
3281 
3282  if (v2_ok)
3283  f01 = input[v2 * width + u1];
3284  }
3285 
3286  if (u2 >= 0 && u2 < width)
3287  {
3288  if (v1_ok)
3289  f10 = input[v1 * width + u2];
3290 
3291  if (v2_ok)
3292  f11 = input[v2 * width + u2];
3293  }
3294 
3295  const float x = u_ - u1;
3296  const float y = v_ - v1;
3297 
3298  output[offset] = (unsigned char) (f00 * (1 - x) * (1 - y) + f10 * x * (1 - y) + f01 * (1 - x) * y + f11 * x * y + 0.5f);
3299  }
3300  }
3301  }
3302  else if (type == CByteImage::eRGB24)
3303  {
3304  for (int v = 0, offset = 0; v < height; v++)
3305  {
3306  for (int u = 0; u < width; u++, offset += 3)
3307  {
3308  const float u_ = (a1 * u + a2 * v + a3) / (a7 * u + a8 * v + a9);
3309  const float v_ = (a4 * u + a5 * v + a6) / (a7 * u + a8 * v + a9);
3310 
3311  const int u1 = int(floor(u_));
3312  const int v1 = int(floor(v_));
3313  const int u2 = u1 + 1;
3314  const int v2 = v1 + 1;
3315 
3316  unsigned char f00_r = 0, f00_g = 0, f00_b = 0;
3317  unsigned char f10_r = 0, f10_g = 0, f10_b = 0;
3318  unsigned char f01_r = 0, f01_g = 0, f01_b = 0;
3319  unsigned char f11_r = 0, f11_g = 0, f11_b = 0;
3320 
3321  const bool v1_ok = v1 >= 0 && v1 < height;
3322  const bool v2_ok = v2 >= 0 && v2 < height;
3323 
3324  if (u1 >= 0 && u1 < width)
3325  {
3326  if (v1_ok)
3327  {
3328  const int x = 3 * (v1 * width + u1);
3329  f00_r = input[x];
3330  f00_g = input[x + 1];
3331  f00_b = input[x + 2];
3332  }
3333 
3334  if (v2_ok)
3335  {
3336  const int x = 3 * (v2 * width + u1);
3337  f01_r = input[x];
3338  f01_g = input[x + 1];
3339  f01_b = input[x + 2];
3340  }
3341  }
3342 
3343  if (u2 >= 0 && u2 < width)
3344  {
3345  if (v1_ok)
3346  {
3347  const int x = 3 * (v1 * width + u2);
3348  f10_r = input[x];
3349  f10_g = input[x + 1];
3350  f10_b = input[x + 2];
3351  }
3352 
3353  if (v2_ok)
3354  {
3355  const int x = 3 * (v2 * width + u2);
3356  f11_r = input[x];
3357  f11_g = input[x + 1];
3358  f11_b = input[x + 2];
3359  }
3360  }
3361 
3362  const float x = u_ - u1;
3363  const float y = v_ - v1;
3364 
3365  output[offset ] = (unsigned char) (f00_r * (1 - x) * (1 - y) + f10_r * x * (1 - y) + f01_r * (1 - x) * y + f11_r * x * y + 0.5f);
3366  output[offset + 1] = (unsigned char) (f00_g * (1 - x) * (1 - y) + f10_g * x * (1 - y) + f01_g * (1 - x) * y + f11_g * x * y + 0.5f);
3367  output[offset + 2] = (unsigned char) (f00_b * (1 - x) * (1 - y) + f10_b * x * (1 - y) + f01_b * (1 - x) * y + f11_b * x * y + 0.5f);
3368  }
3369  }
3370  }
3371  }
3372  else
3373  {
3374  if (type == CByteImage::eGrayScale)
3375  {
3376  for (int v = 0, offset = 0; v < output_height; v++)
3377  {
3378  for (int u = 0; u < output_width; u++, offset++)
3379  {
3380  const int u_ = (int) ((a1 * u + a2 * v + a3) / (a7 * u + a8 * v + 1.0f) + 0.5f);
3381  const int v_ = (int) ((a4 * u + a5 * v + a6) / (a7 * u + a8 * v + 1.0f) + 0.5f);
3382 
3383  if (u_ < 0 || u_ >= width || v_ < 0 || v_ >= height)
3384  output[offset] = 0;
3385  else
3386  output[offset] = input[v_ * width + u_];
3387  }
3388  }
3389  }
3390  else if (type == CByteImage::eRGB24)
3391  {
3392  for (int v = 0, offset = 0; v < output_height; v++)
3393  {
3394  for (int u = 0; u < output_width; u++, offset += 3)
3395  {
3396  const int u_ = (int) ((a1 * u + a2 * v + a3) / (a7 * u + a8 * v + 1.0f) + 0.5f);
3397  const int v_ = (int) ((a4 * u + a5 * v + a6) / (a7 * u + a8 * v + 1.0f) + 0.5f);
3398 
3399  if (u_ < 0 || u_ >= width || v_ < 0 || v_ >= height)
3400  output[offset] = output[offset + 1] = output[offset + 2] = 0;
3401  else
3402  {
3403  const int offset2 = 3 * (v_ * width + u_);
3404  output[offset] = input[offset2];
3405  output[offset + 1] = input[offset2 + 1];
3406  output[offset + 2] = input[offset2 + 2];
3407  }
3408  }
3409  }
3410  }
3411  }
3412 
3413  if (pSaveOutputImage)
3414  {
3415  CopyImage(pOutputImage, pSaveOutputImage);
3416  delete pOutputImage;
3417  }
3418 
3419  return true;
3420 }
3421 
3422 bool ImageProcessor::Resize(const CByteImage *pInputImage, CByteImage *pOutputImage, const MyRegion *pROI, bool bInterpolation)
3423 {
3424  if (pInputImage->type != pOutputImage->type || pInputImage->type == CByteImage::eRGB24Split)
3425  {
3426  printf("error: input and output image do not match in ImageProcessor::Resize\n");
3427  return false;
3428  }
3429 
3430  if (pInputImage->width == pOutputImage->width && pInputImage->height == pOutputImage->height)
3431  {
3432  CopyImage(pInputImage, pOutputImage);
3433  return false;
3434  }
3435 
3436  const CByteImage::ImageType type = pInputImage->type;
3437 
3438  const int output_width = pOutputImage->width;
3439  const int output_height = pOutputImage->height;
3440  unsigned char *output = pOutputImage->pixels;
3441 
3442  const int input_width = pInputImage->width;
3443  const int input_height = pInputImage->height;
3444  const unsigned char *input = pInputImage->pixels;
3445 
3446  const int start_x = pROI ? pROI->min_x : 0;
3447  const int start_y = pROI ? pROI->min_y : 0;
3448 
3449  const int width = pROI ? pROI->max_x - pROI->min_x + 1 : input_width;
3450  const int height = pROI ? pROI->max_y - pROI->min_y + 1 : input_height;
3451 
3452  if (pROI)
3453  {
3454  if (pROI->min_x < 0 || pROI->min_y < 0 || pROI->max_x >= input_width || pROI->max_y >= input_height)
3455  {
3456  printf("error: provided ROI in ImageProcessor::Resize exceeds input image boundaries\n");
3457  return false;
3458  }
3459  }
3460 
3461  if (width > output_width && width % output_width == 0 &&
3462  height > output_height && height % output_height == 0)
3463  {
3464  if (type == CByteImage::eGrayScale)
3465  {
3466  const int a1 = width / output_width;
3467  const int a4 = height / output_height;
3468  const int delta = a4 * input_width - width;
3469 
3470  for (int v = 0, offset = 0, offset_ = start_y * input_width + start_x; v < output_height; v++, offset_ += delta)
3471  {
3472  for (int u = 0; u < output_width; u++, offset++, offset_ += a1)
3473  output[offset] = input[offset_];
3474  }
3475  }
3476  else if (type == CByteImage::eRGB24)
3477  {
3478  const int a1 = 3 * (width / output_width);
3479  const int a4 = height / output_height;
3480  const int delta = 3 * (a4 * input_width - width);
3481 
3482  for (int v = 0, offset = 0, offset_ = 3 * (start_y * input_width + start_x); v < output_height; v++, offset_ += delta)
3483  {
3484  for (int u = 0; u < output_width; u++, offset += 3, offset_ += a1)
3485  {
3486  output[offset] = input[offset_];
3487  output[offset + 1] = input[offset_ + 1];
3488  output[offset + 2] = input[offset_ + 2];
3489  }
3490  }
3491  }
3492 
3493  return true;
3494  }
3495 
3496  const float a1 = float(width) / output_width;
3497  const float a4 = float(height) / output_height;
3498 
3499  int *pXOffsets = 0, *pYOffsets = 0;
3500  int *pXCoordinates = 0, *pYCoordinates = 0;
3501 
3502  const int min_width = MY_MIN(width, output_width);
3503  const int min_height = MY_MIN(height, output_height);
3504  int i;
3505 
3506  if (type == CByteImage::eGrayScale)
3507  {
3508  pXOffsets = new int[output_width];
3509  pYOffsets = new int[output_height];
3510 
3511  if (bInterpolation)
3512  {
3513  pXCoordinates = new int[output_width];
3514  pYCoordinates = new int[output_height];
3515 
3516  for (i = 0; i < output_width; i++)
3517  {
3518  register int x = (i * width) / output_width;
3519 
3520  if (start_x + x < input_width - 1)
3521  {
3522  pXOffsets[i] = start_x + x;
3523  pXCoordinates[i] = int((i * a1 - x) * 1024);
3524  }
3525  else
3526  {
3527  pXOffsets[i] = input_width - 2;
3528  pXCoordinates[i] = 1024;
3529  }
3530  }
3531 
3532  for (i = 0; i < output_height; i++)
3533  {
3534  register int y = (i * height) / output_height;
3535 
3536  if (start_y + y < input_height - 1)
3537  {
3538  pYOffsets[i] = input_width * (start_y + y);
3539  pYCoordinates[i] = int((i * a4 - y) * 1024);
3540  }
3541  else
3542  {
3543  pYOffsets[i] = input_width * (input_height - 2);
3544  pYCoordinates[i] = 1024;
3545  }
3546  }
3547  }
3548  else
3549  {
3550  for (i = 0; i < output_width; i++)
3551  pXOffsets[i] = start_x + (((i * width) << 1) + min_width - 1) / (output_width << 1);
3552 
3553  for (i = 0; i < output_height; i++)
3554  pYOffsets[i] = input_width * (start_y + (((i * height) << 1) + min_height - 1) / (output_height << 1));
3555  }
3556  }
3557  else
3558  {
3559  pXOffsets = new int[3 * output_width];
3560  pYOffsets = new int[output_height];
3561 
3562  int offset = 0;
3563 
3564  if (bInterpolation)
3565  {
3566  pXCoordinates = new int[3 * output_width];
3567  pYCoordinates = new int[output_height];
3568 
3569  for (i = 0; i < output_width; i++, offset += 3)
3570  {
3571  register int x = (i * width) / output_width;
3572 
3573  if (start_x + x < input_width - 1)
3574  {
3575  pXOffsets[offset] = 3 * (start_x + x);
3576  pXCoordinates[offset] = int((i * a1 - x) * 1024);
3577  }
3578  else
3579  {
3580  pXOffsets[offset] = 3 * (input_width - 2);
3581  pXCoordinates[offset] = 1024;
3582  }
3583  }
3584 
3585  for (i = 0; i < output_height; i++)
3586  {
3587  register int y = (i * height) / output_height;
3588 
3589  if (start_y + y < input_height - 1)
3590  {
3591  pYOffsets[i] = 3 * input_width * (start_y + y);
3592  pYCoordinates[i] = int((i * a4 - y) * 1024);
3593  }
3594  else
3595  {
3596  pYOffsets[i] = 3 * input_width * (input_height - 2);
3597  pYCoordinates[i] = 1024;
3598  }
3599  }
3600  }
3601  else
3602  {
3603  for (i = 0; i < output_width; i++, offset += 3)
3604  pXOffsets[offset] = 3 * (start_x + (((i * width) << 1) + min_width - 1) / (output_width << 1));
3605 
3606  for (i = 0; i < output_height; i++)
3607  pYOffsets[i] = 3 * input_width * (start_y + (((i * height) << 1) + min_height - 1) / (output_height << 1));
3608  }
3609  }
3610 
3611  if (bInterpolation)
3612  {
3613  if (type == CByteImage::eGrayScale)
3614  {
3615  const int last_u = MY_MAX(0, output_width - output_width % 4);
3616 
3617  for (int v = 0; v < output_height; v++)
3618  {
3619  const unsigned char *input_helper = input + pYOffsets[v];
3620  const int y = pYCoordinates[v];
3621  int u;
3622 
3623  for (u = 0; u < last_u; u += 4)
3624  {
3625  int offset;
3626  int x;
3627 
3628  offset = pXOffsets[u];
3629  x = pXCoordinates[u];
3630  output[u] = (unsigned char) (
3631  ((input_helper[offset] * (1024 - x) * (1024 - y)) +
3632  (input_helper[offset + 1] * x * (1024 - y)) +
3633  (input_helper[offset + input_width] * (1024 - x) * y) +
3634  (input_helper[offset + input_width + 1] * x * y)) >> 20
3635  );
3636 
3637  offset = pXOffsets[u + 1];
3638  x = pXCoordinates[u + 1];
3639  output[u + 1] = (unsigned char) (
3640  ((input_helper[offset] * (1024 - x) * (1024 - y)) +
3641  (input_helper[offset + 1] * x * (1024 - y)) +
3642  (input_helper[offset + input_width] * (1024 - x) * y) +
3643  (input_helper[offset + input_width + 1] * x * y)) >> 20
3644  );
3645 
3646  offset = pXOffsets[u + 2];
3647  x = pXCoordinates[u + 2];
3648  output[u + 2] = (unsigned char) (
3649  ((input_helper[offset] * (1024 - x) * (1024 - y)) +
3650  (input_helper[offset + 1] * x * (1024 - y)) +
3651  (input_helper[offset + input_width] * (1024 - x) * y) +
3652  (input_helper[offset + input_width + 1] * x * y)) >> 20
3653  );
3654 
3655  offset = pXOffsets[u + 3];
3656  x = pXCoordinates[u + 3];
3657  output[u + 3] = (unsigned char) (
3658  ((input_helper[offset] * (1024 - x) * (1024 - y)) +
3659  (input_helper[offset + 1] * x * (1024 - y)) +
3660  (input_helper[offset + input_width] * (1024 - x) * y) +
3661  (input_helper[offset + input_width + 1] * x * y)) >> 20
3662  );
3663  }
3664 
3665  for (u = last_u; u < output_width; u++)
3666  {
3667  const int offset = pXOffsets[u];
3668  const int x = pXCoordinates[u];
3669  output[u] = (unsigned char) (
3670  ((input_helper[offset] * (1024 - x) * (1024 - y)) +
3671  (input_helper[offset + 1] * x * (1024 - y)) +
3672  (input_helper[offset + input_width] * (1024 - x) * y) +
3673  (input_helper[offset + input_width + 1] * x * y)) >> 20
3674  );
3675  }
3676 
3677  output += output_width;
3678  }
3679  }
3680  else if (type == CByteImage::eRGB24)
3681  {
3682  const int output_width3 = 3 * output_width;
3683  const int input_width3 = 3 * input_width;
3684 
3685  for (int v = 0; v < output_height; v++)
3686  {
3687  const unsigned char *input_helper = input + pYOffsets[v];
3688  const int y = pYCoordinates[v];
3689 
3690  for (int u = 0; u < output_width3; u += 3)
3691  {
3692  const int x = pXCoordinates[u];
3693  const int f00 = (1024 - x) * (1024 - y);
3694  const int f10 = x * (1024 - y);
3695  const int f01 = (1024 - x) * y;
3696  const int f11 = x * y;
3697 
3698  register int offset;
3699 
3700  offset = pXOffsets[u];
3701  output[u] = (unsigned char) (
3702  ((input_helper[offset] * f00) +
3703  (input_helper[offset + 3] * f10) +
3704  (input_helper[offset + input_width3] * f01) +
3705  (input_helper[offset + input_width3 + 3] * f11)) >> 20
3706  );
3707 
3708  offset++;
3709  output[u + 1] = (unsigned char) (
3710  ((input_helper[offset] * f00) +
3711  (input_helper[offset + 3] * f10) +
3712  (input_helper[offset + input_width3] * f01) +
3713  (input_helper[offset + input_width3 + 3] * f11)) >> 20
3714  );
3715 
3716  offset++;
3717  output[u + 2] = (unsigned char) (
3718  ((input_helper[offset] * f00) +
3719  (input_helper[offset + 3] * f10) +
3720  (input_helper[offset + input_width3] * f01) +
3721  (input_helper[offset + input_width3 + 3] * f11)) >> 20
3722  );
3723  }
3724 
3725  output += output_width3;
3726  }
3727  }
3728  }
3729  else
3730  {
3731  if (type == CByteImage::eGrayScale)
3732  {
3733  const int last_u = MY_MAX(0, output_width - output_width % 4);
3734 
3735  for (int v = 0; v < output_height; v++)
3736  {
3737  const unsigned char *input_helper = input + pYOffsets[v];
3738  int u;
3739 
3740  for (u = 0; u < last_u; u += 4)
3741  {
3742  output[u] = input_helper[pXOffsets[u]];
3743  output[u + 1] = input_helper[pXOffsets[u + 1]];
3744  output[u + 2] = input_helper[pXOffsets[u + 2]];
3745  output[u + 3] = input_helper[pXOffsets[u + 3]];
3746  }
3747 
3748  for (u = last_u; u < output_width; u++)
3749  output[u] = input_helper[pXOffsets[u]];
3750 
3751  output += output_width;
3752  }
3753  }
3754  else if (type == CByteImage::eRGB24)
3755  {
3756  const int output_width3 = 3 * output_width;
3757  const int last_u = 3 * MY_MAX(0, output_width - output_width % 4);
3758 
3759  for (int v = 0; v < output_height; v++)
3760  {
3761  const unsigned char *input_helper = input + pYOffsets[v];
3762  int u;
3763 
3764  for (u = 0; u < last_u; u += 12)
3765  {
3766  register int input_offset;
3767 
3768  input_offset = pXOffsets[u];
3769  output[u] = input_helper[input_offset];
3770  output[u + 1] = input_helper[input_offset + 1];
3771  output[u + 2] = input_helper[input_offset + 2];
3772 
3773  input_offset = pXOffsets[u + 3];
3774  output[u + 3] = input_helper[input_offset];
3775  output[u + 4] = input_helper[input_offset + 1];
3776  output[u + 5] = input_helper[input_offset + 2];
3777 
3778  input_offset = pXOffsets[u + 6];
3779  output[u + 6] = input_helper[input_offset];
3780  output[u + 7] = input_helper[input_offset + 1];
3781  output[u + 8] = input_helper[input_offset + 2];
3782 
3783  input_offset = pXOffsets[u + 9];
3784  output[u + 9] = input_helper[input_offset];
3785  output[u + 10] = input_helper[input_offset + 1];
3786  output[u + 11] = input_helper[input_offset + 2];
3787  }
3788 
3789  for (u = last_u; u < output_width3; u += 3)
3790  {
3791  register int input_offset = pXOffsets[u];
3792  output[u] = input_helper[input_offset];
3793  output[u + 1] = input_helper[input_offset + 1];
3794  output[u + 2] = input_helper[input_offset + 2];
3795  }
3796 
3797  output += output_width3;
3798  }
3799  }
3800  }
3801 
3802  delete [] pXOffsets;
3803  delete [] pYOffsets;
3804 
3805  if (pXCoordinates)
3806  {
3807  delete [] pXCoordinates;
3808  delete [] pYCoordinates;
3809  }
3810 
3811  return true;
3812 }
3813 
3814 
3815 bool ImageProcessor::Rotate(const CByteImage *pInputImage, CByteImage *pOutputImage, float mx, float my, float theta, bool bInterpolation)
3816 {
3817  const float cos_theta = cosf(theta);
3818  const float sin_theta = sinf(theta);
3819 
3820  return ApplyHomography(pInputImage, pOutputImage,
3821  cos_theta, -sin_theta, mx * (1 - cos_theta) + my * sin_theta,
3822  sin_theta, cos_theta, my * (1 - cos_theta) - mx * sin_theta,
3823  0, 0,
3824  bInterpolation);
3825 }
3826 
3827 bool ImageProcessor::Amplify(const CByteImage *pInputImage, CByteImage *pOutputImage, float fFactor)
3828 {
3829  OPTIMIZED_FUNCTION_HEADER_3(Amplify, pInputImage, pOutputImage, fFactor)
3830 
3831  if (!pInputImage->IsCompatible(pOutputImage))
3832  {
3833  printf("error: input and output image do not match for ImageProcessor::Amplify\n");
3834  return false;
3835  }
3836 
3837  const int nBytes = pInputImage->width * pInputImage->height * pInputImage->bytesPerPixel;
3838  const unsigned char *input = pInputImage->pixels;
3839  unsigned char *output = pOutputImage->pixels;
3840 
3841  for (int i = 0; i < nBytes; i++)
3842  {
3843  const int v = int(fFactor * input[i] + 0.5f);
3844  output[i] = v < 0 ? 0 : (v > 255 ? 255 : (unsigned char) v);
3845  }
3846 
3848 
3849  return true;
3850 }
3851 
3852 
3853 bool ImageProcessor::CalculateSaturationImage(const CByteImage *pInputImage, CByteImage *pOutputImage)
3854 {
3855  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
3856  (pInputImage->type != CByteImage::eRGB24 && pInputImage->type != CByteImage::eRGB24Split) || pOutputImage->type != CByteImage::eGrayScale)
3857  {
3858  printf("error: input and output image do not match for ImageProcessor::CalculateSaturationImage\n");
3859  return false;
3860  }
3861 
3862  const int nPixels = pInputImage->width * pInputImage->height;
3863  unsigned char *output = pOutputImage->pixels;
3864 
3865  if (pInputImage->type == CByteImage::eRGB24)
3866  {
3867  const unsigned char *input = pInputImage->pixels;
3868 
3869  for (int i = 0, offset = 0; i < nPixels; i++)
3870  {
3871  const int r = input[offset];
3872  const int g = input[offset + 1];
3873  const int b = input[offset + 2];
3874  const int min = MY_MIN(r, MY_MIN(g, b));
3875  const int max = MY_MAX(r, MY_MAX(g, b));
3876  output[i] = (255 * (max - min) * division_table[max]) >> 20;
3877  offset += 3;
3878  }
3879  }
3880  else
3881  {
3882  const unsigned char *input_r = pInputImage->pixels;
3883  const unsigned char *input_g = input_r + nPixels;
3884  const unsigned char *input_b = input_g + nPixels;
3885 
3886  for (int i = 0; i < nPixels; i++)
3887  {
3888  const int r = input_r[i];
3889  const int g = input_g[i];
3890  const int b = input_b[i];
3891  const int min = MY_MIN(r, MY_MIN(g, b));
3892  const int max = MY_MAX(r, MY_MAX(g, b));
3893  output[i] = (255 * (max - min) * division_table[max]) >> 20;
3894  }
3895  }
3896 
3897  return true;
3898 }
3899 
3900 
3901 bool ImageProcessor::ThresholdBinarize(const CByteImage *pInputImage, CByteImage *pOutputImage, unsigned char nThreshold)
3902 {
3903  OPTIMIZED_FUNCTION_HEADER_3(ThresholdBinarize, pInputImage, pOutputImage, nThreshold)
3904 
3905  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
3906  pInputImage->type != pOutputImage->type || pInputImage->type != CByteImage::eGrayScale)
3907  {
3908  printf("error: input and output image do not match for ImageProcessor::ThresholdBinarize\n");
3909  return false;
3910  }
3911 
3912  const int nPixels = pInputImage->width * pInputImage->height;
3913  const unsigned char *input = pInputImage->pixels;
3914  unsigned char *output = pOutputImage->pixels;
3915 
3916  for (int i = 0; i < nPixels; i++)
3917  output[i] = input[i] >= nThreshold ? 255 : 0;
3918 
3920 
3921  return true;
3922 }
3923 
3924 bool ImageProcessor::ThresholdBinarize(const CByteImage *pInputImage, CByteImage *pOutputImage, unsigned char nMinThreshold, unsigned char nMaxThreshold)
3925 {
3926  //OPTIMIZED_FUNCTION_HEADER_4(ThresholdBinarize, pInputImage, pOutputImage, nMinThreshold, nMaxThreshold)
3927 
3928  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
3929  pInputImage->type != pOutputImage->type || pInputImage->type != CByteImage::eGrayScale)
3930  {
3931  printf("error: input and output image do not match for ImageProcessor::ThresholdBinarize\n");
3932  return false;
3933  }
3934 
3935  const int nPixels = pInputImage->width * pInputImage->height;
3936  const unsigned char *input = pInputImage->pixels;
3937  unsigned char *output = pOutputImage->pixels;
3938 
3939  for (int i = 0; i < nPixels; i++)
3940  output[i] = input[i] >= nMinThreshold && input[i] <= nMaxThreshold ? 255 : 0;
3941 
3942  //OPTIMIZED_FUNCTION_FOOTER
3943 
3944  return true;
3945 }
3946 
3947 bool ImageProcessor::ThresholdBinarize(const CFloatMatrix *pInputMatrix, CFloatMatrix *pOutputMatrix, float fThreshold)
3948 {
3949  if (pInputMatrix->columns != pOutputMatrix->columns || pInputMatrix->rows != pOutputMatrix->rows)
3950  {
3951  printf("error: input and output matrix do not match for ImageProcessor::ThresholdBinarize\n");
3952  return false;
3953  }
3954 
3955  const int n = pInputMatrix->columns * pInputMatrix->rows;
3956  const float *input = pInputMatrix->data;
3957  float *output = pOutputMatrix->data;
3958 
3959  for (int i = 0; i < n; i++)
3960  output[i] = input[i] >= fThreshold ? 255.0f : 0;
3961 
3962  return true;
3963 }
3964 
3965 bool ImageProcessor::ThresholdBinarizeInverse(const CByteImage *pInputImage, CByteImage *pOutputImage, unsigned char nThreshold)
3966 {
3967  OPTIMIZED_FUNCTION_HEADER_3(ThresholdBinarizeInverse, pInputImage, pOutputImage, nThreshold)
3968 
3969  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
3970  pInputImage->type != pOutputImage->type || pInputImage->type != CByteImage::eGrayScale)
3971  {
3972  printf("error: input and output image do not match for ImageProcessor::ThresholdBinarizeInverse\n");
3973  return false;
3974  }
3975 
3976  unsigned char *input = pInputImage->pixels;
3977  unsigned char *output = pOutputImage->pixels;
3978  const int nPixels = pInputImage->width * pInputImage->height;
3979 
3980  for (int i = 0; i < nPixels; i++)
3981  output[i] = input[i] <= nThreshold ? 255 : 0;
3982 
3984 
3985  return true;
3986 }
3987 
3988 bool ImageProcessor::ThresholdFilter(const CByteImage *pInputImage, CByteImage *pOutputImage, unsigned char nThreshold)
3989 {
3990  OPTIMIZED_FUNCTION_HEADER_3(ThresholdFilter, pInputImage, pOutputImage, nThreshold)
3991 
3992  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
3993  pInputImage->type != pOutputImage->type || pInputImage->type != CByteImage::eGrayScale)
3994  {
3995  printf("error: input and output image do not match for ImageProcessor::ThresholdFilter\n");
3996  return false;
3997  }
3998 
3999  const int nPixels = pInputImage->width * pInputImage->height;
4000  const unsigned char *input = pInputImage->pixels;
4001  unsigned char *output = pOutputImage->pixels;
4002 
4003  for (int i = 0; i < nPixels; i++)
4004  output[i] = input[i] >= nThreshold ? input[i] : 0;
4005 
4007 
4008  return true;
4009 }
4010 
4011 bool ImageProcessor::ThresholdFilterInverse(const CByteImage *pInputImage, CByteImage *pOutputImage, unsigned char nThreshold)
4012 {
4013  OPTIMIZED_FUNCTION_HEADER_3(ThresholdFilterInverse, pInputImage, pOutputImage, nThreshold)
4014 
4015  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
4016  pInputImage->type != pOutputImage->type || pInputImage->type != CByteImage::eGrayScale)
4017  {
4018  printf("error: input and output image do not match for ImageProcessor::ThresholdFilterInverse\n");
4019  return false;
4020  }
4021 
4022  unsigned char *input = pInputImage->pixels;
4023  unsigned char *output = pOutputImage->pixels;
4024  const int nPixels = pInputImage->width * pInputImage->height;
4025 
4026  for (int i = 0; i < nPixels; i++)
4027  output[i] = input[i] <= nThreshold ? input[i] : 0;
4028 
4030 
4031  return true;
4032 }
4033 
4034 
4035 bool ImageProcessor::FilterRGB(const CByteImage *pInputImage, CByteImage *pOutputImage, CRGBColorModel *pColorModel, float fThreshold)
4036 {
4037  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
4038  pInputImage->type != CByteImage::eRGB24 || pOutputImage->type != CByteImage::eGrayScale)
4039  {
4040  printf("error: input and output image do not match for ImageProcessor::FilterRGB\n");
4041  return false;
4042  }
4043 
4044  const unsigned char *input = pInputImage->pixels;
4045  unsigned char *output = pOutputImage->pixels;
4046  const int nPixels = pInputImage->width * pInputImage->height;
4047  int offset = 0;
4048 
4049  for (int i = 0; i < nPixels; i++, offset += 3)
4050  {
4051  Vec3d rgb = { input[offset], input[offset + 1], input[offset + 2] };
4052  output[i] = pColorModel->CalculateColorProbability(rgb) > fThreshold ? 255 : 0;
4053  }
4054 
4055  return true;
4056 }
4057 
4058 bool ImageProcessor::FilterHSV(const CByteImage *pInputImage, CByteImage *pOutputImage, unsigned char hue, unsigned char tol_hue, unsigned char min_sat, unsigned char max_sat, unsigned char min_v, unsigned char max_v, const MyRegion *pROI)
4059 {
4060  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
4061  (pInputImage->type != CByteImage::eRGB24 && pInputImage->type != CByteImage::eRGB24Split) || pOutputImage->type != CByteImage::eGrayScale)
4062  {
4063  printf("error: input and output image do not match for ImageProcessor::FilterHSV\n");
4064  return false;
4065  }
4066 
4067  int min_hue = (int) hue - (int) tol_hue;
4068  int max_hue = (int) hue + (int) tol_hue;
4069 
4070  if (min_hue < 0)
4071  min_hue += 180;
4072 
4073  if (max_hue >= 180)
4074  max_hue -= 180;
4075 
4076  if(tol_hue > 89)
4077  {
4078  min_hue = 0;
4079  max_hue = 179;
4080  }
4081 
4082  return FilterHSV2(pInputImage, pOutputImage, (unsigned char) min_hue, (unsigned char) max_hue, min_sat, max_sat, min_v, max_v, pROI);
4083 }
4084 
4085 bool ImageProcessor::FilterHSV2(const CByteImage *pInputImage, CByteImage *pOutputImage, unsigned char min_hue, unsigned char max_hue, unsigned char min_sat, unsigned char max_sat, unsigned char min_v, unsigned char max_v, const MyRegion *pROI)
4086 {
4087  OPTIMIZED_FUNCTION_HEADER_8_ROI(FilterHSV2, pInputImage, pOutputImage, min_hue, max_hue, min_sat, max_sat, min_v, max_v, pROI)
4088 
4089  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
4090  (pInputImage->type != CByteImage::eRGB24 && pInputImage->type != CByteImage::eRGB24Split) || pOutputImage->type != CByteImage::eGrayScale)
4091  {
4092  printf("error: input and output image do not match for ImageProcessor::FilterHSV2\n");
4093  return false;
4094  }
4095 
4096  if (pInputImage->type == CByteImage::eRGB24)
4097  {
4098  const unsigned char *input_h = pInputImage->pixels;
4099  const unsigned char *input_s = pInputImage->pixels + 1;
4100  const unsigned char *input_v = pInputImage->pixels + 2;
4101  unsigned char *output = pOutputImage->pixels;
4102 
4103  if (pROI)
4104  {
4105  const int width = pInputImage->width;
4106  const int height = pInputImage->height;
4107 
4108  int min_x = pROI->min_x;
4109  int max_x = pROI->max_x;
4110  int min_y = pROI->min_y;
4111  int max_y = pROI->max_y;
4112 
4113  if (min_x < 0) min_x = 0;
4114  if (min_x > width - 1) min_x = width - 1;
4115  if (max_x < 0) max_x = 0;
4116  if (max_x > width - 1) max_x = width - 1;
4117  if (min_y < 0) min_y = 0;
4118  if (min_y > height - 1) min_y = height - 1;
4119  if (max_y < 0) max_y = 0;
4120  if (max_y > height - 1) max_y = height - 1;
4121 
4122  const int diff = width - (max_x - min_x + 1);
4123  const int diff_rgb = 3 * diff;
4124 
4125  if (max_hue >= min_hue)
4126  {
4127  for (int y = min_y, offset = min_y * width + min_x, offset_rgb = 3 * (min_y * width + min_x); y <= max_y; y++, offset += diff, offset_rgb += diff_rgb)
4128  for (int x = min_x; x <= max_x; x++, offset++, offset_rgb += 3)
4129  output[offset] = (input_s[offset_rgb] <= max_sat && input_s[offset_rgb] >= min_sat && input_h[offset_rgb] >= min_hue && input_h[offset_rgb] <= max_hue && input_v[offset_rgb] >= min_v && input_v[offset_rgb] <= max_v) * 255;
4130  }
4131  else
4132  {
4133  for (int y = min_y, offset = min_y * width + min_x, offset_rgb = 3 * (min_y * width + min_x); y <= max_y; y++, offset += diff, offset_rgb += diff_rgb)
4134  for (int x = min_x; x <= max_x; x++, offset++, offset_rgb += 3)
4135  output[offset] = (input_s[offset_rgb] <= max_sat && input_s[offset_rgb] >= min_sat && (input_h[offset_rgb] >= min_hue || input_h[offset_rgb] <= max_hue) && input_v[offset_rgb] >= min_v && input_v[offset_rgb] <= max_v) * 255;
4136  }
4137  }
4138  else
4139  {
4140  const int nPixels = pInputImage->width * pInputImage->height;
4141 
4142  if (max_hue >= min_hue)
4143  {
4144  for (int i = 0, offset = 0; i < nPixels; i++, offset += 3)
4145  output[i] = (input_s[offset] <= max_sat && input_s[offset] >= min_sat && input_h[offset] >= min_hue && input_h[offset] <= max_hue && input_v[offset] >= min_v && input_v[offset] <= max_v) * 255;
4146  }
4147  else
4148  {
4149  for (int i = 0, offset = 0; i < nPixels; i++, offset += 3)
4150  output[i] = (input_s[offset] <= max_sat && input_s[offset] >= min_sat && (input_h[offset] >= min_hue || input_h[offset] <= max_hue) && input_v[offset] >= min_v && input_v[offset] <= max_v) * 255;
4151  }
4152  }
4153  }
4154  else
4155  {
4156  const int width = pInputImage->width;
4157  const int height = pInputImage->height;
4158  const int nPixels = width * height;
4159 
4160  const unsigned char *input_h = pInputImage->pixels;
4161  const unsigned char *input_s = input_h + nPixels;
4162  const unsigned char *input_v = input_s + nPixels;
4163  unsigned char *output = pOutputImage->pixels;
4164 
4165  if (pROI)
4166  {
4167  int min_x = pROI->min_x;
4168  int max_x = pROI->max_x;
4169  int min_y = pROI->min_y;
4170  int max_y = pROI->max_y;
4171 
4172  if (min_x < 0) min_x = 0;
4173  if (min_x > width - 1) min_x = width - 1;
4174  if (max_x < 0) max_x = 0;
4175  if (max_x > width - 1) max_x = width - 1;
4176  if (min_y < 0) min_y = 0;
4177  if (min_y > height - 1) min_y = height - 1;
4178  if (max_y < 0) max_y = 0;
4179  if (max_y > height - 1) max_y = height - 1;
4180 
4181  const int diff = width - (max_x - min_x + 1);
4182 
4183  if (max_hue >= min_hue)
4184  {
4185  for (int y = min_y, offset = min_y * width + min_x; y <= max_y; y++, offset += diff)
4186  for (int x = min_x; x <= max_x; x++, offset++)
4187  output[offset] = (input_s[offset] <= max_sat && input_s[offset] >= min_sat && input_h[offset] >= min_hue && input_h[offset] <= max_hue && input_v[offset] >= min_v && input_v[offset] <= max_v) * 255;
4188  }
4189  else
4190  {
4191  for (int y = min_y, offset = min_y * width + min_x; y <= max_y; y++, offset += diff)
4192  for (int x = min_x; x <= max_x; x++, offset++)
4193  output[offset] = (input_s[offset] <= max_sat && input_s[offset] >= min_sat && (input_h[offset] >= min_hue || input_h[offset] <= max_hue) && input_v[offset] >= min_v && input_v[offset] <= max_v) * 255;
4194  }
4195  }
4196  else
4197  {
4198  if (max_hue >= min_hue)
4199  {
4200  for (int i = 0; i < nPixels; i++)
4201  output[i] = (input_s[i] <= max_sat && input_s[i] >= min_sat && input_h[i] >= min_hue && input_h[i] <= max_hue && input_v[i] >= min_v && input_v[i] <= max_v) * 255;
4202  }
4203  else
4204  {
4205  for (int i = 0; i < nPixels; i++)
4206  output[i] = (input_s[i] <= max_sat && input_s[i] >= min_sat && (input_h[i] >= min_hue || input_h[i] <= max_hue) && input_v[i] >= min_v && input_v[i] <= max_v) * 255;
4207  }
4208  }
4209  }
4210 
4212 
4213  return true;
4214 }
4215 
4216 
4217 
4218 bool ImageProcessor::FilterColor(const CByteImage *pInputImage, CByteImage *pOutputImage, ObjectColor cColor, CColorParameterSet* pColorParameterSet, bool bImageIsHSV)
4219 {
4220  const int* pParams = pColorParameterSet->GetColorParameters(cColor);
4221 
4222  bool bRet;
4223  if (bImageIsHSV)
4224  {
4225  bRet = FilterHSV(pInputImage, pOutputImage, pParams[0], pParams[1], pParams[2], pParams[3], pParams[4], pParams[5]);
4226  }
4227  else
4228  {
4229  CByteImage* pImage = new CByteImage(pInputImage);
4230  CalculateHSVImage(pInputImage, pImage);
4231  bRet = FilterHSV(pImage, pOutputImage, pParams[0], pParams[1], pParams[2], pParams[3], pParams[4], pParams[5]);
4232  delete pImage;
4233  }
4234  return bRet;
4235 }
4236 
4237 
4238 
4239 // Original version of this function by Olaf Fischer
4240 bool ImageProcessor::Rotate180Degrees(const CByteImage *pInputImage, CByteImage *pOutputImage)
4241 {
4242  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height || pInputImage->type != pOutputImage->type || pInputImage->type == CByteImage::eRGB24Split)
4243  {
4244  printf("error: input and output image do not match for ImageProcessor::Rotate180Degrees\n");
4245  return false;
4246  }
4247 
4248  CByteImage *pSaveOutputImage = 0;
4249  if (pInputImage->pixels == pOutputImage->pixels)
4250  {
4251  pSaveOutputImage = pOutputImage;
4252  pOutputImage = new CByteImage(pInputImage);
4253  }
4254 
4255  const int nBytes = pInputImage->width * pInputImage->height * pInputImage->bytesPerPixel;
4256  const int nLastPixel = nBytes - pInputImage->bytesPerPixel;
4257  const unsigned char *input = pInputImage->pixels;
4258  unsigned char *output = pOutputImage->pixels;
4259 
4260  if (pInputImage->type == CByteImage::eGrayScale)
4261  {
4262  for (int i = 0; i < nBytes; i++)
4263  output[i] = input[nLastPixel - i];
4264  }
4265  else if (pInputImage->type == CByteImage::eRGB24)
4266  {
4267  for (int i = 0; i < nBytes; i += 3)
4268  {
4269  output[i + 2] = input[nLastPixel - i + 2];
4270  output[i + 1] = input[nLastPixel - i + 1];
4271  output[i] = input[nLastPixel - i];
4272  }
4273  }
4274 
4275  if (pSaveOutputImage)
4276  {
4277  CopyImage(pOutputImage, pSaveOutputImage);
4278  delete pOutputImage;
4279  }
4280 
4281  return true;
4282 }
4283 
4284 
4285 static int _RegionGrowing(unsigned char *pixels, int width, int offset, int *stack, int *region, MyRegion &resultRegion, int nMinimumPointsPerRegion, int nMaximumPointsPerRegion, bool bCalculateBoundingBox)
4286 {
4287  resultRegion.nSeedOffset = offset;
4288 
4289  int sp = 0, nPixels = 0;
4290 
4291  stack[sp++] = offset;
4292  pixels[offset] = 254;
4293 
4294  while (sp--)
4295  {
4296  const int offset = stack[sp];
4297 
4298  if (pixels[offset - width] == 255)
4299  {
4300  pixels[offset - width] = 254;
4301  stack[sp++] = offset - width;
4302  }
4303 
4304  if (pixels[offset - 1] == 255)
4305  {
4306  pixels[offset - 1] = 254;
4307  stack[sp++] = offset - 1;
4308  }
4309 
4310  if (pixels[offset + 1] == 255)
4311  {
4312  pixels[offset + 1] = 254;
4313  stack[sp++] = offset + 1;
4314  }
4315 
4316  if (pixels[offset + width] == 255)
4317  {
4318  pixels[offset + width] = 254;
4319  stack[sp++] = offset + width;
4320  }
4321 
4322  region[nPixels++] = offset;
4323  }
4324 
4325  if ((nMinimumPointsPerRegion > 0 && nPixels < nMinimumPointsPerRegion) || (nMaximumPointsPerRegion > 0 && nPixels > nMaximumPointsPerRegion))
4326  return 0;
4327 
4328  int cx = 0, cy = 0;
4329 
4330  if (bCalculateBoundingBox)
4331  {
4332  const int x = offset % width;
4333  const int y = offset / width;
4334  int min_x = x, max_x = x, min_y = y, max_y = y;
4335 
4336  for (int i = 0; i < nPixels; i++)
4337  {
4338  const int offset = region[i];
4339  const int x = offset % width;
4340  const int y = offset / width;
4341 
4342  cx += x;
4343  cy += y;
4344 
4345  if (x < min_x)
4346  min_x = x;
4347 
4348  if (x > max_x)
4349  max_x = x;
4350 
4351  if (y < min_y)
4352  min_y = y;
4353 
4354  if (y > max_y)
4355  max_y = y;
4356  }
4357 
4358  resultRegion.min_x = min_x;
4359  resultRegion.min_y = min_y;
4360  resultRegion.max_x = max_x;
4361  resultRegion.max_y = max_y;
4362  resultRegion.ratio = float(max_x - min_x + 1) / float(max_y - min_y + 1);
4363  }
4364  else
4365  {
4366  for (int i = 0; i < nPixels; i++)
4367  {
4368  const int offset = region[i];
4369 
4370  cx += offset % width;
4371  cy += offset / width;
4372  }
4373 
4374  resultRegion.min_x = -1;
4375  resultRegion.min_y = -1;
4376  resultRegion.max_x = -1;
4377  resultRegion.max_y = -1;
4378  resultRegion.ratio = 0.0f;
4379  }
4380 
4381  // fill rest of region struct
4382  resultRegion.nPixels = nPixels;
4383  resultRegion.centroid.x = float(cx) / nPixels;
4384  resultRegion.centroid.y = float(cy) / nPixels;
4385 
4386  return nPixels;
4387 }
4388 
4389 int ImageProcessor::RegionGrowing(const CByteImage *pImage, MyRegion &resultRegion, int x, int y, int nMinimumPointsPerRegion, int nMaximumPointsPerRegion, bool bCalculateBoundingBox, bool bStorePixels)
4390 {
4391  if (pImage->type != CByteImage::eGrayScale)
4392  {
4393  printf("error: input image should be grayscale for ImageProcessor::RegionGrowing\n");
4394  return -1;
4395  }
4396 
4397  const int width = pImage->width;
4398  const int height = pImage->height;
4399 
4400  if (x < 0 || x >= width || y < 0 || y >= height)
4401  {
4402  printf("error: x/y is not within bounds of image in ImageProcessor::RegionGrowing\n");
4403  return -1;
4404  }
4405 
4406  // create image with additional 1 pixel border
4407  CByteImage *pTempImage = new CByteImage(width + 2, height + 2, CByteImage::eGrayScale);
4408 
4409  const int temp_width = pTempImage->width;
4410  const int temp_height = pTempImage->height;
4411 
4412  unsigned char *temp = pTempImage->pixels;
4413 
4414  // copy contents
4415  unsigned char *temp_helper = temp + temp_width + 1;
4416 
4417  for (int i = 0; i < pImage->height; i++)
4418  memcpy(temp_helper + i * temp_width, pImage->pixels + i * width, width);
4419 
4420  // zero frame
4421  ImageProcessor::ZeroFrame(pTempImage);
4422 
4423  // allocate memory
4424  const int nPixels = temp_width * temp_height;
4425 
4426  int *pStack = new int[nPixels];
4427  int *pRegionPixels = new int[nPixels];
4428 
4429  // perform region growing
4430  const int nRegionPixels = _RegionGrowing(temp, temp_width, (y + 1) * temp_width + (x + 1), pStack, pRegionPixels, resultRegion, nMinimumPointsPerRegion, nMaximumPointsPerRegion, bCalculateBoundingBox);
4431 
4432  if (resultRegion.pPixels)
4433  {
4434  delete [] resultRegion.pPixels;
4435  resultRegion.pPixels = 0;
4436  }
4437 
4438  if (nRegionPixels > 0)
4439  {
4440  if (bStorePixels)
4441  {
4442  resultRegion.pPixels = new int[nRegionPixels];
4443 
4444  for (int j = 0; j < nRegionPixels; j++)
4445  {
4446  const int x = pRegionPixels[j] % temp_width;
4447  const int y = pRegionPixels[j] / temp_width;
4448 
4449  resultRegion.pPixels[j] = (y - 1) * width + x - 1;
4450  }
4451  }
4452 
4453  // correct coordinates
4454  resultRegion.centroid.x -= 1.0f;
4455  resultRegion.centroid.y -= 1.0f;
4456 
4457  resultRegion.min_x -= 1;
4458  resultRegion.min_y -= 1;
4459  resultRegion.max_x -= 1;
4460  resultRegion.max_y -= 1;
4461 
4462  resultRegion.nSeedOffset = (resultRegion.nSeedOffset / temp_width - 1) * width + (resultRegion.nSeedOffset % temp_width - 1);
4463  }
4464 
4465  // free memory
4466  delete pTempImage;
4467  delete [] pStack;
4468  delete [] pRegionPixels;
4469 
4470  return nRegionPixels;
4471 }
4472 
4473 bool ImageProcessor::FindRegions(const CByteImage *pImage, RegionList &regionList, int nMinimumPointsPerRegion, int nMaximumPointsPerRegion, bool bCalculateBoundingBox, bool bStorePixels)
4474 {
4475  // clear result list
4476  regionList.clear();
4477 
4478  if (pImage->type != CByteImage::eGrayScale)
4479  {
4480  printf("error: input image should be grayscale for ImageProcessor::FindRegions\n");
4481  return false;
4482  }
4483 
4484  const int width = pImage->width;
4485  const int height = pImage->height;
4486 
4487  // create image with additional 1 pixel border
4488  CByteImage *pTempImage = new CByteImage(width + 2, height + 2, CByteImage::eGrayScale);
4489 
4490  const int temp_width = pTempImage->width;
4491  const int temp_height = pTempImage->height;
4492 
4493  unsigned char *temp = pTempImage->pixels;
4494 
4495  // copy contents
4496  unsigned char *temp_helper = temp + temp_width + 1;
4497 
4498  for (int y = 0; y < pImage->height; y++)
4499  memcpy(temp_helper + y * temp_width, pImage->pixels + y * width, width);
4500 
4501  // zero frame
4502  ImageProcessor::ZeroFrame(pTempImage);
4503 
4504  // allocate memory
4505  const int nPixels = temp_width * temp_height;
4506 
4507  int *pStack = new int[nPixels];
4508  int *pRegionPixels = new int[nPixels];
4509 
4510  // go through image
4511  for (int i = 0; i < nPixels; i++)
4512  {
4513  if (temp[i] == 255)
4514  {
4515  MyRegion region;
4516 
4517  // do region growing
4518  const int nRegionPixels = _RegionGrowing(temp, temp_width, i, pStack, pRegionPixels, region, nMinimumPointsPerRegion, nMaximumPointsPerRegion, bCalculateBoundingBox);
4519 
4520  if (nRegionPixels > 0)
4521  {
4522  // first add
4523  regionList.push_back(region);
4524 
4525  // then copy (this way a double copy through copy constructor is avoided)
4526  // and correct coordinates
4527  MyRegion &addedEntry = regionList.back();
4528 
4529  // correct coordinates
4530  addedEntry.centroid.x -= 1.0f;
4531  addedEntry.centroid.y -= 1.0f;
4532 
4533  addedEntry.min_x -= 1;
4534  addedEntry.min_y -= 1;
4535  addedEntry.max_x -= 1;
4536  addedEntry.max_y -= 1;
4537 
4538  if (bStorePixels)
4539  {
4540  addedEntry.pPixels = new int[nRegionPixels];
4541 
4542  for (int j = 0; j < nRegionPixels; j++)
4543  {
4544  const int x = pRegionPixels[j] % temp_width;
4545  const int y = pRegionPixels[j] / temp_width;
4546 
4547  addedEntry.pPixels[j] = (y - 1) * width + x - 1;
4548  }
4549 
4550  addedEntry.nSeedOffset = (addedEntry.nSeedOffset / temp_width - 1) * width + (addedEntry.nSeedOffset % temp_width - 1);
4551  }
4552  }
4553  }
4554  }
4555 
4556  // free memory
4557  delete pTempImage;
4558  delete [] pStack;
4559  delete [] pRegionPixels;
4560 
4561  return true;
4562 }
4563 
4564 bool ImageProcessor::FindRegions(const CByteImage *pImage, CRegionArray &regionList, int nMinimumPointsPerRegion, int nMaximumPointsPerRegion, bool bCalculateBoundingBox, bool bStorePixels)
4565 {
4566  // clear result list
4567  regionList.Clear();
4568 
4569  if (pImage->type != CByteImage::eGrayScale)
4570  {
4571  printf("error: input image should be grayscale for ImageProcessor::FindRegions\n");
4572  return false;
4573  }
4574 
4575  const int width = pImage->width;
4576  const int height = pImage->height;
4577 
4578  // create image with additional 1 pixel border
4579  CByteImage *pTempImage = new CByteImage(width + 2, height + 2, CByteImage::eGrayScale);
4580 
4581  const int temp_width = pTempImage->width;
4582  const int temp_height = pTempImage->height;
4583 
4584  unsigned char *temp = pTempImage->pixels;
4585 
4586  // copy contents
4587  unsigned char *temp_helper = temp + temp_width + 1;
4588 
4589  for (int y = 0; y < pImage->height; y++)
4590  memcpy(temp_helper + y * temp_width, pImage->pixels + y * width, width);
4591 
4592  // zero frame
4593  ImageProcessor::ZeroFrame(pTempImage);
4594 
4595  // allocate memory
4596  const int nPixels = temp_width * temp_height;
4597 
4598  int *pStack = new int[nPixels];
4599  int *pRegionPixels = new int[nPixels];
4600 
4601  // go through image
4602  for (int i = 0; i < nPixels; i++)
4603  {
4604  if (temp[i] == 255)
4605  {
4606  MyRegion region;
4607 
4608  // do region growing
4609  const int nRegionPixels = _RegionGrowing(temp, temp_width, i, pStack, pRegionPixels, region, nMinimumPointsPerRegion, nMaximumPointsPerRegion, bCalculateBoundingBox);
4610 
4611  if (nRegionPixels > 0)
4612  {
4613  // first add
4614  regionList.AddElement(region);
4615 
4616  // then copy (this way a double copy through copy constructor is avoided)
4617  // and correct coordinates
4618  MyRegion &addedEntry = regionList[regionList.GetSize() - 1];
4619 
4620  // correct coordinates
4621  addedEntry.centroid.x -= 1.0f;
4622  addedEntry.centroid.y -= 1.0f;
4623 
4624  addedEntry.min_x -= 1;
4625  addedEntry.min_y -= 1;
4626  addedEntry.max_x -= 1;
4627  addedEntry.max_y -= 1;
4628 
4629  if (bStorePixels)
4630  {
4631  addedEntry.pPixels = new int[nRegionPixels];
4632 
4633  for (int j = 0; j < nRegionPixels; j++)
4634  {
4635  const int x = pRegionPixels[j] % temp_width;
4636  const int y = pRegionPixels[j] / temp_width;
4637 
4638  addedEntry.pPixels[j] = (y - 1) * width + x - 1;
4639  }
4640 
4641  addedEntry.nSeedOffset = (addedEntry.nSeedOffset / temp_width - 1) * width + (addedEntry.nSeedOffset % temp_width - 1);
4642  }
4643  }
4644  }
4645  }
4646 
4647  // free memory
4648  delete pTempImage;
4649  delete [] pStack;
4650  delete [] pRegionPixels;
4651 
4652  return true;
4653 }
4654 
4655 
4656 bool ImageProcessor::CalculateHSVImage(const CByteImage *pInputImage, CByteImage *pOutputImage, const MyRegion *pROI)
4657 {
4658  OPTIMIZED_FUNCTION_HEADER_2_ROI(CalculateHSVImage, pInputImage, pOutputImage, pROI)
4659 
4660  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height ||
4661  pInputImage->type != pOutputImage->type || (pInputImage->type != CByteImage::eRGB24 && pInputImage->type != CByteImage::eRGB24Split))
4662  {
4663  printf("error: input and output image do not match for ImageProcessor::CalculateHSVImage\n");
4664  return false;
4665  }
4666 
4667  const unsigned char *input = pInputImage->pixels;
4668  unsigned char *output = pOutputImage->pixels;
4669 
4670  if (pROI)
4671  {
4672  if (pInputImage->type == CByteImage::eRGB24)
4673  {
4674  const int width = pInputImage->width;
4675  const int height = pInputImage->height;
4676 
4677  int min_x = pROI->min_x;
4678  int max_x = pROI->max_x;
4679  int min_y = pROI->min_y;
4680  int max_y = pROI->max_y;
4681 
4682  if (min_x < 0) min_x = 0;
4683  if (min_x > width - 1) min_x = width - 1;
4684  if (max_x < 0) max_x = 0;
4685  if (max_x > width - 1) max_x = width - 1;
4686  if (min_y < 0) min_y = 0;
4687  if (min_y > height - 1) min_y = height - 1;
4688  if (max_y < 0) max_y = 0;
4689  if (max_y > height - 1) max_y = height - 1;
4690 
4691  const int diff = 3 * (width - (max_x - min_x + 1));
4692 
4693  for (int y = min_y, offset = 3 * (min_y * width + min_x); y <= max_y; y++, offset += diff)
4694  {
4695  for (int x = min_x; x <= max_x; x++, offset += 3)
4696  {
4697  const int r = input[offset];
4698  const int g = input[offset + 1];
4699  const int b = input[offset + 2];
4700 
4701  const int max = MY_MAX(MY_MAX(r, g), b);
4702  const int min = MY_MIN(MY_MIN(r, g), b);
4703  const int delta = max - min;
4704 
4705  // unoptimized: delta * 255 / max;
4706  const int s = (255 * delta * division_table[max]) >> 20;
4707  int h;
4708 
4709  // unoptimized: 30 * (g - b) / delta (etc.)
4710  if (r == max)
4711  h = g > b ? 180 + ((30 * (g - b) * division_table[delta]) >> 20) : 180 - ((30 * (b - g) * division_table[delta]) >> 20);
4712  else if (g == max)
4713  h = b > r ? 60 + ((30 * (b - r) * division_table[delta]) >> 20) : 60 - ((30 * (r - b) * division_table[delta]) >> 20);
4714  else
4715  h = r > g ? 120 + ((30 * (r - g) * division_table[delta]) >> 20) : 120 - ((30 * (g - r) * division_table[delta]) >> 20);
4716 
4717  if (h >= 180) h -= 180;
4718 
4719  output[offset] = h;
4720  output[offset + 1] = s;
4721  output[offset + 2] = max;
4722  }
4723  }
4724  }
4725  else
4726  {
4727  const int width = pInputImage->width;
4728  const int height = pInputImage->height;
4729 
4730  const int nPixels = width * height;
4731 
4732  const unsigned char *input_r = input;
4733  const unsigned char *input_g = input_r + nPixels;
4734  const unsigned char *input_b = input_g + nPixels;
4735  unsigned char *output_r = output;
4736  unsigned char *output_g = output_r + nPixels;
4737  unsigned char *output_b = output_g + nPixels;
4738 
4739  int min_x = pROI->min_x;
4740  int max_x = pROI->max_x;
4741  int min_y = pROI->min_y;
4742  int max_y = pROI->max_y;
4743 
4744  if (min_x < 0) min_x = 0;
4745  if (min_x > width - 1) min_x = width - 1;
4746  if (max_x < 0) max_x = 0;
4747  if (max_x > width - 1) max_x = width - 1;
4748  if (min_y < 0) min_y = 0;
4749  if (min_y > height - 1) min_y = height - 1;
4750  if (max_y < 0) max_y = 0;
4751  if (max_y > height - 1) max_y = height - 1;
4752 
4753  const int diff = width - (max_x - min_x + 1);
4754 
4755  for (int y = min_y, offset = min_y * width + min_x; y <= max_y; y++, offset += diff)
4756  {
4757  for (int x = min_x; x <= max_x; x++, offset++)
4758  {
4759  const int r = input_r[offset];
4760  const int g = input_g[offset];
4761  const int b = input_b[offset];
4762 
4763  const int max = MY_MAX(MY_MAX(r, g), b);
4764  const int min = MY_MIN(MY_MIN(r, g), b);
4765  const int delta = max - min;
4766 
4767  // unoptimized: delta * 255 / max;
4768  const int s = (255 * delta * division_table[max]) >> 20;
4769  int h;
4770 
4771  // unoptimized: 30 * (g - b) / delta (etc.)
4772  if (r == max)
4773  h = g > b ? 180 + ((30 * (g - b) * division_table[delta]) >> 20) : 180 - ((30 * (b - g) * division_table[delta]) >> 20);
4774  else if (g == max)
4775  h = b > r ? 60 + ((30 * (b - r) * division_table[delta]) >> 20) : 60 - ((30 * (r - b) * division_table[delta]) >> 20);
4776  else
4777  h = r > g ? 120 + ((30 * (r - g) * division_table[delta]) >> 20) : 120 - ((30 * (g - r) * division_table[delta]) >> 20);
4778 
4779  if (h >= 180) h -= 180;
4780 
4781  output_r[offset] = h;
4782  output_g[offset] = s;
4783  output_b[offset] = max;
4784  }
4785  }
4786  }
4787  }
4788  else
4789  {
4790  if (pInputImage->type == CByteImage::eRGB24)
4791  {
4792  const int maxi = pInputImage->width * pInputImage->height * 3;
4793 
4794  for (int i = 0; i < maxi; i += 3)
4795  {
4796  const int r = input[i];
4797  const int g = input[i + 1];
4798  const int b = input[i + 2];
4799 
4800  const int max = MY_MAX(MY_MAX(r, g), b);
4801  const int min = MY_MIN(MY_MIN(r, g), b);
4802  const int delta = max - min;
4803 
4804  // unoptimized: delta * 255 / max;
4805  const int s = (255 * delta * division_table[max]) >> 20;
4806  int h;
4807 
4808  // unoptimized: 30 * (g - b) / delta (etc.)
4809  if (r == max)
4810  h = g > b ? 180 + ((30 * (g - b) * division_table[delta]) >> 20) : 180 - ((30 * (b - g) * division_table[delta]) >> 20);
4811  else if (g == max)
4812  h = b > r ? 60 + ((30 * (b - r) * division_table[delta]) >> 20) : 60 - ((30 * (r - b) * division_table[delta]) >> 20);
4813  else
4814  h = r > g ? 120 + ((30 * (r - g) * division_table[delta]) >> 20) : 120 - ((30 * (g - r) * division_table[delta]) >> 20);
4815 
4816  if (h >= 180) h -= 180;
4817 
4818  output[i] = h;
4819  output[i + 1] = s;
4820  output[i + 2] = max;
4821  }
4822  }
4823  else
4824  {
4825  const int nPixels = pInputImage->width * pInputImage->height;
4826 
4827  const unsigned char *input_r = input;
4828  const unsigned char *input_g = input_r + nPixels;
4829  const unsigned char *input_b = input_g + nPixels;
4830  unsigned char *output_r = output;
4831  unsigned char *output_g = output_r + nPixels;
4832  unsigned char *output_b = output_g + nPixels;
4833 
4834  for (int i = 0; i < nPixels; i++)
4835  {
4836  const int r = input_r[i];
4837  const int g = input_g[i];
4838  const int b = input_b[i];
4839 
4840  const int max = MY_MAX(MY_MAX(r, g), b);
4841  const int min = MY_MIN(MY_MIN(r, g), b);
4842  const int delta = max - min;
4843 
4844  // unoptimized: delta * 255 / max;
4845  const int s = (255 * delta * division_table[max]) >> 20;
4846  int h;
4847 
4848  // unoptimized: 30 * (g - b) / delta (etc.)
4849  if (r == max)
4850  h = g > b ? 180 + ((30 * (g - b) * division_table[delta]) >> 20) : 180 - ((30 * (b - g) * division_table[delta]) >> 20);
4851  else if (g == max)
4852  h = b > r ? 60 + ((30 * (b - r) * division_table[delta]) >> 20) : 60 - ((30 * (r - b) * division_table[delta]) >> 20);
4853  else
4854  h = r > g ? 120 + ((30 * (r - g) * division_table[delta]) >> 20) : 120 - ((30 * (g - r) * division_table[delta]) >> 20);
4855 
4856  if (h >= 180) h -= 180;
4857 
4858  output_r[i] = h;
4859  output_g[i] = s;
4860  output_b[i] = max;
4861  }
4862  }
4863  }
4864 
4866 
4867  return true;
4868 }
4869 
4870 
4871 bool ImageProcessor::HoughTransformLines(const CByteImage *pImage, CByteImage *pVisualizationImage, Vec2dList &resultLines, int nLinesToExtract, int nMinHits)
4872 {
4873  if (pImage->type == CByteImage::eGrayScale)
4874  {
4875  printf("error: input image must be of type CByteImage::eGrayScale\n");
4876  return false;
4877  }
4878 
4879  const int width = pImage->width;
4880  const int height = pImage->height;
4881  const unsigned char *pixels = pImage->pixels;
4882 
4883  const int m = 1 + 2 * (int(sqrtf(float(width * width + height * height))) + 1);
4884  const int m2 = m / 2;
4885  const float m2f = float(m2) + 0.5f;
4886  const int n = 180;
4887  const int nHoughPixels = n * m;
4888 
4889  CShortImage houghSpace(m, n);
4890 
4891  // reset bins
4892  ImageProcessor::Zero(&houghSpace);
4893  short *houghspace = houghSpace.pixels;
4894 
4895  // initialize lookup tables for sin and cos
4896  // function does initialization only for the first call
4897  InitSinCosTables();
4898 
4899  // perform hough transform
4900  for (int y = 0, offset = 0; y < height; y++)
4901  {
4902  for (int x = 0; x < width; x++, offset++)
4903  {
4904  if (pixels[offset])
4905  {
4906  for (int t = 0; t < n; t++)
4907  {
4908  const float r = x * cos_table[t] + y * sin_table[t];
4909  const int r_ = int(r + m2f);
4910 
4911  houghspace[t * m + r_]++;
4912  }
4913  }
4914  }
4915  }
4916 
4917  resultLines.clear();
4918 
4919  // find maxima
4920  for (int nn = 0; nn < nLinesToExtract; nn++)
4921  {
4922  int max = nMinHits - 1, best_i = -1;
4923 
4924  for (int i = 0; i < nHoughPixels; i++)
4925  {
4926  if (houghspace[i] > max)
4927  {
4928  max = houghspace[i];
4929  best_i = i;
4930  }
4931  }
4932 
4933  if (best_i != -1)
4934  {
4935  const int t = best_i / m;
4936  const int r = best_i % m;
4937 
4938  const int w = 10;
4939 
4940  for (int j = -w; j <= w; j++)
4941  {
4942  for (int k = -w; k <= w; k++)
4943  {
4944  const int r_ = r + j;
4945  int t_ = t + k;
4946 
4947  if (t_ >= n)
4948  t_ -= n;
4949 
4950  if (t_ < 0)
4951  t_ += n;
4952 
4953  if (r_ >= 0 && r_ < m)
4954  houghspace[t_ * m + r_] = 0;
4955  }
4956  }
4957 
4958  const float theta = t * FLOAT_DEG2RAD;
4959  const int r_value = r - m2;
4960 
4961  const Vec2d g = { theta, float(r_value) };
4962  resultLines.push_back(g);
4963 
4964  if (pVisualizationImage)
4965  {
4966  //printf("%i hits: (theta, r) = (%i, %i)\n", max, t, r_value);
4967  PrimitivesDrawer::DrawLinePolar(pVisualizationImage, theta, float(r_value), 255, 0, 0);
4968  }
4969  }
4970  else
4971  break;
4972  }
4973 
4974  return true;
4975 }
4976 
4977 bool ImageProcessor::HoughTransformCircles(const CByteImage *pImage, CByteImage *pVisualizationImage, Vec3dList &resultCircles, int rmin, int rmax, int nCirclesToExtract, int nMinHits)
4978 {
4979  if (rmin > rmax)
4980  {
4981  printf("error: rmin (%i) may not be greater than rmax (%i) for ImageProcessor::HoughTransformCircles\n", rmin, rmax);
4982  return false;
4983  }
4984 
4985  const int width = pImage->width;
4986  const int height = pImage->height;
4987  const unsigned char *pixels = pImage->pixels;
4988 
4989  const int n = width + 2 * rmax;
4990  const int m = height + 2 * rmax;
4991  const int nHoughPixels = n * m * (rmax - rmin + 1);
4992 
4993  short *houghspace = new short[nHoughPixels];
4994 
4995  // reset bins
4996  memset(houghspace, 0, nHoughPixels * sizeof(short));
4997 
4998  // initialize lookup tables for sin and cos
4999  // function does initialization only for the first call
5000  InitSinCosTables();
5001 
5002  const int mn = m * n;
5003  short *helper = houghspace - rmin * mn + rmax * m + rmax;
5004 
5005  // perform hough transform
5006  for (int v = 0, offset = 0; v < height; v++)
5007  {
5008  for (int u = 0; u < width; u++, offset++)
5009  {
5010  if (pixels[offset] == 255)
5011  {
5012  for (int t = 0; t < 180; t++)
5013  {
5014  for (int r = rmin; r <= rmax; r++)
5015  {
5016  const int um = int(u - r * cos_table[t] + 0.5f);
5017  const int vm1 = int(v - r * sin_table[t] + 0.5f);
5018  const int vm2 = int(v + r * sin_table[t] + 0.5f);
5019 
5020  //const int base = (r - rmin) * mn + (rmax + um) * m + rmax;
5021  //houghspace[base + vm1]++;
5022  //houghspace[base + vm2]++;
5023 
5024  const int base = r * mn + um * m;
5025  helper[base + vm1]++;
5026  helper[base + vm2]++;
5027  }
5028  }
5029  }
5030  }
5031  }
5032 
5033  resultCircles.clear();
5034 
5035  // find maxima
5036  for (int nn = 0; nn < nCirclesToExtract; nn++)
5037  {
5038  int max = nMinHits - 1, best_i = -1;
5039 
5040  for (int i = 0; i < nHoughPixels; i++)
5041  {
5042  if (houghspace[i] > max)
5043  {
5044  max = houghspace[i];
5045  best_i = i;
5046  }
5047  }
5048 
5049  if (best_i != -1)
5050  {
5051  int um = (best_i % mn) / m;
5052  int vm = (best_i % mn) % m;
5053  int r = best_i / mn;
5054 
5055  const int w = 5;
5056 
5057  for (int j = -w; j <= w; j++)
5058  {
5059  for (int k = -w; k <= w; k++)
5060  {
5061  for (int l = -w; l <= w; l++)
5062  {
5063  const int um_ = um + j;
5064  const int vm_ = vm + k;
5065  const int r_ = r + l;
5066 
5067  if (um_ >= 0 && um_ < n && vm >= 0 && vm < m && r_ >= 0 && r_ <= rmax - rmin)
5068  houghspace[r_ * mn + um_ * m + vm_] = 0;
5069  }
5070  }
5071  }
5072 
5073  um -= rmax;
5074  vm -= rmax;
5075  r += rmin;
5076 
5077  //printf("%i hits: (um vm) = (%i %i) -- r = = %i\n", max, um, vm, r);
5078 
5079  const Vec3d circle = { float(um), float(vm), float(r) };
5080  resultCircles.push_back(circle);
5081 
5082  if (pVisualizationImage)
5083  {
5084  Vec2d center = { float(um), float(vm) };
5085  PrimitivesDrawer::DrawCircle(pVisualizationImage, center, float(r), 255, 0, 0, 1);
5086  }
5087  }
5088  else
5089  break;
5090  }
5091 
5092  delete [] houghspace;
5093 
5094  return true;
5095 }
5096 
5097 void ImageProcessor::HoughTransformLines(const CVec2dArray &edgePoints, const CVec2dArray &edgeDirections, int width, int height, int nLinesToExtract, int nMinHits, CVec2dArray &resultLines, CIntArray &resultHits, CByteImage *pVisualizationImage)
5098 {
5099  // clear result list
5100  resultLines.Clear();
5101 
5102  // perform detection
5103  CStraightLine2dArray resultLines_;
5104 
5105  HoughTransformLines(edgePoints, edgeDirections, width, height, nLinesToExtract, nMinHits, resultLines_, resultHits, pVisualizationImage);
5106 
5107  // fill result list
5108  const int nResultLines = resultLines_.GetSize();
5109 
5110  for (int i = 0; i < nResultLines; i++)
5111  {
5112  const StraightLine2d &line = resultLines_[i];
5113 
5114  Vec2d x = { atan2f(line.normalVector.y, line.normalVector.x), -line.c };
5115 
5116  resultLines.AddElement(x);
5117  }
5118 }
5119 
5120 void ImageProcessor::HoughTransformLines(const CVec2dArray &edgePoints, const CVec2dArray &edgeDirections, int width, int height, int nLinesToExtract, int nMinHits, CStraightLine2dArray &resultLines, CIntArray &resultHits, CByteImage *pVisualizationImage)
5121 {
5122  // clear result list
5123  resultLines.Clear();
5124 
5125  const int m = 1 + 2 * (int(sqrtf(float(width * width + height * height))) + 1);
5126  const int m2 = m / 2;
5127  const float m2f = float(m2) + 0.5f;
5128  const int n = 360;
5129  const int nHoughPixels = n * m;
5130 
5131  CShortImage houghSpace(m, n);
5132 
5133  // reset bins
5134  ImageProcessor::Zero(&houghSpace);
5135  short *houghspace = houghSpace.pixels;
5136 
5137  // initialize lookup tables for sin and cos
5138  // function does initialization only for the first call
5139  InitSinCosTables();
5140 
5141  // perform hough transform
5142  const int nEdgePoints = edgePoints.GetSize();
5143  for (int i = 0; i < nEdgePoints; i++)
5144  {
5145  const Vec2d &point = edgePoints[i];
5146  const Vec2d &direction = edgeDirections[i];
5147 
5148  const float angle = atan2f(direction.y, direction.x) + FLOAT_PI;
5149  const int t0 = int(angle * FLOAT_RAD2DEG + 0.5f);
5150 
5151  const int t1 = t0 - 5;
5152  const int t2 = t0 + 5;
5153 
5154  for (int t = t1; t <= t2; t++)
5155  {
5156  const float r = point.x * cos_table[t] + point.y * sin_table[t];
5157  const int r_ = int(r + m2f);
5158 
5159  houghspace[((t + 360) % 360) * m + r_]++;
5160  }
5161  }
5162 
5163  resultLines.Clear();
5164  resultHits.Clear();
5165 
5166  // find maxima
5167  for (int nn = 0; nn < nLinesToExtract; nn++)
5168  {
5169  short max = nMinHits - 1;
5170  int best_i = -1;
5171 
5172  for (int i = 0; i < nHoughPixels; i++)
5173  {
5174  if (houghspace[i] > max)
5175  {
5176  max = houghspace[i];
5177  best_i = i;
5178  }
5179  }
5180 
5181  if (best_i != -1)
5182  {
5183  const int t = best_i / m;
5184  const int r = best_i % m;
5185 
5186  const int w = 10;
5187 
5188  for (int j = -w; j <= w; j++)
5189  {
5190  for (int k = -w; k <= w; k++)
5191  {
5192  const int r_ = r + j;
5193  int t_ = t + k;
5194 
5195  if (t_ >= n)
5196  t_ -= n;
5197 
5198  if (t_ < 0)
5199  t_ += n;
5200 
5201  if (r_ >= 0 && r_ < m)
5202  houghspace[t_ * m + r_] = 0;
5203  }
5204  }
5205 
5206  const float theta = t * FLOAT_DEG2RAD;
5207  const int r_value = r - m2;
5208 
5209  StraightLine2d line(theta, -float(r_value));
5210  resultLines.AddElement(line);
5211 
5212  if (pVisualizationImage)
5213  {
5214  //printf("%i hits: (theta, r) = (%i, %i)\n", max, t, r_value);
5215  PrimitivesDrawer::DrawLine(pVisualizationImage, line, 255, 0, 0);
5216  }
5217  }
5218  else
5219  break;
5220  }
5221 }
5222 
5223 bool ImageProcessor::HoughTransformCircles(const CVec2dArray &edgePoints, const CVec2dArray &edgeDirections, int width, int height, int rmin, int rmax, int nCirclesToExtract, int nMinHits, CVec3dArray &resultCircles, CIntArray &resultHits, CByteImage *pVisualizationImage)
5224 {
5225  // clear result list
5226  resultCircles.Clear();
5227 
5228  // perform detection
5229  CCircle2dArray resultCircles_;
5230 
5231  if (!HoughTransformCircles(edgePoints, edgeDirections, width, height, rmin, rmax, nCirclesToExtract, nMinHits, resultCircles_, resultHits, pVisualizationImage))
5232  return false;
5233 
5234  // fill result list
5235  const int nResultCircles = resultCircles_.GetSize();
5236 
5237  for (int i = 0; i < nResultCircles; i++)
5238  {
5239  const Circle2d &circle = resultCircles_[i];
5240 
5241  Vec3d x = { circle.center.x, circle.center.y, circle.radius };
5242 
5243  resultCircles.AddElement(x);
5244  }
5245 
5246  return true;
5247 }
5248 
5249 bool ImageProcessor::HoughTransformCircles(const CVec2dArray &edgePoints, const CVec2dArray &edgeDirections, int width, int height, int rmin, int rmax, int nCirclesToExtract, int nMinHits, CCircle2dArray &resultCircles, CIntArray &resultHits, CByteImage *pVisualizationImage)
5250 {
5251  // clear result list
5252  resultCircles.Clear();
5253 
5254  if (rmin > rmax)
5255  {
5256  printf("error: rmin (%i) may not be greater than rmax (%i) for ImageProcessor::HoughTransformCircles\n", rmin, rmax);
5257  return false;
5258  }
5259 
5260  const int n = width + 2 * rmax;
5261  const int m = height + 2 * rmax;
5262  const int nHoughPixels = n * m * (rmax - rmin + 1);
5263 
5264  short *houghspace = new short[nHoughPixels];
5265 
5266  // reset bins
5267  memset(houghspace, 0, nHoughPixels * sizeof(short));
5268 
5269  // initialize lookup tables for sin and cos
5270  // function does initialization only for the first call
5271  InitSinCosTables();
5272 
5273  const int mn = m * n;
5274  short *helper = houghspace - rmin * mn + rmax * m + rmax;
5275 
5276  // perform hough transform
5277  const int nEdgePoints = edgePoints.GetSize();
5278 
5279  for (int i = 0; i < nEdgePoints; i++)
5280  {
5281  const Vec2d &point = edgePoints[i];
5282  const Vec2d &direction = edgeDirections[i];
5283 
5284  const float u = point.x + 0.5f;
5285  const float v = point.y + 0.5f;
5286 
5287  const float angle = atan2f(direction.y, direction.x) + FLOAT_PI;
5288  const int t0 = int(angle * FLOAT_RAD2DEG + 0.5f);
5289 
5290  const int t1 = t0 - 3;
5291  const int t2 = t0 + 3;
5292 
5293  for (int t = t1; t <= t2; t++)
5294  {
5295  for (int r = rmin; r <= rmax; r++)
5296  {
5297  int um, vm;
5298 
5299  um = int(u + r * cos_table[t]);
5300  vm = int(v + r * sin_table[t]);
5301  helper[r * mn + um * m + vm]++;
5302 
5303  um = int(u - r * cos_table[t]);
5304  vm = int(v - r * sin_table[t]);
5305  helper[r * mn + um * m + vm]++;
5306 
5307  //const int offset = (r - rmin) * mn + (rmax + um) * m + rmax + vm;
5308  }
5309  }
5310  }
5311 
5312  resultCircles.Clear();
5313 
5314  // find maxima
5315  for (int nn = 0; nn < nCirclesToExtract; nn++)
5316  {
5317  int max = nMinHits - 1, best_i = -1;
5318 
5319  for (int i = 0; i < nHoughPixels; i++)
5320  {
5321  if (houghspace[i] > max)
5322  {
5323  max = houghspace[i];
5324  best_i = i;
5325  }
5326  }
5327 
5328  if (best_i != -1)
5329  {
5330  int um = (best_i % mn) / m;
5331  int vm = (best_i % mn) % m;
5332  int r = best_i / mn;
5333 
5334  const int w = 5;
5335 
5336  for (int j = -w; j <= w; j++)
5337  {
5338  for (int k = -w; k <= w; k++)
5339  {
5340  for (int l = -w; l <= w; l++)
5341  {
5342  int um_ = um + j;
5343  int vm_ = vm + k;
5344  int r_ = r + l;
5345 
5346  if (um_ >= 0 && um_ < n && vm >= 0 && vm < m && r_ >= 0 && r_ <= rmax - rmin)
5347  houghspace[r_ * mn + um_ * m + vm_] = 0;
5348  }
5349  }
5350  }
5351 
5352  um -= rmax;
5353  vm -= rmax;
5354  r += rmin;
5355 
5356  //printf("%i hits: (um vm) = (%i %i) -- r = = %i\n", max, um, vm, r);
5357 
5358  Circle2d circle;
5359  Math2d::SetVec(circle.center, float(um), float(vm));
5360  circle.radius = float(r);
5361 
5362  resultCircles.AddElement(circle);
5363  resultHits.AddElement(max);
5364 
5365  if (pVisualizationImage)
5366  PrimitivesDrawer::DrawCircle(pVisualizationImage, circle, 255, 0, 0, 1);
5367  }
5368  else
5369  break;
5370  }
5371 
5372  delete [] houghspace;
5373 
5374  return true;
5375 }
5376 
5377 
5378 
5379 bool ImageProcessor::DetermineHomography(const Vec2d *pSourcePoints, const Vec2d *pTargetPoints, int nPoints,
5380  float &a1, float &a2, float &a3, float &a4, float &a5, float &a6, float &a7, float &a8)
5381 {
5382  Mat3d A;
5383 
5384  if (!LinearAlgebra::DetermineHomography(pSourcePoints, pTargetPoints, nPoints, A, false))
5385  return false;
5386 
5387  a1 = A.r1;
5388  a2 = A.r2;
5389  a3 = A.r3;
5390  a4 = A.r4;
5391  a5 = A.r5;
5392  a6 = A.r6;
5393  a7 = A.r7;
5394  a8 = A.r8;
5395 
5396  return true;
5397 }
5398 
5399 bool ImageProcessor::DetermineAffineTransformation(const Vec2d *pSourcePoints, const Vec2d *pTargetPoints, int nPoints,
5400  float &a1, float &a2, float &a3, float &a4, float &a5, float &a6)
5401 {
5402  Mat3d A;
5403 
5404  if (!LinearAlgebra::DetermineAffineTransformation(pSourcePoints, pTargetPoints, nPoints, A, false))
5405  return false;
5406 
5407  a1 = A.r1;
5408  a2 = A.r2;
5409  a3 = A.r3;
5410  a4 = A.r4;
5411  a5 = A.r5;
5412  a6 = A.r6;
5413 
5414  return true;
5415 }
5416 
5417 
5418 bool ImageProcessor::NormalizeColor(const CByteImage *pInputImage, CByteImage *pOutputImage)
5419 {
5420  if (pInputImage->type == CByteImage::eGrayScale)
5421  {
5422  printf("error: input image must be of type CByteImage::eRGB24 or CByteImage::eRGB24Split for ImageProcessor::NormalizeColor\n");
5423  return false;
5424  }
5425 
5426  if (!pInputImage->IsCompatible(pOutputImage))
5427  {
5428  printf("error: input and output image must be of same size and type for ImageProcessor::NormalizeColor\n");
5429  return false;
5430  }
5431 
5432  const int nPixels = pInputImage->width * pInputImage->height;
5433  const unsigned char *input = pInputImage->pixels;
5434 
5435  int redHistogram[256], greenHistogram[256], blueHistogram[256];
5436  float fNormalizeConstant = 255.0f / nPixels;
5437  int i;
5438 
5439  // reset bins
5440  memset(redHistogram, 0, sizeof(redHistogram));
5441  memset(greenHistogram, 0, sizeof(greenHistogram));
5442  memset(blueHistogram, 0, sizeof(blueHistogram));
5443 
5444  // calculate histograms
5445  int offset = 0;
5446  for (i = 0; i < nPixels; i++)
5447  {
5448  redHistogram[input[offset]]++;
5449  greenHistogram[input[offset + 1]]++;
5450  blueHistogram[input[offset + 2]]++;
5451  offset += 3;
5452  }
5453 
5454  // normalize histograms
5455  for (i = 1; i < 256; i++)
5456  {
5457  redHistogram[i] += redHistogram[i - 1];
5458  greenHistogram[i] += greenHistogram[i - 1];
5459  blueHistogram[i] += blueHistogram[i - 1];
5460  }
5461 
5462  for (i = 0; i < 256; i++)
5463  {
5464  redHistogram[i] = int(redHistogram[i] * fNormalizeConstant + 0.5f);
5465  greenHistogram[i] = int(greenHistogram[i] * fNormalizeConstant + 0.5f);
5466  blueHistogram[i] = int(blueHistogram[i] * fNormalizeConstant + 0.5f);
5467  }
5468 
5469  // apply normalization
5470  if (pInputImage->type == CByteImage::eRGB24)
5471  {
5472  const unsigned char *input_r = input;
5473  const unsigned char *input_g = input_r + nPixels;
5474  const unsigned char *input_b = input_g + nPixels;
5475  unsigned char *output_r = pOutputImage->pixels;
5476  unsigned char *output_g = output_r + nPixels;
5477  unsigned char *output_b = output_g + nPixels;
5478 
5479  for (int i = 0; i < nPixels; i++)
5480  {
5481  output_r[i] = redHistogram[input_r[i]];
5482  output_g[i] = greenHistogram[input_g[i]];
5483  output_b[i] = blueHistogram[input_b[i]];
5484  }
5485  }
5486  else if (pInputImage->type == CByteImage::eRGB24Split)
5487  {
5488  unsigned char *output = pOutputImage->pixels;
5489 
5490  for (int i = 0, offset = 0; i < nPixels; i++, offset += 3)
5491  {
5492  output[offset] = redHistogram[input[offset]];
5493  output[offset + 1] = greenHistogram[input[offset + 1]];
5494  output[offset + 2] = blueHistogram[input[offset + 2]];
5495  }
5496  }
5497 
5498  return true;
5499 }
5500 
5501 
5502 bool ImageProcessor::GaussianSmooth5x5(const CFloatMatrix *pInputImage, CFloatMatrix *pOutputImage, float fVariance)
5503 {
5504  if (pInputImage->columns != pOutputImage->columns || pInputImage->rows != pOutputImage->rows)
5505  {
5506  printf("error: input and output matrix do not match for ImageProcessor::GaussianSmooth5x5\n");
5507  return false;
5508  }
5509 
5510  if (pInputImage->columns < 5 || pInputImage->rows < 5)
5511  {
5512  printf("error: matrices must be at least of size 5x5 for ImageProcessor::GaussianSmooth5x5\n");
5513  return false;
5514  }
5515 
5516  const int nKernelSize = 5;
5517  const int k = 2;//(nKernelSize - 1) / 2;
5518 
5519  float pFilter[nKernelSize];
5520  float sum = 0.0f;
5521  int i;
5522 
5523  // construct Gaussian kernel
5524  for (i = 0; i < nKernelSize; i++)
5525  {
5526  pFilter[i] = expf(-(i - k) * (i - k) / (2.0f * fVariance));
5527  sum += pFilter[i];
5528  }
5529 
5530  for (i = 0; i < nKernelSize; i++)
5531  pFilter[i] /= sum;
5532 
5533  // create temp image if necessary
5534  CFloatMatrix *pTempImage = new CFloatMatrix(pInputImage);
5535 
5536  const int width = pInputImage->columns;
5537  const int height = pInputImage->rows;
5538 
5539  const float *input = pInputImage->data;
5540  float *temp = pTempImage->data;
5541  float *output = pOutputImage->data;
5542 
5543  int x, y, d, offset;
5544 
5545 
5546  // x direction
5547  for (y = 0, offset = 0; y < height; y++)
5548  {
5549  for (x = 0; x < k; x++, offset++)
5550  {
5551  float sum = 0.0f;
5552 
5553  for (d = -k; d < -x; d++)
5554  sum += input[y * width] * pFilter[k + d];
5555 
5556  for (d = -x; d <= k; d++)
5557  sum += input[offset + d] * pFilter[k + d];
5558 
5559  temp[offset] = sum;
5560  }
5561 
5562  // core loop (optimized)
5563  for (x = 2 * k; x < width; x++, offset++)
5564  {
5565  temp[offset] =
5566  input[offset - 2] * pFilter[0] +
5567  input[offset - 1] * pFilter[1] +
5568  input[offset] * pFilter[2] +
5569  input[offset + 1] * pFilter[3] +
5570  input[offset + 2] * pFilter[4];
5571  }
5572 
5573  for (x = width - k; x < width; x++, offset++)
5574  {
5575  float sum = 0.0f;
5576 
5577  for (d = -k; d < width - x; d++)
5578  sum += input[offset + d] * pFilter[k + d];
5579 
5580  for (d = width - x; d <= k; d++)
5581  sum += input[y * width + width - 1] * pFilter[k + d];
5582 
5583  temp[offset] = sum;
5584  }
5585  }
5586 
5587 
5588  // y direction
5589  for (y = 0; y < k; y++)
5590  {
5591  for (x = 0; x < width; x++)
5592  {
5593  float sum = 0.0f;
5594 
5595  for (d = -k; d < -y; d++)
5596  sum += temp[x] * pFilter[k + d];
5597 
5598  for (d = -y; d <= k; d++)
5599  sum += temp[(y + d) * width + x] * pFilter[k + d];
5600 
5601  output[y * width + x] = sum;
5602  }
5603  }
5604 
5605  // core loop (optimized)
5606  for (y = 2 * k, offset = k * width; y < height; y++)
5607  {
5608  for (x = 0; x < width; x++, offset++)
5609  {
5610  output[offset] =
5611  temp[offset - (width << 1)] * pFilter[0] +
5612  temp[offset - width] * pFilter[1] +
5613  temp[offset] * pFilter[2] +
5614  temp[offset + width] * pFilter[3] +
5615  temp[offset + (width << 1)] * pFilter[4];
5616  }
5617  }
5618 
5619  for (y = height - k; y < height; y++)
5620  {
5621  for (x = 0; x < width; x++)
5622  {
5623  float sum = 0.0f;
5624 
5625  for (d = -k; d < height - y; d++)
5626  sum += temp[(y + d) * width + x] * pFilter[k + d];
5627 
5628  for (d = height - y; d < k; d++)
5629  sum += temp[(height - 1) * width + x] * pFilter[k + d];
5630 
5631  output[y * width + x] = sum;
5632  }
5633  }
5634 
5635 
5636  delete pTempImage;
5637 
5638  return true;
5639 }
5640 
5641 bool ImageProcessor::GaussianSmooth(const CByteImage *pInputImage, CByteImage *pOutputImage, float fVariance, int nKernelSize)
5642 {
5643  if (pInputImage->width != pOutputImage->width || pInputImage->height != pOutputImage->height || pInputImage->type != pOutputImage->type || pInputImage->type != CByteImage::eGrayScale)
5644  {
5645  printf("error: input and output image do not match for ImageProcessor::GaussianSmooth\n");
5646  return false;
5647  }
5648 
5649  if (pInputImage->width < nKernelSize || pInputImage->height < nKernelSize)
5650  {
5651  printf("error: image must be at least of size nKernelSize x nKernelSize for ImageProcessor::GaussianSmooth\n");
5652  return false;
5653  }
5654 
5655  const int k = (nKernelSize - 1) / 2;
5656 
5657  float *pFilter = new float[nKernelSize];
5658  float sum = 0.0f;
5659  int i;
5660 
5661  // construct Gaussian kernel
5662  for (i = 0; i < nKernelSize; i++)
5663  {
5664  pFilter[i] = expf(-(i - k) * (i - k) / (2.0f * fVariance));
5665  sum += pFilter[i];
5666  }
5667 
5668  for (i = 0; i < nKernelSize; i++)
5669  pFilter[i] /= sum;
5670 
5671  // create temp image
5672  CByteImage *pTempImage = new CByteImage(pInputImage);
5673 
5674  const int width = pInputImage->width;
5675  const int height = pInputImage->height;
5676 
5677  const unsigned char *input = pInputImage->pixels;
5678  unsigned char *temp = pTempImage->pixels;
5679  unsigned char *output = pOutputImage->pixels;
5680 
5681  int x, y, d, offset;
5682 
5683 
5684  // x direction
5685  for (y = 0, offset = 0; y < height; y++)
5686  {
5687  for (x = 0; x < k; x++, offset++)
5688  {
5689  float sum = 0.0f;
5690 
5691  for (d = -k; d < -x; d++)
5692  sum += input[y * width] * pFilter[k + d];
5693 
5694  for (d = -x; d <= k; d++)
5695  sum += input[offset + d] * pFilter[k + d];
5696 
5697  temp[offset] = (unsigned char) (sum + 0.5f);
5698  }
5699 
5700  // core loop
5701  for (x = 2 * k; x < width; x++, offset++)
5702  {
5703  float sum = 0.0f;
5704 
5705  for (d = -k; d <= k; d++)
5706  sum += input[offset + d] * pFilter[d + k];
5707 
5708  temp[offset] = (unsigned char) (sum + 0.5f);
5709  }
5710 
5711  for (x = width - k; x < width; x++, offset++)
5712  {
5713  float sum = 0.0f;
5714 
5715  for (d = -k; d < width - x; d++)
5716  sum += input[offset + d] * pFilter[k + d];
5717 
5718  for (d = width - x; d <= k; d++)
5719  sum += input[y * width + width - 1] * pFilter[k + d];
5720 
5721  temp[offset] = (unsigned char) (sum + 0.5f);
5722  }
5723  }
5724 
5725 
5726  // y direction
5727  for (y = 0; y < k; y++)
5728  {
5729  for (x = 0; x < width; x++)
5730  {
5731  float sum = 0.0f;
5732 
5733  for (d = -k; d < -y; d++)
5734  sum += temp[x] * pFilter[k + d];
5735 
5736  for (d = -y; d <= k; d++)
5737  sum += temp[(y + d) * width + x] * pFilter[k + d];
5738 
5739  output[y * width + x] = (unsigned char) (sum + 0.5f);
5740  }
5741  }
5742 
5743  // core loop
5744  for (y = 2 * k, offset = k * width; y < height; y++)
5745  {
5746  for (x = 0; x < width; x++, offset++)
5747  {
5748  float sum = 0.0f;
5749 
5750  for (d = -k; d <= k; d++)
5751  sum += temp[offset + d * width] * pFilter[d + k];
5752 
5753  output[offset] = (unsigned char) (sum + 0.5f);
5754  }
5755  }
5756 
5757  for (y = height - k; y < height; y++)
5758  {
5759  for (x = 0; x < width; x++)
5760  {
5761  float sum = 0.0f;
5762 
5763  for (d = -k; d < height - y; d++)
5764  sum += temp[(y + d) * width + x] * pFilter[k + d];
5765 
5766  for (d = height - y; d < k; d++)
5767  sum += temp[(height - 1) * width + x] * pFilter[k + d];
5768 
5769  output[y * width + x] = (unsigned char) (sum + 0.5f);
5770  }
5771  }
5772 
5773 
5774  delete pTempImage;
5775  delete [] pFilter;
5776 
5777  return true;
5778 }
5779 
5780 bool ImageProcessor::GaussianSmooth(const CByteImage *pInputImage, CFloatMatrix *pOutputImage, float fVariance, int nKernelSize)
5781 {
5782  if (pInputImage->width != pOutputImage->columns || pInputImage->height != pOutputImage->rows || pInputImage->type != CByteImage::eGrayScale)
5783  {
5784  printf("error: input image and output matrix do not match for ImageProcessor::GaussianSmooth\n");
5785  return false;
5786  }
5787 
5788  if (pInputImage->width < nKernelSize || pInputImage->height < nKernelSize)
5789  {
5790  printf("error: image must be at least of size nKernelSize x nKernelSize for ImageProcessor::GaussianSmooth\n");
5791  return false;
5792  }
5793 
5794  const int k = (nKernelSize - 1) / 2;
5795 
5796  float *pFilter = new float[nKernelSize];
5797  float sum = 0.0f;
5798  int i;
5799 
5800  // construct Gaussian kernel
5801  for (i = 0; i < nKernelSize; i++)
5802  {
5803  pFilter[i] = expf(-(i - k) * (i - k) / (2.0f * fVariance));
5804  sum += pFilter[i];
5805  }
5806 
5807  for (i = 0; i < nKernelSize; i++)
5808  pFilter[i] /= sum;
5809 
5810  // create temp image
5811  CFloatMatrix *pTempImage = new CFloatMatrix(pInputImage->width, pInputImage->height);
5812 
5813  const int width = pInputImage->width;
5814  const int height = pInputImage->height;
5815 
5816  const unsigned char *input = pInputImage->pixels;
5817  float *temp = pTempImage->data;
5818  float *output = pOutputImage->data;
5819 
5820  int x, y, d, offset;
5821 
5822 
5823  // x direction
5824  for (y = 0, offset = 0; y < height; y++)
5825  {
5826  for (x = 0; x < k; x++, offset++)
5827  {
5828  float sum = 0.0f;
5829 
5830  for (d = -k; d < -x; d++)
5831  sum += input[y * width] * pFilter[k + d];
5832 
5833  for (d = -x; d <= k; d++)
5834  sum += input[offset + d] * pFilter[k + d];
5835 
5836  temp[offset] = sum;
5837  }
5838 
5839  // core loop
5840  for (x = 2 * k; x < width; x++, offset++)
5841  {
5842  float sum = 0.0f;
5843 
5844  for (d = -k; d <= k; d++)
5845  sum += input[offset + d] * pFilter[d + k];
5846 
5847  temp[offset] = sum;
5848  }
5849 
5850  for (x = width - k; x < width; x++, offset++)
5851  {
5852  float sum = 0.0f;
5853 
5854  for (d = -k; d < width - x; d++)
5855  sum += input[offset + d] * pFilter[k + d];
5856 
5857  for (d = width - x; d <= k; d++)
5858  sum += input[y * width + width - 1] * pFilter[k + d];
5859 
5860  temp[offset] = sum;
5861  }
5862  }
5863 
5864 
5865  // y direction
5866  for (y = 0; y < k; y++)
5867  {
5868  for (x = 0; x < width; x++)
5869  {
5870  float sum = 0.0f;
5871 
5872  for (d = -k; d < -y; d++)
5873  sum += temp[x] * pFilter[k + d];
5874 
5875  for (d = -y; d <= k; d++)
5876  sum += temp[(y + d) * width + x] * pFilter[k + d];
5877 
5878  output[y * width + x] = sum;
5879  }
5880  }
5881 
5882  // core loop
5883  for (y = 2 * k, offset = k * width; y < height; y++)
5884  {
5885  for (x = 0; x < width; x++, offset++)
5886  {
5887  float sum = 0.0f;
5888 
5889  for (d = -k; d <= k; d++)
5890  sum += temp[offset + d * width] * pFilter[d + k];
5891 
5892  output[offset] = sum;
5893  }
5894  }
5895 
5896  for (y = height - k; y < height; y++)
5897  {
5898  for (x = 0; x < width; x++)
5899  {
5900  float sum = 0.0f;
5901 
5902  for (d = -k; d < height - y; d++)
5903  sum += temp[(y + d) * width + x] * pFilter[k + d];
5904 
5905  for (d = height - y; d < k; d++)
5906  sum += temp[(height - 1) * width + x] * pFilter[k + d];
5907 
5908  output[y * width + x] = sum;
5909  }
5910  }
5911 
5912 
5913  delete pTempImage;
5914  delete [] pFilter;
5915 
5916  return true;
5917 }
5918 
5919 bool ImageProcessor::GaussianSmooth(const CFloatMatrix *pInputImage, CFloatMatrix *pOutputImage, float fVariance, int nKernelSize)
5920 {
5921  if (pInputImage->columns != pOutputImage->columns || pInputImage->rows != pOutputImage->rows)
5922  {
5923  printf("error: input and output matrix do not match for ImageProcessor::GaussianSmooth\n");
5924  return false;
5925  }
5926 
5927  if (pInputImage->columns < nKernelSize || pInputImage->rows < nKernelSize)
5928  {
5929  printf("error: matrix must be at least of size nKernelSize x nKernelSize for ImageProcessor::GaussianSmooth\n");
5930  return false;
5931  }
5932 
5933  const int k = (nKernelSize - 1) / 2;
5934 
5935  float *pFilter = new float[nKernelSize];
5936  float sum = 0.0f;
5937  int i;
5938 
5939  // construct Gaussian kernel
5940  for (i = 0; i < nKernelSize; i++)
5941  {
5942  pFilter[i] = expf(-(i - k) * (i - k) / (2.0f * fVariance));
5943  sum += pFilter[i];
5944  }
5945 
5946  for (i = 0; i < nKernelSize; i++)
5947  pFilter[i] /= sum;
5948 
5949  // create temp image
5950  CFloatMatrix *pTempImage = new CFloatMatrix(pInputImage);
5951 
5952  const int width = pInputImage->columns;
5953  const int height = pInputImage->rows;
5954 
5955  const float *input = pInputImage->data;
5956  float *temp = pTempImage->data;
5957  float *output = pOutputImage->data;
5958 
5959  int x, y, d, offset;
5960 
5961 
5962  // x direction
5963  for (y = 0, offset = 0; y < height; y++)
5964  {
5965  for (x = 0; x < k; x++, offset++)
5966  {
5967  float sum = 0.0f;
5968 
5969  for (d = -k; d < -x; d++)
5970  sum += input[y * width] * pFilter[k + d];
5971 
5972  for (d = -x; d <= k; d++)
5973  sum += input[offset + d] * pFilter[k + d];
5974 
5975  temp[offset] = sum;
5976  }
5977 
5978  // core loop