AlbumShaper  1.0a3
sharpen.cpp
Go to the documentation of this file.
1 //==============================================
2 // copyright : (C) 2003-2005 by Will Stokes
3 //==============================================
4 // This program is free software; you can redistribute it
5 // and/or modify it under the terms of the GNU General
6 // Public License as published by the Free Software
7 // Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //==============================================
10 
11 //Systemwide includes
12 #include <qimage.h>
13 #include <qstring.h>
14 
15 #define MIN(x,y) ((x) < (y) ? (x) : (y))
16 #define MAX(x,y) ((x) < (y) ? (x) : (y))
17 
18 //Projectwide includes
19 #include "sharpen.h"
20 #include "blur.h"
21 #include "../tools/imageTools.h"
22 
23 //----------------------------------------------
24 // Inputs:
25 // -------
26 // QImage& image - image to blur
27 // float sigma - how much to blur it
28 // QPoint offset - offset within edge image we're working on
29 // QSize fullImageRes - resolution of the full size image
30 // QImage* edgeImage - an edge image constructing using the full size image
31 // bool blurEdges - are we sharpening edges or regions
32 //
33 // Outputs:
34 // --------
35 // Nothing returned, we'll modify the image passed by refference in place
36 //
37 // Description:
38 // ------------
39 // The common approach to sharpening images is subtract a
40 // blurred version of an image using the following equation:
41 //
42 // v' = 2*v - vBlur
43 //
44 // ...where v is the original value (luminance) for a given pixel,
45 // vBlur is the blurred value, and v' is the end result.
46 //
47 // While one could apply this blur-subtraction in the individual color channels
48 // you will likely encounter strange artifacts at color channel boundaries where new
49 // colors are introducted. Sharpening in the value/luminance domain helps bright out
50 // image contrast without introducing color artifacts.
51 //
52 // Unfortunately, sharpening using this approach will magnify all image contrast, both
53 // somewhat strong edges and low level noise. We'd like to be able to aggressively sharpen
54 // images without magnifying CCD/film grain noise, but how?
55 //
56 // A somewhat popular solution to this problem is to use an edge image. Constructing edge images
57 // can be difficult, but when provided such information can tell us when to to sharpen and when not to, or
58 // used more wisely, how to blend the sharpend data with the unsharpened original image data seemlessly.
59 // Grayscale edge images can be used in this way by first blurring slightly, then dividing the
60 // value component of a pixel by 255 to get an alpha value. Near edges the value will be closer to
61 // 255 and the resulting alpha will be closer to 1. In between regions where we don't want to
62 // enhance noise by sharpening alpha values will be close to 0, preventing aggressively
63 // sharpened vaues from being used.
64 //
65 // Algorithm:
66 // ----------
67 // The algorithm works as follows:
68 // 1.) The input image is blurred using the sigma value. The large the sigma value
69 // the more the input image is blurred and the more pronounced edges will become.
70 // 2.) We iterate over each image pixel, fetching the color values of the original and blurred forms
71 // of the image, as well as the color of the give pixel within the edge image.
72 //
73 // An alpha value is computed using the edge image pixel color, which in turn is used
74 // to compute the blended pixel value after sharpening:
75 //
76 // alpha = edgeColor / 255
77 // v' = alpha* min( max( 2*v - vBlur, 0 ), 1) + (1-alpha)*v;
78 //
79 // Finally, we convert the pixel color back to RGB space and write back
80 // to the resulting sharpened image.
81 //
82 // This algorithm works and was initially tested without the use of an edge image. When
83 // no edge image is provided alpha is simply set to 1 and full sharpening is applied
84 // to every image pixel.
85 //
86 // Future work:
87 // ------------
88 // Further work needs to be done regarding bluring/sharpening edges and region independently.
89 // The "blurEdges" param allows the algorithm to concentrate sharpening on regions instead of
90 // object boundaries when an edge image is provided; however, such usage is not well understood
91 // or used at this time.
92 //----------------------------------------------
93 
94 //==============================================
95 void sharpenImage( QImage &image, float sigma,
96  QPoint offset, QSize fullImageRes,
97  QImage* edgeImage, bool blurEdges)
98 {
99  //construct blur copy
100  QImage blurredImage = image.copy();
101  blurImage( blurredImage, sigma );
102 
103  //iterate over each pixel and adjust luminance value
104  int x, y;
105  QRgb *origRgb, *blurredRgb, *edgeRgb;
106  uchar *origScanline;
107  uchar *blurredScanline;
108  uchar *edgesScanline = NULL;
109 
110  for(y=0; y<image.height(); y++)
111  {
112  origScanline = image.scanLine(y);
113  blurredScanline = blurredImage.scanLine(y);
114  if( edgeImage != NULL )
115  {
116  int edgeY = ((edgeImage->height()-1) * (y+offset.y())) / (fullImageRes.height()-1);
117  edgesScanline = edgeImage->scanLine(edgeY);
118  }
119 
120  for(x=0; x<image.width(); x++)
121  {
122  //get rgb triplets
123  origRgb = ((QRgb*)origScanline+x);
124  double r1 = ((double)qRed(*origRgb) )/255.0;
125  double g1 = ((double)qGreen(*origRgb) )/255.0;
126  double b1 = ((double)qBlue(*origRgb) )/255.0;
127 
128  blurredRgb = ((QRgb*)blurredScanline+x);
129  double r2 = ((double)qRed(*blurredRgb) )/255.0;
130  double g2 = ((double)qGreen(*blurredRgb) )/255.0;
131  double b2 = ((double)qBlue(*blurredRgb) )/255.0;
132 
133  //sharpen the entire thing!
134  float alpha;
135  if( edgeImage == NULL)
136  alpha = 1.0f;
137  else
138  {
139  int edgeX = ((edgeImage->width()-1) * (x+offset.x())) / (fullImageRes.width()-1);
140  edgeRgb = ((QRgb*)edgesScanline+edgeX);
141 
142  alpha = ((float) qRed( *edgeRgb )) / 255.0f;
143 
144  //blur regions, not edges
145  if(!blurEdges)
146  alpha = 1.0f - alpha;
147  }
148 
149  //convert to hsv
150  double h1,s1,v1;
151  RGBtoHSV(r1,g1,b1,&h1,&s1,&v1);
152 
153  double h2,s2,v2;
154  RGBtoHSV(r2,g2,b2,&h2,&s2,&v2);
155 
156  //reset v
157  v1 = (alpha * MIN( MAX(2*v1 - v2, 0), 1.0 )) + (1-alpha)*v1;
158 
159  //convert adjusted color back to rgb colorspace and clamp
160  HSVtoRGB( &r1,&g1,&b1, h1,s1,v1);
161  int rp = (int) MIN( MAX((r1*255), 0), 255 );
162  int gp = (int) MIN( MAX((g1*255), 0), 255 );
163  int bp = (int) MIN( MAX((b1*255), 0), 255 );
164 
165  //set adjusted color value
166  *origRgb = qRgb(rp,gp,bp);
167  } //x
168  } //y
169 
170 }
171 //==============================================
void blurImage(QImage &image, float sigma)
Definition: blur.cpp:94
void HSVtoRGB(double *r, double *g, double *b, double h, double s, double v)
Convert a HSV color triplet to RGB.
Definition: imageTools.cpp:264
QImage * edgeImage
Definition: blur.cpp:87
#define MIN(x, y)
Definition: sharpen.cpp:15
void sharpenImage(QImage &image, float sigma, QPoint offset, QSize fullImageRes, QImage *edgeImage, bool blurEdges)
Definition: sharpen.cpp:95
void RGBtoHSV(double r, double g, double b, double *h, double *s, double *v)
Convert a RGB color triplet to HSV.
Definition: imageTools.cpp:231
float b1
Definition: blur.cpp:78
float b2
Definition: blur.cpp:78
#define MAX(x, y)
Definition: sharpen.cpp:16