gaotingquan 1 rok temu
rodzic
commit
b9aa460f84

+ 398 - 158
paddlex/inference/components/task_related/seal_det_warp.py

@@ -1,3 +1,17 @@
+# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve.
+#
+# 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.
+
 import os, sys
 import numpy as np
 from numpy import cos, sin, arctan, sqrt
@@ -6,24 +20,15 @@ import copy
 import time
 
 
-def Homography(image, img_points, world_width, world_height,
-               interpolation=cv2.INTER_CUBIC, ratio_width=1.0, ratio_height=1.0):
-    """
-    将图像透视变换到新的视角,返回变换后的图像。
-    
-    Args:
-        image (np.ndarray): 输入的图像,应为numpy数组类型。
-        img_points (List[Tuple[int, int]]): 图像上的四个点的坐标,顺序为左上角、右上角、右下角、左下角。
-        world_width (int): 变换后图像在世界坐标系中的宽度。
-        world_height (int): 变换后图像在世界坐标系中的高度。
-        interpolation (int, optional): 插值方式,默认为cv2.INTER_CUBIC。
-        ratio_width (float, optional): 变换后图像在x轴上的缩放比例,默认为1.0。
-        ratio_height (float, optional): 变换后图像在y轴上的缩放比例,默认为1.0。
-    
-    Returns:
-        np.ndarray: 变换后的图像,为numpy数组类型。
-    
-    """
+def Homography(
+    image,
+    img_points,
+    world_width,
+    world_height,
+    interpolation=cv2.INTER_CUBIC,
+    ratio_width=1.0,
+    ratio_height=1.0,
+):
     _points = np.array(img_points).reshape(-1, 2).astype(np.float32)
 
     expand_x = int(0.5 * world_width * (ratio_width - 1))
@@ -34,8 +39,7 @@ def Homography(image, img_points, world_width, world_height,
     pt_leftbottom = [expand_x + world_width, expand_y + world_height]
     pt_rightbottom = [expand_x, expand_y + world_height]
 
-    pts_std = np.float32([pt_lefttop, pt_righttop,
-                          pt_leftbottom, pt_rightbottom])
+    pts_std = np.float32([pt_lefttop, pt_righttop, pt_leftbottom, pt_rightbottom])
 
     img_crop_width = int(world_width * ratio_width)
     img_crop_height = int(world_height * ratio_height)
@@ -44,21 +48,89 @@ def Homography(image, img_points, world_width, world_height,
 
     dst_img = cv2.warpPerspective(
         image,
-        M, (img_crop_width, img_crop_height),
+        M,
+        (img_crop_width, img_crop_height),
         borderMode=cv2.BORDER_CONSTANT,  # BORDER_CONSTANT BORDER_REPLICATE
-        flags=interpolation)
+        flags=interpolation,
+    )
 
     return dst_img
 
 
+class PlanB:
+    def __call__(
+        self,
+        image,
+        points,
+        curveTextRectifier,
+        interpolation=cv2.INTER_LINEAR,
+        ratio_width=1.0,
+        ratio_height=1.0,
+        loss_thresh=5.0,
+        square=False,
+    ):
+        """
+        Plan B using sub-image when it failed in original image
+        :param image:
+        :param points:
+        :param curveTextRectifier: CurveTextRectifier
+        :param interpolation: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_AREA, cv2.INTER_CUBIC, cv2.INTER_LANCZOS4
+        :param ratio_width:  roi_image width expansion. It should not be smaller than 1.0
+        :param ratio_height: roi_image height expansion. It should not be smaller than 1.0
+        :param loss_thresh: if loss greater than loss_thresh --> get_rotate_crop_image
+        :param square: crop square image or not. True or False. The default is False
+        :return:
+        """
+        h, w = image.shape[:2]
+        _points = np.array(points).reshape(-1, 2).astype(np.float32)
+        x_min = int(np.min(_points[:, 0]))
+        y_min = int(np.min(_points[:, 1]))
+        x_max = int(np.max(_points[:, 0]))
+        y_max = int(np.max(_points[:, 1]))
+        dx = x_max - x_min
+        dy = y_max - y_min
+        max_d = max(dx, dy)
+        mean_pt = np.mean(_points, 0)
+
+        expand_x = (ratio_width - 1.0) * 0.5 * max_d
+        expand_y = (ratio_height - 1.0) * 0.5 * max_d
+
+        if square:
+            x_min = np.clip(int(mean_pt[0] - max_d - expand_x), 0, w - 1)
+            y_min = np.clip(int(mean_pt[1] - max_d - expand_y), 0, h - 1)
+            x_max = np.clip(int(mean_pt[0] + max_d + expand_x), 0, w - 1)
+            y_max = np.clip(int(mean_pt[1] + max_d + expand_y), 0, h - 1)
+        else:
+            x_min = np.clip(int(x_min - expand_x), 0, w - 1)
+            y_min = np.clip(int(y_min - expand_y), 0, h - 1)
+            x_max = np.clip(int(x_max + expand_x), 0, w - 1)
+            y_max = np.clip(int(y_max + expand_y), 0, h - 1)
+
+        new_image = image[y_min:y_max, x_min:x_max, :].copy()
+        new_points = _points.copy()
+        new_points[:, 0] -= x_min
+        new_points[:, 1] -= y_min
+
+        dst_img, loss = curveTextRectifier(
+            new_image,
+            new_points,
+            interpolation,
+            ratio_width,
+            ratio_height,
+            mode="calibration",
+        )
+
+        return dst_img, loss
+
+
 class CurveTextRectifier:
     """
     spatial transformer via monocular vision
     """
+
     def __init__(self):
         self.get_virtual_camera_parameter()
 
-
     def get_virtual_camera_parameter(self):
         vcam_thz = 0
         vcam_thx1 = 180
@@ -84,21 +156,33 @@ class CurveTextRectifier:
         fv = 100
 
         matT = np.zeros((4, 4))
-        matT[0, 0] = cos(angle_z) * cos(angle_y) - sin(angle_z) * sin(angle_x1) * sin(angle_y)
+        matT[0, 0] = cos(angle_z) * cos(angle_y) - sin(angle_z) * sin(angle_x1) * sin(
+            angle_y
+        )
         matT[0, 1] = cos(angle_z) * sin(angle_y) * sin(angle_x2) - sin(angle_z) * (
-                    cos(angle_x1) * cos(angle_x2) - sin(angle_x1) * cos(angle_y) * sin(angle_x2))
+            cos(angle_x1) * cos(angle_x2) - sin(angle_x1) * cos(angle_y) * sin(angle_x2)
+        )
         matT[0, 2] = cos(angle_z) * sin(angle_y) * cos(angle_x2) + sin(angle_z) * (
-                    cos(angle_x1) * sin(angle_x2) + sin(angle_x1) * cos(angle_y) * cos(angle_x2))
+            cos(angle_x1) * sin(angle_x2) + sin(angle_x1) * cos(angle_y) * cos(angle_x2)
+        )
         matT[0, 3] = optic_x
-        matT[1, 0] = sin(angle_z) * cos(angle_y) + cos(angle_z) * sin(angle_x1) * sin(angle_y)
+        matT[1, 0] = sin(angle_z) * cos(angle_y) + cos(angle_z) * sin(angle_x1) * sin(
+            angle_y
+        )
         matT[1, 1] = sin(angle_z) * sin(angle_y) * sin(angle_x2) + cos(angle_z) * (
-                    cos(angle_x1) * cos(angle_x2) - sin(angle_x1) * cos(angle_y) * sin(angle_x2))
+            cos(angle_x1) * cos(angle_x2) - sin(angle_x1) * cos(angle_y) * sin(angle_x2)
+        )
         matT[1, 2] = sin(angle_z) * sin(angle_y) * cos(angle_x2) - cos(angle_z) * (
-                    cos(angle_x1) * sin(angle_x2) + sin(angle_x1) * cos(angle_y) * cos(angle_x2))
+            cos(angle_x1) * sin(angle_x2) + sin(angle_x1) * cos(angle_y) * cos(angle_x2)
+        )
         matT[1, 3] = optic_y
         matT[2, 0] = -cos(angle_x1) * sin(angle_y)
-        matT[2, 1] = cos(angle_x1) * cos(angle_y) * sin(angle_x2) + sin(angle_x1) * cos(angle_x2)
-        matT[2, 2] = cos(angle_x1) * cos(angle_y) * cos(angle_x2) - sin(angle_x1) * sin(angle_x2)
+        matT[2, 1] = cos(angle_x1) * cos(angle_y) * sin(angle_x2) + sin(angle_x1) * cos(
+            angle_x2
+        )
+        matT[2, 2] = cos(angle_x1) * cos(angle_y) * cos(angle_x2) - sin(angle_x1) * sin(
+            angle_x2
+        )
         matT[2, 3] = optic_z
         matT[3, 0] = 0
         matT[3, 1] = 0
@@ -117,7 +201,6 @@ class CurveTextRectifier:
         self.K = np.dot(matT.T, matS)
         self.K = np.dot(self.K, matT)
 
-
     def vertical_text_process(self, points, org_size):
         """
         change sequence amd process
@@ -134,7 +217,9 @@ class CurveTextRectifier:
         adjusted_points[:, 0] = _points[:, 1]
         adjusted_points[:, 1] = org_h - _points[:, 0] - 1
 
-        _image_coord, _world_coord, _new_image_size = self.horizontal_text_process(adjusted_points)
+        _image_coord, _world_coord, _new_image_size = self.horizontal_text_process(
+            adjusted_points
+        )
 
         # # convert to vertical points back
         image_coord = _points.reshape(1, -1, 2)
@@ -146,7 +231,6 @@ class CurveTextRectifier:
 
         return image_coord, world_coord, new_image_size
 
-
     def horizontal_text_process(self, points):
         """
         get image coordinate and world coordinate
@@ -160,16 +244,19 @@ class CurveTextRectifier:
         for i in range(1, len(poly) // 2):
             xdx = poly[i * 2] - poly[(i - 1) * 2]
             xdy = poly[i * 2 + 1] - poly[(i - 1) * 2 + 1]
-            d = sqrt(xdx ** 2 + xdy ** 2)
+            d = sqrt(xdx**2 + xdy**2)
             dx_list.append(d)
 
         for i in range(0, len(poly) // 4):
             ydx = poly[i * 2] - poly[len(poly) - 1 - (i * 2 + 1)]
             ydy = poly[i * 2 + 1] - poly[len(poly) - 1 - (i * 2)]
-            d = sqrt(ydx ** 2 + ydy ** 2)
+            d = sqrt(ydx**2 + ydy**2)
             dy_list.append(d)
 
-        dx_list = [(dx_list[i] + dx_list[len(dx_list) - 1 - i]) / 2 for i in range(len(dx_list) // 2)]
+        dx_list = [
+            (dx_list[i] + dx_list[len(dx_list) - 1 - i]) / 2
+            for i in range(len(dx_list) // 2)
+        ]
 
         height = np.around(np.mean(dy_list))
 
@@ -210,7 +297,6 @@ class CurveTextRectifier:
 
         return image_coord, world_coord, new_image_size
 
-
     def horizontal_text_estimate(self, points):
         """
         horizontal or vertical text
@@ -225,11 +311,10 @@ class CurveTextRectifier:
         x = x_max - x_min
         y = y_max - y_min
         is_horizontal_text = True
-        if y / x > 1.5: # vertical text condition
+        if y / x > 1.5:  # vertical text condition
             is_horizontal_text = False
         return is_horizontal_text
 
-
     def virtual_camera_to_world(self, size):
         ifu, ifv = self.ifu, self.ifv
         K, matT = self.K, self.matT
@@ -255,24 +340,41 @@ class CurveTextRectifier:
         D0[xp <= 0] = -D0[xp <= 0]
         D1[xp <= 0] = -D1[xp <= 0]
 
-        ratio_a = K[0, 0] * D0 * D0 + K[1, 1] * D1 * D1 + K[2, 2] * D2 * D2 + \
-                  (K[0, 1] + K[1, 0]) * D0 * D1 + (K[0, 2] + K[2, 0]) * D0 * D2 + (K[1, 2] + K[2, 1]) * D1 * D2
-        ratio_b = (K[0, 3] + K[3, 0]) * D0 + (K[1, 3] + K[3, 1]) * D1 + (K[2, 3] + K[3, 2]) * D2
+        ratio_a = (
+            K[0, 0] * D0 * D0
+            + K[1, 1] * D1 * D1
+            + K[2, 2] * D2 * D2
+            + (K[0, 1] + K[1, 0]) * D0 * D1
+            + (K[0, 2] + K[2, 0]) * D0 * D2
+            + (K[1, 2] + K[2, 1]) * D1 * D2
+        )
+        ratio_b = (
+            (K[0, 3] + K[3, 0]) * D0
+            + (K[1, 3] + K[3, 1]) * D1
+            + (K[2, 3] + K[3, 2]) * D2
+        )
         ratio_c = K[3, 3] * np.ones(ratio_b.shape)
 
         delta = ratio_b * ratio_b - 4 * ratio_a * ratio_c
         t = np.zeros(delta.shape)
         t[ratio_a == 0] = -ratio_c[ratio_a == 0] / ratio_b[ratio_a == 0]
-        t[ratio_a != 0] = (-ratio_b[ratio_a != 0] + sqrt(delta[ratio_a != 0])) / (2 * ratio_a[ratio_a != 0])
+        t[ratio_a != 0] = (-ratio_b[ratio_a != 0] + sqrt(delta[ratio_a != 0])) / (
+            2 * ratio_a[ratio_a != 0]
+        )
         t[delta < 0] = 0
 
-        P[:, :, 0] = matT[0, 3] + t * (matT[0, 0] * D0 + matT[0, 1] * D1 + matT[0, 2] * D2)
-        P[:, :, 1] = matT[1, 3] + t * (matT[1, 0] * D0 + matT[1, 1] * D1 + matT[1, 2] * D2)
-        P[:, :, 2] = matT[2, 3] + t * (matT[2, 0] * D0 + matT[2, 1] * D1 + matT[2, 2] * D2)
+        P[:, :, 0] = matT[0, 3] + t * (
+            matT[0, 0] * D0 + matT[0, 1] * D1 + matT[0, 2] * D2
+        )
+        P[:, :, 1] = matT[1, 3] + t * (
+            matT[1, 0] * D0 + matT[1, 1] * D1 + matT[1, 2] * D2
+        )
+        P[:, :, 2] = matT[2, 3] + t * (
+            matT[2, 0] * D0 + matT[2, 1] * D1 + matT[2, 2] * D2
+        )
 
         return P
 
-
     def world_to_image(self, image_size, world, intrinsic, distCoeffs, rotation, tvec):
         r11 = rotation[0, 0]
         r12 = rotation[0, 1]
@@ -343,9 +445,15 @@ class CurveTextRectifier:
         r4 = r2 * r2
         r6 = r2 * r4
 
-        radial_distortion = (1 + k1 * r2 + k2 * r4 + k3 * r6) / (1 + k4 * r2 + k5 * r4 + k6 * r6)
-        x2 = x1 * radial_distortion + p1 * x1y1 + p2 * (r2 + 2 * x12) + s1 * r2 + s2 * r4
-        y2 = y1 * radial_distortion + p2 * x1y1 + p1 * (r2 + 2 * y12) + s3 * r2 + s4 * r4
+        radial_distortion = (1 + k1 * r2 + k2 * r4 + k3 * r6) / (
+            1 + k4 * r2 + k5 * r4 + k6 * r6
+        )
+        x2 = (
+            x1 * radial_distortion + p1 * x1y1 + p2 * (r2 + 2 * x12) + s1 * r2 + s2 * r4
+        )
+        y2 = (
+            y1 * radial_distortion + p2 * x1y1 + p1 * (r2 + 2 * y12) + s3 * r2 + s4 * r4
+        )
 
         x3 = tao11 * x2 + tao12 * y2 + tao13
         y3 = tao21 * x2 + tao22 * y2 + tao23
@@ -356,16 +464,20 @@ class CurveTextRectifier:
 
         return P
 
-
-    def spatial_transform(self, image_data, new_image_size, mtx, dist, rvecs, tvecs, interpolation):
+    def spatial_transform(
+        self, image_data, new_image_size, mtx, dist, rvecs, tvecs, interpolation
+    ):
         rotation, _ = cv2.Rodrigues(rvecs)
         world_map = self.virtual_camera_to_world(new_image_size)
-        image_map = self.world_to_image(new_image_size, world_map, mtx, dist, rotation, tvecs)
+        image_map = self.world_to_image(
+            new_image_size, world_map, mtx, dist, rotation, tvecs
+        )
         image_map = image_map.astype(np.float32)
-        dst = cv2.remap(image_data, image_map[:, :, 0], image_map[:, :, 1], interpolation)
+        dst = cv2.remap(
+            image_data, image_map[:, :, 0], image_map[:, :, 1], interpolation
+        )
         return dst
 
-
     def calibrate(self, org_size, image_coord, world_coord):
         """
         calibration
@@ -378,36 +490,55 @@ class CurveTextRectifier:
         flag = cv2.CALIB_RATIONAL_MODEL
         flag2 = cv2.CALIB_RATIONAL_MODEL | cv2.CALIB_TILTED_MODEL
         flag3 = cv2.CALIB_RATIONAL_MODEL | cv2.CALIB_THIN_PRISM_MODEL
-        flag4 = cv2.CALIB_RATIONAL_MODEL | cv2.CALIB_ZERO_TANGENT_DIST | cv2.CALIB_FIX_ASPECT_RATIO
-        flag5 = cv2.CALIB_RATIONAL_MODEL | cv2.CALIB_TILTED_MODEL | cv2.CALIB_ZERO_TANGENT_DIST
+        flag4 = (
+            cv2.CALIB_RATIONAL_MODEL
+            | cv2.CALIB_ZERO_TANGENT_DIST
+            | cv2.CALIB_FIX_ASPECT_RATIO
+        )
+        flag5 = (
+            cv2.CALIB_RATIONAL_MODEL
+            | cv2.CALIB_TILTED_MODEL
+            | cv2.CALIB_ZERO_TANGENT_DIST
+        )
         flag6 = cv2.CALIB_RATIONAL_MODEL | cv2.CALIB_FIX_ASPECT_RATIO
         flag_list = [flag2, flag3, flag4, flag5, flag6]
 
-        ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(world_coord.astype(np.float32),
-                                                                image_coord.astype(np.float32),
-                                                                org_size,
-                                                                None,
-                                                                None,
-                                                                flags=flag)
+        ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
+            world_coord.astype(np.float32),
+            image_coord.astype(np.float32),
+            org_size,
+            None,
+            None,
+            flags=flag,
+        )
         if ret > 2:
             # strategies
             min_ret = ret
             for i, flag in enumerate(flag_list):
-                _ret, _mtx, _dist, _rvecs, _tvecs = cv2.calibrateCamera(world_coord.astype(np.float32),
-                                                                   image_coord.astype(np.float32),
-                                                                   org_size,
-                                                                   None,
-                                                                   None,
-                                                                   flags=flag)
+                _ret, _mtx, _dist, _rvecs, _tvecs = cv2.calibrateCamera(
+                    world_coord.astype(np.float32),
+                    image_coord.astype(np.float32),
+                    org_size,
+                    None,
+                    None,
+                    flags=flag,
+                )
                 if _ret < min_ret:
                     min_ret = _ret
                     ret, mtx, dist, rvecs, tvecs = _ret, _mtx, _dist, _rvecs, _tvecs
 
         return ret, mtx, dist, rvecs, tvecs
 
-
-    def dc_homo(self, img, img_points, obj_points, is_horizontal_text, interpolation=cv2.INTER_LINEAR,
-                ratio_width=1.0, ratio_height=1.0):
+    def dc_homo(
+        self,
+        img,
+        img_points,
+        obj_points,
+        is_horizontal_text,
+        interpolation=cv2.INTER_LINEAR,
+        ratio_width=1.0,
+        ratio_height=1.0,
+    ):
         """
         divide and conquer: homography
         # ratio_width and ratio_height must be 1.0 here
@@ -423,11 +554,11 @@ class CurveTextRectifier:
             new_img_points = np.zeros((4, 2)).astype(np.float32)
             new_obj_points = np.zeros((4, 2)).astype(np.float32)
 
-            new_img_points[0:2, :] = _img_points[i:(i + 2), :2]
-            new_img_points[2:4, :] = _img_points[::-1, :][i:(i + 2), :2][::-1, :]
+            new_img_points[0:2, :] = _img_points[i : (i + 2), :2]
+            new_img_points[2:4, :] = _img_points[::-1, :][i : (i + 2), :2][::-1, :]
 
-            new_obj_points[0:2, :] = _obj_points[i:(i + 2), :2]
-            new_obj_points[2:4, :] = _obj_points[::-1, :][i:(i + 2), :2][::-1, :]
+            new_obj_points[0:2, :] = _obj_points[i : (i + 2), :2]
+            new_obj_points[2:4, :] = _obj_points[::-1, :][i : (i + 2), :2][::-1, :]
 
             if is_horizontal_text:
                 world_width = np.abs(new_obj_points[1, 0] - new_obj_points[0, 0])
@@ -436,9 +567,15 @@ class CurveTextRectifier:
                 world_width = np.abs(new_obj_points[1, 1] - new_obj_points[0, 1])
                 world_height = np.abs(new_obj_points[3, 0] - new_obj_points[0, 0])
 
-            homo_img = Homography(img, new_img_points, world_width, world_height,
-                                              interpolation=interpolation,
-                                              ratio_width=ratio_width, ratio_height=ratio_height)
+            homo_img = Homography(
+                img,
+                new_img_points,
+                world_width,
+                world_height,
+                interpolation=interpolation,
+                ratio_width=ratio_width,
+                ratio_height=ratio_height,
+            )
 
             homo_img_list.append(homo_img)
             _h, _w = homo_img.shape[:2]
@@ -446,11 +583,13 @@ class CurveTextRectifier:
             height_list.append(_h)
 
         # stitching
-        rectified_image = np.zeros((np.max(height_list), sum(width_list), 3)).astype(np.uint8)
+        rectified_image = np.zeros((np.max(height_list), sum(width_list), 3)).astype(
+            np.uint8
+        )
 
         st = 0
-        for (homo_img, w, h) in zip(homo_img_list, width_list, height_list):
-            rectified_image[:h, st:st + w, :] = homo_img
+        for homo_img, w, h in zip(homo_img_list, width_list, height_list):
+            rectified_image[:h, st : st + w, :] = homo_img
             st += w
 
         if not is_horizontal_text:
@@ -459,24 +598,16 @@ class CurveTextRectifier:
 
         return rectified_image
 
-    def Homography(self, image, img_points, world_width, world_height,
-                interpolation=cv2.INTER_CUBIC, ratio_width=1.0, ratio_height=1.0):
-        """
-        将图像透视变换到新的视角,返回变换后的图像。
-        
-        Args:
-            image (np.ndarray): 输入的图像,应为numpy数组类型。
-            img_points (List[Tuple[int, int]]): 图像上的四个点的坐标,顺序为左上角、右上角、右下角、左下角。
-            world_width (int): 变换后图像在世界坐标系中的宽度。
-            world_height (int): 变换后图像在世界坐标系中的高度。
-            interpolation (int, optional): 插值方式,默认为cv2.INTER_CUBIC。
-            ratio_width (float, optional): 变换后图像在x轴上的缩放比例,默认为1.0。
-            ratio_height (float, optional): 变换后图像在y轴上的缩放比例,默认为1.0。
-        
-        Returns:
-            np.ndarray: 变换后的图像,为numpy数组类型。
-        
-        """
+    def Homography(
+        self,
+        image,
+        img_points,
+        world_width,
+        world_height,
+        interpolation=cv2.INTER_CUBIC,
+        ratio_width=1.0,
+        ratio_height=1.0,
+    ):
         _points = np.array(img_points).reshape(-1, 2).astype(np.float32)
 
         expand_x = int(0.5 * world_width * (ratio_width - 1))
@@ -487,8 +618,7 @@ class CurveTextRectifier:
         pt_leftbottom = [expand_x + world_width, expand_y + world_height]
         pt_rightbottom = [expand_x, expand_y + world_height]
 
-        pts_std = np.float32([pt_lefttop, pt_righttop,
-                            pt_leftbottom, pt_rightbottom])
+        pts_std = np.float32([pt_lefttop, pt_righttop, pt_leftbottom, pt_rightbottom])
 
         img_crop_width = int(world_width * ratio_width)
         img_crop_height = int(world_height * ratio_height)
@@ -497,14 +627,23 @@ class CurveTextRectifier:
 
         dst_img = cv2.warpPerspective(
             image,
-            M, (img_crop_width, img_crop_height),
+            M,
+            (img_crop_width, img_crop_height),
             borderMode=cv2.BORDER_CONSTANT,  # BORDER_CONSTANT BORDER_REPLICATE
-            flags=interpolation)
+            flags=interpolation,
+        )
 
         return dst_img
 
-
-    def __call__(self, image_data, points, interpolation=cv2.INTER_LINEAR, ratio_width=1.0, ratio_height=1.0, mode='calibration'):
+    def __call__(
+        self,
+        image_data,
+        points,
+        interpolation=cv2.INTER_LINEAR,
+        ratio_width=1.0,
+        ratio_height=1.0,
+        mode="calibration",
+    ):
         """
         spatial transform for a poly text
         :param image_data:
@@ -521,22 +660,42 @@ class CurveTextRectifier:
 
         is_horizontal_text = self.horizontal_text_estimate(points)
         if is_horizontal_text:
-            image_coord, world_coord, new_image_size = self.horizontal_text_process(points)
+            image_coord, world_coord, new_image_size = self.horizontal_text_process(
+                points
+            )
         else:
-            image_coord, world_coord, new_image_size = self.vertical_text_process(points, org_size)
-
-        if mode.lower() == 'calibration':
-            ret, mtx, dist, rvecs, tvecs = self.calibrate(org_size, image_coord, world_coord)
-
-            st_size = (int(new_image_size[0]*ratio_width), int(new_image_size[1]*ratio_height))
-            dst = self.spatial_transform(image_data, st_size, mtx, dist[0], rvecs[0], tvecs[0], interpolation)
-        elif mode.lower() == 'homography':
+            image_coord, world_coord, new_image_size = self.vertical_text_process(
+                points, org_size
+            )
+
+        if mode.lower() == "calibration":
+            ret, mtx, dist, rvecs, tvecs = self.calibrate(
+                org_size, image_coord, world_coord
+            )
+
+            st_size = (
+                int(new_image_size[0] * ratio_width),
+                int(new_image_size[1] * ratio_height),
+            )
+            dst = self.spatial_transform(
+                image_data, st_size, mtx, dist[0], rvecs[0], tvecs[0], interpolation
+            )
+        elif mode.lower() == "homography":
             # ratio_width and ratio_height must be 1.0 here and ret set to 0.01 without loss manually
             ret = 0.01
-            dst = self.dc_homo(image_data, image_coord, world_coord, is_horizontal_text,
-                               interpolation=interpolation, ratio_width=1.0, ratio_height=1.0)
+            dst = self.dc_homo(
+                image_data,
+                image_coord,
+                world_coord,
+                is_horizontal_text,
+                interpolation=interpolation,
+                ratio_width=1.0,
+                ratio_height=1.0,
+            )
         else:
-            raise ValueError('mode must be ["calibration", "homography"], but got {}'.format(mode))
+            raise ValueError(
+                'mode must be ["calibration", "homography"], but got {}'.format(mode)
+            )
 
         return dst, ret
 
@@ -547,7 +706,9 @@ class AutoRectifier:
         self.curveTextRectifier = CurveTextRectifier()
 
     @staticmethod
-    def get_rotate_crop_image(img, points, interpolation=cv2.INTER_CUBIC, ratio_width=1.0, ratio_height=1.0):
+    def get_rotate_crop_image(
+        img, points, interpolation=cv2.INTER_CUBIC, ratio_width=1.0, ratio_height=1.0
+    ):
         """
         crop or homography
         :param img:
@@ -579,33 +740,65 @@ class AutoRectifier:
             img_crop_width = int(
                 max(
                     np.linalg.norm(_points[0] - _points[1]),
-                    np.linalg.norm(_points[2] - _points[3])))
+                    np.linalg.norm(_points[2] - _points[3]),
+                )
+            )
             img_crop_height = int(
                 max(
                     np.linalg.norm(_points[0] - _points[3]),
-                    np.linalg.norm(_points[1] - _points[2])))
-
-            dst_img = Homography(img, _points, img_crop_width, img_crop_height, interpolation, ratio_width, ratio_height)
+                    np.linalg.norm(_points[1] - _points[2]),
+                )
+            )
+
+            dst_img = Homography(
+                img,
+                _points,
+                img_crop_width,
+                img_crop_height,
+                interpolation,
+                ratio_width,
+                ratio_height,
+            )
 
         return dst_img
 
-
     def visualize(self, image_data, points_list):
         visualization = image_data.copy()
 
         for box in points_list:
             box = np.array(box).reshape(-1, 2).astype(np.int32)
-            cv2.drawContours(visualization, [np.array(box).reshape((-1, 1, 2))], -1, (0, 0, 255), 2)
+            cv2.drawContours(
+                visualization, [np.array(box).reshape((-1, 1, 2))], -1, (0, 0, 255), 2
+            )
             for i, p in enumerate(box):
                 if i != 0:
-                    cv2.circle(visualization, tuple(p), radius=1, color=(255, 0, 0), thickness=2)
+                    cv2.circle(
+                        visualization,
+                        tuple(p),
+                        radius=1,
+                        color=(255, 0, 0),
+                        thickness=2,
+                    )
                 else:
-                    cv2.circle(visualization, tuple(p), radius=1, color=(255, 255, 0), thickness=2)
+                    cv2.circle(
+                        visualization,
+                        tuple(p),
+                        radius=1,
+                        color=(255, 255, 0),
+                        thickness=2,
+                    )
         return visualization
 
-
-    def __call__(self, image_data, points, interpolation=cv2.INTER_LINEAR,
-                 ratio_width=1.0, ratio_height=1.0, loss_thresh=5.0, mode='calibration'):
+    def __call__(
+        self,
+        image_data,
+        points,
+        interpolation=cv2.INTER_LINEAR,
+        ratio_width=1.0,
+        ratio_height=1.0,
+        loss_thresh=5.0,
+        mode="calibration",
+    ):
         """
         rectification in strategies for a poly text
         :param image_data:
@@ -617,26 +810,41 @@ class AutoRectifier:
         :param mode: 'calibration' or 'homography'. when homography, ratio_width and ratio_height must be 1.0
         :return:
         """
-        _points = np.array(points).reshape(-1,2)
+        _points = np.array(points).reshape(-1, 2)
         if len(_points) >= self.npoints and len(_points) % 2 == 0:
             try:
                 curveTextRectifier = CurveTextRectifier()
 
-                dst_img, loss = curveTextRectifier(image_data, points, interpolation, ratio_width, ratio_height, mode)
+                dst_img, loss = curveTextRectifier(
+                    image_data, points, interpolation, ratio_width, ratio_height, mode
+                )
                 if loss >= 2:
                     # for robust
                     # large loss means it cannot be reconstruct correctly, we must find other way to reconstruct
                     img_list, loss_list = [dst_img], [loss]
-                    _dst_img, _loss = PlanB()(image_data, points, curveTextRectifier,
-                                              interpolation, ratio_width, ratio_height,
-                                              loss_thresh=loss_thresh,
-                                              square=True)
+                    _dst_img, _loss = PlanB()(
+                        image_data,
+                        points,
+                        curveTextRectifier,
+                        interpolation,
+                        ratio_width,
+                        ratio_height,
+                        loss_thresh=loss_thresh,
+                        square=True,
+                    )
                     img_list += [_dst_img]
                     loss_list += [_loss]
 
-                    _dst_img, _loss = PlanB()(image_data, points, curveTextRectifier,
-                                              interpolation, ratio_width, ratio_height,
-                                              loss_thresh=loss_thresh, square=False)
+                    _dst_img, _loss = PlanB()(
+                        image_data,
+                        points,
+                        curveTextRectifier,
+                        interpolation,
+                        ratio_width,
+                        ratio_height,
+                        loss_thresh=loss_thresh,
+                        square=False,
+                    )
                     img_list += [_dst_img]
                     loss_list += [_loss]
 
@@ -644,20 +852,37 @@ class AutoRectifier:
                     dst_img = img_list[loss_list.index(min_loss)]
 
                     if min_loss >= loss_thresh:
-                        print('calibration loss: {} is too large for spatial transformer. It is failed. Using get_rotate_crop_image'.format(loss))
-                        dst_img = self.get_rotate_crop_image(image_data, points, interpolation, ratio_width, ratio_height)
-                        print('here')
+                        print(
+                            "calibration loss: {} is too large for spatial transformer. It is failed. Using get_rotate_crop_image".format(
+                                loss
+                            )
+                        )
+                        dst_img = self.get_rotate_crop_image(
+                            image_data, points, interpolation, ratio_width, ratio_height
+                        )
+                        print("here")
             except Exception as e:
                 print(e)
-                dst_img = self.get_rotate_crop_image(image_data, points, interpolation, ratio_width, ratio_height)
+                dst_img = self.get_rotate_crop_image(
+                    image_data, points, interpolation, ratio_width, ratio_height
+                )
         else:
-            dst_img = self.get_rotate_crop_image(image_data, _points, interpolation, ratio_width, ratio_height)
+            dst_img = self.get_rotate_crop_image(
+                image_data, _points, interpolation, ratio_width, ratio_height
+            )
 
         return dst_img
 
-
-    def run(self, image_data, points_list, interpolation=cv2.INTER_LINEAR,
-            ratio_width=1.0, ratio_height=1.0, loss_thresh=5.0, mode='calibration'):
+    def run(
+        self,
+        image_data,
+        points_list,
+        interpolation=cv2.INTER_LINEAR,
+        ratio_width=1.0,
+        ratio_height=1.0,
+        loss_thresh=5.0,
+        mode="calibration",
+    ):
         """
         run for texts in an image
         :param image_data: numpy.ndarray. The shape is [h, w, 3]
@@ -678,22 +903,37 @@ class AutoRectifier:
                 raise ValueError
 
         if ratio_width < 1.0 or ratio_height < 1.0:
-            raise ValueError('ratio_width and ratio_height cannot be smaller than 1, but got {}', (ratio_width, ratio_height))
-
-        if mode.lower() != 'calibration' and mode.lower() != 'homography':
-            raise ValueError('mode must be ["calibration", "homography"], but got {}'.format(mode))
-
-        if mode.lower() == 'homography' and ratio_width != 1.0 and ratio_height != 1.0:
-            raise ValueError('ratio_width and ratio_height must be 1.0 when mode is homography, but got mode:{}, ratio:({},{})'.format(mode, ratio_width, ratio_height))
+            raise ValueError(
+                "ratio_width and ratio_height cannot be smaller than 1, but got {}",
+                (ratio_width, ratio_height),
+            )
+
+        if mode.lower() != "calibration" and mode.lower() != "homography":
+            raise ValueError(
+                'mode must be ["calibration", "homography"], but got {}'.format(mode)
+            )
+
+        if mode.lower() == "homography" and ratio_width != 1.0 and ratio_height != 1.0:
+            raise ValueError(
+                "ratio_width and ratio_height must be 1.0 when mode is homography, but got mode:{}, ratio:({},{})".format(
+                    mode, ratio_width, ratio_height
+                )
+            )
 
         res = []
         for points in points_list:
-            rectified_img = self(image_data, points, interpolation, ratio_width, ratio_height,
-                                 loss_thresh=loss_thresh, mode=mode)
+            rectified_img = self(
+                image_data,
+                points,
+                interpolation,
+                ratio_width,
+                ratio_height,
+                loss_thresh=loss_thresh,
+                mode=mode,
+            )
             res.append(rectified_img)
 
         # visualize
         visualized_image = self.visualize(image_data, points_list)
 
         return res, visualized_image
-

+ 88 - 75
paddlex/inference/components/task_related/text_det.py

@@ -432,7 +432,7 @@ class CropByPolys(BaseComponent):
     def apply(self, img_path, dt_polys):
         """apply"""
         img = self._reader.read(img_path)
-        
+
         # TODO
         # dt_boxes = self.sorted_boxes(data[K.DT_POLYS])
         if self.det_box_type == "quad":
@@ -443,8 +443,11 @@ class CropByPolys(BaseComponent):
                 tmp_box = copy.deepcopy(dt_boxes[bno])
                 img_crop = self.get_minarea_rect_crop(img, tmp_box)
                 output_list.append(
-                {"img": img_crop, "img_size": [img_crop.shape[1], img_crop.shape[0]]}
-            )
+                    {
+                        "img": img_crop,
+                        "img_size": [img_crop.shape[1], img_crop.shape[0]],
+                    }
+                )
         elif self.det_box_type == "poly":
             output_list = []
             dt_boxes = dt_polys
@@ -452,7 +455,10 @@ class CropByPolys(BaseComponent):
                 tmp_box = copy.deepcopy(dt_boxes[bno])
                 img_crop = self.get_poly_rect_crop(img.copy(), tmp_box)
                 output_list.append(
-                {"img": img_crop, "img_size": [img_crop.shape[1], img_crop.shape[0]]}
+                    {
+                        "img": img_crop,
+                        "img_size": [img_crop.shape[1], img_crop.shape[0]],
+                    }
                 )
         else:
             raise NotImplementedError
@@ -574,22 +580,21 @@ class CropByPolys(BaseComponent):
         assert points.shape[0] >= 4
         assert points.shape[1] == 2
 
-        orientation_thr=2.0             # 一个经验超参数
+        orientation_thr = 2.0  # 一个经验超参数
 
         head_inds, tail_inds = self.find_head_tail(points, orientation_thr)
         head_edge, tail_edge = points[head_inds], points[tail_inds]
 
-
         pad_points = np.vstack([points, points])
         if tail_inds[1] < 1:
             tail_inds[1] = len(points)
-        sideline1 = pad_points[head_inds[1]:tail_inds[1]]
-        sideline2 = pad_points[tail_inds[1]:(head_inds[1] + len(points))]
+        sideline1 = pad_points[head_inds[1] : tail_inds[1]]
+        sideline2 = pad_points[tail_inds[1] : (head_inds[1] + len(points))]
         return head_edge, tail_edge, sideline1, sideline2
 
     def vector_slope(self, vec):
         assert len(vec) == 2
-        return abs(vec[1] / (vec[0] + 1e-8)) 
+        return abs(vec[1] / (vec[0] + 1e-8))
 
     def find_head_tail(self, points, orientation_thr):
         """Find the head edge and tail edge of a text polygon.
@@ -619,20 +624,19 @@ class CropByPolys(BaseComponent):
             for i, edge_vec1 in enumerate(edge_vec):
                 adjacent_ind = [x % len(edge_vec) for x in [i - 1, i + 1]]
                 adjacent_edge_vec = edge_vec[adjacent_ind]
-                temp_theta_sum = np.sum(
-                    self.vector_angle(edge_vec1, adjacent_edge_vec))
-                temp_adjacent_theta = self.vector_angle(adjacent_edge_vec[0],
-                                                        adjacent_edge_vec[1])
+                temp_theta_sum = np.sum(self.vector_angle(edge_vec1, adjacent_edge_vec))
+                temp_adjacent_theta = self.vector_angle(
+                    adjacent_edge_vec[0], adjacent_edge_vec[1]
+                )
                 theta_sum.append(temp_theta_sum)
                 adjacent_vec_theta.append(temp_adjacent_theta)
             theta_sum_score = np.array(theta_sum) / np.pi
             adjacent_theta_score = np.array(adjacent_vec_theta) / np.pi
             poly_center = np.mean(points, axis=0)
             edge_dist = np.maximum(
-                norm(
-                    pad_points[1:] - poly_center, axis=-1),
-                norm(
-                    pad_points[:-1] - poly_center, axis=-1))
+                norm(pad_points[1:] - poly_center, axis=-1),
+                norm(pad_points[:-1] - poly_center, axis=-1),
+            )
             dist_score = edge_dist / np.max(edge_dist)
             position_score = np.zeros(len(edge_vec))
             score = 0.5 * theta_sum_score + 0.15 * adjacent_theta_score
@@ -644,15 +648,21 @@ class CropByPolys(BaseComponent):
             pad_score = np.concatenate([score, score])
             score_matrix = np.zeros((len(score), len(score) - 3))
             x = np.arange(len(score) - 3) / float(len(score) - 4)
-            gaussian = 1. / (np.sqrt(2. * np.pi) * 0.5) * np.exp(-np.power(
-                (x - 0.5) / 0.5, 2.) / 2)
+            gaussian = (
+                1.0
+                / (np.sqrt(2.0 * np.pi) * 0.5)
+                * np.exp(-np.power((x - 0.5) / 0.5, 2.0) / 2)
+            )
             gaussian = gaussian / np.max(gaussian)
             for i in range(len(score)):
-                score_matrix[i, :] = score[i] + pad_score[(i + 2):(i + len(
-                    score) - 1)] * gaussian * 0.3
+                score_matrix[i, :] = (
+                    score[i]
+                    + pad_score[(i + 2) : (i + len(score) - 1)] * gaussian * 0.3
+                )
 
-            head_start, tail_increment = np.unravel_index(score_matrix.argmax(),
-                                                            score_matrix.shape)
+            head_start, tail_increment = np.unravel_index(
+                score_matrix.argmax(), score_matrix.shape
+            )
             tail_start = (head_start + tail_increment + 2) % len(points)
             head_end = (head_start + 1) % len(points)
             tail_end = (tail_start + 1) % len(points)
@@ -663,22 +673,27 @@ class CropByPolys(BaseComponent):
             head_inds = [head_start, head_end]
             tail_inds = [tail_start, tail_end]
         else:
-            if vector_slope(points[1] - points[0]) + vector_slope(points[
-                    3] - points[2]) < vector_slope(points[2] - points[
-                        1]) + vector_slope(points[0] - points[3]):
+            if self.vector_slope(points[1] - points[0]) + self.vector_slope(
+                points[3] - points[2]
+            ) < self.vector_slope(points[2] - points[1]) + self.vector_slope(
+                points[0] - points[3]
+            ):
                 horizontal_edge_inds = [[0, 1], [2, 3]]
                 vertical_edge_inds = [[3, 0], [1, 2]]
             else:
                 horizontal_edge_inds = [[3, 0], [1, 2]]
                 vertical_edge_inds = [[0, 1], [2, 3]]
 
-            vertical_len_sum = norm(points[vertical_edge_inds[0][0]] - points[
-                vertical_edge_inds[0][1]]) + norm(points[vertical_edge_inds[1][
-                    0]] - points[vertical_edge_inds[1][1]])
-            horizontal_len_sum = norm(points[horizontal_edge_inds[0][
-                0]] - points[horizontal_edge_inds[0][1]]) + norm(points[
-                    horizontal_edge_inds[1][0]] - points[horizontal_edge_inds[1]
-                                                            [1]])
+            vertical_len_sum = norm(
+                points[vertical_edge_inds[0][0]] - points[vertical_edge_inds[0][1]]
+            ) + norm(
+                points[vertical_edge_inds[1][0]] - points[vertical_edge_inds[1][1]]
+            )
+            horizontal_len_sum = norm(
+                points[horizontal_edge_inds[0][0]] - points[horizontal_edge_inds[0][1]]
+            ) + norm(
+                points[horizontal_edge_inds[1][0]] - points[horizontal_edge_inds[1][1]]
+            )
 
             if vertical_len_sum > horizontal_len_sum * orientation_thr:
                 head_inds = horizontal_edge_inds[0]
@@ -700,7 +715,6 @@ class CropByPolys(BaseComponent):
             unit_vec2 = vec2 / (norm(vec2, axis=-1) + 1e-8)
         return np.arccos(np.clip(np.sum(unit_vec1 * unit_vec2, axis=-1), -1.0, 1.0))
 
-
     def get_minarea_rect(self, img, points):
         bounding_box = cv2.minAreaRect(points)
         points = sorted(list(cv2.boxPoints(bounding_box)), key=lambda x: x[0])
@@ -734,6 +748,7 @@ class CropByPolys(BaseComponent):
             resampled_line (ndarray): The points composing the resampled line.
         """
         from numpy.linalg import norm
+
         # 断言检查输入参数的有效性
         assert line.ndim == 2
         assert line.shape[0] >= 2
@@ -741,9 +756,7 @@ class CropByPolys(BaseComponent):
         assert isinstance(n, int)
         assert n > 0
 
-        length_list = [
-            norm(line[i + 1] - line[i]) for i in range(len(line) - 1)
-        ]
+        length_list = [norm(line[i + 1] - line[i]) for i in range(len(line) - 1)]
         total_length = sum(length_list)
         length_cumsum = np.cumsum([0.0] + length_list)
         delta_length = total_length / (float(n) + 1e-8)
@@ -752,19 +765,20 @@ class CropByPolys(BaseComponent):
 
         for i in range(1, n):
             current_line_len = i * delta_length
-            while current_edge_ind + 1 < len(
-                    length_cumsum) and current_line_len >= length_cumsum[
-                        current_edge_ind + 1]:
+            while (
+                current_edge_ind + 1 < len(length_cumsum)
+                and current_line_len >= length_cumsum[current_edge_ind + 1]
+            ):
                 current_edge_ind += 1
-            current_edge_end_shift = current_line_len - length_cumsum[
-                current_edge_ind]
+            current_edge_end_shift = current_line_len - length_cumsum[current_edge_ind]
             if current_edge_ind >= len(length_list):
                 break
-            end_shift_ratio = current_edge_end_shift / length_list[
-                current_edge_ind]
-            current_point = line[current_edge_ind] + (line[current_edge_ind + 1]
-                                                    - line[current_edge_ind]
-                                                    ) * end_shift_ratio
+            end_shift_ratio = current_edge_end_shift / length_list[current_edge_ind]
+            current_point = (
+                line[current_edge_ind]
+                + (line[current_edge_ind + 1] - line[current_edge_ind])
+                * end_shift_ratio
+            )
             resampled_line.append(current_point)
         resampled_line.append(line[-1])
         resampled_line = np.array(resampled_line)
@@ -786,14 +800,12 @@ class CropByPolys(BaseComponent):
         assert isinstance(n, int)
         assert n > 0
 
-        length_list = [
-            norm(line[i + 1] - line[i]) for i in range(len(line) - 1)
-        ]
+        length_list = [norm(line[i + 1] - line[i]) for i in range(len(line) - 1)]
         total_length = sum(length_list)
         mean_length = total_length / (len(length_list) + 1e-8)
         group = [[0]]
         for i in range(len(length_list)):
-            point_id = i+1
+            point_id = i + 1
             if length_list[i] < 0.9 * mean_length:
                 for g in group:
                     if i in g:
@@ -807,36 +819,37 @@ class CropByPolys(BaseComponent):
         if top_tail_len < 0.9 * mean_length:
             group[0].extend(g)
             group.remove(g)
-        mean_positions = []  
-        for indices in group:  
-            x_sum = 0  
-            y_sum = 0  
-            for index in indices:  
-                x, y = line[index]  
-                x_sum += x  
-                y_sum += y  
-            num_points = len(indices)  
-            mean_x = x_sum / num_points  
-            mean_y = y_sum / num_points  
-            mean_positions.append((mean_x, mean_y)) 
+        mean_positions = []
+        for indices in group:
+            x_sum = 0
+            y_sum = 0
+            for index in indices:
+                x, y = line[index]
+                x_sum += x
+                y_sum += y
+            num_points = len(indices)
+            mean_x = x_sum / num_points
+            mean_y = y_sum / num_points
+            mean_positions.append((mean_x, mean_y))
         resampled_line = np.array(mean_positions)
         return resampled_line
 
     def get_poly_rect_crop(self, img, points):
-        '''
-            修改该函数,实现使用polygon,对不规则、弯曲文本的矫正以及crop
-            args: img: 图片 ndarrary格式
-            points: polygon格式的多点坐标 N*2 shape, ndarray格式
-            return: 矫正后的图片 ndarray格式
-        '''
+        """
+        修改该函数,实现使用polygon,对不规则、弯曲文本的矫正以及crop
+        args: img: 图片 ndarrary格式
+        points: polygon格式的多点坐标 N*2 shape, ndarray格式
+        return: 矫正后的图片 ndarray格式
+        """
         points = np.array(points).astype(np.int32).reshape(-1, 2)
         temp_crop_img, temp_box = self.get_minarea_rect(img, points)
+
         # 计算最小外接矩形与polygon的IoU
         def get_union(pD, pG):
             return Polygon(pD).union(Polygon(pG)).area
 
         def get_intersection_over_union(pD, pG):
-            return get_intersection(pD, pG) / (get_union(pD, pG)+ 1e-10)
+            return get_intersection(pD, pG) / (get_union(pD, pG) + 1e-10)
 
         def get_intersection(pD, pG):
             return Polygon(pD).intersection(Polygon(pG)).area
@@ -854,9 +867,9 @@ class CropByPolys(BaseComponent):
         resample_top_line = self.sample_points_on_bbox_bp(top_line, 15)
         resample_bot_line = self.sample_points_on_bbox_bp(bot_line, 15)
 
-        sideline_mean_shift = np.mean(
-            resample_top_line, axis=0) - np.mean(
-                resample_bot_line, axis=0)
+        sideline_mean_shift = np.mean(resample_top_line, axis=0) - np.mean(
+            resample_bot_line, axis=0
+        )
         if sideline_mean_shift[1] > 0:
             resample_bot_line, resample_top_line = resample_top_line, resample_bot_line
         rectifier = AutoRectifier()
@@ -864,6 +877,6 @@ class CropByPolys(BaseComponent):
         new_points_list = list(new_points.astype(np.float32).reshape(1, -1).tolist())
 
         if len(img.shape) == 2:
-            img = np.stack((img,)*3, axis=-1)
-        img_crop, image = rectifier.run(img, new_points_list, mode='homography')
+            img = np.stack((img,) * 3, axis=-1)
+        img_crop, image = rectifier.run(img, new_points_list, mode="homography")
         return img_crop[0]

+ 22 - 19
paddlex/modules/multilabel_classification/dataset_checker/dataset_src/convert_dataset.py

@@ -45,7 +45,7 @@ def convert(dataset_type, input_dir):
     """convert dataset to multilabel format"""
     # check format validity
     check_src_dataset(input_dir, dataset_type)
-    
+
     if dataset_type in ("COCO"):
         convert_coco_dataset(input_dir)
     else:
@@ -55,7 +55,7 @@ def convert(dataset_type, input_dir):
 
 
 def convert_coco_dataset(root_dir):
-    for anno in  ["annotations/instance_train.json", "annotations/instance_val.json"]:
+    for anno in ["annotations/instance_train.json", "annotations/instance_val.json"]:
         src_img_dir = root_dir
         src_anno_path = os.path.join(root_dir, anno)
         coco2multilabels(src_img_dir, src_anno_path, root_dir)
@@ -63,33 +63,37 @@ def convert_coco_dataset(root_dir):
 
 def coco2multilabels(src_img_dir, src_anno_path, root_dir):
     image_dir = os.path.join(root_dir, "images")
-    label_type = os.path.basename(src_anno_path).replace("instance_","").replace(".json","")
-    anno_save_path = os.path.join(root_dir, "{}.txt".format(label_type))  
+    label_type = (
+        os.path.basename(src_anno_path).replace("instance_", "").replace(".json", "")
+    )
+    anno_save_path = os.path.join(root_dir, "{}.txt".format(label_type))
     coco = COCO(src_anno_path)
     cat_id_map = {
-        old_cat_id: new_cat_id
-        for new_cat_id, old_cat_id in enumerate(coco.getCatIds())
+        old_cat_id: new_cat_id for new_cat_id, old_cat_id in enumerate(coco.getCatIds())
     }
     num_classes = len(list(cat_id_map.keys()))
 
-    with open(anno_save_path, 'w') as fp:
+    with open(anno_save_path, "w") as fp:
         lines = []
         for img_id in tqdm(sorted(coco.getImgIds())):
             img_info = coco.loadImgs([img_id])[0]
-            img_filename = img_info['file_name']
-            img_w = img_info['width']
-            img_h = img_info['height']
+            img_filename = img_info["file_name"]
+            img_w = img_info["width"]
+            img_h = img_info["height"]
 
             img_filepath = os.path.join(image_dir, img_filename)
             if not os.path.exists(img_filepath):
-                warning('Illegal image file: {}, '
-                               'and it will be ignored'.format(img_filepath))
+                warning(
+                    "Illegal image file: {}, "
+                    "and it will be ignored".format(img_filepath)
+                )
                 continue
 
             if img_w < 0 or img_h < 0:
-                warning(msg)(
-                    'Illegal width: {} or height: {} in annotation, '
-                    'and im_id: {} will be ignored'.format(img_w, img_h, img_id))
+                warning(
+                    "Illegal width: {} or height: {} in annotation, "
+                    "and im_id: {} will be ignored".format(img_w, img_h, img_id)
+                )
                 continue
 
             ins_anno_ids = coco.getAnnIds(imgIds=[img_id])
@@ -97,13 +101,13 @@ def coco2multilabels(src_img_dir, src_anno_path, root_dir):
 
             label = [0] * num_classes
             for instance in instances:
-                label[cat_id_map[instance['category_id']]] = 1
+                label[cat_id_map[instance["category_id"]]] = 1
             img_filename = os.path.join("images", img_filename)
-            fp.writelines("{}\t{}\n".format(img_filename, ','.join(map(str, label))))
+            fp.writelines("{}\t{}\n".format(img_filename, ",".join(map(str, label))))
         fp.close()
     if label_type == "train":
         label_txt_save_path = os.path.join(root_dir, "label.txt")
-        with open(label_txt_save_path, 'w') as fp:
+        with open(label_txt_save_path, "w") as fp:
             label_name_list = []
             for cat in coco.cats.values():
                 id = cat["id"]
@@ -111,4 +115,3 @@ def coco2multilabels(src_img_dir, src_anno_path, root_dir):
                 fp.writelines("{} {}\n".format(id, name))
             fp.close()
             info("Save label names to {}.".format(label_txt_save_path))
-