mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-11-15 06:07:33 +03:00
Merge pull request #8438 from radarhere/filter
This commit is contained in:
commit
7777260b6b
|
@ -35,16 +35,25 @@ from .helper import assert_image_equal, hopper
|
||||||
ImageFilter.UnsharpMask(10),
|
ImageFilter.UnsharpMask(10),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize("mode", ("L", "I", "RGB", "CMYK"))
|
@pytest.mark.parametrize(
|
||||||
def test_sanity(filter_to_apply: ImageFilter.Filter, mode: str) -> None:
|
"mode", ("L", "I", "I;16", "I;16L", "I;16B", "I;16N", "RGB", "CMYK")
|
||||||
|
)
|
||||||
|
def test_sanity(
|
||||||
|
filter_to_apply: ImageFilter.Filter | type[ImageFilter.Filter], mode: str
|
||||||
|
) -> None:
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
if mode != "I" or isinstance(filter_to_apply, ImageFilter.BuiltinFilter):
|
if mode[0] != "I" or (
|
||||||
|
callable(filter_to_apply)
|
||||||
|
and issubclass(filter_to_apply, ImageFilter.BuiltinFilter)
|
||||||
|
):
|
||||||
out = im.filter(filter_to_apply)
|
out = im.filter(filter_to_apply)
|
||||||
assert out.mode == im.mode
|
assert out.mode == im.mode
|
||||||
assert out.size == im.size
|
assert out.size == im.size
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ("L", "I", "RGB", "CMYK"))
|
@pytest.mark.parametrize(
|
||||||
|
"mode", ("L", "I", "I;16", "I;16L", "I;16B", "I;16N", "RGB", "CMYK")
|
||||||
|
)
|
||||||
def test_sanity_error(mode: str) -> None:
|
def test_sanity_error(mode: str) -> None:
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
|
@ -145,7 +154,9 @@ def test_kernel_not_enough_coefficients() -> None:
|
||||||
ImageFilter.Kernel((3, 3), (0, 0))
|
ImageFilter.Kernel((3, 3), (0, 0))
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ("L", "LA", "I", "RGB", "CMYK"))
|
@pytest.mark.parametrize(
|
||||||
|
"mode", ("L", "LA", "I", "I;16", "I;16L", "I;16B", "I;16N", "RGB", "CMYK")
|
||||||
|
)
|
||||||
def test_consistency_3x3(mode: str) -> None:
|
def test_consistency_3x3(mode: str) -> None:
|
||||||
with Image.open("Tests/images/hopper.bmp") as source:
|
with Image.open("Tests/images/hopper.bmp") as source:
|
||||||
with Image.open("Tests/images/hopper_emboss.bmp") as reference:
|
with Image.open("Tests/images/hopper_emboss.bmp") as reference:
|
||||||
|
@ -161,7 +172,9 @@ def test_consistency_3x3(mode: str) -> None:
|
||||||
assert_image_equal(source.filter(kernel), reference)
|
assert_image_equal(source.filter(kernel), reference)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ("L", "LA", "I", "RGB", "CMYK"))
|
@pytest.mark.parametrize(
|
||||||
|
"mode", ("L", "LA", "I", "I;16", "I;16L", "I;16B", "I;16N", "RGB", "CMYK")
|
||||||
|
)
|
||||||
def test_consistency_5x5(mode: str) -> None:
|
def test_consistency_5x5(mode: str) -> None:
|
||||||
with Image.open("Tests/images/hopper.bmp") as source:
|
with Image.open("Tests/images/hopper.bmp") as source:
|
||||||
with Image.open("Tests/images/hopper_emboss_more.bmp") as reference:
|
with Image.open("Tests/images/hopper_emboss_more.bmp") as reference:
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
|
|
||||||
#include "Imaging.h"
|
#include "Imaging.h"
|
||||||
|
|
||||||
|
#define ROUND_UP(f) ((int)((f) >= 0.0 ? (f) + 0.5F : (f) - 0.5F))
|
||||||
|
|
||||||
static inline UINT8
|
static inline UINT8
|
||||||
clip8(float in) {
|
clip8(float in) {
|
||||||
if (in <= 0.0) {
|
if (in <= 0.0) {
|
||||||
|
@ -105,6 +107,22 @@ ImagingExpand(Imaging imIn, int xmargin, int ymargin) {
|
||||||
return imOut;
|
return imOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float
|
||||||
|
kernel_i16(int size, UINT8 *in0, int x, const float *kernel, int bigendian) {
|
||||||
|
int i;
|
||||||
|
float result = 0;
|
||||||
|
int half_size = (size - 1) / 2;
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
int x1 = x + i - half_size;
|
||||||
|
result += _i2f(
|
||||||
|
in0[x1 * 2 + (bigendian ? 1 : 0)] +
|
||||||
|
(in0[x1 * 2 + (bigendian ? 0 : 1)] >> 8)
|
||||||
|
) *
|
||||||
|
kernel[i];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
|
ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
|
||||||
#define KERNEL1x3(in0, x, kernel, d) \
|
#define KERNEL1x3(in0, x, kernel, d) \
|
||||||
|
@ -135,6 +153,16 @@ ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
|
||||||
out[x] = in0[x];
|
out[x] = in0[x];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
int bigendian = 0;
|
||||||
|
if (im->type == IMAGING_TYPE_SPECIAL) {
|
||||||
|
if (strcmp(im->mode, "I;16B") == 0
|
||||||
|
#ifdef WORDS_BIGENDIAN
|
||||||
|
|| strcmp(im->mode, "I;16N") == 0
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
|
bigendian = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (y = 1; y < im->ysize - 1; y++) {
|
for (y = 1; y < im->ysize - 1; y++) {
|
||||||
UINT8 *in_1 = (UINT8 *)im->image[y - 1];
|
UINT8 *in_1 = (UINT8 *)im->image[y - 1];
|
||||||
UINT8 *in0 = (UINT8 *)im->image[y];
|
UINT8 *in0 = (UINT8 *)im->image[y];
|
||||||
|
@ -142,14 +170,31 @@ ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
|
||||||
UINT8 *out = (UINT8 *)imOut->image[y];
|
UINT8 *out = (UINT8 *)imOut->image[y];
|
||||||
|
|
||||||
out[0] = in0[0];
|
out[0] = in0[0];
|
||||||
|
if (im->type == IMAGING_TYPE_SPECIAL) {
|
||||||
|
out[1] = in0[1];
|
||||||
|
}
|
||||||
for (x = 1; x < im->xsize - 1; x++) {
|
for (x = 1; x < im->xsize - 1; x++) {
|
||||||
float ss = offset;
|
float ss = offset;
|
||||||
ss += KERNEL1x3(in1, x, &kernel[0], 1);
|
if (im->type == IMAGING_TYPE_SPECIAL) {
|
||||||
ss += KERNEL1x3(in0, x, &kernel[3], 1);
|
ss += kernel_i16(3, in1, x, &kernel[0], bigendian);
|
||||||
ss += KERNEL1x3(in_1, x, &kernel[6], 1);
|
ss += kernel_i16(3, in0, x, &kernel[3], bigendian);
|
||||||
out[x] = clip8(ss);
|
ss += kernel_i16(3, in_1, x, &kernel[6], bigendian);
|
||||||
|
int ss_int = ROUND_UP(ss);
|
||||||
|
out[x * 2 + (bigendian ? 1 : 0)] = clip8(ss_int % 256);
|
||||||
|
out[x * 2 + (bigendian ? 0 : 1)] = clip8(ss_int >> 8);
|
||||||
|
} else {
|
||||||
|
ss += KERNEL1x3(in1, x, &kernel[0], 1);
|
||||||
|
ss += KERNEL1x3(in0, x, &kernel[3], 1);
|
||||||
|
ss += KERNEL1x3(in_1, x, &kernel[6], 1);
|
||||||
|
out[x] = clip8(ss);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (im->type == IMAGING_TYPE_SPECIAL) {
|
||||||
|
out[x * 2] = in0[x * 2];
|
||||||
|
out[x * 2 + 1] = in0[x * 2 + 1];
|
||||||
|
} else {
|
||||||
|
out[x] = in0[x];
|
||||||
}
|
}
|
||||||
out[x] = in0[x];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -261,6 +306,16 @@ ImagingFilter5x5(Imaging imOut, Imaging im, const float *kernel, float offset) {
|
||||||
out[x + 1] = in0[x + 1];
|
out[x + 1] = in0[x + 1];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
int bigendian = 0;
|
||||||
|
if (im->type == IMAGING_TYPE_SPECIAL) {
|
||||||
|
if (strcmp(im->mode, "I;16B") == 0
|
||||||
|
#ifdef WORDS_BIGENDIAN
|
||||||
|
|| strcmp(im->mode, "I;16N") == 0
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
|
bigendian = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (y = 2; y < im->ysize - 2; y++) {
|
for (y = 2; y < im->ysize - 2; y++) {
|
||||||
UINT8 *in_2 = (UINT8 *)im->image[y - 2];
|
UINT8 *in_2 = (UINT8 *)im->image[y - 2];
|
||||||
UINT8 *in_1 = (UINT8 *)im->image[y - 1];
|
UINT8 *in_1 = (UINT8 *)im->image[y - 1];
|
||||||
|
@ -271,17 +326,39 @@ ImagingFilter5x5(Imaging imOut, Imaging im, const float *kernel, float offset) {
|
||||||
|
|
||||||
out[0] = in0[0];
|
out[0] = in0[0];
|
||||||
out[1] = in0[1];
|
out[1] = in0[1];
|
||||||
|
if (im->type == IMAGING_TYPE_SPECIAL) {
|
||||||
|
out[2] = in0[2];
|
||||||
|
out[3] = in0[3];
|
||||||
|
}
|
||||||
for (x = 2; x < im->xsize - 2; x++) {
|
for (x = 2; x < im->xsize - 2; x++) {
|
||||||
float ss = offset;
|
float ss = offset;
|
||||||
ss += KERNEL1x5(in2, x, &kernel[0], 1);
|
if (im->type == IMAGING_TYPE_SPECIAL) {
|
||||||
ss += KERNEL1x5(in1, x, &kernel[5], 1);
|
ss += kernel_i16(5, in2, x, &kernel[0], bigendian);
|
||||||
ss += KERNEL1x5(in0, x, &kernel[10], 1);
|
ss += kernel_i16(5, in1, x, &kernel[5], bigendian);
|
||||||
ss += KERNEL1x5(in_1, x, &kernel[15], 1);
|
ss += kernel_i16(5, in0, x, &kernel[10], bigendian);
|
||||||
ss += KERNEL1x5(in_2, x, &kernel[20], 1);
|
ss += kernel_i16(5, in_1, x, &kernel[15], bigendian);
|
||||||
out[x] = clip8(ss);
|
ss += kernel_i16(5, in_2, x, &kernel[20], bigendian);
|
||||||
|
int ss_int = ROUND_UP(ss);
|
||||||
|
out[x * 2 + (bigendian ? 1 : 0)] = clip8(ss_int % 256);
|
||||||
|
out[x * 2 + (bigendian ? 0 : 1)] = clip8(ss_int >> 8);
|
||||||
|
} else {
|
||||||
|
ss += KERNEL1x5(in2, x, &kernel[0], 1);
|
||||||
|
ss += KERNEL1x5(in1, x, &kernel[5], 1);
|
||||||
|
ss += KERNEL1x5(in0, x, &kernel[10], 1);
|
||||||
|
ss += KERNEL1x5(in_1, x, &kernel[15], 1);
|
||||||
|
ss += KERNEL1x5(in_2, x, &kernel[20], 1);
|
||||||
|
out[x] = clip8(ss);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (im->type == IMAGING_TYPE_SPECIAL) {
|
||||||
|
out[x * 2 + 0] = in0[x * 2 + 0];
|
||||||
|
out[x * 2 + 1] = in0[x * 2 + 1];
|
||||||
|
out[x * 2 + 2] = in0[x * 2 + 2];
|
||||||
|
out[x * 2 + 3] = in0[x * 2 + 3];
|
||||||
|
} else {
|
||||||
|
out[x + 0] = in0[x + 0];
|
||||||
|
out[x + 1] = in0[x + 1];
|
||||||
}
|
}
|
||||||
out[x + 0] = in0[x + 0];
|
|
||||||
out[x + 1] = in0[x + 1];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -383,7 +460,8 @@ ImagingFilter(Imaging im, int xsize, int ysize, const FLOAT32 *kernel, FLOAT32 o
|
||||||
Imaging imOut;
|
Imaging imOut;
|
||||||
ImagingSectionCookie cookie;
|
ImagingSectionCookie cookie;
|
||||||
|
|
||||||
if (im->type != IMAGING_TYPE_UINT8 && im->type != IMAGING_TYPE_INT32) {
|
if (im->type == IMAGING_TYPE_FLOAT32 ||
|
||||||
|
(im->type == IMAGING_TYPE_SPECIAL && im->bands != 1)) {
|
||||||
return (Imaging)ImagingError_ModeError();
|
return (Imaging)ImagingError_ModeError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user