Pillow icon indicating copy to clipboard operation
Pillow copied to clipboard

Bilinear resize is incorrect

Open ppwwyyxx opened this issue 5 years ago • 2 comments

What did you do?

import numpy as np
from PIL import Image
import cv2

def pil_linear(im, shape_wh):
    im = Image.fromarray(im.astype("float32"), mode="F")
    return np.array(im.resize(shape_wh, Image.BILINEAR))

def cv_linear(im, shape_wh):
    return cv2.resize(im, shape_wh, interpolation=cv2.INTER_LINEAR)

np.set_printoptions(linewidth=200)
input = np.arange(100).reshape(10, 10).astype("float32")
shape1 = (5, 5)

output_1 = pil_linear(input, shape1)
output_2 = cv_linear(input, shape1)

print("INPUT:\n", input)
print("Pillow: \n", output_1)
print("OpenCV: \n", output_2)

What did you expect to happen?

I expect Pillow to produce the same results as output_2

What actually happened?

The output is

INPUT:
 [[ 0.  1.  2.  3.  4.  5.  6.  7.  8.  9.]
 [10. 11. 12. 13. 14. 15. 16. 17. 18. 19.]
 [20. 21. 22. 23. 24. 25. 26. 27. 28. 29.]
 [30. 31. 32. 33. 34. 35. 36. 37. 38. 39.]
 [40. 41. 42. 43. 44. 45. 46. 47. 48. 49.]
 [50. 51. 52. 53. 54. 55. 56. 57. 58. 59.]
 [60. 61. 62. 63. 64. 65. 66. 67. 68. 69.]
 [70. 71. 72. 73. 74. 75. 76. 77. 78. 79.]
 [80. 81. 82. 83. 84. 85. 86. 87. 88. 89.]
 [90. 91. 92. 93. 94. 95. 96. 97. 98. 99.]]
Pillow: 
 [[ 7.857143  9.642858 11.642858 13.642858 15.428572]
 [25.714285 27.5      29.5      31.5      33.285713]
 [45.714287 47.5      49.5      51.5      53.285713]
 [65.71429  67.5      69.5      71.5      73.28571 ]
 [83.57143  85.35714  87.35714  89.35714  91.14285 ]]
OpenCV: 
 [[ 5.5  7.5  9.5 11.5 13.5]
 [25.5 27.5 29.5 31.5 33.5]
 [45.5 47.5 49.5 51.5 53.5]
 [65.5 67.5 69.5 71.5 73.5]
 [85.5 87.5 89.5 91.5 93.5]]

I understand that Pillow does not necessarily produce the same outputs as OpenCV, and there are a few issues about such inconsistency - some of them are OpenCV's fault.

However, in this particular example, I think Pillow is incorrect at the boundaries. I expect the input pixels [[0, 1], [10, 11]] to become 5.5 after downsampling.

What are your OS, Python and Pillow versions?

  • OS: Linux
  • Python: 3.8
  • Pillow: 7.0.0

ppwwyyxx avatar Mar 16 '20 03:03 ppwwyyxx

Any comments on this issue may also apply to #4445

radarhere avatar Mar 16 '20 10:03 radarhere

what would be the possible reason for these kind of phenomenon?

actually I also tried to use PIL, matlab and cv2 to do BICUBIC downsampling and they all shows different result.

but the math formula is fixed??? how come we try to implement same math formula but end up with different outputs???

and for those people who work on image restoration area...i believe they might suffer from this issue...

I am confused...

image

BarCodeReader avatar Aug 14 '20 09:08 BarCodeReader

Regarding the first post, Pillow performs two passes over the image - horizontal, and then vertical.

https://github.com/python-pillow/Pillow/blob/b4bf2885f365b23e16772380173e971f89b208bf/src/libImaging/Resample.c#L655

So why is the first value 7.857143?

The coefficients generated by our bilinear function are 0.428571, 0.428571 and 0.142857.

Applying that horizontally,

0 * 0.428571 + 1 * 0.428571 + 2 * 0.142857 = 0.714285
10 * 0.428571 + 11 * 0.428571 + 12 * 0.142857 = 10.714275
20 * 0.428571 + 21 * 0.428571 + 22 * 0.142857 = 20.714265

and then vertically,

0.714285 * 0.428571 + 10.714275 * 0.428571 + 20.714265 * 0.142857 = 7.85712714287

You suggest that Pillow should only consider 0, 1, 10 and 11 to get the first pixel value. Instead, Pillow is also considering 2, 12, 20, 21 and 22. That is different to OpenCV, but I see no reason why it should be thought of as incorrect. 0, 1, 10 and 11 are still all considered equally.

radarhere avatar Oct 30 '22 20:10 radarhere

It is hard to discuss the second problem here because there isn't an image or code provided.

We don't aim to mimic OpenCV. If you have an image and code, we can talk about how Pillow calculates the final value. If you have a reference for how bicubic downsampling should work, we can talk about how Pillow differs from that.

radarhere avatar Oct 30 '22 20:10 radarhere