diff --git a/python/paddle/utils/__init__.py b/python/paddle/utils/__init__.py index 4a786679727fb1b42c216146685e0e6524e858c9..77f5ef7e9661e0ff8f4f200ff7e789ba475459c2 100644 --- a/python/paddle/utils/__init__.py +++ b/python/paddle/utils/__init__.py @@ -16,6 +16,7 @@ from .profiler import ProfilerOptions from .profiler import Profiler from .profiler import get_profiler from .deprecated import deprecated +from .lazy_import import try_import from ..fluid.framework import unique_name from ..fluid.framework import load_op_library from ..fluid.framework import require_version diff --git a/python/paddle/utils/lazy_import.py b/python/paddle/utils/lazy_import.py new file mode 100644 index 0000000000000000000000000000000000000000..69a32b77a8f3da7a5a3432b4c0cbf746bd0b0833 --- /dev/null +++ b/python/paddle/utils/lazy_import.py @@ -0,0 +1,34 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Lazy imports for heavy dependencies.""" + +import importlib + + +def try_import(module_name): + """Try importing a module, with an informative error message on failure.""" + install_name = module_name + if module_name == 'cv2': + install_name = 'opencv-python' + + try: + mod = importlib.import_module(module_name) + return mod + except ImportError: + err_msg = ( + "Failed importing {}. This likely means that some paddle modules " + "requires additional dependencies that have to be " + "manually installed (usually with `pip install {}`). ").format( + module_name, install_name) + raise ImportError(err_msg) diff --git a/python/paddle/vision/datasets/folder.py b/python/paddle/vision/datasets/folder.py index 725fd9acafbab7b6adaf07139d02da8e2c9aaada..8a3053abefc1b28ba36150a1ff68a4dd4c3469c9 100644 --- a/python/paddle/vision/datasets/folder.py +++ b/python/paddle/vision/datasets/folder.py @@ -14,9 +14,9 @@ import os import sys -import cv2 from paddle.io import Dataset +from paddle.utils import try_import __all__ = ["DatasetFolder", "ImageFolder"] @@ -191,6 +191,7 @@ IMG_EXTENSIONS = ('.jpg', '.jpeg', '.png', '.ppm', '.bmp', '.pgm', '.tif', def cv2_loader(path): + cv2 = try_import('cv2') return cv2.imread(path) diff --git a/python/paddle/vision/transforms/functional.py b/python/paddle/vision/transforms/functional.py index b5668fa8c7d6812664512a58faf836b5d9f09300..acceb111e6f84bc444dcfc7427653f77e3eedd3a 100644 --- a/python/paddle/vision/transforms/functional.py +++ b/python/paddle/vision/transforms/functional.py @@ -18,10 +18,11 @@ import random import math import functools -import cv2 import numbers import numpy as np +from paddle.utils import try_import + if sys.version_info < (3, 3): Sequence = collections.Sequence Iterable = collections.Iterable @@ -54,8 +55,8 @@ def flip(image, code): Accordding to the code (the type of flip), flip the input image Args: - image: Input image, with (H, W, C) shape - code: Code that indicates the type of flip. + image (np.ndarray): Input image, with (H, W, C) shape + code (int): Code that indicates the type of flip. -1 : Flip horizontally and vertically 0 : Flip vertically 1 : Flip horizontally @@ -77,18 +78,28 @@ def flip(image, code): # flip horizontally F.flip(fake_img, 1) """ + cv2 = try_import('cv2') return cv2.flip(image, flipCode=code) @keepdims -def resize(img, size, interpolation=cv2.INTER_LINEAR): +def resize(img, size, interpolation=1): """ resize the input data to given size Args: - input: Input data, could be image or masks, with (H, W, C) shape - size: Target size of input data, with (height, width) shape. - interpolation: Interpolation method. + input (np.ndarray): Input data, could be image or masks, with (H, W, C) shape + size (int|list|tuple): Target size of input data, with (height, width) shape. + interpolation (int, optional): Interpolation method. + 0 : cv2.INTER_NEAREST + 1 : cv2.INTER_LINEAR + 2 : cv2.INTER_CUBIC + 3 : cv2.INTER_AREA + 4 : cv2.INTER_LANCZOS4 + 5 : cv2.INTER_LINEAR_EXACT + 7 : cv2.INTER_MAX + 8 : cv2.WARP_FILL_OUTLIERS + 16: cv2.WARP_INVERSE_MAP Examples: .. code-block:: python @@ -102,7 +113,7 @@ def resize(img, size, interpolation=cv2.INTER_LINEAR): F.resize(fake_img, (200, 150)) """ - + cv2 = try_import('cv2') if isinstance(interpolation, Sequence): interpolation = random.choice(interpolation) @@ -179,6 +190,8 @@ def pad(img, padding, fill=(0, 0, 0), padding_mode='constant'): assert padding_mode in ['constant', 'edge', 'reflect', 'symmetric'], \ 'Expected padding mode be either constant, edge, reflect or symmetric, but got {}'.format(padding_mode) + cv2 = try_import('cv2') + PAD_MOD = { 'constant': cv2.BORDER_CONSTANT, 'edge': cv2.BORDER_REPLICATE, @@ -214,18 +227,22 @@ def pad(img, padding, fill=(0, 0, 0), padding_mode='constant'): @keepdims -def rotate(img, - angle, - interpolation=cv2.INTER_LINEAR, - expand=False, - center=None): +def rotate(img, angle, interpolation=1, expand=False, center=None): """Rotates the image by angle. Args: img (numpy.ndarray): Image to be rotated. angle (float|int): In degrees clockwise order. - interpolation (int, optional): - interpolation: Interpolation method. + interpolation (int, optional): Interpolation method. Default: 1. + 0 : cv2.INTER_NEAREST + 1 : cv2.INTER_LINEAR + 2 : cv2.INTER_CUBIC + 3 : cv2.INTER_AREA + 4 : cv2.INTER_LANCZOS4 + 5 : cv2.INTER_LINEAR_EXACT + 7 : cv2.INTER_MAX + 8 : cv2.WARP_FILL_OUTLIERS + 16: cv2.WARP_INVERSE_MAP expand (bool|optional): Optional expansion flag. If true, expands the output image to make it large enough to hold the entire rotated image. If false or omitted, make the output image the same size as the input image. @@ -250,8 +267,9 @@ def rotate(img, fake_img = rotate(fake_img, 10) print(fake_img.shape) """ - dtype = img.dtype + cv2 = try_import('cv2') + dtype = img.dtype h, w, _ = img.shape point = center or (w / 2, h / 2) M = cv2.getRotationMatrix2D(point, angle=-angle, scale=1) @@ -312,6 +330,7 @@ def to_grayscale(img, num_output_channels=1): fake_img = to_grayscale(fake_img) print(fake_img.shape) """ + cv2 = try_import('cv2') if num_output_channels == 1: img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) diff --git a/python/paddle/vision/transforms/transforms.py b/python/paddle/vision/transforms/transforms.py index 14809e0c1acaa1b6d5a494e6e3df1801e1c8f61b..9ea828271765cb8976909724bbf6079aa02c2460 100644 --- a/python/paddle/vision/transforms/transforms.py +++ b/python/paddle/vision/transforms/transforms.py @@ -17,7 +17,6 @@ from __future__ import division import math import sys import random -import cv2 import numpy as np import numbers @@ -26,6 +25,7 @@ import collections import warnings import traceback +from paddle.utils import try_import from . import functional as F if sys.version_info < (3, 3): @@ -214,7 +214,16 @@ class Resize(object): smaller edge of the image will be matched to this number. i.e, if height > width, then image will be rescaled to (size * height / width, size) - interpolation (int): Interpolation mode of resize. Default: cv2.INTER_LINEAR. + interpolation (int, optional): Interpolation mode of resize. Default: 1. + 0 : cv2.INTER_NEAREST + 1 : cv2.INTER_LINEAR + 2 : cv2.INTER_CUBIC + 3 : cv2.INTER_AREA + 4 : cv2.INTER_LANCZOS4 + 5 : cv2.INTER_LINEAR_EXACT + 7 : cv2.INTER_MAX + 8 : cv2.WARP_FILL_OUTLIERS + 16: cv2.WARP_INVERSE_MAP Examples: @@ -232,7 +241,7 @@ class Resize(object): print(fake_img.shape) """ - def __init__(self, size, interpolation=cv2.INTER_LINEAR): + def __init__(self, size, interpolation=1): assert isinstance(size, int) or (isinstance(size, Iterable) and len(size) == 2) self.size = size @@ -252,6 +261,16 @@ class RandomResizedCrop(object): output_size (int|list|tuple): Target size of output image, with (height, width) shape. scale (list|tuple): Range of size of the origin size cropped. Default: (0.08, 1.0) ratio (list|tuple): Range of aspect ratio of the origin aspect ratio cropped. Default: (0.75, 1.33) + interpolation (int, optional): Interpolation mode of resize. Default: 1. + 0 : cv2.INTER_NEAREST + 1 : cv2.INTER_LINEAR + 2 : cv2.INTER_CUBIC + 3 : cv2.INTER_AREA + 4 : cv2.INTER_LANCZOS4 + 5 : cv2.INTER_LINEAR_EXACT + 7 : cv2.INTER_MAX + 8 : cv2.WARP_FILL_OUTLIERS + 16: cv2.WARP_INVERSE_MAP Examples: @@ -273,7 +292,7 @@ class RandomResizedCrop(object): output_size, scale=(0.08, 1.0), ratio=(3. / 4, 4. / 3), - interpolation=cv2.INTER_LINEAR): + interpolation=1): if isinstance(output_size, int): self.output_size = (output_size, output_size) else: @@ -328,7 +347,16 @@ class CenterCropResize(object): Args: size (int|list|tuple): Target size of output image, with (height, width) shape. crop_padding (int): Center crop with the padding. Default: 32. - interpolation (int): Interpolation mode of resize. Default: cv2.INTER_LINEAR. + interpolation (int, optional): Interpolation mode of resize. Default: 1. + 0 : cv2.INTER_NEAREST + 1 : cv2.INTER_LINEAR + 2 : cv2.INTER_CUBIC + 3 : cv2.INTER_AREA + 4 : cv2.INTER_LANCZOS4 + 5 : cv2.INTER_LINEAR_EXACT + 7 : cv2.INTER_MAX + 8 : cv2.WARP_FILL_OUTLIERS + 16: cv2.WARP_INVERSE_MAP Examples: @@ -346,7 +374,7 @@ class CenterCropResize(object): print(fake_img.shape) """ - def __init__(self, size, crop_padding=32, interpolation=cv2.INTER_LINEAR): + def __init__(self, size, crop_padding=32, interpolation=1): if isinstance(size, int): self.size = (size, size) else: @@ -661,6 +689,7 @@ class ContrastTransform(object): if self.value == 0: return img + cv2 = try_import('cv2') dtype = img.dtype img = img.astype(np.float32) alpha = np.random.uniform(max(0, 1 - self.value), 1 + self.value) @@ -701,6 +730,8 @@ class SaturationTransform(object): if self.value == 0: return img + cv2 = try_import('cv2') + dtype = img.dtype img = img.astype(np.float32) alpha = np.random.uniform(max(0, 1 - self.value), 1 + self.value) @@ -742,6 +773,7 @@ class HueTransform(object): if self.value == 0: return img + cv2 = try_import('cv2') dtype = img.dtype img = img.astype(np.uint8) hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV_FULL) @@ -1036,7 +1068,16 @@ class RandomRotate(object): degrees (sequence or float or int): Range of degrees to select from. If degrees is a number instead of sequence like (min, max), the range of degrees will be (-degrees, +degrees) clockwise order. - interpolation (int|optional): Interpolation mode of resize. Default: cv2.INTER_LINEAR. + interpolation (int, optional): Interpolation mode of resize. Default: 1. + 0 : cv2.INTER_NEAREST + 1 : cv2.INTER_LINEAR + 2 : cv2.INTER_CUBIC + 3 : cv2.INTER_AREA + 4 : cv2.INTER_LANCZOS4 + 5 : cv2.INTER_LINEAR_EXACT + 7 : cv2.INTER_MAX + 8 : cv2.WARP_FILL_OUTLIERS + 16: cv2.WARP_INVERSE_MAP expand (bool|optional): Optional expansion flag. Default: False. If true, expands the output to make it large enough to hold the entire rotated image. If false or omitted, make the output image the same size as the input image. @@ -1061,11 +1102,7 @@ class RandomRotate(object): print(fake_img.shape) """ - def __init__(self, - degrees, - interpolation=cv2.INTER_LINEAR, - expand=False, - center=None): + def __init__(self, degrees, interpolation=1, expand=False, center=None): if isinstance(degrees, numbers.Number): if degrees < 0: raise ValueError( diff --git a/python/requirements.txt b/python/requirements.txt index 478884247553fbe44ba0295e6398d086cfcd9e41..6a88d61a94c182db3320e4de30f45be4c852f70f 100644 --- a/python/requirements.txt +++ b/python/requirements.txt @@ -1,4 +1,3 @@ -opencv-python<=4.2.0.32 requests>=2.20.0 numpy>=1.13, <=1.16.4 ; python_version<"3.5" numpy>=1.13 ; python_version>="3.5" diff --git a/python/setup.py.in b/python/setup.py.in index 773166400347ab550f82e4fabcb0d89b90818fc2..d85a23a5edd31f77514b468731097759f47533c1 100644 --- a/python/setup.py.in +++ b/python/setup.py.in @@ -237,9 +237,6 @@ if sys.version_info >= (3,7): setup_requires_tmp+=[setup_requires_i] setup_requires = setup_requires_tmp -if '${CMAKE_SYSTEM_PROCESSOR}' not in ['arm', 'armv7-a', 'aarch64']: - setup_requires+=['opencv-python'] - # the prefix is sys.prefix which should always be usr paddle_bins = ''