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 00016 //Projectwide includes 00017 #include "contrast.h" 00018 #include "../tools/imageTools.h" 00019 #include "../../gui/statusWidget.h" 00020 00021 //---------------------------------------------- 00022 // Inputs: 00023 // ------- 00024 // QString filename - location of original image on disk 00025 // StatusWidget* status - widget for making progress visible to user 00026 // 00027 // Outputs: 00028 // -------- 00029 // QImage* returned - constructed image 00030 // 00031 // Description: 00032 // ------------ 00033 // This method simply stretchs the luminance rnage to use up the entire dynamic range, in our 00034 // case from [0,255]. This algorithm is very similar to the color enhancement algorithm, except 00035 // in this case we convert color values to HSL space and work with the L values (luminance). 00036 // 00037 // The algorithm can be broken up into two steps: 00038 // 1.) finding the current luminance endpoints (e.g. [6,120]) 00039 // 2.) stretching the luminance range to fit the full [0,255] range 00040 // 00041 // One could simply iterate over the entire image and find the minimum and 00042 // maximum luminance values. However; often there is residual 00043 // noise at the endpoints since camera sensors always provide some noise, 00044 // while the majority of pixels fall somewhere in the middle. If we constructed 00045 // a histogram for of pixel luminance values it might look like this: 00046 // 5 | . 00047 // . | . . 00048 // . | . . 00049 // 1 | . . . . . 00050 // 0 |. .... ........ ............. ..... 00051 // |-X------------*--------------*-----------X----- 00052 // 0 100 175 255 00053 // 00054 // Here luminance values are plotted across the x axis, while the y axis indicates 00055 // how offten a particular luminance value was seen. 00056 // 00057 // In this example we notice most pixel luminance values were observed fall in 00058 // the 100-175 range. A few pixels with values of 5 or 10 were seen, and a 00059 // few with around 230 were seen. We want to stretch the common case, 100-175, 00060 // out to the full 0-255 range. That will make the pixels that were 175 now 255, 00061 // those that wer 100 now 0, thus darks will become darker, brights will become 00062 // brigther. We'll lineraly scale all pixels inbetween. Any pixels beyond the 00063 // 100-175 range will get clamped at 0 and 255 respectively. 00064 // 00065 // Now, if we simply find the smallest and largest values we'll get soemthinglike 00066 // 5 and 235, and stretching will have very little effect on the image. Instead, 00067 // we first actually populate a histogram array, then determine the 1% and 00068 // 99% boundaries but walking from the end points of hte histogram array and 00069 // adding up the elements. Once 1% of the pixels have been accounted for we set 00070 // the boundary endpoint to the current index, thus giving us 100,175 in this case. 00071 // 00072 // Scaling the pixels is only marginally more complicated when in 00073 // the color enhancement algorithm. A second pass over the 00074 // image takes place during which a pixels color is fetched, converted to HSL, 00075 // the luminance is stretched according to the following equation, the color is 00076 // converted back to RGB, and the value is written back to the image. 00077 // 00078 // l' = [255*(l-lLow)] / [lHigh - lLow] 00079 // 00080 // where l[Low/High] refer to the 1% and 99% endpoints for the luminance histogram. 00081 // 00082 //---------------------------------------------- 00083 00084 //============================================== 00085 QImage* enhanceImageContrast( QString filename, StatusWidget* status ) 00086 { 00087 //load original image 00088 QImage* editedImage = new QImage( filename ); 00089 00090 //convert to 32-bit depth if necessary 00091 if( editedImage->depth() < 32 ) 00092 { 00093 QImage* tmp = editedImage; 00094 editedImage = new QImage( tmp->convertDepth( 32, Qt::AutoColor ) ); 00095 delete tmp; tmp=NULL; 00096 } 00097 00098 //enhance contrast 00099 enhanceImageContrast( editedImage, status ); 00100 00101 //return pointer to edited image 00102 return editedImage; 00103 } 00104 //============================================== 00105 void enhanceImageContrast( QImage* editedImage, StatusWidget* status ) 00106 { 00107 //setup progress bar 00108 if(status) 00109 { 00110 QString statusMessage = qApp->translate( "enhanceImageContrast", "Enhancing Contrast:" ); 00111 status->showProgressBar( statusMessage, 100 ); 00112 qApp->processEvents(); 00113 } 00114 00115 //update progress bar for every 1% of completion 00116 const int updateIncrement = (int) ( 0.01 * editedImage->width() * editedImage->height() ); 00117 int newProgress = 0; 00118 00122 00123 //construct intensity histograph 00124 int grayVals[256]; 00125 int i=0; 00126 for(i=0; i<256; i++) { grayVals[i] = 0; } 00127 00128 //populate histogram by iterating over all image pixels 00129 int numPixels = editedImage->width()*editedImage->height(); 00130 QRgb* rgb; 00131 double grayValue; 00132 uchar* scanLine; 00133 int x, y; 00134 for( y=0; y<editedImage->height(); y++) 00135 { 00136 //iterate over each selected pixel in scanline 00137 scanLine = editedImage->scanLine(y); 00138 for( x=0; x<editedImage->width(); x++) 00139 { 00140 rgb = ((QRgb*)scanLine+x); 00141 grayValue = RGBtoL(rgb); 00142 grayVals[(int)grayValue]++; 00143 } //for x 00144 } //for y 00145 00146 //find 1% and 99% precenticles 00147 //we'll stretch these values so we avoid outliers from affecting the stretch 00148 int sum=0; 00149 double indexLow, indexHigh; 00150 indexLow = -1.0; indexHigh = -1.0; 00151 for(i=0; i<256; i++) 00152 { 00153 sum+=grayVals[i]; 00154 00155 //if 1% not found yet and criteria met set index 00156 if(indexLow < 0 && 00157 sum >= 0.01*numPixels) 00158 { 00159 indexLow = ((double)i)/255.0; 00160 } 00161 00162 //if 99% not found yet and criteria met set index 00163 if(indexHigh < 0 && 00164 sum >= 0.99*numPixels) 00165 { 00166 indexHigh = ((double)i)/255.0; 00167 } 00168 } 00169 00170 //only apply scaling if indexHigh > indexLow 00171 if(indexHigh > indexLow) 00172 { 00173 //run through all image pixels a second time, this time scaling coordinates as necessary 00174 for( y=0; y<editedImage->height(); y++) 00175 { 00176 //iterate over each selected pixel in scanline 00177 scanLine = editedImage->scanLine(y); 00178 for( x=0; x<editedImage->width(); x++) 00179 { 00180 //get color coordinates and convert to 0-1 scale 00181 rgb = ((QRgb*)scanLine+x); 00182 double r = ((double)qRed(*rgb) )/255.0; 00183 double g = ((double)qGreen(*rgb) )/255.0; 00184 double b = ((double)qBlue(*rgb) )/255.0; 00185 00186 //convert to hsv 00187 double h,s,v; 00188 RGBtoHSV(r,g,b,&h,&s,&v); 00189 00190 //scale and clamp v 00191 v = (v-indexLow)/(indexHigh-indexLow); 00192 00193 //convert adjusted color back to rgb colorspace and clamp 00194 HSVtoRGB( &r,&g,&b, h,s,v); 00195 int rp = (int) QMIN( QMAX((r*255), 0), 255 ); 00196 int gp = (int) QMIN( QMAX((g*255), 0), 255 ); 00197 int bp = (int) QMIN( QMAX((b*255), 0), 255 ); 00198 00199 //set adjusted color value 00200 *rgb = qRgb(rp,gp,bp); 00201 00202 //update status bar if significant progress has been made since last update 00203 if(status) 00204 { 00205 newProgress++; 00206 if(newProgress >= updateIncrement) 00207 { 00208 newProgress = 0; 00209 status->incrementProgress(); 00210 qApp->processEvents(); 00211 } 00212 } 00213 00214 } //for x 00215 } //for y 00216 } //if scaling should be preforemd 00217 00218 //remove status bar 00219 if(status) 00220 { 00221 status->setStatus( "" ); 00222 qApp->processEvents(); 00223 } 00224 } 00225 //==============================================