|
AlbumShaper
1.0a3
|
#include <qimage.h>#include <qstring.h>#include <qapplication.h>#include "redEye.h"#include "redEye_internal.h"#include "../../gui/statusWidget.h"
Go to the source code of this file.
Defines | |
| #define | MIN_RED_VAL 40 |
Functions | |
| QImage * | removeRedeyeRegions (QString filename, QPoint topLeftExtreme, QPoint bottomRightExtreme, StatusWidget *statusWidget) |
| void | findRegionOfInterest (QPoint topLeftExtreme, QPoint bottomRightExtreme) |
| void | pushPixel (int x, int y, int id) |
| void | findBlobs () |
| void | sortBlobsByDecreasingSize () |
| void | findBestTwoBlobs () |
| bool | IDedPixel (int x, int y) |
| double | desaturateAlpha (int x, int y) |
| void | desaturateBlobs () |
| void | desaturateEntireImage (QPoint topLeftExtreme, QPoint bottomRightExtreme) |
| #define MIN_RED_VAL 40 |
Definition at line 302 of file redEye.cpp.
Referenced by findBlobs(), and findRegionOfInterest().
| double desaturateAlpha | ( | int | x, |
| int | y | ||
| ) |
Definition at line 572 of file redEye.cpp.
References IDedPixel().
Referenced by desaturateBlobs().
{
int n = 0;
if( IDedPixel(x ,y ) ) n++;
if(n == 1)
return 1.0;
if( IDedPixel(x-1,y-1) ) n++;
if( IDedPixel(x ,y-1) ) n++;
if( IDedPixel(x+1,y-1) ) n++;
if( IDedPixel(x-1,y ) ) n++;
if( IDedPixel(x+1,y ) ) n++;
if( IDedPixel(x-1,y+1) ) n++;
if( IDedPixel(x ,y+1) ) n++;
if( IDedPixel(x+1,y+1) ) n++;
if( IDedPixel(x-2,y-2) ) n++;
if( IDedPixel(x-1,y-2) ) n++;
if( IDedPixel(x ,y-2) ) n++;
if( IDedPixel(x+1,y-2) ) n++;
if( IDedPixel(x+2,y-2) ) n++;
if( IDedPixel(x-2,y-1) ) n++;
if( IDedPixel(x+2,y-1) ) n++;
if( IDedPixel(x-2,y ) ) n++;
if( IDedPixel(x+2,y ) ) n++;
if( IDedPixel(x-2,y+1) ) n++;
if( IDedPixel(x+2,y+1) ) n++;
if( IDedPixel(x-2,y+2) ) n++;
if( IDedPixel(x-1,y+2) ) n++;
if( IDedPixel(x ,y+2) ) n++;
if( IDedPixel(x+1,y+2) ) n++;
if( IDedPixel(x+2,y+2) ) n++;
return ((double)n) / 25;
}
| void desaturateBlobs | ( | ) |
Definition at line 612 of file redEye.cpp.
References bottomRight, desaturateAlpha(), editedImage, and topLeft.
Referenced by removeRedeyeRegions().
{
//desaturate bad pixels
int x, y;
double r;
QRgb* rgb;
uchar* scanLine;
for( y = QMAX( topLeft.y()-1, 0);
y<= QMIN( bottomRight.y()+1, editedImage->height()-1 );
y++)
{
scanLine = editedImage->scanLine(y);
for( x = QMAX( topLeft.x()-1, 0);
x <= QMIN( bottomRight.x()+1, editedImage->width()-1 );
x++)
{
double alpha = desaturateAlpha( x, y );
if( alpha > 0)
{
rgb = ((QRgb*)scanLine+x);
r = alpha*(0.05*qRed(*rgb) + 0.6*qGreen(*rgb) + 0.3*qBlue(*rgb)) +
(1-alpha)*qRed(*rgb);
*rgb = qRgb( (int)r,
qGreen(*rgb),
qBlue(*rgb) );
} //alpha > 0
} //x
} //y
}
| void desaturateEntireImage | ( | QPoint | topLeftExtreme, |
| QPoint | bottomRightExtreme | ||
| ) |
Definition at line 643 of file redEye.cpp.
References editedImage.
Referenced by removeRedeyeRegions().
{
//desaturate bad pixels
int x, y;
QRgb* rgb;
uchar* scanLine;
for( y=topLeftExtreme.y(); y<=bottomRightExtreme.y(); y++)
{
scanLine = editedImage->scanLine(y);
for( x=topLeftExtreme.x(); x<=bottomRightExtreme.x(); x++)
{
rgb = ((QRgb*)scanLine+x);
if( qRed(*rgb) > 2*qGreen(*rgb) )
{
*rgb = qRgb( (int) (0.05*qRed(*rgb) + 0.6*qGreen(*rgb) + 0.3*qBlue(*rgb)),
qGreen(*rgb),
qBlue(*rgb) );
} // > thresh
} //x
} //y
}
| void findBestTwoBlobs | ( | ) |
Definition at line 506 of file redEye.cpp.
References blobCount, id1, id2, ids, ratios, and sizes.
Referenced by removeRedeyeRegions().
{
id1 = -1;
id2 = -1;
int i;
//special case: 2 blobs found, both larger than 1 pixel
if(blobCount == 2 &&
sizes[0] > 1 &&
sizes[1] > 1)
{
id1 = ids[0];
id2 = ids[1];
}
else
{
for(i=0; i<blobCount-2; i++)
{
//once we hit blobs that are only one pixel large stop because they are probably just noise
if( sizes[i+1] <= 1 ) break;
double as1 = ratios[i];
double as2 = ratios[i+1];
if(as1 < 1) as1 = 1.0/as1;
if(as2 < 1) as2 = 1.0/as2;
if( //both blobs must be semi-circular, prefer those that are wider
ratios[i] > 0.75 && ratios[i] < 2 &&
ratios[i+1] > 0.75 && ratios[i+1] < 2 &&
//both blobs must be similar in shape
QMAX(as2,as1)/QMIN(as2,as1) < 2 &&
//both blobs must be similar in size
((double)QMAX( sizes[i], sizes[i+1] )) / QMIN( sizes[i], sizes[i+1] ) < 1.5 &&
//both blobs must be above a certain thresh size, this prevents selecting blobs that are very very tiny
//if only tiny blobs are around we'll end up desaturating entire region
QMAX( sizes[i], sizes[i+1] ) > 20 )
{
id1 = ids[i];
id2 = ids[i+1];
break;
}
}
}
//Comment this sectionin to see what blobs were found and selected
/* cout << "-----\n";
for(i=0; i<blobCount-1; i++)
{
if( ids[i] == id1 || ids[i] == id2 )
cout << "--->";
cout << "ID: " << ids[i] << "count: " << sizes[i] << " w:h: " << ratios[i] << "\n";
}*/
}
| void findBlobs | ( | ) |
Definition at line 372 of file redEye.cpp.
References blobAspectRatios, blobBottomRight, blobIDs, blobPixelCount, blobSizes, blobTopLeft, bottomRight, MIN_RED_VAL, pushPixel(), rawImage, regionHeight, regionOfInterest, regionWidth, spreadablePixels, and topLeft.
Referenced by removeRedeyeRegions().
{
//create small matrix for region of interest
regionWidth = bottomRight.x() - topLeft.x() + 1;
regionHeight = bottomRight.y() - topLeft.y() + 1;
regionOfInterest = new int[ regionWidth * regionHeight ];
//set all pixels that meet thresh to 1, all others to 0
int x, y;
int x2, y2;
QRgb* rgb;
uchar* scanLine;
for( y=topLeft.y(); y<=bottomRight.y(); y++)
{
y2 = y - topLeft.y();
scanLine = rawImage.scanLine(y);
for( x=topLeft.x(); x<=bottomRight.x(); x++)
{
x2 = x - topLeft.x();
rgb = ((QRgb*)scanLine+x);
bool threshMet = qRed(*rgb) > 2*qGreen(*rgb) &&
qRed(*rgb) > MIN_RED_VAL;
if(threshMet)
regionOfInterest[ x2 + y2*regionWidth ] = 1;
else
regionOfInterest[ x2 + y2*regionWidth ] = 0;
}
}
//walk over region of interest and propogate blobs
int nextValidID = 2;
for(x = 0; x<regionWidth; x++)
{
for(y = 0; y<regionHeight; y++)
{
//if any blobs can be propogated handle them first
while( !spreadablePixels.empty() )
{
QPoint point = spreadablePixels.pop();
int id = regionOfInterest[ point.x() + point.y()*regionWidth ];
pushPixel( point.x()-1, point.y()-1, id );
pushPixel( point.x(), point.y()-1, id );
pushPixel( point.x()+1, point.y()-1, id );
pushPixel( point.x()-1, point.y(), id );
pushPixel( point.x()+1, point.y(), id );
pushPixel( point.x()-1, point.y()+1, id );
pushPixel( point.x(), point.y()+1, id );
pushPixel( point.x()+1, point.y()+1, id );
}
//if this pixel has met thresh and has not yet been assigned a unique ID,
//assign it the next unique id and push all valid neighbors
if( regionOfInterest[ x + y*regionWidth ] == 1 )
{
//print last blob stats
if( nextValidID > 2)
{
blobIDs.push( (nextValidID - 1) );
blobSizes.push( blobPixelCount );
blobAspectRatios.push( ((double)(blobBottomRight.x() - blobTopLeft.x()+1)) /
(blobBottomRight.y() - blobTopLeft.y()+1) );
}
regionOfInterest[x + y*regionWidth] = nextValidID;
pushPixel( x-1, y-1, nextValidID );
pushPixel( x, y-1, nextValidID );
pushPixel( x+1, y-1, nextValidID );
pushPixel( x-1, y, nextValidID );
pushPixel( x+1, y, nextValidID );
pushPixel( x-1, y+1, nextValidID );
pushPixel( x, y+1, nextValidID );
pushPixel( x+1, y+1, nextValidID );
nextValidID++;
blobPixelCount = 1;
blobTopLeft = QPoint( x, y );
blobBottomRight = QPoint( x, y );
}
} //y
} //x
//insert last blob stats
if( nextValidID > 2)
{
blobIDs.push( (nextValidID - 1) );
blobSizes.push( blobPixelCount );
blobAspectRatios.push( ((double)(blobBottomRight.x() - blobTopLeft.x()+1)) / (blobBottomRight.y() - blobTopLeft.y()+1) );
}
}
| void findRegionOfInterest | ( | QPoint | topLeftExtreme, |
| QPoint | bottomRightExtreme | ||
| ) |
Definition at line 305 of file redEye.cpp.
References bottomRight, StatusWidget::incrementProgress(), MIN_RED_VAL, newProgress, rawImage, status, topLeft, and updateIncrement.
Referenced by removeRedeyeRegions().
{
topLeft = QPoint(-1,-1);
bottomRight = QPoint(-1,-1);
int x, y;
QRgb* rgb;
uchar* scanLine;
for( y=topLeftExtreme.y(); y<=bottomRightExtreme.y(); y++)
{
scanLine = rawImage.scanLine(y);
for( x=topLeftExtreme.x(); x<=bottomRightExtreme.x(); x++)
{
rgb = ((QRgb*)scanLine+x);
bool threshMet = qRed(*rgb) > 2*qGreen(*rgb) &&
qRed(*rgb) > MIN_RED_VAL;
if(threshMet)
{
//first pixel
if(topLeft.x() == -1)
{
topLeft = QPoint(x,y);
bottomRight = QPoint(x,y);
}
if(x < topLeft.x() ) topLeft.setX( x );
if(y < topLeft.y() ) topLeft.setY( y );
if(x > bottomRight.x() ) bottomRight.setX( x );
if(y > bottomRight.y() ) bottomRight.setY( y );
}
//update status bar if significant progress has been made since last update
newProgress++;
if(newProgress >= updateIncrement)
{
newProgress = 0;
status->incrementProgress();
qApp->processEvents();
}
}
}
}
| bool IDedPixel | ( | int | x, |
| int | y | ||
| ) |
Definition at line 561 of file redEye.cpp.
References bottomRight, id1, id2, regionIndex(), regionOfInterest, regionWidth, and topLeft.
Referenced by desaturateAlpha().
{
if( x < topLeft.x() || y < topLeft.y() ||
x > bottomRight.x() || y > bottomRight.y() )
return false;
int regionIndex = x - topLeft.x() + (y-topLeft.y())*regionWidth;
return ( regionOfInterest[regionIndex] == id1 ||
regionOfInterest[regionIndex] == id2 );
}
| void pushPixel | ( | int | x, |
| int | y, | ||
| int | id | ||
| ) |
Definition at line 350 of file redEye.cpp.
References blobBottomRight, blobPixelCount, blobTopLeft, regionHeight, regionOfInterest, regionWidth, and spreadablePixels.
Referenced by findBlobs().
{
//if pixel off image or below thresh ignore push attempt
if( x < 0 ||
y < 0 ||
x >= regionWidth ||
y >= regionHeight ||
regionOfInterest[ x + y*regionWidth ] != 1 )
return;
//passes! set id and actually put pixel onto stack
regionOfInterest[ x + y*regionWidth] = id;
spreadablePixels.push( QPoint( x, y ) );
//increase blob pixel count and update topLeft and bottomRight
blobPixelCount++;
blobTopLeft.setX( QMIN( x, blobTopLeft.x() ) );
blobTopLeft.setY( QMIN( y, blobTopLeft.y() ) );
blobBottomRight.setX( QMAX( x, blobBottomRight.x() ) );
blobBottomRight.setY( QMAX( y, blobBottomRight.y() ) );
}
| QImage* removeRedeyeRegions | ( | QString | filename, |
| QPoint | topLeftExtreme, | ||
| QPoint | bottomRightExtreme, | ||
| StatusWidget * | statusWidget | ||
| ) |
Definition at line 206 of file redEye.cpp.
References desaturateBlobs(), desaturateEntireImage(), editedImage, findBestTwoBlobs(), findBlobs(), findRegionOfInterest(), id1, newProgress, rawImage, StatusWidget::setStatus(), StatusWidget::showProgressBar(), sortBlobsByDecreasingSize(), status, topLeft, and updateIncrement.
Referenced by EditingInterface::removeRedeye().
{
//store handle to status widget
status = statusWidget;
//load original image
rawImage = QImage( filename );
//sanity check: unable to load image
if(rawImage.isNull()) { return NULL; }
//convert to 32-bit depth if necessary
if( rawImage.depth() < 32 ) { rawImage = rawImage.convertDepth( 32, Qt::AutoColor ); }
//sanity check: make sure topLeftExtreme and bottomRightExtreme are within image boundary
topLeftExtreme.setX( QMAX( topLeftExtreme.x(), 0 ) );
topLeftExtreme.setY( QMAX( topLeftExtreme.y(), 0 ) );
bottomRightExtreme.setX( QMIN( bottomRightExtreme.x(), rawImage.width()-1 ) );
bottomRightExtreme.setY( QMIN( bottomRightExtreme.y(), rawImage.height()-1 ) );
//setup progress bar
QString statusMessage = qApp->translate( "removeRedeyeRegions", "Removing Red-Eye:" );
status->showProgressBar( statusMessage, 100 );
qApp->processEvents();
//update progress bar for every 1% of completion
updateIncrement = (int) ( 0.01 *
( bottomRightExtreme.x() - topLeftExtreme.x() + 1 ) *
( bottomRightExtreme.y() - topLeftExtreme.y() + 1 ) );
newProgress = 0;
//find region of interest: constrain search box to boundary that actually contains red enough pixels
findRegionOfInterest(topLeftExtreme, bottomRightExtreme);
//if no pixels were found then immediately return a NULL pointer signaling no change
if(topLeft.x() == -1)
{
//hide progress bar
status->setStatus( "" );
qApp->processEvents();
return NULL;
}
//load an editing image
//two images mus be loaded becuase pixel values are replaced
//using a compbination of niehgbors and their own in order
//to avoid sharp lines at the edge of the saturated region
editedImage = new QImage( filename );
//sanity check: unable to allocated edited image
if( editedImage == NULL)
{
//hide progress bar
status->setStatus( "" );
qApp->processEvents();
return NULL;
}
//convert to 32-bit depth if necessary
if( editedImage->depth() < 32 )
{
QImage* tmp = editedImage;
editedImage = new QImage( tmp->convertDepth( 32, Qt::AutoColor ) );
delete tmp; tmp=NULL;
}
findBlobs();
sortBlobsByDecreasingSize();
findBestTwoBlobs();
//if we found two good blobs then desaturate those only
if(id1 != -1)
{
desaturateBlobs();
}
//else desaturate all pixels above thresh within selection area
else
{
desaturateEntireImage(topLeftExtreme, bottomRightExtreme);
}
//remove status bar
status->setStatus( "" );
qApp->processEvents();
//return pointer to edited image
return editedImage;
}
| void sortBlobsByDecreasingSize | ( | ) |
Definition at line 468 of file redEye.cpp.
References blobAspectRatios, blobCount, blobIDs, blobSizes, ids, ratios, and sizes.
Referenced by removeRedeyeRegions().
{
blobCount = blobIDs.count();
ids = new int[blobCount];
sizes = new int[blobCount];
ratios = new double[blobCount];
int i,j;
for(i=0; i<blobCount; i++)
{
ids[i] = blobIDs.pop();
sizes[i] = blobSizes.pop();
ratios[i] = blobAspectRatios.pop();
}
//quick and dirty bubble sort
for(j = blobCount-1; j>0; j--)
{
for(i=0; i<j; i++)
{
if( sizes[i+1] > sizes[i] )
{
int t = sizes[i+1];
sizes[i+1] = sizes[i];
sizes[i] = t;
t = ids[i+1];
ids[i+1] = ids[i];
ids[i] = t;
double tR = ratios[i+1];
ratios[i+1] = ratios[i];
ratios[i] = tR;
}
}
}
}
1.7.5.1