-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Slightly different Box down-sampling result #8587
Comments
I suspect that rounding has been sacrificed for the sake of performance in Pillow, but I wonder if @homm could confirm? |
BTW, is filter-based approach really optimal/needed? My code above on Node.js for scaling down a gray-scaled image (https://i.imgur.com/DR94LKg.jpeg) 1000 times works for 19 seconds. PIL's Box filter for 12 seconds. I expected a bigger difference. # ...
print(size) # (961, 1266)
# benchmark gray:
image_gray = image.convert("L")
print("bench gray")
start_time = time.time()
for i in range(1000):
image_box = image_gray.resize(size, Image.Resampling.BOX)
print(time.time() - start_time) # 12.385019302368164
start_time = time.time()
for i in range(1000):
image_lanc = image_gray.resize(size, Image.Resampling.LANCZOS)
print(time.time() - start_time) # 26.357529163360596 And Lanczos only ~2 times slower than Box resampling. |
Here is a picture of the table of 51x51 px squares (1px border), the image has odd width and height (2755x1837). from PIL import Image
image_path = "51-squares/_original.png"
image = Image.open(image_path)
newHeight = image.height // 2
size = (int(image.width / image.height * newHeight), newHeight)
print(image.width, image.height) # 2755 1837
print(image.width / size[0]) # 2.0021802325581395
print(image.height / size[1]) # 2.0010893246187362
print(size) # (1376, 918)
# image_gray = image.convert("L")
image_box = image.resize(size, Image.Resampling.BOX)
image_box.save("pil-box.png")
image_lanc = image.resize(size, Image.Resampling.LANCZOS)
image_lanc.save("pil-lanczos.png") |
One more about the central lines (the central cross that divides the image to 4 parts) and odd image dimension. I compared down-scaling with You need to toggle between DR94LKg-box.png and DR94LKg-lanczos.png to see it. It's pretty visible. My scaler produces a visually identical result to PIL's Box filter one, except the horizontal central 1px line (you need to use zooming to see it). It's possible the same bug as in my previous message. Or look at this: image_path = "DR94LKg.jpeg" # 1923x2533
image = Image.open(image_path)
newHeight = image.height // 2
size = (int(image.width / image.height * newHeight), newHeight)
print(image.width, image.height) # 1923 2533
print(image.width / size[0]) # 2.001040582726327
print(image.height / size[1]) # 2.000789889415482
print(size) # (961, 1266)
image_gray = image.convert("L")
image_box = image_gray.resize(size, Image.Resampling.BOX)
image_box.save("DR94LKg-box.png")
image_lanc = image_gray.resize(size, Image.Resampling.LANCZOS)
image_lanc.save("DR94LKg-lanczos.png") If I crop the image by 1 px for both sides: to 1922x2532, than everything is fine. |
What did you do?
I have written an image down-scaler in JavaScript using the Box algorithm. I compare the result of it with the result or resampling with PIL's
resize
withImage.Resampling.BOX
option. However, the results are slightly different.Here is my TypeScript code:
When PIL works as expected
To make things simpler, let's use 1D (1 pixel height) gray-scaled (1 channel) images.
In most cases PIL produces the expected result, for example:
[255, 0, 255, 0]
->[(255 + 0) / 2, (0 + 255) / 2]
->[127.5, 127.5]
-> rounding (+ 0.5 << 0
) ->[128, 128]
[255, 0, 255, 0, 255]
->[(255 + 0 + 255) / 3, (0 + 255) / 2]
->[170, 127.5]
->[170, 128]
As well as for the most simple transform — the transform from 1D gray image to 1x1 image:
[1,2,3,4,5,6,7,8]
->[(1+2+3+4+5+6+7+8) / 8]
->[4.5]
->[5]
[0,0,0,0, 0,0,0,0, 220]
->[220 / 9]
->[24.444444444444443]
->[24]
What actually happened?
However, when the group/box/area size is
10
,12
,14
,20
,22
,26
,30
,36
,38
,42
,...
pixels, then PIL produces the unexpected result — it rounds.5
to down.For example:
[0,0,0,0, 0,0,0,0, 0,255]
->[25.5]
->[25]
It prints:
What did you expect to happen?
Rounding of
25.5
should be26
(Math.round(25.5)
/25.5 + 0.5 << 0
/int(25.5 + 0.5)
.Here is more complex example:
Try to add/remove zeros from the
pixel_values
array to change its length, to see that there are magic array sizes that PIL behaves strangely with when rounding.5
.What are your OS, Python and Pillow versions?
The text was updated successfully, but these errors were encountered: