PaddleOCR/ppocr/data/imaug/operators.py

532 lines
17 KiB
Python
Raw Normal View History

2020-10-13 17:13:33 +08:00
"""
# Copyright (c) 2020 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.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import sys
import six
import cv2
import numpy as np
2022-02-13 11:48:18 +08:00
import math
from PIL import Image
2020-10-13 17:13:33 +08:00
class DecodeImage(object):
"""decode image"""
2020-10-13 17:13:33 +08:00
def __init__(
self, img_mode="RGB", channel_first=False, ignore_orientation=False, **kwargs
):
2020-10-13 17:13:33 +08:00
self.img_mode = img_mode
self.channel_first = channel_first
2022-01-27 18:07:40 +08:00
self.ignore_orientation = ignore_orientation
2020-10-13 17:13:33 +08:00
def __call__(self, data):
img = data["image"]
2020-10-13 17:13:33 +08:00
if six.PY2:
assert (
type(img) is str and len(img) > 0
), "invalid input 'img' in DecodeImage"
2020-10-13 17:13:33 +08:00
else:
assert (
type(img) is bytes and len(img) > 0
), "invalid input 'img' in DecodeImage"
img = np.frombuffer(img, dtype="uint8")
2022-01-27 18:07:40 +08:00
if self.ignore_orientation:
img = cv2.imdecode(img, cv2.IMREAD_IGNORE_ORIENTATION | cv2.IMREAD_COLOR)
2022-01-27 18:07:40 +08:00
else:
img = cv2.imdecode(img, 1)
2020-12-09 14:59:04 +08:00
if img is None:
return None
if self.img_mode == "GRAY":
2020-10-13 17:13:33 +08:00
img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
elif self.img_mode == "RGB":
assert img.shape[2] == 3, "invalid shape of image[%s]" % (img.shape)
2020-10-13 17:13:33 +08:00
img = img[:, :, ::-1]
if self.channel_first:
img = img.transpose((2, 0, 1))
data["image"] = img
2020-10-13 17:13:33 +08:00
return data
class NormalizeImage(object):
"""normalize image such as substract mean, divide std"""
2020-10-13 17:13:33 +08:00
def __init__(self, scale=None, mean=None, std=None, order="chw", **kwargs):
2020-10-13 17:13:33 +08:00
if isinstance(scale, str):
scale = eval(scale)
self.scale = np.float32(scale if scale is not None else 1.0 / 255.0)
mean = mean if mean is not None else [0.485, 0.456, 0.406]
std = std if std is not None else [0.229, 0.224, 0.225]
shape = (3, 1, 1) if order == "chw" else (1, 1, 3)
self.mean = np.array(mean).reshape(shape).astype("float32")
self.std = np.array(std).reshape(shape).astype("float32")
2020-10-13 17:13:33 +08:00
def __call__(self, data):
img = data["image"]
2020-10-13 17:13:33 +08:00
from PIL import Image
2020-10-13 17:13:33 +08:00
if isinstance(img, Image.Image):
img = np.array(img)
assert isinstance(img, np.ndarray), "invalid input 'img' in NormalizeImage"
data["image"] = (img.astype("float32") * self.scale - self.mean) / self.std
2020-10-13 17:13:33 +08:00
return data
class ToCHWImage(object):
"""convert hwc image to chw image"""
2020-10-13 17:13:33 +08:00
def __init__(self, **kwargs):
pass
def __call__(self, data):
img = data["image"]
2020-10-13 17:13:33 +08:00
from PIL import Image
2020-10-13 17:13:33 +08:00
if isinstance(img, Image.Image):
img = np.array(img)
data["image"] = img.transpose((2, 0, 1))
2020-10-13 17:13:33 +08:00
return data
2021-08-30 14:32:54 +08:00
class Fasttext(object):
def __init__(self, path="None", **kwargs):
2021-12-31 13:08:51 +08:00
import fasttext
2021-08-30 14:32:54 +08:00
self.fast_model = fasttext.load_model(path)
def __call__(self, data):
label = data["label"]
2021-08-30 14:32:54 +08:00
fast_label = self.fast_model[label]
data["fast_label"] = fast_label
2021-08-30 14:32:54 +08:00
return data
2020-11-04 20:43:27 +08:00
class KeepKeys(object):
2020-10-13 17:13:33 +08:00
def __init__(self, keep_keys, **kwargs):
self.keep_keys = keep_keys
def __call__(self, data):
data_list = []
for key in self.keep_keys:
data_list.append(data[key])
return data_list
2022-02-13 11:48:18 +08:00
class Pad(object):
2022-02-14 11:48:22 +08:00
def __init__(self, size=None, size_div=32, **kwargs):
if size is not None and not isinstance(size, (int, list, tuple)):
raise TypeError(
"Type of target_size is invalid. Now is {}".format(type(size))
)
2022-02-14 11:48:22 +08:00
if isinstance(size, int):
size = [size, size]
self.size = size
2022-02-13 11:48:18 +08:00
self.size_div = size_div
def __call__(self, data):
img = data["image"]
2022-02-14 11:48:22 +08:00
img_h, img_w = img.shape[0], img.shape[1]
if self.size:
resize_h2, resize_w2 = self.size
assert (
img_h < resize_h2 and img_w < resize_w2
), "(h, w) of target size should be greater than (img_h, img_w)"
2022-02-14 11:48:22 +08:00
else:
resize_h2 = max(
int(math.ceil(img.shape[0] / self.size_div) * self.size_div),
self.size_div,
)
2022-02-14 11:48:22 +08:00
resize_w2 = max(
int(math.ceil(img.shape[1] / self.size_div) * self.size_div),
self.size_div,
)
2022-02-13 11:48:18 +08:00
img = cv2.copyMakeBorder(
img,
0,
2022-02-14 11:48:22 +08:00
resize_h2 - img_h,
2022-02-13 11:48:18 +08:00
0,
2022-02-14 11:48:22 +08:00
resize_w2 - img_w,
2022-02-13 11:48:18 +08:00
cv2.BORDER_CONSTANT,
value=0,
)
data["image"] = img
2022-02-13 11:48:18 +08:00
return data
2021-08-31 19:43:00 +08:00
class Resize(object):
def __init__(self, size=(640, 640), **kwargs):
self.size = size
def resize_image(self, img):
resize_h, resize_w = self.size
ori_h, ori_w = img.shape[:2] # (h, w, c)
ratio_h = float(resize_h) / ori_h
ratio_w = float(resize_w) / ori_w
img = cv2.resize(img, (int(resize_w), int(resize_h)))
return img, [ratio_h, ratio_w]
def __call__(self, data):
img = data["image"]
if "polys" in data:
text_polys = data["polys"]
2021-08-31 19:43:00 +08:00
img_resize, [ratio_h, ratio_w] = self.resize_image(img)
if "polys" in data:
new_boxes = []
for box in text_polys:
new_box = []
for cord in box:
new_box.append([cord[0] * ratio_w, cord[1] * ratio_h])
new_boxes.append(new_box)
data["polys"] = np.array(new_boxes, dtype=np.float32)
data["image"] = img_resize
2021-08-31 19:43:00 +08:00
return data
2020-10-13 17:13:33 +08:00
class DetResizeForTest(object):
def __init__(self, **kwargs):
super(DetResizeForTest, self).__init__()
self.resize_type = 0
2022-06-27 16:26:53 +08:00
self.keep_ratio = False
if "image_shape" in kwargs:
self.image_shape = kwargs["image_shape"]
2020-10-13 17:13:33 +08:00
self.resize_type = 1
if "keep_ratio" in kwargs:
self.keep_ratio = kwargs["keep_ratio"]
elif "limit_side_len" in kwargs:
self.limit_side_len = kwargs["limit_side_len"]
self.limit_type = kwargs.get("limit_type", "min")
elif "resize_long" in kwargs:
2020-12-09 14:45:25 +08:00
self.resize_type = 2
self.resize_long = kwargs.get("resize_long", 960)
2020-10-13 17:13:33 +08:00
else:
self.limit_side_len = 736
self.limit_type = "min"
2020-10-13 17:13:33 +08:00
def __call__(self, data):
img = data["image"]
2020-12-09 14:45:25 +08:00
src_h, src_w, _ = img.shape
2022-09-02 10:45:33 +08:00
if sum([src_h, src_w]) < 64:
img = self.image_padding(img)
2020-10-13 17:13:33 +08:00
if self.resize_type == 0:
2020-12-09 14:45:25 +08:00
# img, shape = self.resize_image_type0(img)
img, [ratio_h, ratio_w] = self.resize_image_type0(img)
elif self.resize_type == 2:
img, [ratio_h, ratio_w] = self.resize_image_type2(img)
2020-10-13 17:13:33 +08:00
else:
2020-12-09 14:45:25 +08:00
# img, shape = self.resize_image_type1(img)
img, [ratio_h, ratio_w] = self.resize_image_type1(img)
data["image"] = img
data["shape"] = np.array([src_h, src_w, ratio_h, ratio_w])
2020-10-13 17:13:33 +08:00
return data
2022-09-02 10:45:33 +08:00
def image_padding(self, im, value=0):
h, w, c = im.shape
im_pad = np.zeros((max(32, h), max(32, w), c), np.uint8) + value
im_pad[:h, :w, :] = im
return im_pad
2020-10-13 17:13:33 +08:00
def resize_image_type1(self, img):
resize_h, resize_w = self.image_shape
ori_h, ori_w = img.shape[:2] # (h, w, c)
2022-06-28 11:28:53 +08:00
if self.keep_ratio is True:
2022-06-27 16:26:53 +08:00
resize_w = ori_w * resize_h / ori_h
N = math.ceil(resize_w / 32)
resize_w = N * 32
2020-12-09 14:45:25 +08:00
ratio_h = float(resize_h) / ori_h
ratio_w = float(resize_w) / ori_w
2020-10-13 17:13:33 +08:00
img = cv2.resize(img, (int(resize_w), int(resize_h)))
2020-12-09 14:45:25 +08:00
# return img, np.array([ori_h, ori_w])
return img, [ratio_h, ratio_w]
2020-10-13 17:13:33 +08:00
def resize_image_type0(self, img):
"""
resize image to a size multiple of 32 which is required by the network
args:
img(array): array with shape [h, w, c]
return(tuple):
img, (ratio_h, ratio_w)
"""
limit_side_len = self.limit_side_len
2021-06-10 14:24:59 +08:00
h, w, c = img.shape
2020-10-13 17:13:33 +08:00
# limit the max side
if self.limit_type == "max":
2020-10-13 17:13:33 +08:00
if max(h, w) > limit_side_len:
if h > w:
ratio = float(limit_side_len) / h
else:
ratio = float(limit_side_len) / w
else:
ratio = 1.0
elif self.limit_type == "min":
2020-10-13 17:13:33 +08:00
if min(h, w) < limit_side_len:
if h < w:
ratio = float(limit_side_len) / h
else:
ratio = float(limit_side_len) / w
else:
ratio = 1.0
elif self.limit_type == "resize_long":
2021-08-31 19:43:00 +08:00
ratio = float(limit_side_len) / max(h, w)
2021-06-10 14:24:59 +08:00
else:
raise Exception("not support limit type, image ")
2020-10-13 17:13:33 +08:00
resize_h = int(h * ratio)
resize_w = int(w * ratio)
2021-02-09 18:44:40 +08:00
resize_h = max(int(round(resize_h / 32) * 32), 32)
resize_w = max(int(round(resize_w / 32) * 32), 32)
2020-10-13 17:13:33 +08:00
try:
if int(resize_w) <= 0 or int(resize_h) <= 0:
return None, (None, None)
img = cv2.resize(img, (int(resize_w), int(resize_h)))
except:
print(img.shape, resize_w, resize_h)
sys.exit(0)
2020-12-09 14:45:25 +08:00
ratio_h = resize_h / float(h)
ratio_w = resize_w / float(w)
return img, [ratio_h, ratio_w]
2020-12-09 20:30:50 +08:00
2020-12-09 14:45:25 +08:00
def resize_image_type2(self, img):
h, w, _ = img.shape
resize_w = w
resize_h = h
if resize_h > resize_w:
ratio = float(self.resize_long) / resize_h
else:
ratio = float(self.resize_long) / resize_w
resize_h = int(resize_h * ratio)
resize_w = int(resize_w * ratio)
max_stride = 128
resize_h = (resize_h + max_stride - 1) // max_stride * max_stride
resize_w = (resize_w + max_stride - 1) // max_stride * max_stride
img = cv2.resize(img, (int(resize_w), int(resize_h)))
ratio_h = resize_h / float(h)
ratio_w = resize_w / float(w)
return img, [ratio_h, ratio_w]
2021-03-08 14:15:47 +08:00
class E2EResizeForTest(object):
def __init__(self, **kwargs):
super(E2EResizeForTest, self).__init__()
self.max_side_len = kwargs["max_side_len"]
self.valid_set = kwargs["valid_set"]
2021-03-08 14:15:47 +08:00
def __call__(self, data):
img = data["image"]
2021-03-08 14:15:47 +08:00
src_h, src_w, _ = img.shape
if self.valid_set == "totaltext":
2021-03-08 14:15:47 +08:00
im_resized, [ratio_h, ratio_w] = self.resize_image_for_totaltext(
img, max_side_len=self.max_side_len
)
2021-03-08 14:15:47 +08:00
else:
im_resized, (ratio_h, ratio_w) = self.resize_image(
img, max_side_len=self.max_side_len
)
data["image"] = im_resized
data["shape"] = np.array([src_h, src_w, ratio_h, ratio_w])
2021-03-08 14:15:47 +08:00
return data
def resize_image_for_totaltext(self, im, max_side_len=512):
h, w, _ = im.shape
2021-03-08 14:15:47 +08:00
resize_w = w
resize_h = h
ratio = 1.25
if h * ratio > max_side_len:
ratio = float(max_side_len) / resize_h
resize_h = int(resize_h * ratio)
resize_w = int(resize_w * ratio)
max_stride = 128
resize_h = (resize_h + max_stride - 1) // max_stride * max_stride
resize_w = (resize_w + max_stride - 1) // max_stride * max_stride
im = cv2.resize(im, (int(resize_w), int(resize_h)))
ratio_h = resize_h / float(h)
ratio_w = resize_w / float(w)
return im, (ratio_h, ratio_w)
def resize_image(self, im, max_side_len=512):
"""
resize image to a size multiple of max_stride which is required by the network
:param im: the resized image
:param max_side_len: limit of max image size to avoid out of memory in gpu
:return: the resized image and the resize ratio
"""
h, w, _ = im.shape
resize_w = w
resize_h = h
# Fix the longer side
if resize_h > resize_w:
ratio = float(max_side_len) / resize_h
else:
ratio = float(max_side_len) / resize_w
resize_h = int(resize_h * ratio)
resize_w = int(resize_w * ratio)
max_stride = 128
resize_h = (resize_h + max_stride - 1) // max_stride * max_stride
resize_w = (resize_w + max_stride - 1) // max_stride * max_stride
im = cv2.resize(im, (int(resize_w), int(resize_h)))
ratio_h = resize_h / float(h)
ratio_w = resize_w / float(w)
return im, (ratio_h, ratio_w)
2021-10-09 15:40:25 +08:00
class KieResize(object):
def __init__(self, **kwargs):
super(KieResize, self).__init__()
self.max_side, self.min_side = kwargs["img_scale"][0], kwargs["img_scale"][1]
2021-10-09 15:40:25 +08:00
def __call__(self, data):
img = data["image"]
points = data["points"]
2021-10-09 15:40:25 +08:00
src_h, src_w, _ = img.shape
(
im_resized,
scale_factor,
[ratio_h, ratio_w],
[new_h, new_w],
) = self.resize_image(img)
2021-10-09 15:40:25 +08:00
resize_points = self.resize_boxes(img, points, scale_factor)
data["ori_image"] = img
data["ori_boxes"] = points
data["points"] = resize_points
data["image"] = im_resized
data["shape"] = np.array([new_h, new_w])
2021-10-09 15:40:25 +08:00
return data
def resize_image(self, img):
norm_img = np.zeros([1024, 1024, 3], dtype="float32")
2021-10-09 15:40:25 +08:00
scale = [512, 1024]
h, w = img.shape[:2]
max_long_edge = max(scale)
max_short_edge = min(scale)
scale_factor = min(max_long_edge / max(h, w), max_short_edge / min(h, w))
resize_w, resize_h = int(w * float(scale_factor) + 0.5), int(
h * float(scale_factor) + 0.5
)
2021-10-09 15:50:52 +08:00
max_stride = 32
resize_h = (resize_h + max_stride - 1) // max_stride * max_stride
resize_w = (resize_w + max_stride - 1) // max_stride * max_stride
im = cv2.resize(img, (resize_w, resize_h))
2021-10-09 15:40:25 +08:00
new_h, new_w = im.shape[:2]
w_scale = new_w / w
h_scale = new_h / h
scale_factor = np.array([w_scale, h_scale, w_scale, h_scale], dtype=np.float32)
2021-10-09 15:40:25 +08:00
norm_img[:new_h, :new_w, :] = im
2021-10-09 15:50:52 +08:00
return norm_img, scale_factor, [h_scale, w_scale], [new_h, new_w]
2021-10-09 15:40:25 +08:00
def resize_boxes(self, im, points, scale_factor):
points = points * scale_factor
img_shape = im.shape[:2]
points[:, 0::2] = np.clip(points[:, 0::2], 0, img_shape[1])
points[:, 1::2] = np.clip(points[:, 1::2], 0, img_shape[0])
return points
class SRResize(object):
def __init__(
self,
imgH=32,
imgW=128,
down_sample_scale=4,
keep_ratio=False,
min_ratio=1,
mask=False,
infer_mode=False,
**kwargs,
):
self.imgH = imgH
self.imgW = imgW
self.keep_ratio = keep_ratio
self.min_ratio = min_ratio
self.down_sample_scale = down_sample_scale
self.mask = mask
self.infer_mode = infer_mode
def __call__(self, data):
imgH = self.imgH
imgW = self.imgW
images_lr = data["image_lr"]
transform2 = ResizeNormalize(
(imgW // self.down_sample_scale, imgH // self.down_sample_scale)
)
images_lr = transform2(images_lr)
data["img_lr"] = images_lr
if self.infer_mode:
return data
images_HR = data["image_hr"]
label_strs = data["label"]
transform = ResizeNormalize((imgW, imgH))
images_HR = transform(images_HR)
data["img_hr"] = images_HR
return data
class ResizeNormalize(object):
def __init__(self, size, interpolation=Image.BICUBIC):
self.size = size
self.interpolation = interpolation
def __call__(self, img):
img = img.resize(self.size, self.interpolation)
img_numpy = np.array(img).astype("float32")
img_numpy = img_numpy.transpose((2, 0, 1)) / 255
return img_numpy
class GrayImageChannelFormat(object):
"""
format gray scale image's channel: (3,h,w) -> (1,h,w)
Args:
inverse: inverse gray image
"""
def __init__(self, inverse=False, **kwargs):
self.inverse = inverse
def __call__(self, data):
img = data["image"]
img_single_channel = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_expanded = np.expand_dims(img_single_channel, 0)
if self.inverse:
data["image"] = np.abs(img_expanded - 1)
else:
data["image"] = img_expanded
data["src_image"] = img
return data