Main Page | Namespace List | Class Hierarchy | Class List | File List | Class Members | File Members

sharpen.cpp

Go to the documentation of this file.
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 00015 //Projectwide includes 00016 #include "sharpen.h" 00017 #include "blur.h" 00018 #include "../tools/imageTools.h" 00019 00020 //---------------------------------------------- 00021 // Inputs: 00022 // ------- 00023 // QImage& image - image to blur 00024 // float sigma - how much to blur it 00025 // QPoint offset - offset within edge image we're working on 00026 // QSize fullImageRes - resolution of the full size image 00027 // QImage* edgeImage - an edge image constructing using the full size image 00028 // bool blurEdges - are we sharpening edges or regions 00029 // 00030 // Outputs: 00031 // -------- 00032 // Nothing returned, we'll modify the image passed by refference in place 00033 // 00034 // Description: 00035 // ------------ 00036 // The common approach to sharpening images is subtract a 00037 // blurred version of an image using the following equation: 00038 // 00039 // v' = 2*v - vBlur 00040 // 00041 // ...where v is the original value (luminance) for a given pixel, 00042 // vBlur is the blurred value, and v' is the end result. 00043 // 00044 // While one could apply this blur-subtraction in the individual color channels 00045 // you will likely encounter strange artifacts at color channel boundaries where new 00046 // colors are introducted. Sharpening in the value/luminance domain helps bright out 00047 // image contrast without introducing color artifacts. 00048 // 00049 // Unfortunately, sharpening using this approach will magnify all image contrast, both 00050 // somewhat strong edges and low level noise. We'd like to be able to aggressively sharpen 00051 // images without magnifying CCD/film grain noise, but how? 00052 // 00053 // A somewhat popular solution to this problem is to use an edge image. Constructing edge images 00054 // can be difficult, but when provided such information can tell us when to to sharpen and when not to, or 00055 // used more wisely, how to blend the sharpend data with the unsharpened original image data seemlessly. 00056 // Grayscale edge images can be used in this way by first blurring slightly, then dividing the 00057 // value component of a pixel by 255 to get an alpha value. Near edges the value will be closer to 00058 // 255 and the resulting alpha will be closer to 1. In between regions where we don't want to 00059 // enhance noise by sharpening alpha values will be close to 0, preventing aggressively 00060 // sharpened vaues from being used. 00061 // 00062 // Algorithm: 00063 // ---------- 00064 // The algorithm works as follows: 00065 // 1.) The input image is blurred using the sigma value. The large the sigma value 00066 // the more the input image is blurred and the more pronounced edges will become. 00067 // 2.) We iterate over each image pixel, fetching the color values of the original and blurred forms 00068 // of the image, as well as the color of the give pixel within the edge image. 00069 // 00070 // An alpha value is computed using the edge image pixel color, which in turn is used 00071 // to compute the blended pixel value after sharpening: 00072 // 00073 // alpha = edgeColor / 255 00074 // v' = alpha* min( max( 2*v - vBlur, 0 ), 1) + (1-alpha)*v; 00075 // 00076 // Finally, we convert the pixel color back to RGB space and write back 00077 // to the resulting sharpened image. 00078 // 00079 // This algorithm works and was initially tested without the use of an edge image. When 00080 // no edge image is provided alpha is simply set to 1 and full sharpening is applied 00081 // to every image pixel. 00082 // 00083 // Future work: 00084 // ------------ 00085 // Further work needs to be done regarding bluring/sharpening edges and region independently. 00086 // The "blurEdges" param allows the algorithm to concentrate sharpening on regions instead of 00087 // object boundaries when an edge image is provided; however, such usage is not well understood 00088 // or used at this time. 00089 //---------------------------------------------- 00090 00091 //============================================== 00092 void sharpenImage( QImage &image, float sigma, 00093 QPoint offset, QSize fullImageRes, 00094 QImage* edgeImage, bool blurEdges) 00095 { 00096 //construct blur copy 00097 QImage blurredImage = image.copy(); 00098 blurImage( blurredImage, sigma ); 00099 00100 //iterate over each pixel and adjust luminance value 00101 int x, y; 00102 QRgb *origRgb, *blurredRgb, *edgeRgb; 00103 uchar *origScanline; 00104 uchar *blurredScanline; 00105 uchar *edgesScanline = NULL; 00106 00107 for(y=0; y<image.height(); y++) 00108 { 00109 origScanline = image.scanLine(y); 00110 blurredScanline = blurredImage.scanLine(y); 00111 if( edgeImage != NULL ) 00112 { 00113 int edgeY = ((edgeImage->height()-1) * (y+offset.y())) / (fullImageRes.height()-1); 00114 edgesScanline = edgeImage->scanLine(edgeY); 00115 } 00116 00117 for(x=0; x<image.width(); x++) 00118 { 00119 //get rgb triplets 00120 origRgb = ((QRgb*)origScanline+x); 00121 double r1 = ((double)qRed(*origRgb) )/255.0; 00122 double g1 = ((double)qGreen(*origRgb) )/255.0; 00123 double b1 = ((double)qBlue(*origRgb) )/255.0; 00124 00125 blurredRgb = ((QRgb*)blurredScanline+x); 00126 double r2 = ((double)qRed(*blurredRgb) )/255.0; 00127 double g2 = ((double)qGreen(*blurredRgb) )/255.0; 00128 double b2 = ((double)qBlue(*blurredRgb) )/255.0; 00129 00130 //sharpen the entire thing! 00131 float alpha; 00132 if( edgeImage == NULL) 00133 alpha = 1.0f; 00134 else 00135 { 00136 int edgeX = ((edgeImage->width()-1) * (x+offset.x())) / (fullImageRes.width()-1); 00137 edgeRgb = ((QRgb*)edgesScanline+edgeX); 00138 00139 alpha = ((float) qRed( *edgeRgb )) / 255.0f; 00140 00141 //blur regions, not edges 00142 if(!blurEdges) 00143 alpha = 1.0f - alpha; 00144 } 00145 00146 //convert to hsv 00147 double h1,s1,v1; 00148 RGBtoHSV(r1,g1,b1,&h1,&s1,&v1); 00149 00150 double h2,s2,v2; 00151 RGBtoHSV(r2,g2,b2,&h2,&s2,&v2); 00152 00153 //reset v 00154 v1 = (alpha * QMIN( QMAX(2*v1 - v2, 0), 1.0 )) + (1-alpha)*v1; 00155 00156 //convert adjusted color back to rgb colorspace and clamp 00157 HSVtoRGB( &r1,&g1,&b1, h1,s1,v1); 00158 int rp = (int) QMIN( QMAX((r1*255), 0), 255 ); 00159 int gp = (int) QMIN( QMAX((g1*255), 0), 255 ); 00160 int bp = (int) QMIN( QMAX((b1*255), 0), 255 ); 00161 00162 //set adjusted color value 00163 *origRgb = qRgb(rp,gp,bp); 00164 } //x 00165 } //y 00166 00167 } 00168 //==============================================

Generated on Sun Mar 4 19:42:56 2007 for AlbumShaper by doxygen 1.3.7