gimp r27912 - in trunk: . plug-ins/common
- From: neo svn gnome org
- To: svn-commits-list gnome org
- Subject: gimp r27912 - in trunk: . plug-ins/common
- Date: Sat, 17 Jan 2009 22:17:42 +0000 (UTC)
Author: neo
Date: Sat Jan 17 22:17:42 2009
New Revision: 27912
URL: http://svn.gnome.org/viewvc/gimp?rev=27912&view=rev
Log:
2009-01-17 Sven Neumann <sven gimp org>
Bug 568095 â Patch to improve unsharp mask performance
* plug-ins/common/unsharp-mask.c (unsharp_region): applied patch
from Winston Chang that improves performance for larger radii by
approximating the gaussian blur with a three-pass box blur.
Modified:
trunk/ChangeLog
trunk/plug-ins/common/unsharp-mask.c
Modified: trunk/plug-ins/common/unsharp-mask.c
==============================================================================
--- trunk/plug-ins/common/unsharp-mask.c (original)
+++ trunk/plug-ins/common/unsharp-mask.c Sat Jan 17 22:17:42 2009
@@ -61,17 +61,23 @@
gint *nreturn_vals,
GimpParam **return_vals);
-static void blur_line (const gdouble *cmatrix,
+static void gaussian_blur_line (const gdouble *cmatrix,
const gint cmatrix_length,
const guchar *src,
guchar *dest,
const gint len,
- const gint bytes);
+ const gint bpp);
+static void box_blur_line (const gint box_width,
+ const gint even_offset,
+ const guchar *src,
+ guchar *dest,
+ const gint len,
+ const gint bpp);
static gint gen_convolve_matrix (gdouble std_dev,
gdouble **cmatrix);
static void unsharp_region (GimpPixelRgn *srcPTR,
GimpPixelRgn *dstPTR,
- gint bytes,
+ gint bpp,
gdouble radius,
gdouble amount,
gint x1,
@@ -235,17 +241,114 @@
#endif
}
+/* This function is written as if it is blurring a row of pixels,
+ * even though it can operate on colums, too. There is no difference
+ * in the processing of the lines, at least to the blur_line function.
+ */
+static void
+box_blur_line (const gint box_width, /* Width of the kernel */
+ const gint even_offset,/* If even width,
+ offset to left or right */
+ const guchar *src, /* Pointer to source buffer */
+ guchar *dest, /* Pointer to destination buffer */
+ const gint len, /* Length of buffer, in pixels */
+ const gint bpp) /* Bytes per pixel */
+{
+ gint i;
+ gint lead; /* This marks the leading edge of the kernel */
+ gint output; /* This marks the center of ther kernel */
+ gint trail; /* This marks the pixel BEHIND the last 1 in the
+ kernel; it's the pixel to remove from the accumulator. */
+ gint ac[bpp]; /* Accumulator for each channel */
+
+
+ /* The algorithm differs for even and odd-sized kernels.
+ * With the output at the center,
+ * If odd, the kernel might look like this: 0011100
+ * If even, the kernel will either be centered on the boundary between
+ * the output and its left neighbor, or on the boundary between the
+ * output and its right neighbor, depending on even_lr.
+ * So it might be 0111100 or 0011110, where output is on the center
+ * of these arrays.
+ */
+ lead = 0;
+
+ if (box_width % 2)
+ /* Odd-width kernel */
+ {
+ output = lead - (box_width - 1) / 2;
+ trail = lead - box_width;
+ }
+ else
+ /* Even-width kernel. */
+ {
+ /* Right offset */
+ if (even_offset == 1)
+ {
+ output = lead + 1 - box_width / 2;
+ trail = lead - box_width;
+ }
+ /* Left offset */
+ else if (even_offset == -1)
+ {
+ output = lead - box_width / 2;
+ trail = lead - box_width;
+ }
+ /* If even_offset isn't 1 or -1, there's some error. */
+ else
+ g_assert_not_reached ();
+ }
+
+ /* Initialize accumulator */
+ for (i = 0; i < bpp; i++)
+ ac[i] = 0;
+
+ while (output < len)
+ {
+ /* The number of pixels that are both in the image and
+ * currently covered by the kernel. This is necessary to
+ * handle edge cases. */
+ guint coverage = (lead < len ? lead : len-1) - (trail >=0 ? trail : -1);
+
+ for (i = 0; i < bpp; i++)
+ {
+ /* If the leading edge of the kernel is still on the image,
+ * add the value there to the accumulator. */
+ if (lead < len)
+ ac[i] += src[bpp * lead + i];
+
+ /* If the trailing edge of the kernel is on the image,
+ * subtract the value there from the accumulator. */
+ if (trail >= 0)
+ ac[i] -= src[bpp * trail + i];
+
+ /* Take the averaged value in the accumulator and store
+ * that value in the output. The number of pixels currently
+ * stored in the accumulator can be less than the nominal
+ * width of the kernel because the kernel can go "over the edge"
+ * of the image. */
+ if (output >= 0)
+ dest[bpp * output + i] = (ac[i] + (coverage >> 1)) / coverage;
+ }
+
+ lead++;
+ output++;
+ trail++;
+ }
+}
+
+
/* This function is written as if it is blurring a column at a time,
* even though it can operate on rows, too. There is no difference
* in the processing of the lines, at least to the blur_line function.
*/
static void
-blur_line (const gdouble *cmatrix,
- const gint cmatrix_length,
- const guchar *src,
- guchar *dest,
- const gint len,
- const gint bytes)
+gaussian_blur_line (const gdouble *cmatrix,
+ const gint cmatrix_length,
+ const guchar *src,
+ guchar *dest,
+ const gint len,
+ const gint bpp)
{
const gdouble *cmatrix_p;
const guchar *src_p;
@@ -275,7 +378,7 @@
src_p = src;
- for (i = 0; i < bytes; i++)
+ for (i = 0; i < bpp; i++)
{
gdouble sum = 0;
@@ -287,7 +390,7 @@
j + cmatrix_middle - row < cmatrix_length)
sum += *src_p1 * cmatrix[j];
- src_p1 += bytes;
+ src_p1 += bpp;
}
*dest++ = (guchar) ROUND (sum / scale);
@@ -307,7 +410,7 @@
src_p = src;
- for (i = 0; i < bytes; i++)
+ for (i = 0; i < bpp; i++)
{
gdouble sum = 0;
@@ -316,7 +419,7 @@
for (j = cmatrix_middle - row; j < cmatrix_length; j++)
{
sum += *src_p1 * cmatrix[j];
- src_p1 += bytes;
+ src_p1 += bpp;
}
*dest++ = (guchar) ROUND (sum / scale);
@@ -326,9 +429,9 @@
/* go through each pixel in each col */
for (; row < len - cmatrix_middle; row++)
{
- src_p = src + (row - cmatrix_middle) * bytes;
+ src_p = src + (row - cmatrix_middle) * bpp;
- for (i = 0; i < bytes; i++)
+ for (i = 0; i < bpp; i++)
{
gdouble sum = 0;
@@ -338,7 +441,7 @@
for (j = 0; j < cmatrix_length; j++)
{
sum += cmatrix[j] * *src_p1;
- src_p1 += bytes;
+ src_p1 += bpp;
}
src_p++;
@@ -355,9 +458,9 @@
for (j = 0; j < len - row + cmatrix_middle; j++)
scale += cmatrix[j];
- src_p = src + (row - cmatrix_middle) * bytes;
+ src_p = src + (row - cmatrix_middle) * bpp;
- for (i = 0; i < bytes; i++)
+ for (i = 0; i < bpp; i++)
{
gdouble sum = 0;
@@ -366,7 +469,7 @@
for (j = 0; j < len - row + cmatrix_middle; j++)
{
sum += *src_p1 * cmatrix[j];
- src_p1 += bytes;
+ src_p1 += bpp;
}
*dest++ = (guchar) ROUND (sum / scale);
@@ -409,8 +512,8 @@
static void
unsharp_region (GimpPixelRgn *srcPR,
GimpPixelRgn *destPR,
- gint bytes,
- gdouble radius,
+ gint bpp,
+ gdouble radius, /* Radius, AKA standard deviation */
gdouble amount,
gint x1,
gint x2,
@@ -418,42 +521,115 @@
gint y2,
gboolean show_progress)
{
- guchar *src;
- guchar *dest;
- gint width = x2 - x1;
- gint height = y2 - y1;
- gdouble *cmatrix = NULL;
- gint cmatrix_length;
- gint row, col;
- gint threshold = unsharp_params.threshold;
+ guchar *src; /* Temporary copy of source row/col */
+ guchar *dest; /* Temporary copy of destination row/col */
+ const gint width = x2 - x1;
+ const gint height = y2 - y1;
+ gdouble *cmatrix = NULL; /* Convolution matrix (for gaussian) */
+ gint cmatrix_length = 0;
+ gint row, col; /* Row,column counter */
+ const gint threshold = unsharp_params.threshold;
+ gboolean box_blur; /* If we want to use a three pass box blur
+ * instead of a gaussian blur
+ */
+ gint box_width = 0;
if (show_progress)
gimp_progress_init (_("Blurring"));
- /* generate convolution matrix
- and make sure it's smaller than each dimension */
- cmatrix_length = gen_convolve_matrix (radius, &cmatrix);
-
- /* allocate buffers */
- src = g_new (guchar, MAX (width, height) * bytes);
- dest = g_new (guchar, MAX (width, height) * bytes);
+ /* If the radius is less than 10, use a true gaussian kernel. This
+ * is slower, but more accurate and allows for finer adjustments.
+ * Otherwise use a three-pass box blur; this is much faster but it
+ * isn't a perfect approximation, and it only allows radius
+ * increments of about 0.42.
+ */
+ if (radius < 10)
+ {
+ box_blur = FALSE;
+ /* If true gaussian, generate convolution matrix
+ and make sure it's smaller than each dimension */
+ cmatrix_length = gen_convolve_matrix (radius, &cmatrix);
+ }
+ else
+ {
+ box_blur = TRUE;
+ /* Three box blurs of this width approximate a gaussian */
+ box_width = ROUND (radius * 3 * sqrt (2 * G_PI) / 4);
+ }
- /* blur the rows */
+ /* Allocate buffers temporary copies of a row/column */
+ src = g_new (guchar, MAX (width, height) * bpp);
+ dest = g_new (guchar, MAX (width, height) * bpp);
+
+ /* Blur the rows */
for (row = 0; row < height; row++)
{
gimp_pixel_rgn_get_row (srcPR, src, x1, y1 + row, width);
- blur_line (cmatrix, cmatrix_length, src, dest, width, bytes);
+
+ if (box_blur)
+ {
+ /* Odd-width box blur: repeat 3 times, centered on output pixel.
+ * Swap back and forth between the buffers. */
+ if (box_width % 2)
+ {
+ box_blur_line (box_width, 0, src, dest, width, bpp);
+ box_blur_line (box_width, 0, dest, src, width, bpp);
+ box_blur_line (box_width, 0, src, dest, width, bpp);
+ }
+ /* Even-width box blur:
+ * This method is suggested by the specification for SVG.
+ * One pass with width n, centered between output and right pixel
+ * One pass with width n, centered between output and left pixel
+ * One pass with width n+1, centered on output pixel
+ * Swap back and forth between buffers.
+ */
+ else
+ {
+ box_blur_line (box_width, -1, src, dest, width, bpp);
+ box_blur_line (box_width, 1, dest, src, width, bpp);
+ box_blur_line (box_width+1, 0, src, dest, width, bpp);
+ }
+ }
+ else
+ {
+ /* Gaussian blur */
+ gaussian_blur_line (cmatrix, cmatrix_length, src, dest, width, bpp);
+ }
+
gimp_pixel_rgn_set_row (destPR, dest, x1, y1 + row, width);
if (show_progress && row % 16 == 0)
gimp_progress_update ((gdouble) row / (3 * height));
}
- /* blur the cols */
+ /* Blur the cols. Essentially same as above. */
for (col = 0; col < width; col++)
{
gimp_pixel_rgn_get_col (destPR, src, x1 + col, y1, height);
- blur_line (cmatrix, cmatrix_length, src, dest, height, bytes);
+
+ if (box_blur)
+ {
+ /* Odd-width box blur */
+ if (box_width % 2)
+ {
+ box_blur_line (box_width, 0, src, dest, height, bpp);
+ box_blur_line (box_width, 0, dest, src, height, bpp);
+ box_blur_line (box_width, 0, src, dest, height, bpp);
+ }
+ /* Even-width box blur */
+ else
+ {
+ box_blur_line (box_width, -1, src, dest, height, bpp);
+ box_blur_line (box_width, 1, dest, src, height, bpp);
+ box_blur_line (box_width+1, 0, src, dest, height, bpp);
+ }
+ }
+ else
+ {
+ /* Gaussian blur */
+ gaussian_blur_line (cmatrix, cmatrix_length, src, dest,height, bpp);
+ }
+
gimp_pixel_rgn_set_col (destPR, dest, x1 + col, y1, height);
if (show_progress && col % 16 == 0)
@@ -480,7 +656,7 @@
/* combine the two */
for (u = 0; u < width; u++)
{
- for (v = 0; v < bytes; v++)
+ for (v = 0; v < bpp; v++)
{
gint value;
gint diff = *s - *d;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]