Skip to content

MNT Removes externals._pilutil and uses Pillow directly #22743

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

Merged
merged 2 commits into from
Mar 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions sklearn/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

from sklearn.utils import _IS_32BIT
from sklearn.utils._openmp_helpers import _openmp_effective_n_threads
from sklearn.externals import _pilutil
from sklearn._min_dependencies import PYTEST_MIN_VERSION
from sklearn.utils.fixes import parse_version
from sklearn.datasets import fetch_20newsgroups
Expand Down Expand Up @@ -169,7 +168,14 @@ def pytest_collection_modifyitems(config, items):
# details.
if item.name != "sklearn._config.config_context":
item.add_marker(skip_marker)
elif not _pilutil.pillow_installed:
try:
import PIL # noqa

pillow_installed = True
except ImportError:
pillow_installed = False

if not pillow_installed:
skip_marker = pytest.mark.skip(reason="pillow (or PIL) not installed!")
for item in items:
if item.name in [
Expand Down
14 changes: 11 additions & 3 deletions sklearn/datasets/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1387,8 +1387,15 @@ def load_sample_images():
>>> first_img_data.dtype #doctest: +SKIP
dtype('uint8')
"""
# import PIL only when needed
from ..externals._pilutil import imread
try:
from PIL import Image
except ImportError:
raise ImportError(
"The Python Imaging Library (PIL) is required to load data "
"from jpeg files. Please refer to "
"https://pillow.readthedocs.io/en/stable/installation.html "
"for installing PIL."
)

descr = load_descr("README.txt", descr_module=IMAGES_MODULE)

Expand All @@ -1397,7 +1404,8 @@ def load_sample_images():
if filename.endswith(".jpg"):
filenames.append(filename)
with resources.open_binary(IMAGES_MODULE, filename) as image_file:
image = imread(image_file)
pil_image = Image.open(image_file)
image = np.asarray(pil_image)
images.append(image)

return Bunch(images=images, filenames=filenames, DESCR=descr)
Expand Down
23 changes: 16 additions & 7 deletions sklearn/datasets/_lfw.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,15 @@ def _check_fetch_lfw(data_home=None, funneled=True, download_if_missing=True):

def _load_imgs(file_paths, slice_, color, resize):
"""Internally used to load images"""
# import PIL only when needed
from ..externals._pilutil import imread, imresize
try:
from PIL import Image
except ImportError:
raise ImportError(
"The Python Imaging Library (PIL) is required to load data "
"from jpeg files. Please refer to "
"https://pillow.readthedocs.io/en/stable/installation.html "
"for installing PIL."
)

# compute the portion of the images to load to respect the slice_ parameter
# given by the caller
Expand Down Expand Up @@ -151,17 +158,19 @@ def _load_imgs(file_paths, slice_, color, resize):

# Checks if jpeg reading worked. Refer to issue #3594 for more
# details.
img = imread(file_path)
if img.ndim == 0:
pil_img = Image.open(file_path)
pil_img.crop((w_slice.start, h_slice.start, w_slice.stop, h_slice.stop))
if resize is not None:
pil_img = pil_img.resize((w, h))
face = np.asarray(pil_img, dtype=np.float32)

if face.ndim == 0:
raise RuntimeError(
"Failed to read the image file %s, "
"Please make sure that libjpeg is installed" % file_path
)

face = np.asarray(img[slice_], dtype=np.float32)
face /= 255.0 # scale uint8 coded colors to the [0.0, 1.0] floats
if resize is not None:
face = imresize(face, resize)
if not color:
# average the color channels to compute a gray levels
# representation
Expand Down
10 changes: 3 additions & 7 deletions sklearn/datasets/tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@
from sklearn.utils._testing import SkipTest
from sklearn.datasets.tests.test_common import check_as_frame

from sklearn.externals._pilutil import pillow_installed


def _remove_dir(path):
if os.path.isdir(path):
Expand Down Expand Up @@ -226,11 +224,9 @@ def test_load_sample_image():


def test_load_missing_sample_image_error():
if pillow_installed:
with pytest.raises(AttributeError):
load_sample_image("blop.jpg")
else:
warnings.warn("Could not load sample images, PIL is not available.")
pytest.importorskip("PIL")
with pytest.raises(AttributeError):
load_sample_image("blop.jpg")


def test_load_diabetes_raw():
Expand Down
11 changes: 3 additions & 8 deletions sklearn/datasets/tests/test_lfw.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,10 @@
import numpy as np
import pytest
from functools import partial
from sklearn.externals._pilutil import pillow_installed, imsave
from sklearn.datasets import fetch_lfw_pairs
from sklearn.datasets import fetch_lfw_people

from sklearn.utils._testing import assert_array_equal
from sklearn.utils._testing import SkipTest
from sklearn.datasets.tests.test_common import check_return_X_y


Expand All @@ -41,8 +39,7 @@

def setup_module():
"""Test fixture run once and common to all tests of this module"""
if not pillow_installed:
raise SkipTest("PIL not installed.")
Image = pytest.importorskip("PIL.Image")

global SCIKIT_LEARN_DATA, SCIKIT_LEARN_EMPTY_DATA, LFW_HOME

Expand All @@ -69,10 +66,8 @@ def setup_module():
for i in range(n_faces):
file_path = os.path.join(folder_name, name + "_%04d.jpg" % i)
uniface = np_rng.randint(0, 255, size=(250, 250, 3))
try:
imsave(file_path, uniface)
except ImportError:
raise SkipTest("PIL not installed")
img = Image.fromarray(uniface.astype(np.uint8))
img.save(file_path)

# add some random file pollution to test robustness
with open(os.path.join(LFW_HOME, "lfw_funneled", ".test.swp"), "wb") as f:
Expand Down
Loading