From 626acf705f01c094ab93682ff20382b85e209b1c Mon Sep 17 00:00:00 2001 From: homm Date: Mon, 6 Oct 2014 20:55:59 +0400 Subject: [PATCH 1/5] convert tabs to spaces --- libImaging/UnsharpMask.c | 358 +++++++++++++++++++-------------------- 1 file changed, 179 insertions(+), 179 deletions(-) diff --git a/libImaging/UnsharpMask.c b/libImaging/UnsharpMask.c index 231826245..fc2b4b175 100644 --- a/libImaging/UnsharpMask.c +++ b/libImaging/UnsharpMask.c @@ -48,9 +48,9 @@ static inline UINT8 clip(double in) { if (in >= 255.0) - return (UINT8) 255; + return (UINT8) 255; if (in <= 0.0) - return (UINT8) 0; + return (UINT8) 0; return (UINT8) in; } @@ -108,31 +108,31 @@ gblur(Imaging im, Imaging imOut, float floatRadius, int channels, int padding) maskData = malloc(radius * sizeof(float)); /* FIXME: error checking */ for (x = 0; x < radius; x++) { - z = ((float) (x + 2) / ((float) radius)); - dev = 0.5 + (((float) (radius * radius)) * 0.001); - /* you can adjust this factor to change the shape/center-weighting - of the gaussian */ - maskData[x] = (float) pow((1.0 / sqrt(2.0 * 3.14159265359 * dev)), - ((-(z - 1.0) * -(x - 1.0)) / - (2.0 * dev))); + z = ((float) (x + 2) / ((float) radius)); + dev = 0.5 + (((float) (radius * radius)) * 0.001); + /* you can adjust this factor to change the shape/center-weighting + of the gaussian */ + maskData[x] = (float) pow((1.0 / sqrt(2.0 * 3.14159265359 * dev)), + ((-(z - 1.0) * -(x - 1.0)) / + (2.0 * dev))); } /* if there's any remainder, multiply the first/last values in MaskData it. this allows us to support float radius values. */ if (remainder > 0.0) { - maskData[0] *= remainder; - maskData[radius - 1] *= remainder; + maskData[0] *= remainder; + maskData[radius - 1] *= remainder; } for (x = 0; x < radius; x++) { - /* this is done separately now due to the correction for float - radius values above */ - sum += maskData[x]; + /* this is done separately now due to the correction for float + radius values above */ + sum += maskData[x]; } for (i = 0; i < radius; i++) { - maskData[i] *= (1.0 / sum); - /* printf("%f\n", maskData[i]); */ + maskData[i] *= (1.0 / sum); + /* printf("%f\n", maskData[i]); */ } /* create a temporary memory buffer for the data for the first pass @@ -140,9 +140,9 @@ gblur(Imaging im, Imaging imOut, float floatRadius, int channels, int padding) /* don't bother about alpha/padding */ buffer = calloc((size_t) (im->xsize * im->ysize * channels), - sizeof(float)); + sizeof(float)); if (buffer == NULL) - return ImagingError_MemoryError(); + return ImagingError_MemoryError(); /* be nice to other threads while you go off to lala land */ ImagingSectionEnter(&cookie); @@ -153,94 +153,94 @@ gblur(Imaging im, Imaging imOut, float floatRadius, int channels, int padding) /* perform a blur on each line, and place in the temporary storage buffer */ for (y = 0; y < im->ysize; y++) { - if (channels == 1 && im->image8 != NULL) { - line8 = (UINT8 *) im->image8[y]; - } else { - line = im->image32[y]; - } - for (x = 0; x < im->xsize; x++) { - newPixel[0] = newPixel[1] = newPixel[2] = newPixel[3] = 0; - /* for each neighbor pixel, factor in its value/weighting to the - current pixel */ - for (pix = 0; pix < radius; pix++) { - /* figure the offset of this neighbor pixel */ - offset = - (int) ((-((float) radius / 2.0) + (float) pix) + 0.5); - if (x + offset < 0) - offset = -x; - else if (x + offset >= im->xsize) - offset = im->xsize - x - 1; + if (channels == 1 && im->image8 != NULL) { + line8 = (UINT8 *) im->image8[y]; + } else { + line = im->image32[y]; + } + for (x = 0; x < im->xsize; x++) { + newPixel[0] = newPixel[1] = newPixel[2] = newPixel[3] = 0; + /* for each neighbor pixel, factor in its value/weighting to the + current pixel */ + for (pix = 0; pix < radius; pix++) { + /* figure the offset of this neighbor pixel */ + offset = + (int) ((-((float) radius / 2.0) + (float) pix) + 0.5); + if (x + offset < 0) + offset = -x; + else if (x + offset >= im->xsize) + offset = im->xsize - x - 1; - /* add (neighbor pixel value * maskData[pix]) to the current - pixel value */ - if (channels == 1) { - buffer[(y * im->xsize) + x] += - ((float) ((UINT8 *) & line8[x + offset])[0]) * - (maskData[pix]); - } else { - for (channel = 0; channel < channels; channel++) { - buffer[(y * im->xsize * channels) + - (x * channels) + channel] += - ((float) ((UINT8 *) & line[x + offset]) - [channel]) * (maskData[pix]); - } - } - } - } + /* add (neighbor pixel value * maskData[pix]) to the current + pixel value */ + if (channels == 1) { + buffer[(y * im->xsize) + x] += + ((float) ((UINT8 *) & line8[x + offset])[0]) * + (maskData[pix]); + } else { + for (channel = 0; channel < channels; channel++) { + buffer[(y * im->xsize * channels) + + (x * channels) + channel] += + ((float) ((UINT8 *) & line[x + offset]) + [channel]) * (maskData[pix]); + } + } + } + } } /* perform a blur on each column in the buffer, and place in the output image */ for (x = 0; x < im->xsize; x++) { - for (y = 0; y < im->ysize; y++) { - newPixel[0] = newPixel[1] = newPixel[2] = newPixel[3] = 0; - /* for each neighbor pixel, factor in its value/weighting to the - current pixel */ - for (pix = 0; pix < radius; pix++) { - /* figure the offset of this neighbor pixel */ - offset = - (int) (-((float) radius / 2.0) + (float) pix + 0.5); - if (y + offset < 0) - offset = -y; - else if (y + offset >= im->ysize) - offset = im->ysize - y - 1; - /* add (neighbor pixel value * maskData[pix]) to the current - pixel value */ - for (channel = 0; channel < channels; channel++) { - newPixel[channel] += - (buffer - [((y + offset) * im->xsize * channels) + - (x * channels) + channel]) * (maskData[pix]); - } - } - /* if the image is RGBX or RGBA, copy the 4th channel data to - newPixel, so it gets put in imOut */ - if (strcmp(im->mode, "RGBX") == 0 - || strcmp(im->mode, "RGBA") == 0) { - newPixel[3] = (float) ((UINT8 *) & line[x + offset])[3]; - } + for (y = 0; y < im->ysize; y++) { + newPixel[0] = newPixel[1] = newPixel[2] = newPixel[3] = 0; + /* for each neighbor pixel, factor in its value/weighting to the + current pixel */ + for (pix = 0; pix < radius; pix++) { + /* figure the offset of this neighbor pixel */ + offset = + (int) (-((float) radius / 2.0) + (float) pix + 0.5); + if (y + offset < 0) + offset = -y; + else if (y + offset >= im->ysize) + offset = im->ysize - y - 1; + /* add (neighbor pixel value * maskData[pix]) to the current + pixel value */ + for (channel = 0; channel < channels; channel++) { + newPixel[channel] += + (buffer + [((y + offset) * im->xsize * channels) + + (x * channels) + channel]) * (maskData[pix]); + } + } + /* if the image is RGBX or RGBA, copy the 4th channel data to + newPixel, so it gets put in imOut */ + if (strcmp(im->mode, "RGBX") == 0 + || strcmp(im->mode, "RGBA") == 0) { + newPixel[3] = (float) ((UINT8 *) & line[x + offset])[3]; + } - /* pack the channels into an INT32 so we can put them back in - the PIL image */ - newPixelFinals = 0; - if (channels == 1) { - newPixelFinals = clip(newPixel[0]); - } else { - /* for RGB, the fourth channel isn't used anyways, so just - pack a 0 in there, this saves checking the mode for each - pixel. */ - /* this doesn't work on little-endian machines... fix it! */ - newPixelFinals = - clip(newPixel[0]) | clip(newPixel[1]) << 8 | - clip(newPixel[2]) << 16 | clip(newPixel[3]) << 24; - } - /* set the resulting pixel in imOut */ - if (channels == 1) { - imOut->image8[y][x] = (UINT8) newPixelFinals; - } else { - imOut->image32[y][x] = newPixelFinals; - } - } + /* pack the channels into an INT32 so we can put them back in + the PIL image */ + newPixelFinals = 0; + if (channels == 1) { + newPixelFinals = clip(newPixel[0]); + } else { + /* for RGB, the fourth channel isn't used anyways, so just + pack a 0 in there, this saves checking the mode for each + pixel. */ + /* this doesn't work on little-endian machines... fix it! */ + newPixelFinals = + clip(newPixel[0]) | clip(newPixel[1]) << 8 | + clip(newPixel[2]) << 16 | clip(newPixel[3]) << 24; + } + /* set the resulting pixel in imOut */ + if (channels == 1) { + imOut->image8[y][x] = (UINT8) newPixelFinals; + } else { + imOut->image32[y][x] = newPixelFinals; + } + } } /* free the buffer */ @@ -258,29 +258,29 @@ Imaging ImagingGaussianBlur(Imaging im, Imaging imOut, float radius) int padding = 0; if (strcmp(im->mode, "RGB") == 0) { - channels = 3; - padding = 1; + channels = 3; + padding = 1; } else if (strcmp(im->mode, "RGBA") == 0) { - channels = 3; - padding = 1; + channels = 3; + padding = 1; } else if (strcmp(im->mode, "RGBX") == 0) { - channels = 3; - padding = 1; + channels = 3; + padding = 1; } else if (strcmp(im->mode, "CMYK") == 0) { - channels = 4; - padding = 0; + channels = 4; + padding = 0; } else if (strcmp(im->mode, "L") == 0) { - channels = 1; - padding = 0; + channels = 1; + padding = 0; } else - return ImagingError_ModeError(); + return ImagingError_ModeError(); return gblur(im, imOut, radius, channels, padding); } Imaging ImagingUnsharpMask(Imaging im, Imaging imOut, float radius, int percent, - int threshold) + int threshold) { ImagingSectionCookie cookie; @@ -302,28 +302,28 @@ ImagingUnsharpMask(Imaging im, Imaging imOut, float radius, int percent, INT32 newPixel = 0; if (strcmp(im->mode, "RGB") == 0) { - channels = 3; - padding = 1; + channels = 3; + padding = 1; } else if (strcmp(im->mode, "RGBA") == 0) { - channels = 3; - padding = 1; + channels = 3; + padding = 1; } else if (strcmp(im->mode, "RGBX") == 0) { - channels = 3; - padding = 1; + channels = 3; + padding = 1; } else if (strcmp(im->mode, "CMYK") == 0) { - channels = 4; - padding = 0; + channels = 4; + padding = 0; } else if (strcmp(im->mode, "L") == 0) { - channels = 1; - padding = 0; + channels = 1; + padding = 0; } else - return ImagingError_ModeError(); + return ImagingError_ModeError(); /* first, do a gaussian blur on the image, putting results in imOut temporarily */ result = gblur(im, imOut, radius, channels, padding); if (!result) - return NULL; + return NULL; /* now, go through each pixel, compare "normal" pixel to blurred pixel. if the difference is more than threshold values, apply @@ -333,63 +333,63 @@ ImagingUnsharpMask(Imaging im, Imaging imOut, float radius, int percent, ImagingSectionEnter(&cookie); for (y = 0; y < im->ysize; y++) { - if (channels == 1) { - lineIn8 = im->image8[y]; - lineOut8 = imOut->image8[y]; - } else { - lineIn = im->image32[y]; - lineOut = imOut->image32[y]; - } - for (x = 0; x < im->xsize; x++) { - newPixel = 0; - /* compare in/out pixels, apply sharpening */ - if (channels == 1) { - diff = - ((UINT8 *) & lineIn8[x])[0] - - ((UINT8 *) & lineOut8[x])[0]; - if (abs(diff) > threshold) { - /* add the diff*percent to the original pixel */ - imOut->image8[y][x] = - clip((((UINT8 *) & lineIn8[x])[0]) + - (diff * ((float) percent) / 100.0)); - } else { - /* newPixel is the same as imIn */ - imOut->image8[y][x] = ((UINT8 *) & lineIn8[x])[0]; - } - } + if (channels == 1) { + lineIn8 = im->image8[y]; + lineOut8 = imOut->image8[y]; + } else { + lineIn = im->image32[y]; + lineOut = imOut->image32[y]; + } + for (x = 0; x < im->xsize; x++) { + newPixel = 0; + /* compare in/out pixels, apply sharpening */ + if (channels == 1) { + diff = + ((UINT8 *) & lineIn8[x])[0] - + ((UINT8 *) & lineOut8[x])[0]; + if (abs(diff) > threshold) { + /* add the diff*percent to the original pixel */ + imOut->image8[y][x] = + clip((((UINT8 *) & lineIn8[x])[0]) + + (diff * ((float) percent) / 100.0)); + } else { + /* newPixel is the same as imIn */ + imOut->image8[y][x] = ((UINT8 *) & lineIn8[x])[0]; + } + } - else { - for (channel = 0; channel < channels; channel++) { - diff = (int) ((((UINT8 *) & lineIn[x])[channel]) - - (((UINT8 *) & lineOut[x])[channel])); - if (abs(diff) > threshold) { - /* add the diff*percent to the original pixel - this may not work for little-endian systems, fix it! */ - newPixel = - newPixel | - clip((float) (((UINT8 *) & lineIn[x])[channel]) - + - (diff * - (((float) percent / - 100.0)))) << (channel * 8); - } else { - /* newPixel is the same as imIn - this may not work for little-endian systems, fix it! */ - newPixel = - newPixel | ((UINT8 *) & lineIn[x])[channel] << - (channel * 8); - } - } - if (strcmp(im->mode, "RGBX") == 0 - || strcmp(im->mode, "RGBA") == 0) { - /* preserve the alpha channel - this may not work for little-endian systems, fix it! */ - newPixel = - newPixel | ((UINT8 *) & lineIn[x])[channel] << 24; - } - imOut->image32[y][x] = newPixel; - } - } + else { + for (channel = 0; channel < channels; channel++) { + diff = (int) ((((UINT8 *) & lineIn[x])[channel]) - + (((UINT8 *) & lineOut[x])[channel])); + if (abs(diff) > threshold) { + /* add the diff*percent to the original pixel + this may not work for little-endian systems, fix it! */ + newPixel = + newPixel | + clip((float) (((UINT8 *) & lineIn[x])[channel]) + + + (diff * + (((float) percent / + 100.0)))) << (channel * 8); + } else { + /* newPixel is the same as imIn + this may not work for little-endian systems, fix it! */ + newPixel = + newPixel | ((UINT8 *) & lineIn[x])[channel] << + (channel * 8); + } + } + if (strcmp(im->mode, "RGBX") == 0 + || strcmp(im->mode, "RGBA") == 0) { + /* preserve the alpha channel + this may not work for little-endian systems, fix it! */ + newPixel = + newPixel | ((UINT8 *) & lineIn[x])[channel] << 24; + } + imOut->image32[y][x] = newPixel; + } + } } ImagingSectionLeave(&cookie); From 05c99131e0e053acfd3c23895a28bd424f529d69 Mon Sep 17 00:00:00 2001 From: homm Date: Mon, 6 Oct 2014 21:27:08 +0400 Subject: [PATCH 2/5] fix blur for RGBA & RGBX --- libImaging/UnsharpMask.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/libImaging/UnsharpMask.c b/libImaging/UnsharpMask.c index fc2b4b175..e3e975ef0 100644 --- a/libImaging/UnsharpMask.c +++ b/libImaging/UnsharpMask.c @@ -79,6 +79,7 @@ gblur(Imaging im, Imaging imOut, float floatRadius, int channels, int padding) int radius = 0; float remainder = 0.0; + int hasAlpha = 0; int i; @@ -189,6 +190,10 @@ gblur(Imaging im, Imaging imOut, float floatRadius, int channels, int padding) } } + if (strcmp(im->mode, "RGBX") == 0 || strcmp(im->mode, "RGBA") == 0) { + hasAlpha = 1; + } + /* perform a blur on each column in the buffer, and place in the output image */ for (x = 0; x < im->xsize; x++) { @@ -204,6 +209,7 @@ gblur(Imaging im, Imaging imOut, float floatRadius, int channels, int padding) offset = -y; else if (y + offset >= im->ysize) offset = im->ysize - y - 1; + /* add (neighbor pixel value * maskData[pix]) to the current pixel value */ for (channel = 0; channel < channels; channel++) { @@ -215,9 +221,8 @@ gblur(Imaging im, Imaging imOut, float floatRadius, int channels, int padding) } /* if the image is RGBX or RGBA, copy the 4th channel data to newPixel, so it gets put in imOut */ - if (strcmp(im->mode, "RGBX") == 0 - || strcmp(im->mode, "RGBA") == 0) { - newPixel[3] = (float) ((UINT8 *) & line[x + offset])[3]; + if (hasAlpha) { + newPixel[3] = (float) ((UINT8 *) & im->image32[y][x])[3]; } /* pack the channels into an INT32 so we can put them back in From ddae12928ae7dd919086ffcc4c0ec940e9024360 Mon Sep 17 00:00:00 2001 From: homm Date: Wed, 8 Oct 2014 06:49:56 +0400 Subject: [PATCH 3/5] add detailed test for blur and simple test for sharp --- Tests/images/color_snakes.png | Bin 0 -> 1311 bytes Tests/test_imageops_usm.py | 34 +++++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 Tests/images/color_snakes.png diff --git a/Tests/images/color_snakes.png b/Tests/images/color_snakes.png new file mode 100644 index 0000000000000000000000000000000000000000..bf3a351964f4d9ce61ef88222d133f66b45e4b2b GIT binary patch literal 1311 zcmZ`(Uue@n9KONSnKDEL_YYZ&;#+e`*WsF_f!eCwSm*2}cA~N@z0SC_iAhRRL73vp z1QC^qj0rwDL>LV8!HGV|UVKu)M<)m}WeDP%h@y9C*Tv8UlH7g2@4Mgc`z3eNLxabf zsdfqg(A<|yr16`Ksiv*?c_Z}Z1%5S7#8NQ;7HM8|a#(cNsA16JV8Pm7S!8{k1hN!UHEdNc z&}3bCTrb*Dh9QY+SEWu{&23~VSXEotAS4mUvmC4^Lyo$Mj6^n-WtEyMqiAZbr>p{z zhVrOjTUf`}O^1716aH7EBO8@uZWAlsL{hnsMwYG>NefOER9oUj*N`9H=txBs*Yk#n zEDOt|?yAI#`T3cRG}56^Rp15vg5#!&oyw%HT1{Te&mXOqzzcc=r1ip(>L4wXP;o Date: Wed, 8 Oct 2014 15:05:48 +0400 Subject: [PATCH 4/5] round pixel values --- Tests/test_imageops_usm.py | 18 +++++++++--------- libImaging/UnsharpMask.c | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Tests/test_imageops_usm.py b/Tests/test_imageops_usm.py index d1bdacd52..5164af015 100644 --- a/Tests/test_imageops_usm.py +++ b/Tests/test_imageops_usm.py @@ -66,7 +66,7 @@ class TestImageOpsUsm(PillowTestCase): def test_blur_accuracy(self): - i = snakes._new(ImageOps.gaussian_blur(snakes, .9)) + i = snakes._new(ImageOps.gaussian_blur(snakes, 1)) # Alpha channel must match whole. self.assertEqual(i.split()[3], snakes.split()[3]) # These pixels surrounded with pixels with 255 intensity. @@ -77,14 +77,14 @@ class TestImageOpsUsm(PillowTestCase): self.assertEqual(i.im.getpixel((x, y))[c], 255) # Fuzzy match. gp = lambda x, y: i.im.getpixel((x, y)) - self.assertTrue(212 <= gp(7, 4)[0] <= 214) - self.assertTrue(212 <= gp(7, 5)[2] <= 214) - self.assertTrue(212 <= gp(7, 6)[2] <= 214) - self.assertTrue(212 <= gp(7, 7)[1] <= 214) - self.assertTrue(212 <= gp(8, 4)[0] <= 214) - self.assertTrue(212 <= gp(8, 5)[2] <= 214) - self.assertTrue(212 <= gp(8, 6)[2] <= 214) - self.assertTrue(212 <= gp(8, 7)[1] <= 214) + self.assertTrue(211 <= gp(7, 4)[0] <= 213) + self.assertTrue(211 <= gp(7, 5)[2] <= 213) + self.assertTrue(211 <= gp(7, 6)[2] <= 213) + self.assertTrue(211 <= gp(7, 7)[1] <= 213) + self.assertTrue(211 <= gp(8, 4)[0] <= 213) + self.assertTrue(211 <= gp(8, 5)[2] <= 213) + self.assertTrue(211 <= gp(8, 6)[2] <= 213) + self.assertTrue(211 <= gp(8, 7)[1] <= 213) if __name__ == '__main__': unittest.main() diff --git a/libImaging/UnsharpMask.c b/libImaging/UnsharpMask.c index e3e975ef0..0e0eb66e4 100644 --- a/libImaging/UnsharpMask.c +++ b/libImaging/UnsharpMask.c @@ -51,7 +51,7 @@ static inline UINT8 clip(double in) return (UINT8) 255; if (in <= 0.0) return (UINT8) 0; - return (UINT8) in; + return (UINT8) (in + 0.5); } static Imaging From bef7e1dce8a47f3136b5b9d94d6e821aa2d7a0d0 Mon Sep 17 00:00:00 2001 From: homm Date: Wed, 8 Oct 2014 17:40:33 +0400 Subject: [PATCH 5/5] cache hasAlpha for ImagingUnsharpMask --- libImaging/UnsharpMask.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libImaging/UnsharpMask.c b/libImaging/UnsharpMask.c index 0e0eb66e4..76d630a8f 100644 --- a/libImaging/UnsharpMask.c +++ b/libImaging/UnsharpMask.c @@ -293,6 +293,7 @@ ImagingUnsharpMask(Imaging im, Imaging imOut, float radius, int percent, int channel = 0; int channels = 0; int padding = 0; + int hasAlpha = 0; int x = 0; int y = 0; @@ -337,6 +338,10 @@ ImagingUnsharpMask(Imaging im, Imaging imOut, float radius, int percent, ImagingSectionEnter(&cookie); + if (strcmp(im->mode, "RGBX") == 0 || strcmp(im->mode, "RGBA") == 0) { + hasAlpha = 1; + } + for (y = 0; y < im->ysize; y++) { if (channels == 1) { lineIn8 = im->image8[y]; @@ -385,8 +390,7 @@ ImagingUnsharpMask(Imaging im, Imaging imOut, float radius, int percent, (channel * 8); } } - if (strcmp(im->mode, "RGBX") == 0 - || strcmp(im->mode, "RGBA") == 0) { + if (hasAlpha) { /* preserve the alpha channel this may not work for little-endian systems, fix it! */ newPixel =