File size: 6,004 Bytes
2366e36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# Copyright (c) OpenMMLab. All rights reserved.
import sys

import cv2
import numpy as np
import pyclipper
from mmcv.utils import print_log
from shapely.geometry import Polygon as plg

import mmocr.utils.check_argument as check_argument


class BaseTextDetTargets:
    """Generate text detector ground truths."""

    def __init__(self):
        pass

    def point2line(self, xs, ys, point_1, point_2):
        """Compute the distance from point to a line. This is adapted from
        https://github.com/MhLiao/DB.

        Args:
            xs (ndarray): The x coordinates of size hxw.
            ys (ndarray): The y coordinates of size hxw.
            point_1 (ndarray): The first point with shape 1x2.
            point_2 (ndarray): The second point with shape 1x2.

        Returns:
            result (ndarray): The distance matrix of size hxw.
        """
        # suppose a triangle with three edge abc with c=point_1 point_2
        # a^2
        a_square = np.square(xs - point_1[0]) + np.square(ys - point_1[1])
        # b^2
        b_square = np.square(xs - point_2[0]) + np.square(ys - point_2[1])
        # c^2
        c_square = np.square(point_1[0] - point_2[0]) + np.square(point_1[1] -
                                                                  point_2[1])
        # -cosC=(c^2-a^2-b^2)/2(ab)
        neg_cos_c = (
            (c_square - a_square - b_square) /
            (np.finfo(np.float32).eps + 2 * np.sqrt(a_square * b_square)))
        # sinC^2=1-cosC^2
        square_sin = 1 - np.square(neg_cos_c)
        square_sin = np.nan_to_num(square_sin)
        # distance=a*b*sinC/c=a*h/c=2*area/c
        result = np.sqrt(a_square * b_square * square_sin /
                         (np.finfo(np.float32).eps + c_square))
        # set result to minimum edge if C<pi/2
        result[neg_cos_c < 0] = np.sqrt(np.fmin(a_square,
                                                b_square))[neg_cos_c < 0]
        return result

    def polygon_area(self, polygon):
        """Compute the polygon area. Please refer to Green's theorem.
        https://en.wikipedia.org/wiki/Green%27s_theorem. This is adapted from
        https://github.com/MhLiao/DB.

        Args:
            polygon (ndarray): The polygon boundary points.
        """

        polygon = polygon.reshape(-1, 2)
        edge = 0
        for i in range(polygon.shape[0]):
            next_index = (i + 1) % polygon.shape[0]
            edge += (polygon[next_index, 0] - polygon[i, 0]) * (
                polygon[next_index, 1] + polygon[i, 1])

        return edge / 2.

    def polygon_size(self, polygon):
        """Estimate the height and width of the minimum bounding box of the
        polygon.

        Args:
            polygon (ndarray): The polygon point sequence.

        Returns:
            size (tuple): The height and width of the minimum bounding box.
        """
        poly = polygon.reshape(-1, 2)
        rect = cv2.minAreaRect(poly.astype(np.int32))
        size = rect[1]
        return size

    def generate_kernels(self,
                         img_size,
                         text_polys,
                         shrink_ratio,
                         max_shrink=sys.maxsize,
                         ignore_tags=None):
        """Generate text instance kernels for one shrink ratio.

        Args:
            img_size (tuple(int, int)): The image size of (height, width).
            text_polys (list[list[ndarray]]: The list of text polygons.
            shrink_ratio (float): The shrink ratio of kernel.

        Returns:
            text_kernel (ndarray): The text kernel mask of (height, width).
        """
        assert isinstance(img_size, tuple)
        assert check_argument.is_2dlist(text_polys)
        assert isinstance(shrink_ratio, float)

        h, w = img_size
        text_kernel = np.zeros((h, w), dtype=np.float32)

        for text_ind, poly in enumerate(text_polys):
            instance = poly[0].reshape(-1, 2).astype(np.int32)
            area = plg(instance).area
            peri = cv2.arcLength(instance, True)
            distance = min(
                int(area * (1 - shrink_ratio * shrink_ratio) / (peri + 0.001) +
                    0.5), max_shrink)
            pco = pyclipper.PyclipperOffset()
            pco.AddPath(instance, pyclipper.JT_ROUND,
                        pyclipper.ET_CLOSEDPOLYGON)
            shrunk = np.array(pco.Execute(-distance))

            # check shrunk == [] or empty ndarray
            if len(shrunk) == 0 or shrunk.size == 0:
                if ignore_tags is not None:
                    ignore_tags[text_ind] = True
                continue
            try:
                shrunk = np.array(shrunk[0]).reshape(-1, 2)

            except Exception as e:
                print_log(f'{shrunk} with error {e}')
                if ignore_tags is not None:
                    ignore_tags[text_ind] = True
                continue
            cv2.fillPoly(text_kernel, [shrunk.astype(np.int32)], text_ind + 1)
        return text_kernel, ignore_tags

    def generate_effective_mask(self, mask_size: tuple, polygons_ignore):
        """Generate effective mask by setting the ineffective regions to 0 and
        effective regions to 1.

        Args:
            mask_size (tuple): The mask size.
            polygons_ignore (list[[ndarray]]: The list of ignored text
                polygons.

        Returns:
            mask (ndarray): The effective mask of (height, width).
        """

        assert check_argument.is_2dlist(polygons_ignore)

        mask = np.ones(mask_size, dtype=np.uint8)

        for poly in polygons_ignore:
            instance = poly[0].reshape(-1,
                                       2).astype(np.int32).reshape(1, -1, 2)
            cv2.fillPoly(mask, instance, 0)

        return mask

    def generate_targets(self, results):
        raise NotImplementedError

    def __call__(self, results):
        results = self.generate_targets(results)
        return results