GaussBackground.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5  *
6  * ArmarX is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * ArmarX is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * @package
19  * @author
20  * @date
21  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22  * GNU General Public License
23  */
24 
25 #include "GaussBackground.h"
26 
28 
30 #include <Image/ImageProcessor.h>
31 
32 CGaussBackground::CGaussBackground(const int nWidth, const int nHeight)
33 {
34  m_pPixelProbabilityDistributions = new CPixelProbabilityDistribution[nWidth * nHeight];
35  m_nImageWidth = nWidth;
36  m_nImageHeight = nHeight;
37 
38  m_pBackgroundRGB = new CByteImage(nWidth, nHeight, CByteImage::eRGB24);
39 }
40 
42 {
43  delete m_pPixelProbabilityDistributions;
44  delete m_pBackgroundRGB;
45 }
46 
47 inline double
48 DetMat2d(const Mat2d m)
49 {
50  return m.r1 * m.r4 - m.r3 * m.r2;
51 }
52 
53 void
54 CGaussBackground::LearnBackgroundRGB(CByteImage** pRGBImages, const int nNumImages)
55 {
56  CByteImage** pHSVImages = new CByteImage*[nNumImages];
57  CByteImage* pSmoothedImage = new CByteImage(m_nImageWidth, m_nImageHeight, CByteImage::eRGB24);
58 
59  for (int i = 0; i < nNumImages; i++)
60  {
61  pHSVImages[i] = new CByteImage(m_nImageWidth, m_nImageHeight, CByteImage::eRGB24);
62  ImageProcessor::GaussianSmooth3x3(pRGBImages[i], pSmoothedImage);
63  ImageProcessor::CalculateHSVImage(pSmoothedImage, pHSVImages[i]);
64  }
65 
66  LearnBackground(pHSVImages, nNumImages);
67 
68  ImageProcessor::CopyImage(pRGBImages[0], m_pBackgroundRGB);
69 
70  for (int i = 0; i < nNumImages; i++)
71  {
72  delete pHSVImages[i];
73  }
74 
75  delete[] pHSVImages;
76  delete pSmoothedImage;
77 }
78 
79 void
80 CGaussBackground::LearnBackground(CByteImage** pHSVImages, const int nNumImages)
81 {
82  if ((pHSVImages[0]->width != m_nImageWidth) || (pHSVImages[0]->height != m_nImageHeight))
83  {
84  ARMARX_WARNING_S << "CGaussBackground::LearnBackground: Image dimensions do not fit!";
85  return;
86  }
87 
88  if (nNumImages < 1)
89  {
90  ARMARX_WARNING_S << "CGaussBackground::LearnBackground: Need at least one image!";
91  return;
92  }
93 
94  CPixelProbabilityDistribution* pPPD = m_pPixelProbabilityDistributions;
96  new CPixelProbabilityDistribution[m_nImageWidth * m_nImageHeight];
97 
98  // reset all pixels
99  for (int j = 0; j < m_nImageWidth * m_nImageHeight; j++)
100  {
101  Math2d::SetVec(pPPD[j].vMean, 0, 0);
102  pPPD[j].mCovariance.r1 = 0;
103  pPPD[j].mCovariance.r2 = 0;
104  pPPD[j].mCovariance.r3 = 0;
105  pPPD[j].mCovariance.r4 = 0;
106 
107  Math2d::SetVec(pShPPD[j].vMean, 0, 0);
108  pShPPD[j].mCovariance.r1 = 0;
109  pShPPD[j].mCovariance.r2 = 0;
110  pShPPD[j].mCovariance.r3 = 0;
111  pShPPD[j].mCovariance.r4 = 0;
112  }
113 
114 
115  // calculate means
116  //ARMARX_VERBOSE_S << "Calculating means";
117  for (int i = 0; i < nNumImages; i++)
118  {
119  //if (i%10==0) ARMARX_VERBOSE_S << " Image %d of %d\n", i, nNumImages);
120  for (int j = 0; j < m_nImageWidth * m_nImageHeight; j++)
121  {
122  pPPD[j].vMean.x += pHSVImages[i]->pixels[3 * j];
123  pPPD[j].vMean.y += pHSVImages[i]->pixels[3 * j + 1];
124 
125  pShPPD[j].vMean.x += (pHSVImages[i]->pixels[3 * j] + 128) % 256;
126  pShPPD[j].vMean.y += pHSVImages[i]->pixels[3 * j + 1];
127  }
128  }
129 
130  for (int j = 0; j < m_nImageWidth * m_nImageHeight; j++)
131  {
132  pPPD[j].vMean.x /= nNumImages;
133  pPPD[j].vMean.y /= nNumImages;
134 
135  pShPPD[j].vMean.x /= nNumImages;
136  pShPPD[j].vMean.y /= nNumImages;
137  }
138 
139 
140  // calculate covariance matrices
141  if (nNumImages > 1)
142  {
143  //ARMARX_VERBOSE_S << " -- Calculating covariance matrices --\n");
144  for (int i = 0; i < nNumImages; i++)
145  {
146  //if (i%10==0) ARMARX_VERBOSE_S << " Image %d of %d\n", i, nNumImages);
147  for (int j = 0; j < m_nImageWidth * m_nImageHeight; j++)
148  {
149  pPPD[j].mCovariance.r1 += (pHSVImages[i]->pixels[3 * j] - pPPD[j].vMean.x) *
150  (pHSVImages[i]->pixels[3 * j] - pPPD[j].vMean.x);
151  pPPD[j].mCovariance.r2 += (pHSVImages[i]->pixels[3 * j] - pPPD[j].vMean.x) *
152  (pHSVImages[i]->pixels[3 * j + 1] - pPPD[j].vMean.y);
153  pPPD[j].mCovariance.r3 += (pHSVImages[i]->pixels[3 * j + 1] - pPPD[j].vMean.y) *
154  (pHSVImages[i]->pixels[3 * j] - pPPD[j].vMean.x);
155  pPPD[j].mCovariance.r4 += (pHSVImages[i]->pixels[3 * j + 1] - pPPD[j].vMean.y) *
156  (pHSVImages[i]->pixels[3 * j + 1] - pPPD[j].vMean.y);
157 
158  pShPPD[j].mCovariance.r1 +=
159  (((pHSVImages[i]->pixels[3 * j] + 128) % 256) - pShPPD[j].vMean.x) *
160  (((pHSVImages[i]->pixels[3 * j] + 128) % 256) - pShPPD[j].vMean.x);
161  pShPPD[j].mCovariance.r2 +=
162  (((pHSVImages[i]->pixels[3 * j] + 128) % 256) - pShPPD[j].vMean.x) *
163  (pHSVImages[i]->pixels[3 * j + 1] - pShPPD[j].vMean.y);
164  pShPPD[j].mCovariance.r3 +=
165  (pHSVImages[i]->pixels[3 * j + 1] - pShPPD[j].vMean.y) *
166  (((pHSVImages[i]->pixels[3 * j] + 128) % 256) - pShPPD[j].vMean.x);
167  pShPPD[j].mCovariance.r4 += (pHSVImages[i]->pixels[3 * j + 1] - pShPPD[j].vMean.y) *
168  (pHSVImages[i]->pixels[3 * j + 1] - pShPPD[j].vMean.y);
169  }
170  }
171 
172  const float fFactor = 1.0f / (float)(nNumImages - 1);
173 
174  for (int j = 0; j < m_nImageWidth * m_nImageHeight; j++)
175  {
176  pPPD[j].mCovariance.r1 *= fFactor;
177  pPPD[j].mCovariance.r2 *= fFactor;
178  pPPD[j].mCovariance.r3 *= fFactor;
179  pPPD[j].mCovariance.r4 *= fFactor;
180 
181  pShPPD[j].mCovariance.r1 *= fFactor;
182  pShPPD[j].mCovariance.r2 *= fFactor;
183  pShPPD[j].mCovariance.r3 *= fFactor;
184  pShPPD[j].mCovariance.r4 *= fFactor;
185 
186 
187  // if shifted version has smaller variance, use it
188  if (DetMat2d(pPPD[j].mCovariance) > DetMat2d(pShPPD[j].mCovariance))
189  {
190  pPPD[j].vMean.x = pShPPD[j].vMean.x;
191  pPPD[j].vMean.y = pShPPD[j].vMean.y;
192 
193  pPPD[j].mCovariance.r1 = pShPPD[j].mCovariance.r1;
194  pPPD[j].mCovariance.r2 = pShPPD[j].mCovariance.r2;
195  pPPD[j].mCovariance.r3 = pShPPD[j].mCovariance.r3;
196  pPPD[j].mCovariance.r4 = pShPPD[j].mCovariance.r4;
197 
198  pPPD[j].bShifted = true;
199  }
200  else
201  {
202  pPPD[j].bShifted = false;
203  }
204 
205  // smooth variance
206  pPPD[j].mCovariance.r1 += 1;
207  pPPD[j].mCovariance.r4 += 1;
208 
209  //if (j%10000==0) ARMARX_VERBOSE_S << "Matrix: (%.2f %.2f %.2f %.2f)\n", pPPD[j].mCovariance.r1, pPPD[j].mCovariance.r2, pPPD[j].mCovariance.r3, pPPD[j].mCovariance.r4);
210 
211  // invert the covariance matrix for faster evaluation
212  Math2d::Invert(pPPD[j].mCovariance, pPPD[j].mCovariance);
213 
214  pPPD[j].fSatWeight = exp(-pPPD[j].vMean.y * pPPD[j].vMean.y / 6400);
215  }
216  }
217  else
218  {
219  for (int j = 0; j < m_nImageWidth * m_nImageHeight; j++)
220  {
221  pPPD[j].mCovariance.r1 = 1.0f / 264.0f;
222  pPPD[j].mCovariance.r2 = 0;
223  pPPD[j].mCovariance.r3 = 0;
224  pPPD[j].mCovariance.r4 = 1.0f / 264.0f;
225 
226  pPPD[j].fSatWeight = exp(-pPPD[j].vMean.y * pPPD[j].vMean.y / 6400);
227 
228  if (pPPD[j].vMean.x < 64 || pPPD[j].vMean.x > 192)
229  {
230  pPPD[j].vMean.x = ((int)pPPD[j].vMean.x + 128) % 256;
231  pPPD[j].bShifted = true;
232  }
233  else
234  {
235  pPPD[j].bShifted = false;
236  }
237  }
238  }
239 
240  delete[] pShPPD;
241 }
242 
243 inline double
244 CGaussBackground::CalcProbOfPixel(const int nIndex, const Vec2d vHS_Value)
245 {
246  double dTemp0;
247  Vec2d vTemp1, vTemp2;
248 
249  // v = pixel value - mean
250  Math2d::SubtractVecVec(vHS_Value, m_pPixelProbabilityDistributions[nIndex].vMean, vTemp1);
251  // d = v^T*Sigma^-1*v
252  Math2d::MulMatVec(m_pPixelProbabilityDistributions[nIndex].mCovariance, vTemp1, vTemp2);
253  dTemp0 = Math2d::ScalarProduct(vTemp1, vTemp2);
254 
255  return exp(-0.03 *
256  (m_pPixelProbabilityDistributions[nIndex].fSatWeight * (vTemp1.y * vTemp1.y) *
257  m_pPixelProbabilityDistributions[nIndex].mCovariance.r4 +
258  (1 - m_pPixelProbabilityDistributions[nIndex].fSatWeight) * dTemp0));
259  //return exp(-0.05 * dTemp0);
260 }
261 
262 void
263 CGaussBackground::BinarizeAndFillHoles(CByteImage* pProbabilityImage)
264 {
265  //char pFileName[] = "/home/staff/schieben/datalog/bg0.bmp";
266  //pProbabilityImage->SaveToFile(pFileName);
267 
268  // binarize
269  for (int i = 0; i < m_nImageWidth * m_nImageHeight; i++)
270  {
271  pProbabilityImage->pixels[i] =
272  (pProbabilityImage->pixels[i] > OLP_FOREGROUND_THRESHOLD) ? 255 : 0;
273  }
274 
275 
276  // remove noise
277  CByteImage* pTempImage = new CByteImage(m_nImageWidth, m_nImageHeight, CByteImage::eGrayScale);
278  ImageProcessor::Dilate(pProbabilityImage, pTempImage);
279  ImageProcessor::Erode(pTempImage, pProbabilityImage);
280  ImageProcessor::Erode(pProbabilityImage, pTempImage);
281  ImageProcessor::Erode(pTempImage, pProbabilityImage);
282  ImageProcessor::Erode(pProbabilityImage, pTempImage);
283  ImageProcessor::Dilate(pTempImage, pProbabilityImage);
284  ImageProcessor::Dilate(pProbabilityImage, pTempImage);
285  ImageProcessor::Dilate(pTempImage, pProbabilityImage);
286  ImageProcessor::Dilate(pProbabilityImage, pTempImage);
287  ImageProcessor::Dilate(pTempImage, pProbabilityImage);
288 
289 
290  // fill holes
291  for (int i = 0; i < 1; i++) // 1
292  {
293  FillHolesHorVertDiag(pProbabilityImage, pTempImage);
294  FillHolesHorVertDiag(pTempImage, pProbabilityImage);
295  //pFileName[31] = '1'+i;
296  //pProbabilityImage->SaveToFile(pFileName);
297  }
298 
299  for (int i = 0; i < 2; i++) // 2
300  {
301  FillHolesHorVert(pProbabilityImage, pTempImage);
302  FillHolesDiag(pTempImage, pProbabilityImage);
303  //pFileName[31] = '2'+i;
304  //pProbabilityImage->SaveToFile(pFileName);
305  }
306 
307  // extend foreground regions
308  for (int i = 0; i < 0; i++) // 1
309  {
310  ImageProcessor::Dilate(pProbabilityImage, pTempImage);
311  ImageProcessor::Dilate(pTempImage, pProbabilityImage);
312  }
313 
314  delete pTempImage;
315 }
316 
317 inline void
318 CGaussBackground::FillHolesHorVert(const CByteImage* pInputImage,
319  CByteImage* pOutputImage,
320  const int nRadius)
321 {
322  for (int i = 0; i < m_nImageWidth * m_nImageHeight; i++)
323  {
324  pOutputImage->pixels[i] = pInputImage->pixels[i];
325  }
326 
327 #pragma omp parallel for schedule(static, 100)
328  for (int i = 0; i < m_nImageWidth * m_nImageHeight; i++)
329  {
330  if (pOutputImage->pixels[i] == 0)
331  {
332  // check for white pixels in all directions
333  int nPixelSumsInDirections[4] = {0, 0, 0, 0};
334  int nIndex;
335 
336  for (int j = 1; j <= nRadius; j++)
337  {
338  // to the left
339  nIndex = i - j;
340 
341  if (nIndex >= 0)
342  {
343  nPixelSumsInDirections[0] += pInputImage->pixels[nIndex];
344  }
345 
346  // to the right
347  nIndex = i + j;
348 
349  if (nIndex < m_nImageWidth * m_nImageHeight)
350  {
351  nPixelSumsInDirections[1] += pInputImage->pixels[nIndex];
352  }
353 
354  // upwards
355  nIndex = i - j * m_nImageWidth;
356 
357  if (nIndex >= 0)
358  {
359  nPixelSumsInDirections[2] += pInputImage->pixels[nIndex];
360  }
361 
362  // downwards
363  nIndex = i + j * m_nImageWidth;
364 
365  if (nIndex < m_nImageWidth * m_nImageHeight)
366  {
367  nPixelSumsInDirections[3] += pInputImage->pixels[nIndex];
368  }
369  }
370 
371  if (nPixelSumsInDirections[0] * nPixelSumsInDirections[1] * nPixelSumsInDirections[2] *
372  nPixelSumsInDirections[3] !=
373  0)
374  {
375  pOutputImage->pixels[i] = 255;
376  }
377  }
378  }
379 }
380 
381 inline void
382 CGaussBackground::FillHolesDiag(const CByteImage* pInputImage,
383  CByteImage* pOutputImage,
384  const int nRadius)
385 {
386  for (int i = 0; i < m_nImageWidth * m_nImageHeight; i++)
387  {
388  pOutputImage->pixels[i] = pInputImage->pixels[i];
389  }
390 
391 #pragma omp parallel for schedule(static, 100)
392  for (int i = 0; i < m_nImageWidth * m_nImageHeight; i++)
393  {
394  if (pOutputImage->pixels[i] == 0)
395  {
396  // check for white pixels in all directions
397  int nPixelSumsInDirections[4] = {0, 0, 0, 0};
398  int nIndex;
399 
400  for (int j = 1; j <= nRadius; j++)
401  {
402  // to the left and up
403  nIndex = i - j * m_nImageWidth - j;
404 
405  if (nIndex >= 0)
406  {
407  nPixelSumsInDirections[0] += pInputImage->pixels[nIndex];
408  }
409 
410  // to the right and up
411  nIndex = i - j * m_nImageWidth + j;
412 
413  if (nIndex >= 0)
414  {
415  nPixelSumsInDirections[1] += pInputImage->pixels[nIndex];
416  }
417 
418  // to the left and down
419  nIndex = i + j * m_nImageWidth - j;
420 
421  if (nIndex < m_nImageWidth * m_nImageHeight)
422  {
423  nPixelSumsInDirections[2] += pInputImage->pixels[nIndex];
424  }
425 
426  // to the right and down
427  nIndex = i + j * m_nImageWidth + j;
428 
429  if (nIndex < m_nImageWidth * m_nImageHeight)
430  {
431  nPixelSumsInDirections[3] += pInputImage->pixels[nIndex];
432  }
433  }
434 
435  if (nPixelSumsInDirections[0] * nPixelSumsInDirections[1] * nPixelSumsInDirections[2] *
436  nPixelSumsInDirections[3] !=
437  0)
438  {
439  pOutputImage->pixels[i] = 255;
440  }
441  }
442  }
443 }
444 
445 inline void
446 CGaussBackground::FillHolesHorVertDiag(const CByteImage* pInputImage,
447  CByteImage* pOutputImage,
448  const int nRadius)
449 {
450  for (int i = 0; i < m_nImageWidth * m_nImageHeight; i++)
451  {
452  pOutputImage->pixels[i] = pInputImage->pixels[i];
453  }
454 
455 #pragma omp parallel for schedule(static, 100)
456  for (int i = 0; i < m_nImageWidth * m_nImageHeight; i++)
457  {
458  if (pOutputImage->pixels[i] == 0)
459  {
460  // check for white pixels in all directions
461  int nPixelSumsInDirections[8] = {0, 0, 0, 0, 0, 0, 0, 0};
462  int nIndex;
463 
464  for (int j = 1; j <= nRadius; j++)
465  {
466  // to the left
467  nIndex = i - j;
468 
469  if (nIndex >= 0)
470  {
471  nPixelSumsInDirections[0] += pInputImage->pixels[nIndex];
472  }
473 
474  // to the right
475  nIndex = i + j;
476 
477  if (nIndex < m_nImageWidth * m_nImageHeight)
478  {
479  nPixelSumsInDirections[1] += pInputImage->pixels[nIndex];
480  }
481 
482  // upwards
483  nIndex = i - j * m_nImageWidth;
484 
485  if (nIndex >= 0)
486  {
487  nPixelSumsInDirections[2] += pInputImage->pixels[nIndex];
488  }
489 
490  // downwards
491  nIndex = i + j * m_nImageWidth;
492 
493  if (nIndex < m_nImageWidth * m_nImageHeight)
494  {
495  nPixelSumsInDirections[3] += pInputImage->pixels[nIndex];
496  }
497 
498  // to the left and up
499  nIndex = i - j * m_nImageWidth - j;
500 
501  if (nIndex >= 0)
502  {
503  nPixelSumsInDirections[4] += pInputImage->pixels[nIndex];
504  }
505 
506  // to the right and up
507  nIndex = i - j * m_nImageWidth + j;
508 
509  if (nIndex >= 0)
510  {
511  nPixelSumsInDirections[5] += pInputImage->pixels[nIndex];
512  }
513 
514  // to the left and down
515  nIndex = i + j * m_nImageWidth - j;
516 
517  if (nIndex < m_nImageWidth * m_nImageHeight)
518  {
519  nPixelSumsInDirections[6] += pInputImage->pixels[nIndex];
520  }
521 
522  // to the right and down
523  nIndex = i + j * m_nImageWidth + j;
524 
525  if (nIndex < m_nImageWidth * m_nImageHeight)
526  {
527  nPixelSumsInDirections[7] += pInputImage->pixels[nIndex];
528  }
529  }
530 
531  if (nPixelSumsInDirections[0] * nPixelSumsInDirections[1] * nPixelSumsInDirections[2] *
532  nPixelSumsInDirections[3] * nPixelSumsInDirections[4] *
533  nPixelSumsInDirections[5] * nPixelSumsInDirections[6] *
534  nPixelSumsInDirections[7] !=
535  0)
536  {
537  pOutputImage->pixels[i] = 255;
538  }
539  }
540  }
541 }
542 
543 void
544 CGaussBackground::GetBinaryForegroundImageRGB(const CByteImage* pInputImageRGB,
545  CByteImage* pForegroundImage)
546 {
547  SegmentImageRGB(pInputImageRGB, pForegroundImage);
548 
549  BinarizeAndFillHoles(pForegroundImage);
550 }
551 
552 void
553 CGaussBackground::SegmentImageRGB(const CByteImage* pInputImageRGB, CByteImage* pProbabilityImage)
554 {
555  CByteImage* pHSVImage = new CByteImage(m_nImageWidth, m_nImageHeight, CByteImage::eRGB24);
556  CByteImage* pSmoothedImage = new CByteImage(m_nImageWidth, m_nImageHeight, CByteImage::eRGB24);
557 
558  ImageProcessor::GaussianSmooth3x3(pInputImageRGB, pSmoothedImage);
559  ImageProcessor::CalculateHSVImage(pSmoothedImage, pHSVImage);
560 
561  SegmentImage(pInputImageRGB, pHSVImage, pProbabilityImage);
562 
563  delete pHSVImage;
564  delete pSmoothedImage;
565 }
566 
567 void
568 CGaussBackground::SegmentImageRGB(const CByteImage* pInputImageRGB, float* pProbabilityImage)
569 {
570  CByteImage* pHSVImage = new CByteImage(m_nImageWidth, m_nImageHeight, CByteImage::eRGB24);
571  CByteImage* pSmoothedImage = new CByteImage(m_nImageWidth, m_nImageHeight, CByteImage::eRGB24);
572 
573  ImageProcessor::GaussianSmooth3x3(pInputImageRGB, pSmoothedImage);
574  ImageProcessor::CalculateHSVImage(pSmoothedImage, pHSVImage);
575 
576  SegmentImage(pInputImageRGB, pHSVImage, pProbabilityImage);
577 
578  delete pHSVImage;
579  delete pSmoothedImage;
580 }
581 
582 void
583 CGaussBackground::SegmentImage(const CByteImage* pInputImageRGB,
584  const CByteImage* pInputImageHSV,
585  CByteImage* pProbabilityImage)
586 {
587  if ((pInputImageHSV->width != m_nImageWidth) || (pInputImageHSV->height != m_nImageHeight))
588  {
589  ARMARX_WARNING_S << "CGaussBackground::SegmentImage: Image dimensions do not fit!";
590  return;
591  }
592 
593  float* pProbabilities = new float[m_nImageWidth * m_nImageHeight];
594  SegmentImage(pInputImageRGB, pInputImageHSV, pProbabilities);
595 
596  for (int i = 0; i < m_nImageWidth * m_nImageHeight; i++)
597  {
598  pProbabilityImage->pixels[i] = (int)(255 * pProbabilities[i]);
599  }
600 
601  delete[] pProbabilities;
602 }
603 
604 void
605 CGaussBackground::SegmentImage(const CByteImage* pInputImageRGB,
606  const CByteImage* pInputImageHSV,
607  float* pProbabilityImage)
608 {
609  if ((pInputImageHSV->width != m_nImageWidth) || (pInputImageHSV->height != m_nImageHeight))
610  {
611  ARMARX_WARNING_S << "CGaussBackground::SegmentImage: Image dimensions do not fit!";
612  return;
613  }
614 
615  const int nNumShifts = 21;
616  int nImageShiftValuesX[nNumShifts] = {-1, 1, -2, 0, 2, -3, -1, 1, 3, -2, 0,
617  2, -3, -1, 1, 3, -2, 0, 2, -1, 1};
618  int nImageShiftValuesY[nNumShifts] = {-3, -3, -2, -2, -2, -1, -1, -1, -1, 0, 0,
619  0, 1, 1, 1, 1, 2, 2, 2, 3, 3};
620  float fProbSums[nNumShifts] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
621  float* pProbabilityImages = new float[nNumShifts * m_nImageWidth * m_nImageHeight];
622 
623  // set to 0
624  for (int i = 0; i < nNumShifts * m_nImageWidth * m_nImageHeight; i++)
625  {
626  pProbabilityImages[i] = 0;
627  }
628 
629 // calc background probability for every pixel
630 #pragma omp parallel for schedule(static, 1)
631  for (int n = 0; n < nNumShifts; n++)
632  {
633  for (int i = 3; i < m_nImageHeight - 3; i++)
634  {
635  for (int j = 3; j < m_nImageWidth - 3; j++)
636  {
637  const int nIndex = i * m_nImageWidth + j;
638  const int nIndexSh =
639  (i + nImageShiftValuesY[n]) * m_nImageWidth + j + nImageShiftValuesX[n];
640 
641  // calculate probability using HS-distribution
642  Vec2d vTemp1;
643 
644  if (m_pPixelProbabilityDistributions[nIndexSh].bShifted)
645  {
646  vTemp1.x = (pInputImageHSV->pixels[3 * nIndex] + 128) %
647  256; // adapt pixel value if the mean is shifted
648  }
649  else
650  {
651  vTemp1.x = pInputImageHSV->pixels[3 * nIndex];
652  }
653 
654  vTemp1.y = pInputImageHSV->pixels[3 * nIndex + 1];
655  const float fProbHSV = 1 - CalcProbOfPixel(nIndexSh, vTemp1);
656 
657  // calculate probability using the RGB image
658  const Vec3d vRGBfromImage = {(float)pInputImageRGB->pixels[3 * nIndex],
659  (float)pInputImageRGB->pixels[3 * nIndex + 1],
660  (float)pInputImageRGB->pixels[3 * nIndex + 2]};
661  const Vec3d vRGBfromBackground = {
662  (float)m_pBackgroundRGB->pixels[3 * nIndexSh],
663  (float)m_pBackgroundRGB->pixels[3 * nIndexSh + 1],
664  (float)m_pBackgroundRGB->pixels[3 * nIndexSh + 2]};
665  const float fDistance = Math3d::Distance(vRGBfromImage, vRGBfromBackground);
666  const float fProbRGB = 1 - exp(-0.01f * fDistance);
667 
668 
669  const float fProb =
670  0.33f *
671  (fProbRGB +
672  2 * fProbHSV); // (fProbRGB*fProbHSV) / (fProbRGB*fProbHSV + (1-fProbRGB)*(1-fProbHSV) + 0.00001f);
673  pProbabilityImages[n * m_nImageWidth * m_nImageHeight + nIndex] = fProb;
674  fProbSums[n] += fProb;
675  }
676  }
677  }
678 
679  float fMinProb = fProbSums[0];
680  int nBestIndex = 0;
681 
682  for (int i = 1; i < nNumShifts; i++)
683  {
684  if (fProbSums[i] < fMinProb)
685  {
686  fMinProb = fProbSums[i];
687  nBestIndex = i;
688  }
689  }
690 
691  ARMARX_VERBOSE_S << "Best background shift: (" << nImageShiftValuesX[nBestIndex] << ", "
692  << nImageShiftValuesY[nBestIndex] << ")";
693 
694  for (int i = 0; i < m_nImageWidth * m_nImageHeight; i++)
695  {
696  pProbabilityImage[i] = pProbabilityImages[nBestIndex * m_nImageWidth * m_nImageHeight + i];
697  }
698 
699  delete[] pProbabilityImages;
700 }
CGaussBackground::LearnBackgroundRGB
void LearnBackgroundRGB(CByteImage **pRGBImages, const int nNumImages)
Definition: GaussBackground.cpp:54
GfxTL::Vec3d
VectorXD< 3, double > Vec3d
Definition: VectorXD.h:737
CPixelProbabilityDistribution::vMean
Vec2d vMean
Definition: GaussBackground.h:38
CGaussBackground::GetBinaryForegroundImageRGB
void GetBinaryForegroundImageRGB(const CByteImage *pInputImageRGB, CByteImage *pForegroundImage)
Definition: GaussBackground.cpp:544
CGaussBackground::CGaussBackground
CGaussBackground(const int nWidth, const int nHeight)
Definition: GaussBackground.cpp:32
CGaussBackground::SegmentImage
void SegmentImage(const CByteImage *pInputImageRGB, const CByteImage *pInputImageHSV, CByteImage *pProbabilityImage)
Definition: GaussBackground.cpp:583
DetMat2d
double DetMat2d(const Mat2d m)
Definition: GaussBackground.cpp:48
CPixelProbabilityDistribution
Definition: GaussBackground.h:36
CGaussBackground::~CGaussBackground
~CGaussBackground()
Definition: GaussBackground.cpp:41
ObjectLearningByPushingDefinitions.h
CPixelProbabilityDistribution::fSatWeight
float fSatWeight
Definition: GaussBackground.h:43
CGaussBackground::SegmentImageRGB
void SegmentImageRGB(const CByteImage *pInputImageRGB, CByteImage *pProbabilityImage)
Definition: GaussBackground.cpp:553
CPixelProbabilityDistribution::bShifted
bool bShifted
Definition: GaussBackground.h:41
GaussBackground.h
ARMARX_WARNING_S
#define ARMARX_WARNING_S
Definition: Logging.h:213
GfxTL::Vec2d
VectorXD< 2, double > Vec2d
Definition: VectorXD.h:736
CGaussBackground::LearnBackground
void LearnBackground(CByteImage **pHSVImages, const int nNumImages)
Definition: GaussBackground.cpp:80
float
#define float
Definition: 16_Level.h:22
CPixelProbabilityDistribution::mCovariance
Mat2d mCovariance
Definition: GaussBackground.h:39
CGaussBackground::BinarizeAndFillHoles
void BinarizeAndFillHoles(CByteImage *pProbabilityImage)
Definition: GaussBackground.cpp:263
ARMARX_VERBOSE_S
#define ARMARX_VERBOSE_S
Definition: Logging.h:207
Logging.h
OLP_FOREGROUND_THRESHOLD
#define OLP_FOREGROUND_THRESHOLD
Definition: ObjectLearningByPushingDefinitions.h:248