//============================================== // copyright : (C) 2003-2005 by Will Stokes //============================================== // This program is free software; you can redistribute it // and/or modify it under the terms of the GNU General // Public License as published by the Free Software // Foundation; either version 2 of the License, or // (at your option) any later version. //============================================== //Systemwide includes #include #include //Projectwide includes #include "sharpen.h" #include "blur.h" #include "../tools/imageTools.h" //---------------------------------------------- // Inputs: // ------- // QImage& image - image to blur // float sigma - how much to blur it // QPoint offset - offset within edge image we're working on // QSize fullImageRes - resolution of the full size image // QImage* edgeImage - an edge image constructing using the full size image // bool blurEdges - are we sharpening edges or regions // // Outputs: // -------- // Nothing returned, we'll modify the image passed by refference in place // // Description: // ------------ // The common approach to sharpening images is subtract a // blurred version of an image using the following equation: // // v' = 2*v - vBlur // // ...where v is the original value (luminance) for a given pixel, // vBlur is the blurred value, and v' is the end result. // // While one could apply this blur-subtraction in the individual color channels // you will likely encounter strange artifacts at color channel boundaries where new // colors are introducted. Sharpening in the value/luminance domain helps bright out // image contrast without introducing color artifacts. // // Unfortunately, sharpening using this approach will magnify all image contrast, both // somewhat strong edges and low level noise. We'd like to be able to aggressively sharpen // images without magnifying CCD/film grain noise, but how? // // A somewhat popular solution to this problem is to use an edge image. Constructing edge images // can be difficult, but when provided such information can tell us when to to sharpen and when not to, or // used more wisely, how to blend the sharpend data with the unsharpened original image data seemlessly. // Grayscale edge images can be used in this way by first blurring slightly, then dividing the // value component of a pixel by 255 to get an alpha value. Near edges the value will be closer to // 255 and the resulting alpha will be closer to 1. In between regions where we don't want to // enhance noise by sharpening alpha values will be close to 0, preventing aggressively // sharpened vaues from being used. // // Algorithm: // ---------- // The algorithm works as follows: // 1.) The input image is blurred using the sigma value. The large the sigma value // the more the input image is blurred and the more pronounced edges will become. // 2.) We iterate over each image pixel, fetching the color values of the original and blurred forms // of the image, as well as the color of the give pixel within the edge image. // // An alpha value is computed using the edge image pixel color, which in turn is used // to compute the blended pixel value after sharpening: // // alpha = edgeColor / 255 // v' = alpha* min( max( 2*v - vBlur, 0 ), 1) + (1-alpha)*v; // // Finally, we convert the pixel color back to RGB space and write back // to the resulting sharpened image. // // This algorithm works and was initially tested without the use of an edge image. When // no edge image is provided alpha is simply set to 1 and full sharpening is applied // to every image pixel. // // Future work: // ------------ // Further work needs to be done regarding bluring/sharpening edges and region independently. // The "blurEdges" param allows the algorithm to concentrate sharpening on regions instead of // object boundaries when an edge image is provided; however, such usage is not well understood // or used at this time. //---------------------------------------------- //============================================== void sharpenImage( QImage &image, float sigma, QPoint offset, QSize fullImageRes, QImage* edgeImage, bool blurEdges) { //construct blur copy QImage blurredImage = image.copy(); blurImage( blurredImage, sigma ); //iterate over each pixel and adjust luminance value int x, y; QRgb *origRgb, *blurredRgb, *edgeRgb; uchar *origScanline; uchar *blurredScanline; uchar *edgesScanline = NULL; for(y=0; yheight()-1) * (y+offset.y())) / (fullImageRes.height()-1); edgesScanline = edgeImage->scanLine(edgeY); } for(x=0; xwidth()-1) * (x+offset.x())) / (fullImageRes.width()-1); edgeRgb = ((QRgb*)edgesScanline+edgeX); alpha = ((float) qRed( *edgeRgb )) / 255.0f; //blur regions, not edges if(!blurEdges) alpha = 1.0f - alpha; } //convert to hsv double h1,s1,v1; RGBtoHSV(r1,g1,b1,&h1,&s1,&v1); double h2,s2,v2; RGBtoHSV(r2,g2,b2,&h2,&s2,&v2); //reset v v1 = (alpha * QMIN( QMAX(2*v1 - v2, 0), 1.0 )) + (1-alpha)*v1; //convert adjusted color back to rgb colorspace and clamp HSVtoRGB( &r1,&g1,&b1, h1,s1,v1); int rp = (int) QMIN( QMAX((r1*255), 0), 255 ); int gp = (int) QMIN( QMAX((g1*255), 0), 255 ); int bp = (int) QMIN( QMAX((b1*255), 0), 255 ); //set adjusted color value *origRgb = qRgb(rp,gp,bp); } //x } //y } //==============================================