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