topical media & game development
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.