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; } } } }