00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include <cstring>
00012 #include "ImageMask.h"
00013
00014
00015 #include <math.h>
00016 #include <iostream>
00017
00018 #define THIS ImageMask
00019
00020 using namespace puma2;
00021
00022
00023 THIS::THIS()
00024 {
00025 m_Width = 0;
00026 m_Height = 0;
00027 m_Data = 0;
00028 }
00029
00030 THIS::THIS ( unsigned width, unsigned height, unsigned char* data )
00031 {
00032 m_Width = width;
00033 m_Height = height;
00034 if ( !data )
00035 {
00036 m_Data = new unsigned char[width*height];
00037 fill ( MASKED );
00038 }
00039 else
00040 {
00041 m_Data = data;
00042 }
00043 }
00044
00045
00046 THIS::THIS ( unsigned width, unsigned height, unsigned char* data, char voidMin, char voidMax )
00047 {
00048 m_Width = width;
00049 m_Height = height;
00050 unsigned dataSize = width * height;
00051 m_Data = new unsigned char[dataSize];
00052 if ( !data )
00053 {
00054
00055 fill ( MASKED );
00056 }
00057 else
00058 {
00059 for ( unsigned i = 0; i < dataSize; i++ )
00060 {
00061 if ( ( data[i] >= voidMin ) && ( data[i] <= voidMax ) )
00062 {
00063 m_Data[i] = MASKED;
00064 }
00065 else
00066 {
00067 m_Data[i] = VISIBLE;
00068 }
00069 }
00070 }
00071 }
00072
00073
00074 THIS::THIS ( const ImageMask& other )
00075 {
00076 m_Data = 0;
00077 operator= ( other );
00078 }
00079
00080 THIS& THIS::operator= ( const ImageMask & other )
00081 {
00082 if ( m_Data ) { delete m_Data; }
00083 m_Width = other.m_Width;
00084 m_Height = other.m_Height;
00085 m_Data = new unsigned char[m_Width*m_Height];
00086 memcpy ( m_Data, other.m_Data, m_Width*m_Height );
00087 return *this;
00088 }
00089
00090 THIS::~THIS()
00091 {
00092 if ( m_Data )
00093 {
00094 delete[] m_Data;
00095 }
00096 }
00097
00098 THIS::THIS ( GrayLevelImage8& image, unsigned char minVal, unsigned char maxVal )
00099 {
00100 m_Width = image.getWidth();
00101 m_Height = image.getHeight();
00102 m_Data = new unsigned char[m_Width*m_Height];
00103 fill ( 255 );
00104
00105 unsigned char* imageRow;
00106 unsigned offsetMask = 0;
00107 for ( unsigned y = 0; y < m_Height; y++ )
00108 {
00109 imageRow = image.unsafeRowPointerArray() [y];
00110 for ( unsigned x = 0; x < m_Width; x++ )
00111 {
00112 if ( ( imageRow[x] >= minVal ) && ( imageRow[x] <= maxVal ) )
00113 {
00114 m_Data[offsetMask] = MASKED;
00115 }
00116 offsetMask++;
00117 }
00118 }
00119 }
00120
00121 THIS::THIS ( puma2::ColorImageUV8& image, unsigned char minValU, unsigned char minValV )
00122 {
00123 m_Width = image.getWidth();
00124 m_Height = image.getHeight();
00125 m_Data = new unsigned char[m_Width*m_Height];
00126 fill ( 255 );
00127
00128 ColorImageUV8::PixelType* imageRow;
00129 unsigned offsetMask = 0;
00130 for ( unsigned y = 0; y < m_Height; y++ )
00131 {
00132 imageRow = image.unsafeRowPointerArray() [y];
00133 for ( unsigned x = 0; x < m_Width; x++ )
00134 {
00135 if ( ( imageRow[x][0] >= minValU ) )
00136 {
00137 m_Data[offsetMask] = MASKED;
00138 }
00139 offsetMask++;
00140 }
00141 }
00142 }
00143
00144 THIS::THIS ( puma2::GrayLevelImage8& foregroundY, puma2::ColorImageUV8& foregroundUv,
00145 puma2::GrayLevelImage8& backgroundY, puma2::ColorImageUV8& backgroundUv,
00146 int threshold )
00147 {
00148 if ( ( foregroundUv.getWidth() != foregroundY.getWidth() ) ||
00149 ( foregroundUv.getHeight() != foregroundY.getHeight() ) ||
00150 ( foregroundUv.getWidth() != backgroundY.getWidth() ) ||
00151 ( foregroundUv.getHeight() != backgroundY.getHeight() ) ||
00152 ( foregroundUv.getWidth() != backgroundUv.getWidth() ) ||
00153 ( foregroundUv.getHeight() != backgroundUv.getHeight() ) )
00154 {
00155
00156 m_Data = 0;
00157 return;
00158 }
00159
00160 m_Width = foregroundY.getWidth();
00161 m_Height = foregroundY.getHeight();
00162 int threshold2 = threshold * threshold;
00163
00164 m_Data = new unsigned char[m_Width*m_Height];
00165 fill ( 255 );
00166
00167 ColorImageUV8::PixelType** foregroundUvRow = foregroundUv.unsafeRowPointerArray();
00168 ColorImageUV8::PixelType** backgroundUvRow = backgroundUv.unsafeRowPointerArray();
00169 puma2::byte** foregroundYRow = foregroundY.unsafeRowPointerArray();
00170 puma2::byte** backgroundYRow = backgroundY.unsafeRowPointerArray();
00171
00172 ColorImageUV8::PixelType* foregroundUvPointer;
00173 ColorImageUV8::PixelType* backgroundUvPointer;
00174 puma2::byte* foregroundYPointer;
00175 puma2::byte* backgroundYPointer;
00176
00177 unsigned char* currentMaskPixel = m_Data;
00178
00179 for ( unsigned int y = 0; y < m_Height ; y++ )
00180 {
00181 foregroundUvPointer = foregroundUvRow[y];
00182 backgroundUvPointer = backgroundUvRow[y];
00183 foregroundYPointer = foregroundYRow[y];
00184 backgroundYPointer = backgroundYRow[y];
00185 for ( unsigned int x = 0; x < m_Width ; x++ )
00186 {
00187 int grayDiff = foregroundYPointer[x] - backgroundYPointer[x];
00188 int uDiff = foregroundUvPointer[x][0] - backgroundUvPointer[x][0];
00189 int vDiff = foregroundUvPointer[x][1] - backgroundUvPointer[x][1];
00190
00191
00192
00193
00194 int distance = grayDiff * grayDiff + uDiff * uDiff + vDiff * vDiff;
00195
00196 if ( distance < threshold2 )
00197 {
00198 *currentMaskPixel = MASKED;
00199 }
00200 currentMaskPixel++;
00201 }
00202 }
00203
00204 }
00205
00206 void THIS::fill ( unsigned char value )
00207 {
00208 if ( !m_Data )
00209 {
00210
00211 return;
00212 }
00213 memset ( m_Data, value, m_Width*m_Height );
00214 }
00215
00216
00217 void THIS::expand ( const ImageMask& other )
00218 {
00219 if ( !m_Data )
00220 {
00221
00222 return;
00223 }
00224 if ( !other.m_Data )
00225 {
00226
00227 return;
00228 }
00229 if ( ( m_Width != other.m_Width ) || ( m_Height != other.m_Height ) )
00230 {
00231
00232 return;
00233 }
00234 for ( unsigned i = 0; i < m_Width*m_Height; i++ )
00235 {
00236 m_Data[i] |= other.m_Data[i];
00237 }
00238 }
00239
00240
00241 void THIS::apply ( GrayLevelImage8& image, unsigned char fillValue )
00242 {
00243 if ( !m_Data )
00244 {
00245
00246 return;
00247 }
00248 if ( ( unsigned ( image.getWidth() ) != m_Width ) != ( unsigned ( image.getHeight() ) != m_Height ) )
00249 {
00250
00251 return;
00252 }
00253
00254 unsigned char* currentYPixel = image.unsafeRowPointerArray() [0];
00255 unsigned char* currentMaskPixel = m_Data;
00256
00257 for ( unsigned y = 0; y < m_Height; y++ )
00258 for ( unsigned x = 0; x < m_Width; x++ )
00259 {
00260 if ( ! ( *currentMaskPixel ) )
00261 {
00262 ( *currentYPixel ) = fillValue;
00263 }
00264 currentYPixel++;
00265 currentMaskPixel++;
00266 }
00267 }
00268
00269
00270 void THIS::apply ( puma2::ColorImageUV8& image, unsigned char fillU, unsigned char fillV )
00271 {
00272 if ( !m_Data )
00273 {
00274
00275 return;
00276 }
00277 if ( ( unsigned ( image.getWidth() ) != m_Width ) != ( unsigned ( image.getHeight() ) != m_Height ) )
00278 {
00279
00280 return;
00281 }
00282
00283 ColorImageUV8::PixelType* currentUvPixel = image.unsafeRowPointerArray() [0];
00284 unsigned char* currentMaskPixel = m_Data;
00285
00286 for ( unsigned y = 0; y < m_Height; y++ )
00287 for ( unsigned x = 0; x < m_Width; x++ )
00288 {
00289 if ( ! ( *currentMaskPixel ) )
00290 {
00291 ( *currentUvPixel ) [0] = fillU;
00292 ( *currentUvPixel ) [1] = fillV;
00293 }
00294 currentUvPixel++;
00295 currentMaskPixel++;
00296 }
00297 }
00298
00299
00300 void THIS::apply ( puma2::ColorImageRGB8& image, unsigned char fillR, unsigned char fillG, unsigned char fillB )
00301 {
00302 if ( !m_Data )
00303 {
00304
00305 return;
00306 }
00307 if ( ( unsigned ( image.getWidth() ) != m_Width ) != ( unsigned ( image.getHeight() ) != m_Height ) )
00308 {
00309
00310 return;
00311 }
00312
00313 ColorImageRGB8::PixelType* currentRgbPixel = image.unsafeRowPointerArray() [0];
00314 unsigned char* currentMaskPixel = m_Data;
00315
00316 for ( unsigned y = 0; y < m_Height; y++ )
00317 for ( unsigned x = 0; x < m_Width; x++ )
00318 {
00319 if ( ! ( *currentMaskPixel ) )
00320 {
00321 ( *currentRgbPixel ) [0] = fillR;
00322 ( *currentRgbPixel ) [1] = fillG;
00323 ( *currentRgbPixel ) [2] = fillB;
00324 }
00325 currentRgbPixel++;
00326 currentMaskPixel++;
00327 }
00328 }
00329
00330
00331 void THIS::grayOut ( puma2::ColorImageRGB8& colorImage, puma2::GrayLevelImage8& graimageY )
00332 {
00333 if ( !m_Data )
00334 {
00335
00336 return;
00337 }
00338 if ( ( ( unsigned ( colorImage.getWidth() ) != m_Width ) != ( unsigned ( colorImage.getHeight() ) != m_Height ) ) ||
00339 ( ( unsigned ( graimageY.getWidth() ) != m_Width ) != ( unsigned ( graimageY.getHeight() ) != m_Height ) ) )
00340 {
00341
00342 return;
00343 }
00344
00345 ColorImageRGB8::PixelType* currentRgbPixel = colorImage.unsafeRowPointerArray() [0];
00346 unsigned char* currentYPixel = graimageY.unsafeRowPointerArray() [0];
00347 unsigned char* currentMaskPixel = m_Data;
00348 unsigned val;
00349
00350 for ( unsigned y = 0; y < m_Height; y++ )
00351 for ( unsigned x = 0; x < m_Width; x++ )
00352 {
00353 if ( ! ( *currentMaskPixel ) )
00354 {
00355 val = *currentYPixel / 2 + 64;
00356 ( *currentRgbPixel ) [0] = val;
00357 ( *currentRgbPixel ) [1] = val;
00358 ( *currentRgbPixel ) [2] = val;
00359 }
00360 currentRgbPixel++;
00361 currentYPixel++;
00362 currentMaskPixel++;
00363 }
00364 }
00365
00366
00367 void THIS::grayOut ( puma2::ColorImageRGB8& colorImage )
00368 {
00369 if ( !m_Data )
00370 {
00371
00372 return;
00373 }
00374
00375 ColorImageRGB8::PixelType* currentRgbPixel = colorImage.unsafeRowPointerArray() [0];
00376 unsigned char* currentMaskPixel = m_Data;
00377 unsigned val;
00378
00379 for ( unsigned y = 0; y < m_Height; y++ )
00380 for ( unsigned x = 0; x < m_Width; x++ )
00381 {
00382 if ( ! ( *currentMaskPixel ) )
00383 {
00384 val = ( ( *currentRgbPixel ) [0] + ( *currentRgbPixel ) [1] + ( *currentRgbPixel ) [2] ) / 6 + 64;
00385 ( *currentRgbPixel ) [0] = val;
00386 ( *currentRgbPixel ) [1] = val;
00387 ( *currentRgbPixel ) [2] = val;
00388 }
00389 currentRgbPixel++;
00390 currentMaskPixel++;
00391 }
00392 }
00393
00394
00395 bool THIS::findValue ( int centerX, int centerY, unsigned char value, float radius )
00396 {
00397 if ( !m_Data )
00398 {
00399
00400 return false;
00401 }
00402
00403 int startX = int ( centerX - radius );
00404 int startY = int ( centerY - radius );
00405 int endX = int ( centerX + radius );
00406 int endY = int ( centerY + radius );
00407
00408 if ( startX < 0 ) { startX = 0; }
00409 if ( startY < 0 ) { startY = 0; }
00410 if ( endX >= int ( m_Width ) ) { endX = m_Width - 1; }
00411 if ( endY >= int ( m_Height ) ) { endY = m_Height - 1; }
00412
00413 float sqrRadius = radius*radius;
00414
00415 for ( int y = startY; y <= endY; y++ )
00416 for ( int x = startX; x <= endX; x++ )
00417 {
00418
00419 if ( m_Data[x+m_Width*y] == value )
00420 {
00421 float sqrDist = float ( x - centerX ) * float ( x - centerX ) + float ( y - centerY ) * float ( y - centerY );
00422 if ( sqrDist <= sqrRadius ) { return true; }
00423 }
00424 }
00425
00426 return false;
00427 }
00428
00429
00430 void THIS::erode ( float radius )
00431 {
00432 maskOperation ( erodeOperation, radius );
00433 }
00434
00435
00436 void THIS::dilate ( float radius )
00437 {
00438 maskOperation ( dilateOperation, radius );
00439 }
00440
00441
00442 void THIS::maskOperation ( maskOperationT operation, float radius )
00443 {
00444 if ( !m_Data )
00445 {
00446
00447 return;
00448 }
00449
00450 if ( radius < 1.0 ) { return; }
00451
00452 int* offsetMask;
00453 int halfMaskSize;
00454 unsigned maskLength;
00455
00456
00457 createCircularKernel ( radius, offsetMask, halfMaskSize, maskLength );
00458
00459
00460 unsigned char* data = new unsigned char[m_Width*m_Height];
00461 memcpy ( data, m_Data, m_Width*m_Height );
00462
00463
00464 unsigned i = halfMaskSize + m_Width * halfMaskSize;
00465 unsigned char fillValue;
00466
00467 switch ( operation )
00468 {
00469 case erodeOperation:
00470 fillValue = MASKED;
00471 break;
00472 case dilateOperation:
00473 fillValue = VISIBLE;
00474 break;
00475 }
00476
00477 for ( unsigned y = halfMaskSize; y < m_Height - halfMaskSize; y++ )
00478 {
00479 for ( unsigned x = halfMaskSize; x < m_Width - halfMaskSize; x++ )
00480 {
00481 if ( m_Data[i] && ! ( m_Data[i-1] && m_Data[i+1] && m_Data[i-m_Width] && m_Data[i+m_Width] ) )
00482 {
00483 for ( unsigned j = 0; j < maskLength; j++ )
00484 {
00485 data[ i + offsetMask[j] ] = fillValue;
00486 }
00487 }
00488 i++;
00489 }
00490 i += halfMaskSize * 2;
00491 }
00492
00493
00494 delete[] m_Data;
00495 m_Data = data;
00496
00497 delete[] offsetMask;
00498 }
00499
00500
00501 void THIS::findBorders( )
00502 {
00503 if ( !m_Data )
00504 {
00505
00506 return;
00507 }
00508
00509
00510 unsigned char* data = new unsigned char[m_Width*m_Height];
00511 memset ( data, VISIBLE, m_Width*m_Height );
00512
00513
00514 unsigned i = m_Width + 1;
00515
00516 for ( unsigned y = 1; y < m_Height - 1; y++ )
00517 {
00518 for ( unsigned x = 1; x < m_Width - 1; x++ )
00519 {
00520 if ( m_Data[i] && ! ( m_Data[i-1] && m_Data[i+1] && m_Data[i-m_Width] && m_Data[i+m_Width] ) )
00521 {
00522 data[i] = MASKED;
00523 }
00524 i++;
00525 }
00526 i += 2;
00527 }
00528
00529
00530 delete[] m_Data;
00531 m_Data = data;
00532 }
00533
00534
00535 void THIS::createCircularKernel ( float radius, int*& offsetMask, int& halfMaskSize, unsigned& maskLength )
00536 {
00537 unsigned maskSize = unsigned ( radius ) * 2 + 1;
00538 halfMaskSize = maskSize / 2;
00539 offsetMask = new int[maskSize*maskSize-1];
00540 unsigned i = 0;
00541 float dist;
00542 for ( int y = -halfMaskSize; y <= halfMaskSize; y++ )
00543 for ( int x = -halfMaskSize; x <= halfMaskSize; x++ )
00544 {
00545 dist = sqrt ( float ( x ) * float ( x ) + float ( y ) * float ( y ) );
00546 if ( dist <= radius )
00547 {
00548 offsetMask[i] = x + m_Width * y;
00549 i++;
00550 }
00551 }
00552 maskLength = i;
00553 }
00554
00555 Point2D THIS::getGravCenter ( )
00556 {
00557 double centerX = 0;
00558 double centerY = 0;
00559 int numPixels = 0;
00560 int i = 0;
00561
00562 for ( unsigned y = 0; y < m_Height; y++ )
00563 {
00564 for ( unsigned x = 0; x < m_Width; x++ )
00565 {
00566 if ( m_Data[i] == VISIBLE )
00567 {
00568 centerX += x;
00569 centerY += y;
00570 numPixels ++;
00571 }
00572 i++;
00573 }
00574 }
00575 centerX /= numPixels;
00576 centerY /= numPixels;
00577
00578 return Point2D( centerX, centerY );
00579 }
00580
00581 Box2D<int> THIS::getBoundingBox()
00582 {
00583
00584 Box2D<int> bBox( m_Width, m_Height, 0, 0 );
00585 int i = 0;
00586
00587 for ( unsigned y = 0; y < m_Height; y++ )
00588 {
00589 for ( unsigned x = 0; x < m_Width; x++ )
00590 {
00591 if ( m_Data[i] == VISIBLE )
00592 {
00593 bBox.enclose( x, y );
00594 }
00595 i++;
00596 }
00597 }
00598 bBox.enclose( bBox.maxX()+1, bBox.maxY()+1 );
00599
00600 return bBox;
00601 }
00602
00603 THIS* THIS::subMask( Box2D<int> area )
00604 {
00605 int newWidth = area.width();
00606 int newHeight= area.height();
00607 THIS* result = new THIS( newWidth, newHeight );
00608 unsigned char* newData = result->getData();
00609
00610 int minX = area.minX();
00611 int minY = area.minY();
00612
00613 int i = 0;
00614
00615 for ( int y = 0; y < newHeight; y++ )
00616 {
00617 for ( int x = 0; x < newWidth; x++ )
00618 {
00619 newData[i] = m_Data[ minX+x + ( minY+y )*m_Width ];
00620 i++;
00621 }
00622 }
00623
00624 return result;
00625 }
00626
00627 #undef DEBUG_OUTPUT
00628 #undef THIS