From 8a035b8c5df1196756e901ef7e2e5aa71cc12793 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 8 May 2019 21:58:33 +1000 Subject: [PATCH 1/5] Improved I mode conversion --- Tests/test_file_png.py | 2 +- Tests/test_image_convert.py | 2 ++ Tests/test_image_getcolors.py | 2 +- Tests/test_image_getdata.py | 2 +- Tests/test_image_getextrema.py | 4 +-- Tests/test_image_histogram.py | 2 +- Tests/test_mode_i16.py | 2 ++ src/libImaging/Convert.c | 60 ++++++++++++---------------------- src/libImaging/Histo.c | 3 +- 9 files changed, 32 insertions(+), 47 deletions(-) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 6a061fe6a..23fa160e7 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -295,7 +295,7 @@ class TestFilePng(PillowTestCase): for mode, num_transparent in { "1": 1994, "L": 559, - "I": 559, + "I": 4096, }.items(): in_file = "Tests/images/"+mode.lower()+"_trns.png" im = Image.open(in_file) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index 970dc8ce1..263608d2a 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -38,6 +38,8 @@ class TestImageConvert(PillowTestCase): def _test_float_conversion(self, im): orig = im.getpixel((5, 5)) + if im.mode[0] == 'I': + orig //= 256 converted = im.convert('F').getpixel((5, 5)) self.assertEqual(orig, converted) diff --git a/Tests/test_image_getcolors.py b/Tests/test_image_getcolors.py index aa2a40976..1f4a9b69d 100644 --- a/Tests/test_image_getcolors.py +++ b/Tests/test_image_getcolors.py @@ -17,13 +17,13 @@ class TestImageGetColors(PillowTestCase): self.assertEqual(getcolors("1"), 2) self.assertEqual(getcolors("L"), 255) - self.assertEqual(getcolors("I"), 255) self.assertEqual(getcolors("F"), 255) self.assertEqual(getcolors("P"), 90) # fixed palette self.assertIsNone(getcolors("RGB")) self.assertIsNone(getcolors("RGBA")) self.assertIsNone(getcolors("CMYK")) self.assertIsNone(getcolors("YCbCr")) + self.assertIsNone(getcolors("I")) self.assertIsNone(getcolors("L", 128)) self.assertEqual(getcolors("L", 1024), 255) diff --git a/Tests/test_image_getdata.py b/Tests/test_image_getdata.py index fe8f9adcd..56ba39cca 100644 --- a/Tests/test_image_getdata.py +++ b/Tests/test_image_getdata.py @@ -21,7 +21,7 @@ class TestImageGetData(PillowTestCase): self.assertEqual(getdata("1"), (0, 960, 960)) self.assertEqual(getdata("L"), (16, 960, 960)) - self.assertEqual(getdata("I"), (16, 960, 960)) + self.assertEqual(getdata("I"), (4313, 960, 960)) self.assertEqual(getdata("F"), (16.0, 960, 960)) self.assertEqual(getdata("RGB"), ((11, 13, 52), 960, 960)) self.assertEqual(getdata("RGBA"), ((11, 13, 52, 255), 960, 960)) diff --git a/Tests/test_image_getextrema.py b/Tests/test_image_getextrema.py index 1689744af..985896e20 100644 --- a/Tests/test_image_getextrema.py +++ b/Tests/test_image_getextrema.py @@ -11,7 +11,7 @@ class TestImageGetExtrema(PillowTestCase): self.assertEqual(extrema("1"), (0, 255)) self.assertEqual(extrema("L"), (0, 255)) - self.assertEqual(extrema("I"), (0, 255)) + self.assertEqual(extrema("I"), (150, 65280)) self.assertEqual(extrema("F"), (0, 255)) self.assertEqual(extrema("P"), (0, 225)) # fixed palette self.assertEqual( @@ -20,7 +20,7 @@ class TestImageGetExtrema(PillowTestCase): extrema("RGBA"), ((0, 255), (0, 255), (0, 255), (255, 255))) self.assertEqual( extrema("CMYK"), ((0, 255), (0, 255), (0, 255), (0, 0))) - self.assertEqual(extrema("I;16"), (0, 255)) + self.assertEqual(extrema("I;16"), (150, 65280)) def test_true_16(self): im = Image.open("Tests/images/16_bit_noise.tif") diff --git a/Tests/test_image_histogram.py b/Tests/test_image_histogram.py index 0a023cd69..c70585874 100644 --- a/Tests/test_image_histogram.py +++ b/Tests/test_image_histogram.py @@ -11,7 +11,7 @@ class TestImageHistogram(PillowTestCase): self.assertEqual(histogram("1"), (256, 0, 10994)) self.assertEqual(histogram("L"), (256, 0, 638)) - self.assertEqual(histogram("I"), (256, 0, 638)) + self.assertEqual(histogram("I"), (256, 1, 662)) self.assertEqual(histogram("F"), (256, 0, 638)) self.assertEqual(histogram("P"), (256, 0, 1871)) self.assertEqual(histogram("RGB"), (768, 4, 675)) diff --git a/Tests/test_mode_i16.py b/Tests/test_mode_i16.py index 80730a312..00caed1f2 100644 --- a/Tests/test_mode_i16.py +++ b/Tests/test_mode_i16.py @@ -17,6 +17,8 @@ class TestModeI16(PillowTestCase): xy = x, y p1 = pix1[xy] p2 = pix2[xy] + if im1.mode[0] != "I" and im2.mode[0] == "I": + p2 //= 256 self.assertEqual( p1, p2, ("got %r from mode %s at %s, expected %r" % diff --git a/src/libImaging/Convert.c b/src/libImaging/Convert.c index a6fefca32..e3dd87048 100644 --- a/src/libImaging/Convert.c +++ b/src/libImaging/Convert.c @@ -231,7 +231,7 @@ rgb2i(UINT8* out_, const UINT8* in, int xsize) int x; INT32* out = (INT32*) out_; for (x = 0; x < xsize; x++, in += 4) - *out++ = L24(in) >> 16; + *out++ = L24(in) >> 8; } static void @@ -575,8 +575,8 @@ l2i(UINT8* out_, const UINT8* in, int xsize) { int x; INT32* out = (INT32*) out_; - for (x = 0; x < xsize; x++) - *out++ = (INT32) *in++; + for (x = 0; x < xsize; x++, in++) + *out++ = (INT32) (*in << 8); } static void @@ -585,12 +585,7 @@ i2l(UINT8* out, const UINT8* in_, int xsize) int x; INT32* in = (INT32*) in_; for (x = 0; x < xsize; x++, in++, out++) { - if (*in <= 0) - *out = 0; - else if (*in >= 255) - *out = 255; - else - *out = (UINT8) *in; + *out = (UINT8) (*in >> 8); } } @@ -601,7 +596,7 @@ i2f(UINT8* out_, const UINT8* in_, int xsize) INT32* in = (INT32*) in_; FLOAT32* out = (FLOAT32*) out_; for (x = 0; x < xsize; x++) - *out++ = (FLOAT32) *in++; + *out++ = (FLOAT32) (*in++ >> 8); } static void @@ -610,12 +605,7 @@ i2rgb(UINT8* out, const UINT8* in_, int xsize) int x; INT32* in = (INT32*) in_; for (x = 0; x < xsize; x++, in++, out+=4) { - if (*in <= 0) - out[0] = out[1] = out[2] = 0; - else if (*in >= 255) - out[0] = out[1] = out[2] = 255; - else - out[0] = out[1] = out[2] = (UINT8) *in; + out[0] = out[1] = out[2] = (UINT8) (*in >> 8); out[3] = 255; } } @@ -664,7 +654,7 @@ f2i(UINT8* out_, const UINT8* in_, int xsize) FLOAT32* in = (FLOAT32*) in_; INT32* out = (INT32*) out_; for (x = 0; x < xsize; x++) - *out++ = (INT32) *in++; + *out++ = (INT32) *in++ << 8; } /* ----------------- */ @@ -722,24 +712,22 @@ ycbcr2la(UINT8* out, const UINT8* in, int xsize) static void I_I16L(UINT8* out, const UINT8* in_, int xsize) { - int x, v; + int x; INT32* in = (INT32*) in_; for (x = 0; x < xsize; x++, in++) { - v = CLIP16(*in); - *out++ = (UINT8) v; - *out++ = (UINT8) (v >> 8); + *out++ = (UINT8) *in; + *out++ = (UINT8) (*in >> 8); } } static void I_I16B(UINT8* out, const UINT8* in_, int xsize) { - int x, v; + int x; INT32* in = (INT32*) in_; for (x = 0; x < xsize; x++, in++) { - v = CLIP16(*in); - *out++ = (UINT8) (v >> 8); - *out++ = (UINT8) v; + *out++ = (UINT8) (*in >> 8); + *out++ = (UINT8) *in; } } @@ -769,7 +757,7 @@ I16L_F(UINT8* out_, const UINT8* in, int xsize) int x; FLOAT32* out = (FLOAT32*) out_; for (x = 0; x < xsize; x++, in += 2) - *out++ = (FLOAT32) (in[0] + ((int) in[1] << 8)); + *out++ = (FLOAT32) in[1]; } @@ -779,7 +767,7 @@ I16B_F(UINT8* out_, const UINT8* in, int xsize) int x; FLOAT32* out = (FLOAT32*) out_; for (x = 0; x < xsize; x++, in += 2) - *out++ = (FLOAT32) (in[1] + ((int) in[0] << 8)); + *out++ = (FLOAT32) in[0]; } static void @@ -787,8 +775,8 @@ L_I16L(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++, in++) { + *out++ = *in << 8; *out++ = *in; - *out++ = 0; } } @@ -797,8 +785,8 @@ L_I16B(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++, in++) { - *out++ = 0; *out++ = *in; + *out++ = *in << 8; } } @@ -807,10 +795,7 @@ I16L_L(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++, in += 2) - if (in[1] != 0) - *out++ = 255; - else - *out++ = in[0]; + *out++ = in[1] + (in[0] << 8); } static void @@ -818,10 +803,7 @@ I16B_L(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++, in += 2) - if (in[0] != 0) - *out++ = 255; - else - *out++ = in[1]; + *out++ = in[0] + (in[1] << 8); } static struct { @@ -1020,7 +1002,7 @@ p2i(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette) int x; INT32* out = (INT32*) out_; for (x = 0; x < xsize; x++) - *out++ = L(&palette[in[x]*4]) / 1000; + *out++ = (L(&palette[in[x]*4]) / 1000) << 8; } static void @@ -1029,7 +1011,7 @@ pa2i(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette) int x; INT32* out = (INT32*) out_; for (x = 0; x < xsize; x++, in += 4) - *out++ = L(&palette[in[0]*4]) / 1000; + *out++ = (L(&palette[in[0]*4]) / 1000) << 8; } static void diff --git a/src/libImaging/Histo.c b/src/libImaging/Histo.c index b7c1a9834..b330f88f8 100644 --- a/src/libImaging/Histo.c +++ b/src/libImaging/Histo.c @@ -135,11 +135,10 @@ ImagingGetHistogram(Imaging im, Imaging imMask, void* minmax) if (imin >= imax) break; ImagingSectionEnter(&cookie); - scale = 255.0F / (imax - imin); for (y = 0; y < im->ysize; y++) { INT32* in = im->image32[y]; for (x = 0; x < im->xsize; x++) { - i = (int) (((*in++)-imin)*scale); + i = (int) (((*in++)-imin) * 255.0F / (imax - imin)); if (i >= 0 && i < 256) h->histogram[i]++; } From ddc83fd8a7b4df57a43dace3697d0f4185977f62 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 8 May 2019 00:22:19 +1000 Subject: [PATCH 2/5] Updated ImageMath tests for scaled I mode conversion --- Tests/test_imagemath.py | 56 ++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/Tests/test_imagemath.py b/Tests/test_imagemath.py index 8273b63c1..8acc9420f 100644 --- a/Tests/test_imagemath.py +++ b/Tests/test_imagemath.py @@ -31,22 +31,22 @@ class TestImageMath(PillowTestCase): def test_sanity(self): self.assertEqual(ImageMath.eval("1"), 1) self.assertEqual(ImageMath.eval("1+A", A=2), 3) - self.assertEqual(pixel(ImageMath.eval("A+B", A=A, B=B)), "I 3") - self.assertEqual(pixel(ImageMath.eval("A+B", images)), "I 3") + self.assertEqual(pixel(ImageMath.eval("A+B", A=A, B=B)), "I 768") + self.assertEqual(pixel(ImageMath.eval("A+B", images)), "I 768") self.assertEqual(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") self.assertEqual(pixel( - ImageMath.eval("int(float(A)+B)", images)), "I 3") + ImageMath.eval("int(float(A)+B)", images)), "I 768") def test_ops(self): - self.assertEqual(pixel(ImageMath.eval("-A", images)), "I -1") + self.assertEqual(pixel(ImageMath.eval("-A", images)), "I -256") self.assertEqual(pixel(ImageMath.eval("+B", images)), "L 2") - self.assertEqual(pixel(ImageMath.eval("A+B", images)), "I 3") - self.assertEqual(pixel(ImageMath.eval("A-B", images)), "I -1") - self.assertEqual(pixel(ImageMath.eval("A*B", images)), "I 2") + self.assertEqual(pixel(ImageMath.eval("A+B", images)), "I 768") + self.assertEqual(pixel(ImageMath.eval("A-B", images)), "I -256") + self.assertEqual(pixel(ImageMath.eval("A*B", images)), "I 131072") self.assertEqual(pixel(ImageMath.eval("A/B", images)), "I 0") - self.assertEqual(pixel(ImageMath.eval("B**2", images)), "I 4") + self.assertEqual(pixel(ImageMath.eval("B**2", images)), "I 262144") self.assertEqual(pixel( ImageMath.eval("B**33", images)), "I 2147483647") @@ -72,61 +72,61 @@ class TestImageMath(PillowTestCase): ImageMath.eval("convert(A+B, 'RGB')", images)), "RGB (3, 3, 3)") def test_compare(self): - self.assertEqual(pixel(ImageMath.eval("min(A, B)", images)), "I 1") - self.assertEqual(pixel(ImageMath.eval("max(A, B)", images)), "I 2") - self.assertEqual(pixel(ImageMath.eval("A == 1", images)), "I 1") + self.assertEqual(pixel(ImageMath.eval("min(A, B)", images)), "I 256") + self.assertEqual(pixel(ImageMath.eval("max(A, B)", images)), "I 512") + self.assertEqual(pixel(ImageMath.eval("A == 256", images)), "I 1") self.assertEqual(pixel(ImageMath.eval("A == 2", images)), "I 0") def test_one_image_larger(self): - self.assertEqual(pixel(ImageMath.eval("A+B", A=A2, B=B)), "I 3") - self.assertEqual(pixel(ImageMath.eval("A+B", A=A, B=B2)), "I 3") + self.assertEqual(pixel(ImageMath.eval("A+B", A=A2, B=B)), "I 768") + self.assertEqual(pixel(ImageMath.eval("A+B", A=A, B=B2)), "I 768") def test_abs(self): - self.assertEqual(pixel(ImageMath.eval("abs(A)", A=A)), "I 1") - self.assertEqual(pixel(ImageMath.eval("abs(B)", B=B)), "I 2") + self.assertEqual(pixel(ImageMath.eval("abs(A)", A=A)), "I 256") + self.assertEqual(pixel(ImageMath.eval("abs(B)", B=B)), "I 512") def test_binary_mod(self): self.assertEqual(pixel(ImageMath.eval("A%A", A=A)), "I 0") self.assertEqual(pixel(ImageMath.eval("B%B", B=B)), "I 0") - self.assertEqual(pixel(ImageMath.eval("A%B", A=A, B=B)), "I 1") + self.assertEqual(pixel(ImageMath.eval("A%B", A=A, B=B)), "I 256") self.assertEqual(pixel(ImageMath.eval("B%A", A=A, B=B)), "I 0") self.assertEqual(pixel(ImageMath.eval("Z%A", A=A, Z=Z)), "I 0") self.assertEqual(pixel(ImageMath.eval("Z%B", B=B, Z=Z)), "I 0") def test_bitwise_invert(self): self.assertEqual(pixel(ImageMath.eval("~Z", Z=Z)), "I -1") - self.assertEqual(pixel(ImageMath.eval("~A", A=A)), "I -2") - self.assertEqual(pixel(ImageMath.eval("~B", B=B)), "I -3") + self.assertEqual(pixel(ImageMath.eval("~A", A=A)), "I -257") + self.assertEqual(pixel(ImageMath.eval("~B", B=B)), "I -513") def test_bitwise_and(self): self.assertEqual(pixel(ImageMath.eval("Z&Z", A=A, Z=Z)), "I 0") self.assertEqual(pixel(ImageMath.eval("Z&A", A=A, Z=Z)), "I 0") self.assertEqual(pixel(ImageMath.eval("A&Z", A=A, Z=Z)), "I 0") - self.assertEqual(pixel(ImageMath.eval("A&A", A=A, Z=Z)), "I 1") + self.assertEqual(pixel(ImageMath.eval("A&A", A=A, Z=Z)), "I 256") def test_bitwise_or(self): self.assertEqual(pixel(ImageMath.eval("Z|Z", A=A, Z=Z)), "I 0") - self.assertEqual(pixel(ImageMath.eval("Z|A", A=A, Z=Z)), "I 1") - self.assertEqual(pixel(ImageMath.eval("A|Z", A=A, Z=Z)), "I 1") - self.assertEqual(pixel(ImageMath.eval("A|A", A=A, Z=Z)), "I 1") + self.assertEqual(pixel(ImageMath.eval("Z|A", A=A, Z=Z)), "I 256") + self.assertEqual(pixel(ImageMath.eval("A|Z", A=A, Z=Z)), "I 256") + self.assertEqual(pixel(ImageMath.eval("A|A", A=A, Z=Z)), "I 256") def test_bitwise_xor(self): self.assertEqual(pixel(ImageMath.eval("Z^Z", A=A, Z=Z)), "I 0") - self.assertEqual(pixel(ImageMath.eval("Z^A", A=A, Z=Z)), "I 1") - self.assertEqual(pixel(ImageMath.eval("A^Z", A=A, Z=Z)), "I 1") + self.assertEqual(pixel(ImageMath.eval("Z^A", A=A, Z=Z)), "I 256") + self.assertEqual(pixel(ImageMath.eval("A^Z", A=A, Z=Z)), "I 256") self.assertEqual(pixel(ImageMath.eval("A^A", A=A, Z=Z)), "I 0") def test_bitwise_leftshift(self): self.assertEqual(pixel(ImageMath.eval("Z<<0", Z=Z)), "I 0") self.assertEqual(pixel(ImageMath.eval("Z<<1", Z=Z)), "I 0") - self.assertEqual(pixel(ImageMath.eval("A<<0", A=A)), "I 1") - self.assertEqual(pixel(ImageMath.eval("A<<1", A=A)), "I 2") + self.assertEqual(pixel(ImageMath.eval("A<<0", A=A)), "I 256") + self.assertEqual(pixel(ImageMath.eval("A<<1", A=A)), "I 512") def test_bitwise_rightshift(self): self.assertEqual(pixel(ImageMath.eval("Z>>0", Z=Z)), "I 0") self.assertEqual(pixel(ImageMath.eval("Z>>1", Z=Z)), "I 0") - self.assertEqual(pixel(ImageMath.eval("A>>0", A=A)), "I 1") - self.assertEqual(pixel(ImageMath.eval("A>>1", A=A)), "I 0") + self.assertEqual(pixel(ImageMath.eval("A>>0", A=A)), "I 256") + self.assertEqual(pixel(ImageMath.eval("A>>1", A=A)), "I 128") def test_logical_eq(self): self.assertEqual(pixel(ImageMath.eval("A==A", A=A)), "I 1") From 2787b9b991f6aa6b977047ea1f006dfbdd1f6c58 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 8 May 2019 13:13:19 +1000 Subject: [PATCH 3/5] Added tests --- Tests/images/hopper_I.png | Bin 0 -> 24139 bytes Tests/test_image_convert.py | 8 ++++++++ 2 files changed, 8 insertions(+) create mode 100644 Tests/images/hopper_I.png diff --git a/Tests/images/hopper_I.png b/Tests/images/hopper_I.png new file mode 100644 index 0000000000000000000000000000000000000000..314ab09c787430c28e770477513d905a08b21d42 GIT binary patch literal 24139 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�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 Date: Thu, 9 May 2019 14:32:14 +1000 Subject: [PATCH 4/5] Updated pixel range documentation [ci skip] --- docs/handbook/concepts.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/handbook/concepts.rst b/docs/handbook/concepts.rst index 055821a15..8f4373fff 100644 --- a/docs/handbook/concepts.rst +++ b/docs/handbook/concepts.rst @@ -25,9 +25,9 @@ Modes ----- The ``mode`` of an image defines the type and depth of a pixel in the image. -Each pixel uses the full range of the bit depth. So a 1-bit pixel has a range -of 0-1, an 8-bit pixel has a range of 0-255 and so on. The current release -supports the following standard modes: +A 1-bit pixel has a range of 0-1, an 8-bit pixel or a 32-bit floating point +pixel has a range of 0-255, and a 32-bit signed integer has a range of 0-65535. +The current release supports the following standard modes: * ``1`` (1-bit pixels, black and white, stored with one pixel per byte) * ``L`` (8-bit pixels, black and white) From a258b8dcdc0db36cddb79fbda2f3993bc5934000 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Thu, 9 May 2019 19:05:06 +1000 Subject: [PATCH 5/5] Apply suggestions from code review [ci skip] Co-Authored-By: Hugo --- docs/handbook/concepts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/handbook/concepts.rst b/docs/handbook/concepts.rst index 8f4373fff..8e4c0d6da 100644 --- a/docs/handbook/concepts.rst +++ b/docs/handbook/concepts.rst @@ -27,7 +27,7 @@ Modes The ``mode`` of an image defines the type and depth of a pixel in the image. A 1-bit pixel has a range of 0-1, an 8-bit pixel or a 32-bit floating point pixel has a range of 0-255, and a 32-bit signed integer has a range of 0-65535. -The current release supports the following standard modes: +These modes are supported: * ``1`` (1-bit pixels, black and white, stored with one pixel per byte) * ``L`` (8-bit pixels, black and white)