Skip to content

Commit c9c8d45

Browse files
authored
Merge pull request #8422 from radarhere/resize_i16
2 parents ea15e0e + 42ad42c commit c9c8d45

File tree

5 files changed

+111
-22
lines changed

5 files changed

+111
-22
lines changed

Tests/test_image_resize.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,19 @@ def test_convolution_modes(self) -> None:
4444
self.resize(hopper("1"), (15, 12), Image.Resampling.BILINEAR)
4545
with pytest.raises(ValueError):
4646
self.resize(hopper("P"), (15, 12), Image.Resampling.BILINEAR)
47-
with pytest.raises(ValueError):
48-
self.resize(hopper("I;16"), (15, 12), Image.Resampling.BILINEAR)
49-
for mode in ["L", "I", "F", "RGB", "RGBA", "CMYK", "YCbCr"]:
47+
for mode in [
48+
"L",
49+
"I",
50+
"I;16",
51+
"I;16L",
52+
"I;16B",
53+
"I;16N",
54+
"F",
55+
"RGB",
56+
"RGBA",
57+
"CMYK",
58+
"YCbCr",
59+
]:
5060
im = hopper(mode)
5161
r = self.resize(im, (15, 12), Image.Resampling.BILINEAR)
5262
assert r.mode == mode
@@ -305,14 +315,14 @@ def test_transposed(self) -> None:
305315
im = im.resize((64, 64))
306316
assert im.size == (64, 64)
307317

308-
@pytest.mark.parametrize("mode", ("L", "RGB", "I", "F"))
318+
@pytest.mark.parametrize(
319+
"mode", ("L", "RGB", "I", "I;16", "I;16L", "I;16B", "I;16N", "F")
320+
)
309321
def test_default_filter_bicubic(self, mode: str) -> None:
310322
im = hopper(mode)
311323
assert im.resize((20, 20), Image.Resampling.BICUBIC) == im.resize((20, 20))
312324

313-
@pytest.mark.parametrize(
314-
"mode", ("1", "P", "I;16", "I;16L", "I;16B", "BGR;15", "BGR;16")
315-
)
325+
@pytest.mark.parametrize("mode", ("1", "P", "BGR;15", "BGR;16"))
316326
def test_default_filter_nearest(self, mode: str) -> None:
317327
im = hopper(mode)
318328
assert im.resize((20, 20), Image.Resampling.NEAREST) == im.resize((20, 20))

docs/releasenotes/11.0.0.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,11 @@ Specific WebP Feature Checks
119119
API Changes
120120
===========
121121

122-
TODO
123-
^^^^
122+
Default resampling filter for I;16* image modes
123+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
124124

125-
TODO
125+
The default resampling filter for I;16, I;16L, I;16B and I;16N has been changed from
126+
``Image.NEAREST`` to ``Image.BICUBIC``, to match the majority of modes.
126127

127128
API Additions
128129
=============

src/PIL/Image.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2278,8 +2278,8 @@ def resize(
22782278
:py:data:`Resampling.BILINEAR`, :py:data:`Resampling.HAMMING`,
22792279
:py:data:`Resampling.BICUBIC` or :py:data:`Resampling.LANCZOS`.
22802280
If the image has mode "1" or "P", it is always set to
2281-
:py:data:`Resampling.NEAREST`. If the image mode specifies a number
2282-
of bits, such as "I;16", then the default filter is
2281+
:py:data:`Resampling.NEAREST`. If the image mode is "BGR;15",
2282+
"BGR;16" or "BGR;24", then the default filter is
22832283
:py:data:`Resampling.NEAREST`. Otherwise, the default filter is
22842284
:py:data:`Resampling.BICUBIC`. See: :ref:`concept-filters`.
22852285
:param box: An optional 4-tuple of floats providing
@@ -2302,8 +2302,8 @@ def resize(
23022302
"""
23032303

23042304
if resample is None:
2305-
type_special = ";" in self.mode
2306-
resample = Resampling.NEAREST if type_special else Resampling.BICUBIC
2305+
bgr = self.mode.startswith("BGR;")
2306+
resample = Resampling.NEAREST if bgr else Resampling.BICUBIC
23072307
elif resample not in (
23082308
Resampling.NEAREST,
23092309
Resampling.BILINEAR,

src/_imaging.c

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1579,16 +1579,12 @@ _putdata(ImagingObject *self, PyObject *args) {
15791579
int bigendian = 0;
15801580
if (image->type == IMAGING_TYPE_SPECIAL) {
15811581
// I;16*
1582-
if (strcmp(image->mode, "I;16N") == 0) {
1582+
if (strcmp(image->mode, "I;16B") == 0
15831583
#ifdef WORDS_BIGENDIAN
1584-
bigendian = 1;
1585-
#else
1586-
bigendian = 0;
1584+
|| strcmp(image->mode, "I;16N") == 0
15871585
#endif
1588-
} else if (strcmp(image->mode, "I;16B") == 0) {
1586+
) {
15891587
bigendian = 1;
1590-
} else {
1591-
bigendian = 0;
15921588
}
15931589
}
15941590
for (i = x = y = 0; i < n; i++) {

src/libImaging/Resample.c

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,83 @@ ImagingResampleVertical_8bpc(
460460
ImagingSectionLeave(&cookie);
461461
}
462462

463+
void
464+
ImagingResampleHorizontal_16bpc(
465+
Imaging imOut, Imaging imIn, int offset, int ksize, int *bounds, double *kk
466+
) {
467+
ImagingSectionCookie cookie;
468+
double ss;
469+
int xx, yy, x, xmin, xmax, ss_int;
470+
double *k;
471+
472+
int bigendian = 0;
473+
if (strcmp(imIn->mode, "I;16N") == 0
474+
#ifdef WORDS_BIGENDIAN
475+
|| strcmp(imIn->mode, "I;16B") == 0
476+
#endif
477+
) {
478+
bigendian = 1;
479+
}
480+
481+
ImagingSectionEnter(&cookie);
482+
for (yy = 0; yy < imOut->ysize; yy++) {
483+
for (xx = 0; xx < imOut->xsize; xx++) {
484+
xmin = bounds[xx * 2 + 0];
485+
xmax = bounds[xx * 2 + 1];
486+
k = &kk[xx * ksize];
487+
ss = 0.0;
488+
for (x = 0; x < xmax; x++) {
489+
ss += (imIn->image8[yy + offset][(x + xmin) * 2 + (bigendian ? 1 : 0)] +
490+
(imIn->image8[yy + offset][(x + xmin) * 2 + (bigendian ? 0 : 1)]
491+
<< 8)) *
492+
k[x];
493+
}
494+
ss_int = ROUND_UP(ss);
495+
imOut->image8[yy][xx * 2 + (bigendian ? 1 : 0)] = CLIP8(ss_int % 256);
496+
imOut->image8[yy][xx * 2 + (bigendian ? 0 : 1)] = CLIP8(ss_int >> 8);
497+
}
498+
}
499+
ImagingSectionLeave(&cookie);
500+
}
501+
502+
void
503+
ImagingResampleVertical_16bpc(
504+
Imaging imOut, Imaging imIn, int offset, int ksize, int *bounds, double *kk
505+
) {
506+
ImagingSectionCookie cookie;
507+
double ss;
508+
int xx, yy, y, ymin, ymax, ss_int;
509+
double *k;
510+
511+
int bigendian = 0;
512+
if (strcmp(imIn->mode, "I;16N") == 0
513+
#ifdef WORDS_BIGENDIAN
514+
|| strcmp(imIn->mode, "I;16B") == 0
515+
#endif
516+
) {
517+
bigendian = 1;
518+
}
519+
520+
ImagingSectionEnter(&cookie);
521+
for (yy = 0; yy < imOut->ysize; yy++) {
522+
ymin = bounds[yy * 2 + 0];
523+
ymax = bounds[yy * 2 + 1];
524+
k = &kk[yy * ksize];
525+
for (xx = 0; xx < imOut->xsize; xx++) {
526+
ss = 0.0;
527+
for (y = 0; y < ymax; y++) {
528+
ss += (imIn->image8[y + ymin][xx * 2 + (bigendian ? 1 : 0)] +
529+
(imIn->image8[y + ymin][xx * 2 + (bigendian ? 0 : 1)] << 8)) *
530+
k[y];
531+
}
532+
ss_int = ROUND_UP(ss);
533+
imOut->image8[yy][xx * 2 + (bigendian ? 1 : 0)] = CLIP8(ss_int % 256);
534+
imOut->image8[yy][xx * 2 + (bigendian ? 0 : 1)] = CLIP8(ss_int >> 8);
535+
}
536+
}
537+
ImagingSectionLeave(&cookie);
538+
}
539+
463540
void
464541
ImagingResampleHorizontal_32bpc(
465542
Imaging imOut, Imaging imIn, int offset, int ksize, int *bounds, double *kk
@@ -574,7 +651,12 @@ ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4]) {
574651
}
575652

576653
if (imIn->type == IMAGING_TYPE_SPECIAL) {
577-
return (Imaging)ImagingError_ModeError();
654+
if (strncmp(imIn->mode, "I;16", 4) == 0) {
655+
ResampleHorizontal = ImagingResampleHorizontal_16bpc;
656+
ResampleVertical = ImagingResampleVertical_16bpc;
657+
} else {
658+
return (Imaging)ImagingError_ModeError();
659+
}
578660
} else if (imIn->image8) {
579661
ResampleHorizontal = ImagingResampleHorizontal_8bpc;
580662
ResampleVertical = ImagingResampleVertical_8bpc;

0 commit comments

Comments
 (0)