Source code for cvbase.image

from __future__ import division

from os import path

import cv2
import numpy as np
import six

from cvbase.det import bbox_clip, bbox_scaling
from cvbase.io import check_file_exist, mkdir_or_exist
from cvbase.opencv import IMREAD_COLOR, INTER_LINEAR


[docs]def read_img(img_or_path, flag=IMREAD_COLOR): """Read an image Args: img_or_path(ndarray or str): either an image or path of an image flag(int): flags specifying the color type of a loaded image Returns: ndarray: image array """ if isinstance(img_or_path, np.ndarray): return img_or_path elif isinstance(img_or_path, six.string_types): check_file_exist(img_or_path, 'img file does not exist: {}'.format(img_or_path)) return cv2.imread(img_or_path, flag) else: raise TypeError('"img" must be a numpy array or a filename')
[docs]def img_from_bytes(content, flag=IMREAD_COLOR): """Read an image from bytes Args: content(bytes): images bytes got from files or other streams flag(int): same as :func:`read_img` Returns: ndarray: image array """ img_np = np.fromstring(content, np.uint8) img = cv2.imdecode(img_np, flag) return img
[docs]def write_img(img, file_path, params=None, auto_mkdir=True): """Write image to file Args: img(ndarray): image to be written to file file_path(str): file path params(None or list): same as opencv imwrite interface auto_mkdir(bool): if the parrent folder of file_path does not exist, whether to create it automatically Returns: bool: successful or not """ if auto_mkdir: dir_name = path.abspath(path.dirname(file_path)) mkdir_or_exist(dir_name) return cv2.imwrite(file_path, img, params)
[docs]def bgr2gray(img, keepdim=False): """Convert a BGR image to grayscale image Args: img(ndarray or str): either an image or path of an image keepdim(bool): if set to False(by default), return the gray image with 2 dims, otherwise 3 dims. Returns: ndarray: the grayscale image """ in_img = read_img(img) out_img = cv2.cvtColor(in_img, cv2.COLOR_BGR2GRAY) if keepdim: out_img = out_img[..., np.newaxis] return out_img
[docs]def gray2bgr(img): """Convert a grayscale image to BGR image Args: img(ndarray or str): either an image or path of an image Returns: ndarray: the BGR image """ in_img = read_img(img) if in_img.ndim == 2: out_img = cv2.cvtColor(in_img[..., np.newaxis], cv2.COLOR_GRAY2BGR) else: out_img = cv2.cvtColor(in_img, cv2.COLOR_GRAY2BGR) return out_img
[docs]def bgr2rgb(img): """Convert a BGR image to RGB image Args: img(ndarray or str): either an image or path of an image Returns: ndarray: the RGB image """ in_img = read_img(img) out_img = cv2.cvtColor(in_img, cv2.COLOR_BGR2RGB) return out_img
[docs]def rgb2bgr(img): """Convert a RGB image to BGR image Args: img(ndarray or str): either an image or path of an image Returns: ndarray: the BGR image """ in_img = read_img(img) out_img = cv2.cvtColor(in_img, cv2.COLOR_RGB2BGR) return out_img
[docs]def bgr2hsv(img): """Convert a BGR image to HSV image Args: img(ndarray or str): either an image or path of an image Returns: ndarray: the HSV image """ in_img = read_img(img) out_img = cv2.cvtColor(in_img, cv2.COLOR_BGR2HSV) return out_img
[docs]def hsv2bgr(img): """Convert a HSV image to BGR image Args: img(ndarray or str): either an image or path of an image Returns: ndarray: the BGR image """ in_img = read_img(img) out_img = cv2.cvtColor(in_img, cv2.COLOR_HSV2BGR) return out_img
[docs]def scale_size(size, scale): """Scale a size Args: size(tuple): w, h scale(float): scaling factor Returns: tuple: scaled size """ w, h = size return int(w * float(scale) + 0.5), int(h * float(scale) + 0.5)
[docs]def resize(img, size, return_scale=False, interpolation=INTER_LINEAR): """Resize image by expected size Args: img(ndarray): image or image path size(tuple): (w, h) return_scale(bool): whether to return w_scale and h_scale interpolation(enum): interpolation method Returns: ndarray: resized image """ img = read_img(img) h, w = img.shape[:2] resized_img = cv2.resize(img, size, interpolation=interpolation) if not return_scale: return resized_img else: w_scale = size[0] / float(w) h_scale = size[1] / float(h) return resized_img, w_scale, h_scale
[docs]def resize_like(img, dst_img, return_scale=False, interpolation=INTER_LINEAR): """Resize image to the same size of a given image Args: img(ndarray): image or image path dst_img(ndarray): the given image with expected size return_scale(bool): whether to return w_scale and h_scale interpolation(enum): interpolation method Returns: ndarray: resized image """ h, w = dst_img.shape[:2] return resize(img, (w, h), return_scale, interpolation)
[docs]def resize_by_ratio(img, ratio, interpolation=INTER_LINEAR): """Resize image by a ratio Args: img(ndarray): image or image path ratio(float): scale factor interpolation(enum): interpolation method Returns: ndarray: resized image """ assert isinstance(ratio, (float, int)) and ratio > 0 img = read_img(img) h, w = img.shape[:2] new_size = scale_size((w, h), ratio) return cv2.resize(img, new_size, interpolation=interpolation)
[docs]def resize_keep_ar(img, max_long_edge, max_short_edge, return_scale=False, interpolation=INTER_LINEAR): """Resize image with aspect ratio unchanged The long edge of resized image is no greater than max_long_edge, the short edge of resized image is no greater than max_short_edge. Args: img(ndarray): image or image path max_long_edge(int): max value of the long edge of resized image max_short_edge(int): max value of the short edge of resized image return_scale(bool): whether to return scale besides the resized image interpolation(enum): interpolation method Returns: tuple: (resized image, scale factor) """ if max_long_edge < max_short_edge: raise ValueError( '"max_long_edge" should not be less than "max_short_edge"') img = read_img(img) h, w = img.shape[:2] scale = min( float(max_long_edge) / max(h, w), float(max_short_edge) / min(h, w)) new_size = scale_size((w, h), scale) resized_img = cv2.resize(img, new_size, interpolation=interpolation) if return_scale: return resized_img, scale else: return resized_img
[docs]def limit_size(img, max_edge, return_scale=False, interpolation=INTER_LINEAR): """Limit the size of an image If the long edge of the image is greater than max_edge, resize the image Args: img(ndarray): input image max_edge(int): max value of long edge return_scale(bool): whether to return scale besides the resized image interpolation(enum): interpolation method Returns: tuple: (resized image, scale factor) """ img = read_img(img) h, w = img.shape[:2] if max(h, w) > max_edge: scale = float(max_edge) / max(h, w) new_size = scale_size((w, h), scale) resized_img = cv2.resize(img, new_size, interpolation=interpolation) else: scale = 1.0 resized_img = img if return_scale: return resized_img, scale else: return resized_img
[docs]def crop_img(img, bboxes, scale_ratio=1.0, pad_fill=None): """Crop image patches 3 steps: scale the bboxes -> clip bboxes -> crop and pad Args: img(ndarray): image to be cropped bboxes(ndarray): shape (k, 4) or (4, ), location of cropped bboxes scale_ratio(float): scale ratio of bboxes, default by 1.0 (no scaling) pad_fill(number or list): value to be filled for padding, None for no padding Returns: list or ndarray: cropped image patches """ chn = 1 if img.ndim == 2 else img.shape[2] if pad_fill is not None: if isinstance(pad_fill, (int, float)): pad_fill = [pad_fill for _ in range(chn)] assert len(pad_fill) == chn img = read_img(img) _bboxes = bboxes[np.newaxis, ...] if bboxes.ndim == 1 else bboxes scaled_bboxes = bbox_scaling(_bboxes, scale_ratio) scaled_bboxes = scaled_bboxes.astype(np.int32) clipped_bbox = bbox_clip(scaled_bboxes, img.shape) patches = [] for i in range(clipped_bbox.shape[0]): x1, y1, x2, y2 = tuple(clipped_bbox[i, :].tolist()) if pad_fill is None: patch = img[y1:y2 + 1, x1:x2 + 1, ...] else: _x1, _y1, _x2, _y2 = tuple(scaled_bboxes[i, :].tolist()) if chn == 2: patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1) else: patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1, chn) patch = np.array( pad_fill, dtype=img.dtype) * np.ones( patch_shape, dtype=img.dtype) x_start = 0 if _x1 >= 0 else -_x1 y_start = 0 if _y1 >= 0 else -_y1 w = x2 - x1 + 1 h = y2 - y1 + 1 patch[y_start:y_start + h, x_start:x_start + w, ...] = img[ y1:y1 + h, x1:x1 + w, ...] patches.append(patch) if bboxes.ndim == 1: return patches[0] else: return patches
[docs]def pad_img(img, shape, pad_val): """Pad an image to a certain shape Args: img(ndarray): image to be padded shape(tuple): expected padding shape pad_val(float or int or list): values to be filled in padding areas Returns: ndarray: padded image """ if not isinstance(pad_val, (int, float)): assert len(pad_val) == img.shape[-1] if len(shape) < len(img.shape): shape = shape + (img.shape[-1], ) assert len(shape) == len(img.shape) for i in range(len(shape) - 1): assert shape[i] >= img.shape[i] pad = np.empty(shape, dtype=img.dtype) pad[...] = pad_val pad[:img.shape[0], :img.shape[1], ...] = img return pad
[docs]def rotate_img(img, angle, center=None, scale=1.0, border_value=0, auto_bound=False): """Rotate an image Args: img(ndarray or str): image to be rotated angle(float): rotation angle in degrees, positive values mean clockwise rotation center(tuple): center of the rotation in the source image, by default it is the center of the image. scale(float): isotropic scale factor border_value(int): border value auto_bound(bool): whether to adjust the image size to cover the whole rotated image Returns: ndarray: rotated image """ if center is not None and auto_bound: raise ValueError('`auto_bound` conflicts with `center`') img = read_img(img) h, w = img.shape[:2] if center is None: center = ((w - 1) / 2, (h - 1) / 2) assert isinstance(center, tuple) matrix = cv2.getRotationMatrix2D(center, -angle, scale) if auto_bound: cos = np.abs(matrix[0, 0]) sin = np.abs(matrix[0, 1]) new_w = h * sin + w * cos new_h = h * cos + w * sin matrix[0, 2] += (new_w - w) / 2 matrix[1, 2] += (new_h - h) / 2 w = int(np.round(new_w)) h = int(np.round(new_h)) rotated = cv2.warpAffine(img, matrix, (w, h), borderValue=border_value) return rotated