Replace misc.imread(path) with np.asarray(Image.open(path)) (or
imageio.v3.imread(path)), misc.imresize with Image.resize (note: Pillow sizes are
width, height), and misc.imsave/misc.toimage with Image.fromarray(arr).save(path),
scaling float data to 0–255 yourself.
The error
from scipy import misc
img = misc.imread("photo.jpg")
example.py:1: DeprecationWarning: scipy.misc is deprecated and will be removed in 2.0.0
from scipy import misc
Traceback (most recent call last):
...
AttributeError: module 'scipy.misc' has no attribute 'imread'
Or, with a direct import:
from scipy.misc import imread, imresize
ImportError: cannot import name 'imread' from 'scipy.misc' (/home/you/.venv/lib/python3.13/site-packages/scipy/misc/__init__.py)
Both come from SciPy 1.18 here. On current SciPy, the whole scipy.misc module is deprecated and empty, so
installing an older SciPy is not a real option either: versions that still had imread do not install on
current Python.
imread
import numpy as np
from PIL import Image
img = np.asarray(Image.open("photo.jpg")) # misc.imread(path)
gray = np.asarray(Image.open("photo.jpg").convert("L")) # misc.imread(path, mode="L")
flat = np.asarray(Image.open("photo.jpg").convert("F")) # misc.imread(path, flatten=True)
print(img.dtype, img.shape)
print(gray.dtype, gray.shape)
print(flat.dtype, flat.shape, f"{flat.max():.1f}")
uint8 (1067, 1600, 3)
uint8 (1067, 1600)
float32 (1067, 1600) 254.8
mode="L" and flatten=True map to Pillow conversions: "L" gives 8-bit greyscale,
"F" gives 32-bit float greyscale in the 0–255 range, which is what flatten=True returned.
Which reader returns what
Three libraries can read images into NumPy. They agree on JPEGs, not on PNGs:
import imageio.v3 as iio
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
print(f"{'file':<10} {'Pillow':<14} {'imageio':<14} {'plt.imread'}")
for f in ["photo.jpg", "photo.png", "logo.png"]:
a, b, c = np.asarray(Image.open(f)), iio.imread(f), plt.imread(f)
print(f"{f:<10} {str(a.dtype):<14} {str(b.dtype):<14} {c.dtype}, max {c.max()}")
file Pillow imageio plt.imread
photo.jpg uint8 uint8 uint8, max 255
photo.png uint8 uint8 float32, max 1.0
logo.png uint8 uint8 float32, max 1.0
matplotlib.pyplot.imread returns PNGs as float32 in 0–1 but JPEGs as uint8 in
0–255. Code that thresholds at 128 works on one and silently fails on the other. Pillow and imageio both return
uint8, like the old SciPy function did.
imresize
The old function took a scale factor, a percentage, or a size in NumPy order (rows first). Pillow's
resize() takes (width, height). Passing a NumPy-style shape straight to Pillow transposes the size
without any error, so this replacement keeps the old convention:
import numpy as np
from PIL import Image
RESAMPLE = {"nearest": Image.NEAREST, "bilinear": Image.BILINEAR, "bicubic": Image.BICUBIC, "lanczos": Image.LANCZOS}
def imresize(arr, size, interp="bilinear"):
"""Replacement for scipy.misc.imresize for uint8 images.
size: a float (scale factor), an int (percentage) or a tuple (rows, cols): height first, like NumPy."""
img = Image.fromarray(arr)
if isinstance(size, float):
size = (round(arr.shape[0] * size), round(arr.shape[1] * size))
elif isinstance(size, int):
size = (round(arr.shape[0] * size / 100), round(arr.shape[1] * size / 100))
rows, cols = size[:2]
return np.asarray(img.resize((cols, rows), RESAMPLE[interp])) # Pillow wants (width, height)
a = np.asarray(Image.open("photo.jpg"))
print(imresize(a, 0.5).shape)
print(imresize(a, 25).shape)
print(imresize(a, (300, 400)).shape) # 300 rows x 400 columns
(534, 800, 3)
(267, 400, 3)
(300, 400, 3)
Two more differences: imresize always returned uint8, and its default filter was bilinear. If
you are porting code that compares results numerically, keep interp="bilinear"; for new code,
"lanczos" looks better when shrinking (see resizing).
imsave and toimage
These rescaled any array to the full 0–255 range before saving. Image.fromarray() does not, so data in
0–1 comes out black, and float colour (3-channel) arrays raise TypeError: Cannot handle this data type:
import numpy as np
from PIL import Image
depth = np.linspace(0.0, 1.0, 200 * 300, dtype=np.float32).reshape(200, 300) # e.g. a model output in 0..1
# misc.imsave / misc.toimage stretched the data to 0..255 automatically ("bytescale")
lo, hi = depth.min(), depth.max()
scaled = ((depth - lo) / (hi - lo) * 255).round().astype(np.uint8)
Image.fromarray(scaled).save("depth.png")
print("saved", Image.open("depth.png").getextrema())
# Without the scaling step, the same values become an almost black image:
print("unscaled:", Image.fromarray((depth).astype(np.uint8)).getextrema())
saved (0, 255)
unscaled: (0, 1)
For float and 16-bit data, see Cannot handle this data type.
misc.face() and misc.ascent(), the sample images, moved to scipy.datasets, which
downloads them on first use and needs the pooch package.
Related
The outputs and images on this page are the real results of running every example with Pillow 12.3.0 on Python 3.13 (Linux) on 2026-10-15. Every example is also re-run automatically on macOS (Darwin 25.6.0, Pillow 12.3.0), Linux (Linux 6.17.0-1022-azure, Pillow 12.3.0), Windows (Windows 2025Server, Pillow 12.3.0); the line under each example shows the result per operating system. The DejaVu Sans font files sit next to each script so font examples behave the same everywhere. Sample photos: NASA/Sam Lott, public domain. Spotted something wrong? Tell us.