AlbumShaper
1.0a3
|
00001 //============================================== 00002 // copyright : (C) 2003-2005 by Will Stokes 00003 //============================================== 00004 // This program is free software; you can redistribute it 00005 // and/or modify it under the terms of the GNU General 00006 // Public License as published by the Free Software 00007 // Foundation; either version 2 of the License, or 00008 // (at your option) any later version. 00009 //============================================== 00010 00011 //Systemwide includes 00012 #include <qimage.h> 00013 #include <qstring.h> 00014 #include <qapplication.h> 00015 #include <math.h> 00016 00017 //Projectwide includes 00018 #include "emboss.h" 00019 #include "manipulationOptions.h" 00020 #include "../tools/imageTools.h" 00021 #include "../../gui/statusWidget.h" 00022 00023 //---------------------------------------------- 00024 // Inputs: 00025 // ------- 00026 // QString filename - location of original image on disk 00027 // StatusWidget* status - widget for making progress visible to user 00028 // 00029 // Outputs: 00030 // -------- 00031 // QImage* returned - constructed image 00032 // 00033 // Description: 00034 // ------------ 00035 // This method constructs an embossed version of 00036 // the image by modifying the luminance of each pixel 00037 // by taking a weighted average of pixel luminance within 00038 // a local neighborhood. Most embossing algorithms convert an 00039 // image to grayscale and then combine a pixel's gray value with 00040 // its neighbors using the following convolution matrix: 00041 // 00042 // [ -1 -1 0 00043 // -1 1 1 00044 // 0 1 1 ] 00045 // 00046 // This apporach has two problems. First, all color information 00047 // is lost. Second, the convolution filter is not a function of image 00048 // size, and as a result embossing results on high resolution images are 00049 // barely when viewed on a high resolution screen or printed at high DPI. 00050 // 00051 // My solution to this problem is to: 00052 // 00053 // 1.) Apply the emboss effect on pixel luminance and leave pixel 00054 // color untouched. The varying lightness that results produces 00055 // the same effect, and if a grayscale image is really desired the 00056 // image can be converted to grayscale either before or after the 00057 // emboss effect takes place. 00058 // 00059 // 2.) using neighboring pixels that are a distance D from the 00060 // pixel being modified in the X or Y dimensions. This simply 00061 // modification allows the embossing procedure to run at the 00062 // same speed (larger convolution matrices would slow the algorithm 00063 // to at best O(N^2 * M^2) where there are N^2 pixels in the image 00064 // and the convolution matrix is M^2 in size) while providing the 00065 // same effective emboss effect at any image resolution. This is 00066 // done by computing distance D using the minimum image dimension: 00067 // 00068 // D = MIN( width, height ) 00069 // 00070 // At each pixel, we compute an average luminance of the 6 00071 // neighbors and combine these values with a 50% luminance value for 00072 // the pixel in question. This luminance value is used to replace 00073 // the current pixel luminance before converting it back to RGB 00074 // space and storing in the edited image object. 00075 // 00076 // Neighbor pixels that are clamped to be on the image plane to 00077 // prevent lookups of neighbor pixels with negative coordinates or 00078 // coordinates beyond the width/height of the image in question. 00079 //---------------------------------------------- 00080 00081 //============================================== 00082 QImage* embossEffect( QString filename, ManipulationOptions* options ) 00083 { 00084 //load original image 00085 QImage originalImage( filename ); 00086 00087 //convert to 32-bit depth if necessary 00088 if( originalImage.depth() < 32 ) { originalImage = originalImage.convertDepth( 32, Qt::AutoColor ); } 00089 00090 //create edited image 00091 QImage* editedImage = new QImage( filename ); 00092 00093 //convert to 32-bit depth if necessary 00094 if( editedImage->depth() < 32 ) 00095 { 00096 QImage* tmp = editedImage; 00097 editedImage = new QImage( tmp->convertDepth( 32, Qt::AutoColor ) ); 00098 delete tmp; tmp=NULL; 00099 } 00100 00101 //determine if busy indicators will be used 00102 bool useBusyIndicators = false; 00103 StatusWidget* status = NULL; 00104 if( options != NULL && options->getStatus() != NULL ) 00105 { 00106 useBusyIndicators = true; 00107 status = options->getStatus(); 00108 } 00109 00110 //setup progress bar 00111 if(useBusyIndicators) 00112 { 00113 QString statusMessage = qApp->translate( "embossEffect", "Applying Emboss Effect:" ); 00114 status->showProgressBar( statusMessage, 100 ); 00115 qApp->processEvents(); 00116 } 00117 00118 //update progress bar for every 1% of completion 00119 const int updateIncrement = (int) ( 0.01 * originalImage.width() * originalImage.height() ); 00120 int newProgress = 0; 00121 00122 //iterate over each selected scanline 00123 int x, y; 00124 QRgb* rgb; 00125 uchar* scanLine; 00126 00127 int yPrev, yNext, xPrev, xNext; 00128 00129 //compute the radius using image resolution 00130 double minDimen = (double) QMIN( editedImage->width(), editedImage->height() ); 00131 const int embossRadius = (int) QMAX( 1, (sqrt(minDimen)/8) ); 00132 00133 for( y=0; y<editedImage->height(); y++) 00134 { 00135 scanLine = originalImage.scanLine(y); 00136 00137 //compute previous and next y pixel coordinates 00138 yPrev = QMAX( y-embossRadius, 0 ); 00139 yNext = QMIN( y+embossRadius, editedImage->height() - 1 ); 00140 00141 //iterate over each selected pixel in scanline 00142 for( x=0; x<editedImage->width(); x++) 00143 { 00144 //compute previous and next x pixel coordinates 00145 xPrev = QMAX( x-embossRadius, 0 ); 00146 xNext = QMIN( x+embossRadius, editedImage->width() - 1 ); 00147 00148 //start with a default luminance of 128 (50% luminance) 00149 int sum = 128; 00150 00151 //sum weighted gray values of neighbors 00152 scanLine = originalImage.scanLine( yPrev ); 00153 sum-= qGray( *((QRgb*)scanLine + xPrev ) ); 00154 sum-= qGray( *((QRgb*)scanLine + x ) ); 00155 00156 scanLine = originalImage.scanLine( y ); 00157 sum-= qGray( *((QRgb*)scanLine + xPrev ) ); 00158 sum+= qGray( *((QRgb*)scanLine + xNext ) ); 00159 00160 scanLine = originalImage.scanLine( yNext ); 00161 sum+= qGray( *((QRgb*)scanLine + x ) ); 00162 sum+= qGray( *((QRgb*)scanLine + xNext ) ); 00163 00164 //clamp sum to within 0-255 range 00165 sum = QMAX( QMIN( sum, 255), 0 ); 00166 00167 //get original pixel color in HSV space 00168 scanLine = editedImage->scanLine(y); 00169 rgb = ((QRgb*)scanLine+x); 00170 double r = ((double)qRed(*rgb) )/255.0; 00171 double g = ((double)qGreen(*rgb) )/255.0; 00172 double b = ((double)qBlue(*rgb) )/255.0; 00173 00174 //convert to hsv 00175 double h,s,v; 00176 RGBtoHSV(r,g,b,&h,&s,&v); 00177 00178 //reset v 00179 v = ((double)sum)/255; 00180 00181 //convert adjusted color back to rgb colorspace and clamp 00182 HSVtoRGB( &r,&g,&b, h,s,v); 00183 int rp = (int) QMIN( QMAX((r*255), 0), 255 ); 00184 int gp = (int) QMIN( QMAX((g*255), 0), 255 ); 00185 int bp = (int) QMIN( QMAX((b*255), 0), 255 ); 00186 00187 //set adjusted color value 00188 *rgb = qRgb(rp,gp,bp); 00189 00190 //update status bar if significant progress has been made since last update 00191 if(useBusyIndicators) 00192 { 00193 newProgress++; 00194 if(newProgress >= updateIncrement) 00195 { 00196 newProgress = 0; 00197 status->incrementProgress(); 00198 qApp->processEvents(); 00199 } 00200 } 00201 00202 } 00203 } 00204 00205 //return pointer to edited image 00206 return editedImage; 00207 } 00208 //==============================================