From 03b0e0c42b571247e7dc0570eab58596fe601ae3 Mon Sep 17 00:00:00 2001 From: LielinJiang <50691816+LielinJiang@users.noreply.github.com> Date: Fri, 18 Sep 2020 11:21:04 +0800 Subject: [PATCH] Remove dependences of cv2 (#27286) * rm dependence of cv2 --- python/paddle/utils/__init__.py | 1 + python/paddle/utils/lazy_import.py | 34 +++++++++++ python/paddle/vision/datasets/folder.py | 3 +- python/paddle/vision/transforms/functional.py | 51 +++++++++++----- python/paddle/vision/transforms/transforms.py | 61 +++++++++++++++---- python/requirements.txt | 1 - python/setup.py.in | 3 - 7 files changed, 121 insertions(+), 33 deletions(-) create mode 100644 python/paddle/utils/lazy_import.py diff --git a/python/paddle/utils/__init__.py b/python/paddle/utils/__init__.py index 4a78667972..77f5ef7e96 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 0000000000..69a32b77a8 --- /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 725fd9acaf..8a3053abef 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 b5668fa8c7..acceb111e6 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 14809e0c1a..9ea8282717 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 4788842475..6a88d61a94 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 7731664003..d85a23a5ed 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 = '' -- GitLab