topical media & game development

talk show tell print

lib-of-vs-addons-ofxOpenCv-src-ofxCvImage.cpp / cpp



  
  include <ofxCvImage.h>
  include <ofxCvGrayscaleImage.h>
  include <ofxCvColorImage.h>
  include <ofxCvFloatImage.h>
  
  //--------------------------------------------------------------------------------
  ofxCvImage::ofxCvImage() {
      width                        = 0;
      height                        = 0;
      roiX            = 0;
      roiY            = 0;
      bUseTexture                = true;
      bTextureDirty   = true;
          bAllocated                = false;
          pixels                        = NULL;
      bPixelsDirty    = true;
      pixelsWidth     = 0;
      pixelsHeight    = 0;
      bUseRoiOffsetWhenDrawing = false;
  }
  
  //--------------------------------------------------------------------------------
  ofxCvImage::~ofxCvImage() {
      clear();
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::allocate( int w, int h ) {
          if (bAllocated == true){
                  ofLog(OF_LOG_WARNING, "in allocate, reallocating a ofxCvImage");
                  clear();
          }
      
          cvImage = cvCreateImage( cvSize(w,h), ipldepth, iplchannels );
          cvImageTemp        = cvCreateImage( cvSize(w,h), ipldepth, iplchannels );
  
          width = w;
          height = h;
      roiStack.push_back( ofRectangle(0,0,w,h) );   // set ROI to full image
          bAllocated = true;
  
      if( bUseTexture ) {
          tex.allocate( width, height, glchannels );
          bTextureDirty = true;
      }
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::clear() {
  
          if (bAllocated == true){
                  if (width > 0 && height > 0){
                          cvReleaseImage( &cvImage );
                          cvReleaseImage( &cvImageTemp );
                  }
          if( pixels != NULL ) {
              delete pixels;
              pixels = NULL;
              bPixelsDirty = true;
              pixelsWidth = 0;
              pixelsHeight = 0;            
          }
                  width = 0;
                  height = 0;
          roiX = 0;    
          roiY = 0;
          roiStack.clear(); 
  
                  if( bUseTexture ) {
                          tex.clear();
              bTextureDirty = true;
                  }
                  
                  bAllocated = false;
          }
  }
  
  //--------------------------------------------------------------------------------
  float ofxCvImage::getWidth(){
          return width;
  }
  
  //--------------------------------------------------------------------------------
  float ofxCvImage::getHeight(){
          return height;
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::setUseTexture( bool bUse ) {
          bUseTexture = bUse;
  }
  
  //--------------------------------------------------------------------------------
  ofTexture& ofxCvImage::getTextureReference() {
          return tex;
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::swapTemp() {
          IplImage*  temp;
          temp = cvImage;
          cvImage        = cvImageTemp;
          cvImageTemp        = temp;
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::flagImageChanged() {
      bTextureDirty = true;
      bPixelsDirty = true;
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::setUseRoiOffsetWhenDrawing( bool bUse ) {
      bUseRoiOffsetWhenDrawing = bUse;
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::rangeMap( IplImage* img, float min1, float max1, float min2, float max2 ) {
      // map from min1-max1 to min2-max2
      float scale = (max2-min2)/(max1-min1);
      cvConvertScale( img, img, scale, -(min1*scale)+min2 );
  }
  
  //--------------------------------------------------------------------------------
  bool ofxCvImage::pushSetBothToTheirIntersectionROI( ofxCvImage& img1, ofxCvImage& img2 ) {
      // calculates intersection ROI
      // pushes the intersection ROI on both the images' roiStack
      // use popROI() to restore previous ROI
      ofRectangle iRoi = getIntersectionROI( img1.getROI(), img2.getROI() );
      if( iRoi.width > 0 && iRoi.height > 0 ) { 
          img1.pushROI();
          img1.setROI( iRoi );
          img2.pushROI();
          img2.setROI( iRoi );            
          return true;
      } else {
          return false;
      }
  }
  
  // ROI - region of interest
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::pushROI() {
      roiStack.push_back( ofRectangle() );
      setROI(0,0,cvImage->width,cvImage->height);
      
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::popROI() {
      if(roiStack.size() > 1) {
          roiStack.pop_back();
      } else {
          ofLog(OF_LOG_WARNING, "in popROI, not popping since there is only one element left.");
      }
      setROI( roiStack.back() );
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::setROI( int x, int y, int w, int h ) {
      
      x = (int)ofClamp(x, 0, (int)cvImage->width-1);
      y = (int)ofClamp(y, 0, (int)cvImage->height-1);
      w = (int)ofClamp(w, 0, (int)cvImage->width - x);
      h = (int)ofClamp(h, 0, (int)cvImage->height - y);
      
      cvSetImageROI( cvImage, cvRect(x,y, w,h) );
      cvSetImageROI( cvImageTemp, cvRect(x,y, w,h) );
      width = w;
      height = h;
      roiX = x;
      roiY = y;
      roiStack.back().x = x;
      roiStack.back().y = y;
      roiStack.back().width = w;
      roiStack.back().height = h;    
      flagImageChanged();
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::setROI( const ofRectangle& rect ) {
      setROI( (int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height );
  }
  
  //--------------------------------------------------------------------------------
  ofRectangle ofxCvImage::getROI() {
      return roiStack.back();
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::resetROI() {
      cvResetImageROI( cvImage );
      cvResetImageROI( cvImageTemp );
      width = cvImage->width;
      height = cvImage->height;
      roiX = 0;
      roiY = 0;
      setROI( 0,0, cvImage->width, cvImage->height );
  }
  
  //--------------------------------------------------------------------------------
  ofRectangle ofxCvImage::getIntersectionROI( const ofRectangle& r1, const ofRectangle& r2 ) {
      int r1x1 = (int)r1.x;
      int r1y1 = (int)r1.y;
      int r1x2 = (int)(r1.x+r1.width);
      int r1y2 = (int)(r1.y+r1.height);
  
      int r2x1 = (int)r2.x;
      int r2y1 = (int)r2.y;
      int r2x2 = (int)(r2.x+r2.width);
      int r2y2 = (int)(r2.y+r2.height);
  
      int r3x1 = 0;
      int r3y1 = 0;
      int r3x2 = 0;
      int r3y2 = 0;
      
      bool bIntersect =  ( ( ofInRange(r2x1, r1x1,r1x2) || ofInRange(r1x1, r2x1,r2x2) ) &&
                           ( ofInRange(r2y1, r1y1,r1y2) || ofInRange(r1y1, r2y1,r2y2) ) );
  
      if( bIntersect ){
          r3x1 = MAX( r1x1, r2x1 );
          r3y1 = MAX( r1y1, r2y1 );
  
          r3x2 = MIN( r1x2, r2x2 );
          r3y2 = MIN( r1y2, r2y2 );
      
          return ofRectangle( r3x1,r3y1, r3x2-r3x1,r3y2-r3y1 );
          
      } else {
          return ofRectangle( 0,0, 0,0 );
      }
  }
  
  // Set Pixel Data
  
  //--------------------------------------------------------------------------------
  void  ofxCvImage::operator = ( const IplImage* mom ) {
          if( mom->width == width && mom->height == height &&
              mom->nChannels == cvImage->nChannels && 
          mom->depth == cvImage->depth )
      {
                  cvCopy( mom, cvImage );
          flagImageChanged();
          } else {
          ofLog(OF_LOG_ERROR, "in =, images need to match in size, channels, and depth");
          }
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::operator -= ( float value ) {
          cvSubS( cvImage, cvScalar(value), cvImageTemp );
          swapTemp();
      flagImageChanged();
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::operator += ( float value ) {
          cvAddS( cvImage, cvScalar(value), cvImageTemp );
          swapTemp();
      flagImageChanged();
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::operator -= ( ofxCvImage& mom ) {
          if( mom.getCvImage()->nChannels == cvImage->nChannels && 
          mom.getCvImage()->depth == cvImage->depth )
      {
          if( pushSetBothToTheirIntersectionROI(*this,mom) ) {
              cvSub( cvImage, mom.getCvImage(), cvImageTemp );
              swapTemp();
              popROI();       //restore prevoius ROI
              mom.popROI();   //restore prevoius ROI              
              flagImageChanged();
          } else {
              ofLog(OF_LOG_ERROR, "in -=, ROI mismatch");
          }
          } else {
          ofLog(OF_LOG_ERROR, "in -=, images need to have matching type");
          }
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::operator += ( ofxCvImage& mom ) {
          if( mom.getCvImage()->nChannels == cvImage->nChannels && 
          mom.getCvImage()->depth == cvImage->depth )
      {
          if( pushSetBothToTheirIntersectionROI(*this,mom) ) {
              cvAdd( cvImage, mom.getCvImage(), cvImageTemp );
              swapTemp();
              popROI();       //restore prevoius ROI
              mom.popROI();   //restore prevoius ROI              
              flagImageChanged();
          } else {
              ofLog(OF_LOG_ERROR, "in +=, ROI mismatch");
          }
          } else {
          ofLog(OF_LOG_ERROR, "in +=, images need to have matching type");
          }
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::operator *= ( ofxCvImage& mom ) {
          if( mom.getCvImage()->nChannels == cvImage->nChannels && 
          mom.getCvImage()->depth == cvImage->depth )
      {
          if( pushSetBothToTheirIntersectionROI(*this,mom) ) {
              float scalef = 1.0f / 255.0f;
              cvMul( cvImage, mom.getCvImage(), cvImageTemp, scalef );
              swapTemp();
              popROI();       //restore prevoius ROI
              mom.popROI();   //restore prevoius ROI              
              flagImageChanged();
          } else {
              ofLog(OF_LOG_ERROR, "in *=, ROI mismatch");
          }
          } else {
          ofLog(OF_LOG_ERROR, "in *=, images need to have matching type");
          }
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::operator &= ( ofxCvImage& mom ) {
          if( mom.getCvImage()->nChannels == cvImage->nChannels && 
          mom.getCvImage()->depth == cvImage->depth )
      {
          if( pushSetBothToTheirIntersectionROI(*this,mom) ) {
              cvAnd( cvImage, mom.getCvImage(), cvImageTemp );
              swapTemp();
              popROI();       //restore prevoius ROI
              mom.popROI();   //restore prevoius ROI              
              flagImageChanged();
          } else {
              ofLog(OF_LOG_ERROR, "in &=, ROI mismatch");
          }
          } else {
          ofLog(OF_LOG_ERROR, "in &=, images need to have matching type");
          }
  }
  
  // Draw Image
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::draw( float x, float y ) {
      draw( x,y, width,height );
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::draw( float x, float y, float w, float h ) {    
      if( bUseTexture ) {
          if( bTextureDirty ) {
              if(tex.getWidth() != width || tex.getHeight() != height) {
                  //ROI was changed
                  // reallocating texture - this could be faster with ROI support
                  tex.clear();
                  tex.allocate( width, height, glchannels );
              }
              tex.loadData( getPixels(), width, height, glchannels );
              bTextureDirty = false;
          }
          
          if( bUseRoiOffsetWhenDrawing ){
              x += roiX;
              y += roiY;
          }        
  
          tex.draw(x,y, w,h);
  
      } else {
          // this is slower than the typical draw method based on textures
          // but useful when dealing with threads GL textures often don't work
          ofLog(OF_LOG_NOTICE, "in draw, using slow texture-less drawing");
          ofLog(OF_LOG_NOTICE, "texture-less drawing - be aware, unlike texture drawing, \
                            this always draws window aligned, rotation not supported");
          
          if( x == 0) {
              x += 0.01;
              ofLog(OF_LOG_NOTICE, "BUG: can't draw at x==0 in texture-less mode.");
          }
          
          if(bAnchorIsPct){
                          x -= anchor.x * w;
                          y -= anchor.y * h;
                  }else{
                          x -= anchor.x;
                          y -= anchor.y;
                  }
          
          if( bUseRoiOffsetWhenDrawing ){
              x += roiX;
              y += roiY;
          }
                  
          glRasterPos2f( x, y+h );
  
          IplImage* tempImg;
          tempImg = cvCreateImage( cvSize((int)w, (int)h), ipldepth, iplchannels );        
          cvResize( cvImage, tempImg, CV_INTER_NN );
          cvFlip( tempImg, tempImg, 0 );
          glDrawPixels( tempImg->width, tempImg->height ,
                        glchannels, gldepth, tempImg->imageData );
          cvReleaseImage( &tempImg );
         
      }
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::setAnchorPercent( float xPct, float yPct ){
      if( bUseTexture ) {
              tex.setAnchorPercent(xPct,yPct);
      } else {
          anchor.x = xPct;
          anchor.y = yPct;
  
          bAnchorIsPct = true;
      }
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::setAnchorPoint( int x, int y ){
      if( bUseTexture ) {
              tex.setAnchorPoint(x,y);
      }else{
          anchor.x = x;
          anchor.y = y;
  
          bAnchorIsPct = false;
      }
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::resetAnchor(){
      if( bUseTexture ) {
              tex.resetAnchor();
      }else{
          anchor.set(0,0);
          bAnchorIsPct = false;
      }
  }
  
  // Image Filter Operations
  //--------------------------------------------------------------------------------
  void ofxCvImage::dilate() {
          cvDilate( cvImage, cvImageTemp, 0, 1 );
          swapTemp();
      flagImageChanged();
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::erode() {
          cvErode( cvImage, cvImageTemp, 0, 1 );
          swapTemp();
      flagImageChanged();
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::blur( int value ) {
      if( value % 2 == 0 ) {
          ofLog(OF_LOG_NOTICE, "in blur, value not odd -> will add 1 to cover your back");
          value++;
      }
          cvSmooth( cvImage, cvImageTemp, CV_BLUR , value);
          swapTemp();
      flagImageChanged();
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::blurGaussian( int value ) {
      if( value % 2 == 0 ) {
          ofLog(OF_LOG_NOTICE, "in blurGaussian, value not odd -> will add 1 to cover your back");
          value++;
      }
          cvSmooth( cvImage, cvImageTemp, CV_GAUSSIAN ,value );
          swapTemp();
      flagImageChanged();
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::invert(){
      cvNot(cvImage, cvImage);
      flagImageChanged();
  }
  
  // Image Transformation Operations
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::mirror( bool bFlipVertically, bool bFlipHorizontally ) {
          int flipMode = 0;
  
          if( bFlipVertically && !bFlipHorizontally ) flipMode = 0;
          else if( !bFlipVertically && bFlipHorizontally ) flipMode = 1;
          else if( bFlipVertically && bFlipHorizontally ) flipMode = -1;
          else return;
  
          cvFlip( cvImage, cvImageTemp, flipMode );
          swapTemp();
      flagImageChanged();
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::translate( float x, float y ) {
      transform( 0, 0,0, 1,1, x,y );
      flagImageChanged();
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::rotate( float angle, float centerX, float centerY ) {
      transform( angle, centerX, centerY, 1,1, 0,0 );
      flagImageChanged();
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::scale( float scaleX, float scaleY ) {
      transform( 0, 0,0, scaleX,scaleY, 0,0 );
      flagImageChanged();
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::transform( float angle, float centerX, float centerY,
                              float scaleX, float scaleY,
                              float moveX, float moveY ){
      float sina = sin(angle * DEG_TO_RAD);
      float cosa = cos(angle * DEG_TO_RAD);
      CvMat*  transmat = cvCreateMat( 2,3, CV_32F );
      cvmSet( transmat, 0,0, scaleX*cosa );
      cvmSet( transmat, 0,1, scaleY*sina );
      cvmSet( transmat, 0,2, -centerX*scaleX*cosa - centerY*scaleY*sina + moveX + centerX );
      cvmSet( transmat, 1,0, -1.0*scaleX*sina );
      cvmSet( transmat, 1,1, scaleY*cosa );
      cvmSet( transmat, 1,2, -centerY*scaleY*cosa + centerX*scaleX*sina + moveY + centerY);
  
      cvWarpAffine( cvImage, cvImageTemp, transmat );
          swapTemp();
      flagImageChanged();
  
      cvReleaseMat( &transmat );
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::undistort( float radialDistX, float radialDistY,
                              float tangentDistX, float tangentDistY,
                              float focalX, float focalY,
                              float centerX, float centerY ){
      float camIntrinsics[] = { focalX, 0, centerX, 0, focalY, centerY, 0, 0, 1 };
      float distortionCoeffs[] = { radialDistX, radialDistY, tangentDistX, tangentDistY };
      cvUnDistortOnce( cvImage, cvImageTemp, camIntrinsics, distortionCoeffs, 1 );
          swapTemp();
      flagImageChanged();
  }
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::remap( IplImage* mapX, IplImage* mapY ) {
      cvRemap( cvImage, cvImageTemp, mapX, mapY );
          swapTemp();
      flagImageChanged();
  }
  
  
A +-------------+ B / \ / \ / \ D +-------------------- + C

  
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::warpPerspective( const ofPoint& A, const ofPoint& B, const ofPoint& C, const ofPoint& D ) { 
      // compute matrix for perspectival warping (homography) 
      CvPoint2D32f cvsrc[4]; 
      CvPoint2D32f cvdst[4]; 
      CvMat* translate = cvCreateMat( 3,3, CV_32FC1 ); 
      cvSetZero( translate ); 
  
      cvdst[0].x = 0; 
      cvdst[0].y = 0; 
      cvdst[1].x = width; 
      cvdst[1].y = 0; 
      cvdst[2].x = width; 
      cvdst[2].y = height; 
      cvdst[3].x = 0; 
      cvdst[3].y = height; 
  
      cvsrc[0].x = A.x; 
      cvsrc[0].y = A.y; 
      cvsrc[1].x = B.x; 
      cvsrc[1].y = B.y; 
      cvsrc[2].x = C.x; 
      cvsrc[2].y = C.y; 
      cvsrc[3].x = D.x; 
      cvsrc[3].y = D.y; 
  
      cvWarpPerspectiveQMatrix( cvsrc, cvdst, translate );  // calculate homography 
      cvWarpPerspective( cvImage, cvImageTemp, translate ); 
      swapTemp();
      flagImageChanged();
      cvReleaseMat( &translate ); 
  } 
  
  //--------------------------------------------------------------------------------
  void ofxCvImage::warpIntoMe( ofxCvImage& mom, const ofPoint src[4], const ofPoint dst[4] ){
      if( mom.getCvImage()->nChannels == cvImage->nChannels && 
          mom.getCvImage()->depth == cvImage->depth ) {
      
              // compute matrix for perspectival warping (homography)
              CvPoint2D32f cvsrc[4];
              CvPoint2D32f cvdst[4];
              CvMat* translate = cvCreateMat( 3, 3, CV_32FC1 );
              cvSetZero( translate );
              for (int i = 0; i < 4; i++ ) {
                      cvsrc[i].x = src[i].x;
                      cvsrc[i].y = src[i].y;
                      cvdst[i].x = dst[i].x;
                      cvdst[i].y = dst[i].y;
              }
              cvWarpPerspectiveQMatrix( cvsrc, cvdst, translate );  // calculate homography
              cvWarpPerspective( mom.getCvImage(), cvImage, translate);
          flagImageChanged();
              cvReleaseMat( &translate );
  
      } else {
          ofLog(OF_LOG_ERROR, "in warpIntoMe: mom image type has to match");
      }        
  }
  
  // Other Image Operations
  
  //--------------------------------------------------------------------------------
  int ofxCvImage::countNonZeroInRegion( int x, int y, int w, int h ) {
      //TODO: test this method
      
          if (w == 0 || h == 0) return 0;
      int count = 0;
      
      // intersect the global ROI with the region to check
      ofRectangle iRoi = getIntersectionROI( getROI(), ofRectangle(x,y,w,h) );
      
      pushROI();
      setROI(iRoi);
  
          count = cvCountNonZero( cvImage );
      
      popROI();
          
          return count;
  }
  
  


(C) Æliens 04/09/2009

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.