gaotingquan 9 сар өмнө
parent
commit
f9c867df9d
31 өөрчлөгдсөн 0 нэмэгдсэн , 6196 устгасан
  1. 0 18
      paddlex/inference/components/__init__.py
  2. 0 292
      paddlex/inference/components/base.py
  3. 0 25
      paddlex/inference/components/llm/__init__.py
  4. 0 65
      paddlex/inference/components/llm/base.py
  5. 0 212
      paddlex/inference/components/llm/erniebot.py
  6. 0 20
      paddlex/inference/components/paddle_predictor/__init__.py
  7. 0 376
      paddlex/inference/components/paddle_predictor/predictor.py
  8. 0 15
      paddlex/inference/components/retrieval/__init__.py
  9. 0 359
      paddlex/inference/components/retrieval/faiss.py
  10. 0 33
      paddlex/inference/components/task_related/__init__.py
  11. 0 124
      paddlex/inference/components/task_related/clas.py
  12. 0 433
      paddlex/inference/components/task_related/det.py
  13. 0 89
      paddlex/inference/components/task_related/instance_seg.py
  14. 0 940
      paddlex/inference/components/task_related/seal_det_warp.py
  15. 0 40
      paddlex/inference/components/task_related/seg.py
  16. 0 191
      paddlex/inference/components/task_related/table_rec.py
  17. 0 895
      paddlex/inference/components/task_related/text_det.py
  18. 0 353
      paddlex/inference/components/task_related/text_rec.py
  19. 0 43
      paddlex/inference/components/task_related/warp.py
  20. 0 16
      paddlex/inference/components/transforms/__init__.py
  21. 0 15
      paddlex/inference/components/transforms/image/__init__.py
  22. 0 598
      paddlex/inference/components/transforms/image/common.py
  23. 0 58
      paddlex/inference/components/transforms/image/funcs.py
  24. 0 67
      paddlex/inference/components/transforms/read_data.py
  25. 0 15
      paddlex/inference/components/transforms/ts/__init__.py
  26. 0 393
      paddlex/inference/components/transforms/ts/common.py
  27. 0 424
      paddlex/inference/components/transforms/ts/funcs.py
  28. 0 1
      paddlex/inference/models/base/__init__.py
  29. 0 15
      paddlex/inference/models/base/pp_infer/__init__.py
  30. 0 17
      paddlex/inference/models/base/pp_infer/base_infer.py
  31. 0 54
      paddlex/inference/utils/process_hook.py

+ 0 - 18
paddlex/inference/components/__init__.py

@@ -1,18 +0,0 @@
-# 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.
-
-from .transforms import *
-from .paddle_predictor import *
-from .task_related import *
-from .retrieval import *

+ 0 - 292
paddlex/inference/components/base.py

@@ -1,292 +0,0 @@
-# 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 inspect
-from abc import ABC, abstractmethod
-from copy import deepcopy
-from types import GeneratorType
-
-from ...utils.flags import INFER_BENCHMARK
-from ...utils import logging
-from ..utils.benchmark import Timer
-
-
-class BaseComponent(ABC):
-
-    YIELD_BATCH = True
-    KEEP_INPUT = True
-    ENABLE_BATCH = False
-
-    INPUT_KEYS = None
-    OUTPUT_KEYS = None
-
-    def __init__(self):
-        self.inputs = self.DEAULT_INPUTS if hasattr(self, "DEAULT_INPUTS") else {}
-        self.outputs = self.DEAULT_OUTPUTS if hasattr(self, "DEAULT_OUTPUTS") else {}
-
-        if INFER_BENCHMARK:
-            self.timer = Timer()
-            self.apply = self.timer.watch_func(self.apply)
-
-    def __call__(self, input_list):
-        # use list type for batched data
-        if not isinstance(input_list, list):
-            input_list = [input_list]
-
-        output_list = []
-        for args, input_ in self._check_input(input_list):
-            output = self.apply(**args)
-            if not output:
-                if self.YIELD_BATCH:
-                    yield input_list
-                else:
-                    for item in input_list:
-                        yield item
-
-            # output may be a generator, when the apply() uses yield
-            if (
-                isinstance(output, GeneratorType)
-                or output.__class__.__name__ == "generator"
-            ):
-                # if output is a generator, use for-in to get every one batch output data and yield one by one
-                for each_output in output:
-                    reassemble_data = self._check_output(each_output, input_)
-                    if self.YIELD_BATCH:
-                        yield reassemble_data
-                    else:
-                        for item in reassemble_data:
-                            yield item
-            # if output is not a generator, process all data of that and yield, so use output_list to collect all reassemble_data
-            else:
-                reassemble_data = self._check_output(output, input_)
-                output_list.extend(reassemble_data)
-
-        # avoid yielding output_list when the output is a generator
-        if len(output_list) > 0:
-            if self.YIELD_BATCH:
-                yield output_list
-            else:
-                for item in output_list:
-                    yield item
-
-    def _check_input(self, input_list):
-        # check if the value of input data meets the requirements of apply(),
-        # and reassemble the parameters of apply() from input_list
-        def _check_type(input_):
-            if not isinstance(input_, dict):
-                if len(self.inputs) == 1:
-                    key = list(self.inputs.keys())[0]
-                    input_ = {key: input_}
-                else:
-                    raise Exception(
-                        f"The input must be a dict or a list of dict, unless the input of the component only requires one argument, but the component({self.__class__.__name__}) requires {list(self.inputs.keys())}!"
-                    )
-            return input_
-
-        def _check_args_key(args):
-            sig = inspect.signature(self.apply)
-            for param in sig.parameters.values():
-                if param.kind == inspect.Parameter.VAR_KEYWORD:
-                    logging.debug(
-                        f"The apply function parameter of {self.__class__.__name__} is **kwargs, so would not inspect!"
-                    )
-                    continue
-                if param.default == inspect.Parameter.empty and param.name not in args:
-                    raise Exception(
-                        f"The parameter ({param.name}) is needed by {self.__class__.__name__}, but {list(args.keys())} only found!"
-                    )
-
-        if self.inputs is None:
-            return [({}, None)]
-
-        if self.need_batch_input:
-            args = {}
-            for input_ in input_list:
-                input_ = _check_type(input_)
-                for k, v in self.inputs.items():
-                    if v not in input_:
-                        raise Exception(
-                            f"The value ({v}) is needed by {self.__class__.__name__}. But not found in Data ({input_.keys()})!"
-                        )
-                    if k not in args:
-                        args[k] = []
-                    args[k].append(input_.get(v))
-                _check_args_key(args)
-            reassemble_input = [(args, input_list)]
-        else:
-            reassemble_input = []
-            for input_ in input_list:
-                input_ = _check_type(input_)
-                args = {}
-                for k, v in self.inputs.items():
-                    if v not in input_:
-                        raise Exception(
-                            f"The value ({v}) is needed by {self.__class__.__name__}. But not found in Data ({input_.keys()})!"
-                        )
-                    args[k] = input_.get(v)
-                _check_args_key(args)
-                reassemble_input.append((args, input_))
-        return reassemble_input
-
-    def _check_output(self, output, ori_data):
-        # check if the value of apply() output data meets the requirements of setting
-        # when the output data is list type, reassemble each of that
-        if isinstance(output, list):
-            if self.need_batch_input:
-                assert isinstance(ori_data, list) and len(ori_data) == len(output)
-                output_list = []
-                for ori_item, output_item in zip(ori_data, output):
-                    data = ori_item.copy() if self.keep_input else {}
-                    if isinstance(self.outputs, type(None)):
-                        logging.debug(
-                            f"The `output_key` of {self.__class__.__name__} is None, so would not inspect!"
-                        )
-                        data.update(output_item)
-                    else:
-                        for k, v in self.outputs.items():
-                            if k not in output_item:
-                                raise Exception(
-                                    f"The value ({k}) is needed by {self.__class__.__name__}. But not found in Data ({output_item.keys()})!"
-                                )
-                            data.update({v: output_item[k]})
-                    output_list.append(data)
-                return output_list
-            else:
-                assert isinstance(ori_data, dict)
-                output_list = []
-                for output_item in output:
-                    data = ori_data.copy() if self.keep_input else {}
-                    if isinstance(self.outputs, type(None)):
-                        logging.debug(
-                            f"The `output_key` of {self.__class__.__name__} is None, so would not inspect!"
-                        )
-                        data.update(output_item)
-                    else:
-                        for k, v in self.outputs.items():
-                            if k not in output_item:
-                                raise Exception(
-                                    f"The value ({k}) is needed by {self.__class__.__name__}. But not found in Data ({output_item.keys()})!"
-                                )
-                            data.update({v: output_item[k]})
-                    output_list.append(data)
-                return output_list
-        else:
-            assert isinstance(ori_data, dict) and isinstance(output, dict)
-            data = ori_data.copy() if self.keep_input else {}
-            if isinstance(self.outputs, type(None)):
-                logging.debug(
-                    f"The `output_key` of {self.__class__.__name__} is None, so would not inspect!"
-                )
-                data.update(output)
-            else:
-                for k, v in self.outputs.items():
-                    if k not in output:
-                        raise Exception(
-                            f"The value of key ({k}) is needed add to Data. But not found in output of {self.__class__.__name__}: ({output.keys()})!"
-                        )
-                    data.update({v: output[k]})
-        return [data]
-
-    def set_inputs(self, inputs):
-        assert isinstance(inputs, dict)
-        input_keys = deepcopy(self.INPUT_KEYS)
-
-        # e.g, input_keys is None or []
-        if input_keys is None or (
-            isinstance(input_keys, list) and len(input_keys) == 0
-        ):
-            self.inputs = {}
-            if inputs:
-                raise Exception
-            return
-
-        # e.g, input_keys is 'img'
-        if not isinstance(input_keys, list):
-            input_keys = [[input_keys]]
-        # e.g, input_keys is ['img'] or [['img']]
-        elif len(input_keys) > 0:
-            # e.g, input_keys is ['img']
-            if not isinstance(input_keys[0], list):
-                input_keys = [input_keys]
-
-        ck_pass = False
-        for key_group in input_keys:
-            for key in key_group:
-                if key not in inputs:
-                    break
-            # check pass
-            else:
-                ck_pass = True
-            if ck_pass == True:
-                break
-        else:
-            raise Exception(
-                f"The input {input_keys} are needed by {self.__class__.__name__}. But only get: {list(inputs.keys())}"
-            )
-        self.inputs = inputs
-
-    def set_outputs(self, outputs):
-        assert isinstance(outputs, dict)
-        output_keys = deepcopy(self.OUTPUT_KEYS)
-        if not isinstance(output_keys, list):
-            output_keys = [output_keys]
-
-        for k in output_keys:
-            if k not in outputs:
-                logging.debug(
-                    f"The output ({k}) of {self.__class__.__name__} would be abandon!"
-                )
-        self.outputs = outputs
-
-    @classmethod
-    def get_input_keys(cls) -> list:
-        return cls.input_keys
-
-    @classmethod
-    def get_output_keys(cls) -> list:
-        return cls.output_keys
-
-    @property
-    def need_batch_input(self):
-        return getattr(self, "ENABLE_BATCH", False)
-
-    @property
-    def keep_input(self):
-        return getattr(self, "KEEP_INPUT", True)
-
-    @property
-    def name(self):
-        return getattr(self, "NAME", self.__class__.__name__)
-
-    @property
-    def sub_cmps(self):
-        return None
-
-    @abstractmethod
-    def apply(self, input):
-        raise NotImplementedError
-
-
-class ComponentsEngine(object):
-    def __init__(self, ops):
-        self.ops = ops
-        self.keys = list(ops.keys())
-
-    def __call__(self, data, i=0):
-        data_gen = self.ops[self.keys[i]](data)
-        if i + 1 < len(self.ops):
-            for data in data_gen:
-                yield from self.__call__(data, i + 1)
-        else:
-            yield from data_gen

+ 0 - 25
paddlex/inference/components/llm/__init__.py

@@ -1,25 +0,0 @@
-# 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.
-from .base import BaseLLM
-from .erniebot import ErnieBot
-
-
-def create_llm_api(model_name: str, params={}) -> BaseLLM:
-    # for CI
-    if model_name == "paddlex_ci":
-        return
-    return BaseLLM.get(model_name)(
-        model_name=model_name,
-        params=params,
-    )

+ 0 - 65
paddlex/inference/components/llm/base.py

@@ -1,65 +0,0 @@
-# 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 base64
-from ..base import BaseComponent
-from ....utils.subclass_register import AutoRegisterABCMetaClass
-
-__all__ = ["BaseLLM"]
-
-
-class BaseLLM(BaseComponent, metaclass=AutoRegisterABCMetaClass):
-    __is_base = True
-
-    ERROR_MASSAGE = ""
-    VECTOR_STORE_PREFIX = "PADDLEX_VECTOR_STORE"
-
-    def __init__(self):
-        super().__init__()
-
-    def pre_process(self, inputs):
-        return inputs
-
-    def post_process(self, outputs):
-        return outputs
-
-    def pred(self, inputs):
-        raise NotImplementedError("The method `pred` has not been implemented yet.")
-
-    def get_vector(self):
-        raise NotImplementedError(
-            "The method `get_vector` has not been implemented yet."
-        )
-
-    def caculate_similar(self):
-        raise NotImplementedError(
-            "The method `caculate_similar` has not been implemented yet."
-        )
-
-    def apply(self, inputs):
-        pre_process_results = self.pre_process(inputs)
-        pred_results = self.pred(pre_process_results)
-        post_process_results = self.post_process(pred_results)
-        return post_process_results
-
-    def is_vector_store(self, s):
-        return s.startswith(self.VECTOR_STORE_PREFIX)
-
-    def encode_vector_store(self, vector_store_bytes):
-        return self.VECTOR_STORE_PREFIX + base64.b64encode(vector_store_bytes).decode(
-            "ascii"
-        )
-
-    def decode_vector_store(self, vector_store_str):
-        return base64.b64decode(vector_store_str[len(self.VECTOR_STORE_PREFIX) :])

+ 0 - 212
paddlex/inference/components/llm/erniebot.py

@@ -1,212 +0,0 @@
-# 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
-import time
-import json
-import erniebot
-
-from pathlib import Path
-from .base import BaseLLM
-from ....utils import logging
-from ....utils.func_register import FuncRegister
-
-from langchain.docstore.document import Document
-from langchain.text_splitter import RecursiveCharacterTextSplitter
-
-from langchain_community.embeddings import QianfanEmbeddingsEndpoint
-from langchain_community.vectorstores import FAISS
-from langchain_community import vectorstores
-from erniebot_agent.extensions.langchain.embeddings import ErnieEmbeddings
-
-__all__ = ["ErnieBot"]
-
-
-class ErnieBot(BaseLLM):
-
-    INPUT_KEYS = ["prompts"]
-    OUTPUT_KEYS = ["cls_res"]
-    DEAULT_INPUTS = {"prompts": "prompts"}
-    DEAULT_OUTPUTS = {"cls_pred": "cls_pred"}
-    API_TYPE = "aistudio"
-
-    entities = [
-        "ernie-4.0",
-        "ernie-3.5",
-        "ernie-3.5-8k",
-        "ernie-lite",
-        "ernie-tiny-8k",
-        "ernie-speed",
-        "ernie-speed-128k",
-        "ernie-char-8k",
-    ]
-
-    _FUNC_MAP = {}
-    register = FuncRegister(_FUNC_MAP)
-
-    def __init__(self, model_name="ernie-4.0", params={}):
-        super().__init__()
-        access_token = params.get("access_token")
-        ak = params.get("ak")
-        sk = params.get("sk")
-        api_type = params.get("api_type")
-        max_retries = params.get("max_retries")
-        assert model_name in self.entities, f"model_name must be in {self.entities}"
-        assert any([access_token, ak, sk]), "access_token or ak and sk must be set"
-        self.model_name = model_name
-        self.config = {
-            "api_type": api_type,
-            "max_retries": max_retries,
-        }
-        if access_token:
-            self.config["access_token"] = access_token
-        else:
-            self.config["ak"] = ak
-            self.config["sk"] = sk
-
-    def pred(self, prompt, temperature=0.001):
-        """
-        llm predict
-        """
-        try:
-            chat_completion = erniebot.ChatCompletion.create(
-                _config_=self.config,
-                model=self.model_name,
-                messages=[{"role": "user", "content": prompt}],
-                temperature=float(temperature),
-            )
-            llm_result = chat_completion.get_result()
-            return llm_result
-        except Exception as e:
-            if len(e.args) < 1:
-                self.ERROR_MASSAGE = (
-                    "当前选择后端为AI Studio,千帆调用失败,请检查token"
-                )
-            elif (
-                e.args[-1]
-                == "暂无权限使用,请在 AI Studio 正确获取访问令牌(access token)使用"
-            ):
-                self.ERROR_MASSAGE = (
-                    "当前选择后端为AI Studio,请正确获取访问令牌(access token)使用"
-                )
-            elif e.args[-1] == "the max length of current question is 4800":
-                self.ERROR_MASSAGE = "大模型调用失败"
-            else:
-                logging.error(e)
-                self.ERROR_MASSAGE = "大模型调用失败"
-        return None
-
-    def get_vector(
-        self,
-        ocr_result,
-        sleep_time=0.5,
-        block_size=300,
-        separators=["\t", "\n", "。", "\n\n", ""],
-    ):
-        """get summary prompt"""
-
-        all_items = []
-        for i, ocr_res in enumerate(ocr_result):
-            for type, text in ocr_res.items():
-                all_items += [f"第{i}页{type}:{text}"]
-
-        text_splitter = RecursiveCharacterTextSplitter(
-            chunk_size=block_size, chunk_overlap=20, separators=separators
-        )
-        texts = text_splitter.split_text("\t".join(all_items))
-
-        all_splits = [Document(page_content=text) for text in texts]
-
-        api_type = self.config["api_type"]
-        if api_type == "qianfan":
-            os.environ["QIANFAN_AK"] = os.environ.get("EB_AK", self.config["ak"])
-            os.environ["QIANFAN_SK"] = os.environ.get("EB_SK", self.config["sk"])
-            user_ak = os.environ.get("EB_AK", self.config["ak"])
-            user_id = hash(user_ak)
-            vectorstore = FAISS.from_documents(
-                documents=all_splits, embedding=QianfanEmbeddingsEndpoint()
-            )
-
-        elif api_type == "aistudio":
-            token = self.config["access_token"]
-            vectorstore = FAISS.from_documents(
-                documents=all_splits[0:1],
-                embedding=ErnieEmbeddings(aistudio_access_token=token),
-            )
-
-            #### ErnieEmbeddings.chunk_size = 16
-            step = min(16, len(all_splits) - 1)
-            for shot_splits in [
-                all_splits[i : i + step] for i in range(1, len(all_splits), step)
-            ]:
-                time.sleep(sleep_time)
-                vectorstore_slice = FAISS.from_documents(
-                    documents=shot_splits,
-                    embedding=ErnieEmbeddings(aistudio_access_token=token),
-                )
-                vectorstore.merge_from(vectorstore_slice)
-        else:
-            raise ValueError(f"Unsupported api_type: {api_type}")
-
-        vectorstore = self.encode_vector_store(vectorstore.serialize_to_bytes())
-        return vectorstore
-
-    def caculate_similar(self, vector, key_list, llm_params=None, sleep_time=0.5):
-        """caculate similar with key and doc"""
-        if not self.is_vector_store(vector):
-            logging.warning(
-                "The retrieved vectorstore is not for PaddleX and will return the visual results of the query image"
-            )
-            return vector
-        # XXX: The initialization parameters are hard-coded.
-        if llm_params:
-            api_type = llm_params.get("api_type")
-            access_token = llm_params.get("access_token")
-            ak = llm_params.get("ak")
-            sk = llm_params.get("sk")
-        else:
-            api_type = self.config["api_type"]
-            access_token = self.config.get("access_token")
-            ak = self.config.get("ak")
-            sk = self.config.get("sk")
-        if api_type == "aistudio":
-            embeddings = ErnieEmbeddings(aistudio_access_token=access_token)
-        elif api_type == "qianfan":
-            embeddings = QianfanEmbeddingsEndpoint(qianfan_ak=ak, qianfan_sk=sk)
-        else:
-            raise ValueError(f"Unsupported api_type: {api_type}")
-
-        vectorstore = vectorstores.FAISS.deserialize_from_bytes(
-            self.decode_vector_store(vector), embeddings
-        )
-
-        # 根据提问匹配上下文
-        Q = []
-        C = []
-        for key in key_list:
-            QUESTION = f"抽取关键信息:{key}"
-            # c_str = ""
-            Q.append(QUESTION)
-            time.sleep(sleep_time)
-            docs = vectorstore.similarity_search_with_relevance_scores(QUESTION, k=2)
-            context = [(document.page_content, score) for document, score in docs]
-            context = sorted(context, key=lambda x: x[1])
-            C.extend([x[0] for x in context[::-1]])
-
-        C = list(set(C))
-        all_C = " ".join(C)
-
-        summary_prompt = all_C
-
-        return summary_prompt

+ 0 - 20
paddlex/inference/components/paddle_predictor/__init__.py

@@ -1,20 +0,0 @@
-# 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.
-
-from .predictor import (
-    BasePaddlePredictor,
-    ImagePredictor,
-    ImageDetPredictor,
-    TSPPPredictor,
-)

+ 0 - 376
paddlex/inference/components/paddle_predictor/predictor.py

@@ -1,376 +0,0 @@
-# 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
-from abc import abstractmethod
-import lazy_paddle as paddle
-import numpy as np
-
-from ....utils.flags import FLAGS_json_format_model
-from ....utils import logging
-from ...utils.pp_option import PaddlePredictorOption
-from ..base import BaseComponent
-
-
-def collect_trt_shapes(
-    model_file, model_params, gpu_id, shape_range_info_path, trt_dynamic_shapes
-):
-    config = paddle.inference.Config(model_file, model_params)
-    config.enable_use_gpu(100, gpu_id)
-    min_arrs, opt_arrs, max_arrs = {}, {}, {}
-    for name, candidate_shapes in trt_dynamic_shapes.items():
-        min_shape, opt_shape, max_shape = candidate_shapes
-        min_arrs[name] = np.ones(min_shape, dtype=np.float32)
-        opt_arrs[name] = np.ones(opt_shape, dtype=np.float32)
-        max_arrs[name] = np.ones(max_shape, dtype=np.float32)
-
-    config.collect_shape_range_info(shape_range_info_path)
-    predictor = paddle.inference.create_predictor(config)
-    # opt_arrs would be used twice to simulate the most common situations
-    for arrs in [min_arrs, opt_arrs, opt_arrs, max_arrs]:
-        for name, arr in arrs.items():
-            input_handler = predictor.get_input_handle(name)
-            input_handler.reshape(arr.shape)
-            input_handler.copy_from_cpu(arr)
-        predictor.run()
-
-
-class Copy2GPU(BaseComponent):
-
-    def __init__(self, input_handlers):
-        super().__init__()
-        self.input_handlers = input_handlers
-
-    def apply(self, x):
-        for idx in range(len(x)):
-            self.input_handlers[idx].reshape(x[idx].shape)
-            self.input_handlers[idx].copy_from_cpu(x[idx])
-
-
-class Copy2CPU(BaseComponent):
-
-    def __init__(self, output_handlers):
-        super().__init__()
-        self.output_handlers = output_handlers
-
-    def apply(self):
-        output = []
-        for out_tensor in self.output_handlers:
-            batch = out_tensor.copy_to_cpu()
-            output.append(batch)
-        return output
-
-
-class Infer(BaseComponent):
-
-    def __init__(self, predictor):
-        super().__init__()
-        self.predictor = predictor
-
-    def apply(self):
-        self.predictor.run()
-
-
-class BasePaddlePredictor(BaseComponent):
-    """Predictor based on Paddle Inference"""
-
-    OUTPUT_KEYS = "pred"
-    DEAULT_OUTPUTS = {"pred": "pred"}
-    ENABLE_BATCH = True
-
-    def __init__(self, model_dir, model_prefix, option):
-        super().__init__()
-        self.model_dir = model_dir
-        self.model_prefix = model_prefix
-        self._update_option(option)
-
-    def _update_option(self, option):
-        if option:
-            if self.option and option == self.option:
-                return
-            self._option = option
-            self._reset()
-
-    @property
-    def option(self):
-        return self._option if hasattr(self, "_option") else None
-
-    @option.setter
-    def option(self, option):
-        self._update_option(option)
-
-    def _reset(self):
-        if not self.option:
-            self.option = PaddlePredictorOption()
-        logging.debug(f"Env: {self.option}")
-        (
-            predictor,
-            input_handlers,
-            output_handlers,
-        ) = self._create()
-        self.copy2gpu = Copy2GPU(input_handlers)
-        self.copy2cpu = Copy2CPU(output_handlers)
-        self.infer = Infer(predictor)
-        self.option.changed = False
-
-    def _create(self):
-        """_create"""
-        from lazy_paddle.inference import Config, create_predictor
-
-        model_postfix = ".json" if FLAGS_json_format_model else ".pdmodel"
-        model_file = (self.model_dir / f"{self.model_prefix}{model_postfix}").as_posix()
-        params_file = (self.model_dir / f"{self.model_prefix}.pdiparams").as_posix()
-        config = Config(model_file, params_file)
-
-        config.enable_memory_optim()
-        if self.option.device in ("gpu", "dcu"):
-            if self.option.device == "gpu":
-                config.exp_disable_mixed_precision_ops({"feed", "fetch"})
-            config.enable_use_gpu(100, self.option.device_id)
-            if self.option.device == "gpu":
-                # NOTE: The pptrt settings are not aligned with those of FD.
-                precision_map = {
-                    "trt_int8": Config.Precision.Int8,
-                    "trt_fp32": Config.Precision.Float32,
-                    "trt_fp16": Config.Precision.Half,
-                }
-                if self.option.run_mode in precision_map.keys():
-                    config.enable_tensorrt_engine(
-                        workspace_size=(1 << 30) * self.option.batch_size,
-                        max_batch_size=self.option.batch_size,
-                        min_subgraph_size=self.option.min_subgraph_size,
-                        precision_mode=precision_map[self.option.run_mode],
-                        use_static=self.option.trt_use_static,
-                        use_calib_mode=self.option.trt_calib_mode,
-                    )
-
-                    if not os.path.exists(self.option.shape_info_filename):
-                        logging.info(
-                            f"Dynamic shape info is collected into: {self.option.shape_info_filename}"
-                        )
-                        collect_trt_shapes(
-                            model_file,
-                            params_file,
-                            self.option.device_id,
-                            self.option.shape_info_filename,
-                            self.option.trt_dynamic_shapes,
-                        )
-                    else:
-                        logging.info(
-                            f"A dynamic shape info file ( {self.option.shape_info_filename} ) already exists. No need to collect again."
-                        )
-                    config.enable_tuned_tensorrt_dynamic_shape(
-                        self.option.shape_info_filename, True
-                    )
-
-        elif self.option.device == "npu":
-            config.enable_custom_device("npu")
-        elif self.option.device == "xpu":
-            pass
-        elif self.option.device == "mlu":
-            config.enable_custom_device("mlu")
-        elif self.option.device == "gcu":
-            assert paddle.device.is_compiled_with_custom_device("gcu"), (
-                "Args device cannot be set as gcu while your paddle "
-                "is not compiled with gcu!"
-            )
-            config.enable_custom_device("gcu")
-            from paddle_custom_device.gcu import passes as gcu_passes
-
-            gcu_passes.setUp()
-            name = "PaddleX_" + self.option.model_name
-            if hasattr(config, "enable_new_ir") and self.option.enable_new_ir:
-                config.enable_new_ir(True)
-                config.enable_new_executor(True)
-                kPirGcuPasses = gcu_passes.inference_passes(use_pir=True, name=name)
-                config.enable_custom_passes(kPirGcuPasses, True)
-            else:
-                config.enable_new_ir(False)
-                config.enable_new_executor(False)
-                pass_builder = config.pass_builder()
-                gcu_passes.append_passes_for_legacy_ir(pass_builder, name)
-        else:
-            assert self.option.device == "cpu"
-            config.disable_gpu()
-            if "mkldnn" in self.option.run_mode:
-                try:
-                    config.enable_mkldnn()
-                    if "bf16" in self.option.run_mode:
-                        config.enable_mkldnn_bfloat16()
-                except Exception as e:
-                    logging.warning(
-                        "MKL-DNN is not available. We will disable MKL-DNN."
-                    )
-                config.set_mkldnn_cache_capacity(-1)
-            else:
-                if hasattr(config, "disable_mkldnn"):
-                    config.disable_mkldnn()
-
-        # Disable paddle inference logging
-        config.disable_glog_info()
-
-        config.set_cpu_math_library_num_threads(self.option.cpu_threads)
-
-        if not (self.option.device == "gpu" and self.option.run_mode.startswith("trt")):
-            if self.option.device in ("cpu", "gpu"):
-                if hasattr(config, "enable_new_ir"):
-                    config.enable_new_ir(self.option.enable_new_ir)
-                config.set_optimization_level(3)
-            if hasattr(config, "enable_new_executor"):
-                config.enable_new_executor()
-
-        for del_p in self.option.delete_pass:
-            config.delete_pass(del_p)
-
-        if self.option.device in ("gpu", "dcu"):
-            if paddle.is_compiled_with_rocm():
-                # Delete unsupported passes in dcu
-                config.delete_pass("conv2d_add_act_fuse_pass")
-                config.delete_pass("conv2d_add_fuse_pass")
-
-        predictor = create_predictor(config)
-
-        # Get input and output handlers
-        input_names = predictor.get_input_names()
-        input_names.sort()
-        input_handlers = []
-        output_handlers = []
-        for input_name in input_names:
-            input_handler = predictor.get_input_handle(input_name)
-            input_handlers.append(input_handler)
-        output_names = predictor.get_output_names()
-        for output_name in output_names:
-            output_handler = predictor.get_output_handle(output_name)
-            output_handlers.append(output_handler)
-        return predictor, input_handlers, output_handlers
-
-    def apply(self, **kwargs):
-        if self.option.changed:
-            self._reset()
-        batches = self.to_batch(**kwargs)
-        self.copy2gpu.apply(batches)
-        self.infer.apply()
-        pred = self.copy2cpu.apply()
-        return self.format_output(pred)
-
-    @property
-    def sub_cmps(self):
-        return {
-            "Copy2GPU": self.copy2gpu,
-            "Infer": self.infer,
-            "Copy2CPU": self.copy2cpu,
-        }
-
-    @abstractmethod
-    def to_batch(self):
-        raise NotImplementedError
-
-    @abstractmethod
-    def format_output(self, pred):
-        return [{"pred": res} for res in zip(*pred)]
-
-
-class ImagePredictor(BasePaddlePredictor):
-    INPUT_KEYS = "img"
-    OUTPUT_KEYS = "pred"
-    DEAULT_INPUTS = {"img": "img"}
-    DEAULT_OUTPUTS = {"pred": "pred"}
-
-    def to_batch(self, img):
-        return [np.stack(img, axis=0).astype(dtype=np.float32, copy=False)]
-
-    def format_output(self, pred):
-        return [{"pred": res} for res in zip(*pred)]
-
-
-class ImageDetPredictor(BasePaddlePredictor):
-
-    INPUT_KEYS = [
-        ["img", "scale_factors"],
-        ["img", "scale_factors", "img_size"],
-        ["img", "img_size"],
-    ]
-    OUTPUT_KEYS = [["boxes"], ["boxes", "masks"]]
-    DEAULT_INPUTS = {"img": "img", "scale_factors": "scale_factors"}
-    DEAULT_OUTPUTS = None
-
-    def to_batch(self, img, scale_factors=[[1.0, 1.0]], img_size=None):
-        scale_factors = [scale_factor[::-1] for scale_factor in scale_factors]
-        if img_size is None:
-            return [
-                np.stack(img, axis=0).astype(dtype=np.float32, copy=False),
-                np.stack(scale_factors, axis=0).astype(dtype=np.float32, copy=False),
-            ]
-        else:
-            img_size = [img_size[::-1] for img_size in img_size]
-            return [
-                np.stack(img_size, axis=0).astype(dtype=np.float32, copy=False),
-                np.stack(img, axis=0).astype(dtype=np.float32, copy=False),
-                np.stack(scale_factors, axis=0).astype(dtype=np.float32, copy=False),
-            ]
-
-    def format_output(self, pred):
-        box_idx_start = 0
-        pred_box = []
-
-        if len(pred) == 4:
-            # Adapt to SOLOv2
-            pred_class_id = []
-            pred_mask = []
-            pred_class_id.append([pred[1], pred[2]])
-            pred_mask.append(pred[3])
-            return [
-                {
-                    "class_id": np.array(pred_class_id[i]),
-                    "masks": np.array(pred_mask[i]),
-                }
-                for i in range(len(pred_class_id))
-            ]
-
-        if len(pred) == 3:
-            # Adapt to Instance Segmentation
-            pred_mask = []
-        for idx in range(len(pred[1])):
-            np_boxes_num = pred[1][idx]
-            box_idx_end = box_idx_start + np_boxes_num
-            np_boxes = pred[0][box_idx_start:box_idx_end]
-            pred_box.append(np_boxes)
-            if len(pred) == 3:
-                np_masks = pred[2][box_idx_start:box_idx_end]
-                pred_mask.append(np_masks)
-            box_idx_start = box_idx_end
-
-        if len(pred) == 3:
-            return [
-                {"boxes": np.array(pred_box[i]), "masks": np.array(pred_mask[i])}
-                for i in range(len(pred_box))
-            ]
-        else:
-            return [{"boxes": np.array(res)} for res in pred_box]
-
-
-class TSPPPredictor(BasePaddlePredictor):
-
-    INPUT_KEYS = "ts"
-    OUTPUT_KEYS = "pred"
-    DEAULT_INPUTS = {"ts": "ts"}
-    DEAULT_OUTPUTS = {"pred": "pred"}
-
-    def to_batch(self, ts):
-        n = len(ts[0])
-        x = [np.stack([lst[i] for lst in ts], axis=0) for i in range(n)]
-        return x
-
-    def format_output(self, pred):
-        return [{"pred": res} for res in zip(*pred)]

+ 0 - 15
paddlex/inference/components/retrieval/__init__.py

@@ -1,15 +0,0 @@
-# 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.
-
-from .faiss import FaissIndexer, FaissBuilder

+ 0 - 359
paddlex/inference/components/retrieval/faiss.py

@@ -1,359 +0,0 @@
-# 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
-import pickle
-from pathlib import Path
-import faiss
-import numpy as np
-
-from ....utils import logging
-from ...utils.io import YAMLWriter, YAMLReader
-from ..base import BaseComponent
-
-
-class IndexData:
-    VECTOR_FN = "vector"
-    VECTOR_SUFFIX = ".index"
-    IDMAP_FN = "id_map"
-    IDMAP_SUFFIX = ".yaml"
-
-    def __init__(self, index, index_info):
-        self._index = index
-        self._index_info = index_info
-        self._id_map = index_info["id_map"]
-        self._metric_type = index_info["metric_type"]
-        self._index_type = index_info["index_type"]
-
-    @property
-    def index(self):
-        return self._index
-
-    @property
-    def index_bytes(self):
-        return faiss.serialize_index(self._index)
-
-    @property
-    def id_map(self):
-        return self._id_map
-
-    @property
-    def metric_type(self):
-        return self._metric_type
-
-    @property
-    def index_type(self):
-        return self._index_type
-
-    @property
-    def index_info(self):
-        return {
-            "index_type": self.index_type,
-            "metric_type": self.metric_type,
-            "id_map": self._convert_int(self.id_map),
-        }
-
-    def _convert_int(self, id_map):
-        return {int(k): str(v) for k, v in id_map.items()}
-
-    @staticmethod
-    def _convert_int64(id_map):
-        return {np.int64(k): str(v) for k, v in id_map.items()}
-
-    def save(self, save_dir):
-        save_dir = Path(save_dir)
-        save_dir.mkdir(parents=True, exist_ok=True)
-        vector_path = (save_dir / f"{self.VECTOR_FN}{self.VECTOR_SUFFIX}").as_posix()
-        index_info_path = (save_dir / f"{self.IDMAP_FN}{self.IDMAP_SUFFIX}").as_posix()
-
-        if self.metric_type in FaissBuilder.BINARY_METRIC_TYPE:
-            faiss.write_index_binary(self.index, vector_path)
-        else:
-            faiss.write_index(self.index, vector_path)
-
-        yaml_writer = YAMLWriter()
-        yaml_writer.write(
-            index_info_path,
-            self.index_info,
-            default_flow_style=False,
-            allow_unicode=True,
-        )
-
-    @classmethod
-    def load(cls, index):
-        if isinstance(index, str):
-            index_root = Path(index)
-            vector_path = index_root / f"{cls.VECTOR_FN}{cls.VECTOR_SUFFIX}"
-            index_info_path = index_root / f"{cls.IDMAP_FN}{cls.IDMAP_SUFFIX}"
-
-            assert (
-                vector_path.exists()
-            ), f"Not found the {cls.VECTOR_FN}{cls.VECTOR_SUFFIX} file in {index}!"
-            assert (
-                index_info_path.exists()
-            ), f"Not found the {cls.IDMAP_FN}{cls.IDMAP_SUFFIX} file in {index}!"
-
-            yaml_reader = YAMLReader()
-            index_info = yaml_reader.read(index_info_path)
-            assert (
-                "id_map" in index_info
-                and "metric_type" in index_info
-                and "index_type" in index_info
-            ), f"The index_info file({index_info_path}) may have been damaged, `id_map` or `metric_type` or `index_type` not found in `index_info`."
-            id_map = IndexData._convert_int64(index_info["id_map"])
-
-            if index_info["metric_type"] in FaissBuilder.BINARY_METRIC_TYPE:
-                index = faiss.read_index_binary(vector_path.as_posix())
-            else:
-                index = faiss.read_index(vector_path.as_posix())
-            assert index.ntotal == len(
-                id_map
-            ), "data number in index is not equal in in id_map"
-
-            return index, id_map, index_info["metric_type"], index_info["index_type"]
-        else:
-            assert isinstance(index, IndexData)
-            return index.index, index.id_map, index.metric_type, index.index_type
-
-
-class FaissIndexer(BaseComponent):
-
-    INPUT_KEYS = "feature"
-    OUTPUT_KEYS = ["label", "score"]
-    DEAULT_INPUTS = {"feature": "feature"}
-    DEAULT_OUTPUTS = {"label": "label", "score": "score"}
-
-    ENABLE_BATCH = True
-
-    def __init__(
-        self,
-        index,
-        return_k=1,
-        score_thres=None,
-        hamming_radius=None,
-    ):
-        super().__init__()
-        self._indexer, self.id_map, self.metric_type, index_type = IndexData.load(index)
-        self.return_k = return_k
-        if self.metric_type in FaissBuilder.BINARY_METRIC_TYPE:
-            self.hamming_radius = hamming_radius
-        else:
-            self.score_thres = score_thres
-
-    def apply(self, feature):
-        """apply"""
-        scores_list, ids_list = self._indexer.search(np.array(feature), self.return_k)
-        preds = []
-        for scores, ids in zip(scores_list, ids_list):
-            labels = []
-            for id in ids:
-                if id > 0:
-                    labels.append(self.id_map[id])
-            preds.append({"score": scores, "label": labels})
-
-        if self.metric_type in FaissBuilder.BINARY_METRIC_TYPE:
-            idxs = np.where(scores_list[:, 0] > self.hamming_radius)[0]
-        else:
-            idxs = np.where(scores_list[:, 0] < self.score_thres)[0]
-        for idx in idxs:
-            preds[idx] = {"score": None, "label": None}
-        return preds
-
-
-class FaissBuilder:
-
-    SUPPORT_METRIC_TYPE = ("hamming", "IP", "L2")
-    SUPPORT_INDEX_TYPE = ("Flat", "IVF", "HNSW32")
-    BINARY_METRIC_TYPE = ("hamming",)
-    BINARY_SUPPORT_INDEX_TYPE = ("Flat", "IVF", "BinaryHash")
-
-    @classmethod
-    def _get_index_type(cls, metric_type, index_type, num=None):
-        # if IVF method, cal ivf number automaticlly
-        if index_type == "IVF":
-            index_type = index_type + str(min(int(num // 8), 65536))
-            if metric_type in cls.BINARY_METRIC_TYPE:
-                index_type += ",BFlat"
-            else:
-                index_type += ",Flat"
-
-        # for binary index, add B at head of index_type
-        if metric_type in cls.BINARY_METRIC_TYPE:
-            assert (
-                index_type in cls.BINARY_SUPPORT_INDEX_TYPE
-            ), f"The metric type({metric_type}) only support {cls.BINARY_SUPPORT_INDEX_TYPE} index types!"
-            index_type = "B" + index_type
-
-        if index_type == "HNSW32":
-            logging.warning("The HNSW32 method dose not support 'remove' operation")
-            index_type = "HNSW32"
-
-        if index_type == "Flat":
-            index_type = "Flat"
-
-        return index_type
-
-    @classmethod
-    def _get_metric_type(cls, metric_type):
-        if metric_type == "hamming":
-            return faiss.METRIC_Hamming
-        elif metric_type == "jaccard":
-            return faiss.METRIC_Jaccard
-        elif metric_type == "IP":
-            return faiss.METRIC_INNER_PRODUCT
-        elif metric_type == "L2":
-            return faiss.METRIC_L2
-
-    @classmethod
-    def build(
-        cls,
-        gallery_imgs,
-        gallery_label,
-        predict_func,
-        metric_type="IP",
-        index_type="HNSW32",
-    ):
-        assert (
-            index_type in cls.SUPPORT_INDEX_TYPE
-        ), f"Supported index types only: {cls.SUPPORT_INDEX_TYPE}!"
-
-        assert (
-            metric_type in cls.SUPPORT_METRIC_TYPE
-        ), f"Supported metric types only: {cls.SUPPORT_METRIC_TYPE}!"
-
-        if isinstance(gallery_label, str):
-            gallery_docs, gallery_list = cls.load_gallery(gallery_label, gallery_imgs)
-        else:
-            gallery_docs, gallery_list = gallery_label, gallery_imgs
-
-        features = [res["feature"] for res in predict_func(gallery_list)]
-        dtype = np.uint8 if metric_type in cls.BINARY_METRIC_TYPE else np.float32
-        features = np.array(features).astype(dtype)
-        vector_num, vector_dim = features.shape
-
-        if metric_type in cls.BINARY_METRIC_TYPE:
-            index = faiss.index_binary_factory(
-                vector_dim,
-                cls._get_index_type(metric_type, index_type, vector_num),
-                cls._get_metric_type(metric_type),
-            )
-        else:
-            index = faiss.index_factory(
-                vector_dim,
-                cls._get_index_type(metric_type, index_type, vector_num),
-                cls._get_metric_type(metric_type),
-            )
-            index = faiss.IndexIDMap2(index)
-        ids = {}
-
-        # calculate id for new data
-        index, ids = cls._add_gallery(
-            metric_type, index, ids, features, gallery_docs, mode="new"
-        )
-        return IndexData(
-            index, {"id_map": ids, "metric_type": metric_type, "index_type": index_type}
-        )
-
-    @classmethod
-    def remove(
-        cls,
-        remove_ids,
-        index,
-    ):
-        index, ids, metric_type, index_type = IndexData.load(index)
-        if index_type == "HNSW32":
-            raise RuntimeError(
-                "The index_type: HNSW32 dose not support 'remove' operation"
-            )
-        if isinstance(remove_ids, str):
-            lines = []
-            with open(remove_ids) as f:
-                lines = f.readlines()
-            remove_ids = []
-            for line in lines:
-                id_ = int(line.strip().split(" ")[0])
-                remove_ids.append(id_)
-            remove_ids = np.asarray(remove_ids)
-        else:
-            remove_ids = np.asarray(remove_ids)
-
-        # remove ids in id_map, remove index data in faiss index
-        index.remove_ids(remove_ids)
-        ids = {k: v for k, v in ids.items() if k not in remove_ids}
-        return IndexData(
-            index, {"id_map": ids, "metric_type": metric_type, "index_type": index_type}
-        )
-
-    @classmethod
-    def append(cls, gallery_imgs, gallery_label, predict_func, index):
-        index, ids, metric_type, index_type = IndexData.load(index)
-        assert (
-            metric_type in cls.SUPPORT_METRIC_TYPE
-        ), f"Supported metric types only: {cls.SUPPORT_METRIC_TYPE}!"
-
-        if isinstance(gallery_label, str):
-            gallery_docs, gallery_list = cls.load_gallery(gallery_label, gallery_imgs)
-        else:
-            gallery_docs, gallery_list = gallery_label, gallery_imgs
-
-        features = [res["feature"] for res in predict_func(gallery_list)]
-        dtype = np.uint8 if metric_type in cls.BINARY_METRIC_TYPE else np.float32
-        features = np.array(features).astype(dtype)
-
-        # calculate id for new data
-        index, ids = cls._add_gallery(
-            metric_type, index, ids, features, gallery_docs, mode="append"
-        )
-        return IndexData(
-            index, {"id_map": ids, "metric_type": metric_type, "index_type": index_type}
-        )
-
-    @classmethod
-    def _add_gallery(
-        cls, metric_type, index, ids, gallery_features, gallery_docs, mode
-    ):
-        start_id = max(ids.keys()) + 1 if ids else 0
-        ids_now = (np.arange(0, len(gallery_docs)) + start_id).astype(np.int64)
-
-        # only train when new index file
-        if mode == "new":
-            if metric_type in cls.BINARY_METRIC_TYPE:
-                index.add(gallery_features)
-            else:
-                index.train(gallery_features)
-
-        if metric_type not in cls.BINARY_METRIC_TYPE:
-            index.add_with_ids(gallery_features, ids_now)
-        # TODO(gaotingquan): how append when using hamming metric type
-        # else:
-        #   pass
-
-        for i, d in zip(list(ids_now), gallery_docs):
-            ids[i] = d
-        return index, ids
-
-    @classmethod
-    def load_gallery(cls, gallery_label_path, gallery_imgs_root="", delimiter=" "):
-        lines = []
-        files = []
-        labels = []
-        root = Path(gallery_imgs_root)
-        with open(gallery_label_path, "r", encoding="utf-8") as f:
-            lines = f.readlines()
-        for line in lines:
-            path, label = line.strip().split(delimiter)
-            file_path = root / path
-            files.append(file_path.as_posix())
-            labels.append(label)
-        return labels, files

+ 0 - 33
paddlex/inference/components/task_related/__init__.py

@@ -1,33 +0,0 @@
-# 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.
-
-from .clas import Topk, MultiLabelThreshOutput, NormalizeFeatures
-from .text_det import (
-    DetResizeForTest,
-    NormalizeImage,
-    DBPostProcess,
-    SortBoxes,
-    CropByPolys,
-)
-from .text_rec import (
-    OCRReisizeNormImg,
-    LaTeXOCRReisizeNormImg,
-    CTCLabelDecode,
-    LaTeXOCRDecode,
-)
-from .table_rec import TableLabelDecode
-from .det import DetPostProcess, CropByBoxes, DetPad, WarpAffine
-from .instance_seg import InstanceSegPostProcess
-from .warp import DocTrPostProcess
-from .seg import Map_to_mask

+ 0 - 124
paddlex/inference/components/task_related/clas.py

@@ -1,124 +0,0 @@
-# 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 numpy as np
-
-from ....utils import logging
-from ..base import BaseComponent
-
-
-__all__ = ["Topk", "NormalizeFeatures"]
-
-
-def _parse_class_id_map(class_ids):
-    """parse class id to label map file"""
-    if class_ids is None:
-        return None
-    class_id_map = {id: str(lb) for id, lb in enumerate(class_ids)}
-    return class_id_map
-
-
-class Topk(BaseComponent):
-    """Topk Transform"""
-
-    INPUT_KEYS = ["pred"]
-    OUTPUT_KEYS = [["class_ids", "scores"], ["class_ids", "scores", "label_names"]]
-    DEAULT_INPUTS = {"pred": "pred"}
-    DEAULT_OUTPUTS = {
-        "class_ids": "class_ids",
-        "scores": "scores",
-        "label_names": "label_names",
-    }
-
-    def __init__(self, topk, class_ids=None):
-        super().__init__()
-        assert isinstance(topk, (int,))
-        self.topk = topk
-        self.class_id_map = _parse_class_id_map(class_ids)
-
-    def apply(self, pred):
-        """apply"""
-        cls_pred = pred[0]
-        index = cls_pred.argsort(axis=0)[-self.topk :][::-1].astype("int32")
-        clas_id_list = []
-        score_list = []
-        label_name_list = []
-        for i in index:
-            clas_id_list.append(i.item())
-            score_list.append(cls_pred[i].item())
-            if self.class_id_map is not None:
-                label_name_list.append(self.class_id_map[i.item()])
-        result = {
-            "class_ids": clas_id_list,
-            "scores": np.around(score_list, decimals=5),
-        }
-        if label_name_list is not None:
-            result["label_names"] = label_name_list
-        return result
-
-
-class MultiLabelThreshOutput(BaseComponent):
-
-    INPUT_KEYS = ["pred"]
-    OUTPUT_KEYS = [["class_ids", "scores"], ["class_ids", "scores", "label_names"]]
-    DEAULT_INPUTS = {"pred": "pred"}
-    DEAULT_OUTPUTS = {
-        "class_ids": "class_ids",
-        "scores": "scores",
-        "label_names": "label_names",
-    }
-
-    def __init__(self, threshold=0.5, class_ids=None, delimiter=None):
-        super().__init__()
-        assert isinstance(threshold, (float,))
-        self.threshold = threshold
-        self.delimiter = delimiter if delimiter is not None else " "
-        self.class_id_map = _parse_class_id_map(class_ids)
-
-    def apply(self, pred):
-        """apply"""
-        y = []
-        x = pred[0]
-        pred_index = np.where(x >= self.threshold)[0].astype("int32")
-        index = pred_index[np.argsort(x[pred_index])][::-1]
-        clas_id_list = []
-        score_list = []
-        label_name_list = []
-        for i in index:
-            clas_id_list.append(i.item())
-            score_list.append(x[i].item())
-            if self.class_id_map is not None:
-                label_name_list.append(self.class_id_map[i.item()])
-        result = {
-            "class_ids": clas_id_list,
-            "scores": np.around(score_list, decimals=5),
-        }
-        if label_name_list is not None:
-            result["label_names"] = label_name_list
-        return result
-
-
-class NormalizeFeatures(BaseComponent):
-    """Normalize Features Transform"""
-
-    INPUT_KEYS = ["pred"]
-    OUTPUT_KEYS = ["feature"]
-    DEAULT_INPUTS = {"pred": "pred"}
-    DEAULT_OUTPUTS = {"feature": "feature"}
-
-    def apply(self, pred):
-        """apply"""
-        feas_norm = np.sqrt(np.sum(np.square(pred[0]), axis=0, keepdims=True))
-        feature = np.divide(pred[0], feas_norm)
-        return {"feature": feature}

+ 0 - 433
paddlex/inference/components/task_related/det.py

@@ -1,433 +0,0 @@
-# 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
-import cv2
-
-import numpy as np
-from ...utils.io import ImageReader
-from ..base import BaseComponent
-
-
-def restructured_boxes(boxes, labels, img_size):
-
-    box_list = []
-    w, h = img_size
-
-    for box in boxes:
-        xmin, ymin, xmax, ymax = box[2:]
-        xmin = max(0, xmin)
-        ymin = max(0, ymin)
-        xmax = min(w, xmax)
-        ymax = min(h, ymax)
-        box_list.append(
-            {
-                "cls_id": int(box[0]),
-                "label": labels[int(box[0])],
-                "score": float(box[1]),
-                "coordinate": [xmin, ymin, xmax, ymax],
-            }
-        )
-
-    return box_list
-
-
-def restructured_rotated_boxes(boxes, labels, img_size):
-
-    box_list = []
-    w, h = img_size
-
-    assert boxes.shape[1] == 10, 'The shape of rotated boxes should be [N, 10]'
-    for box in boxes:
-        x1, y1, x2, y2, x3, y3, x4, y4 = box[2:]
-        x1 = min(max(0, x1), w)
-        y1 = min(max(0, y1), h)
-        x2 = min(max(0, x2), w)
-        y2 = min(max(0, y2), h)
-        x3 = min(max(0, x3), w)
-        y3 = min(max(0, y3), h)
-        x4 = min(max(0, x4), w)
-        y4 = min(max(0, y4), h)
-        box_list.append(
-            {
-                "cls_id": int(box[0]),
-                "label": labels[int(box[0])],
-                "score": float(box[1]),
-                "coordinate": [x1, y1, x2, y2, x3, y3, x4, y4],
-            }
-        )
-
-    return box_list
-
-
-def rotate_point(pt, angle_rad):
-    """Rotate a point by an angle.
-    Args:
-        pt (list[float]): 2 dimensional point to be rotated
-        angle_rad (float): rotation angle by radian
-    Returns:
-        list[float]: Rotated point.
-    """
-    assert len(pt) == 2
-    sn, cs = np.sin(angle_rad), np.cos(angle_rad)
-    new_x = pt[0] * cs - pt[1] * sn
-    new_y = pt[0] * sn + pt[1] * cs
-    rotated_pt = [new_x, new_y]
-
-    return rotated_pt
-
-
-def _get_3rd_point(a, b):
-    """To calculate the affine matrix, three pairs of points are required. This
-    function is used to get the 3rd point, given 2D points a & b.
-    The 3rd point is defined by rotating vector `a - b` by 90 degrees
-    anticlockwise, using b as the rotation center.
-    Args:
-        a (np.ndarray): point(x,y)
-        b (np.ndarray): point(x,y)
-    Returns:
-        np.ndarray: The 3rd point.
-    """
-    assert len(a) == 2
-    assert len(b) == 2
-    direction = a - b
-    third_pt = b + np.array([-direction[1], direction[0]], dtype=np.float32)
-
-    return third_pt
-
-
-def get_affine_transform(
-    center, input_size, rot, output_size, shift=(0.0, 0.0), inv=False
-):
-    """Get the affine transform matrix, given the center/scale/rot/output_size.
-    Args:
-        center (np.ndarray[2, ]): Center of the bounding box (x, y).
-        scale (np.ndarray[2, ]): Scale of the bounding box
-            wrt [width, height].
-        rot (float): Rotation angle (degree).
-        output_size (np.ndarray[2, ]): Size of the destination heatmaps.
-        shift (0-100%): Shift translation ratio wrt the width/height.
-            Default (0., 0.).
-        inv (bool): Option to inverse the affine transform direction.
-            (inv=False: src->dst or inv=True: dst->src)
-    Returns:
-        np.ndarray: The transform matrix.
-    """
-    assert len(center) == 2
-    assert len(output_size) == 2
-    assert len(shift) == 2
-    if not isinstance(input_size, (np.ndarray, list)):
-        input_size = np.array([input_size, input_size], dtype=np.float32)
-    scale_tmp = input_size
-
-    shift = np.array(shift)
-    src_w = scale_tmp[0]
-    dst_w = output_size[0]
-    dst_h = output_size[1]
-
-    rot_rad = np.pi * rot / 180
-    src_dir = rotate_point([0.0, src_w * -0.5], rot_rad)
-    dst_dir = np.array([0.0, dst_w * -0.5])
-
-    src = np.zeros((3, 2), dtype=np.float32)
-    src[0, :] = center + scale_tmp * shift
-    src[1, :] = center + src_dir + scale_tmp * shift
-    src[2, :] = _get_3rd_point(src[0, :], src[1, :])
-
-    dst = np.zeros((3, 2), dtype=np.float32)
-    dst[0, :] = [dst_w * 0.5, dst_h * 0.5]
-    dst[1, :] = np.array([dst_w * 0.5, dst_h * 0.5]) + dst_dir
-    dst[2, :] = _get_3rd_point(dst[0, :], dst[1, :])
-
-    if inv:
-        trans = cv2.getAffineTransform(np.float32(dst), np.float32(src))
-    else:
-        trans = cv2.getAffineTransform(np.float32(src), np.float32(dst))
-
-    return trans
-
-
-class WarpAffine(BaseComponent):
-    """Warp affine the image"""
-
-    INPUT_KEYS = ["img"]
-    OUTPUT_KEYS = ["img", "img_size", "scale_factors"]
-    DEAULT_INPUTS = {"img": "img"}
-    DEAULT_OUTPUTS = {
-        "img": "img",
-        "img_size": "img_size",
-        "scale_factors": "scale_factors",
-    }
-
-    def __init__(
-        self,
-        keep_res=False,
-        pad=31,
-        input_h=512,
-        input_w=512,
-        scale=0.4,
-        shift=0.1,
-        down_ratio=4,
-    ):
-        super().__init__()
-        self.keep_res = keep_res
-        self.pad = pad
-        self.input_h = input_h
-        self.input_w = input_w
-        self.scale = scale
-        self.shift = shift
-        self.down_ratio = down_ratio
-
-    def apply(self, img):
-
-        img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
-
-        h, w = img.shape[:2]
-
-        if self.keep_res:
-            # True in detection eval/infer
-            input_h = (h | self.pad) + 1
-            input_w = (w | self.pad) + 1
-            s = np.array([input_w, input_h], dtype=np.float32)
-            c = np.array([w // 2, h // 2], dtype=np.float32)
-
-        else:
-            # False in centertrack eval_mot/eval_mot
-            s = max(h, w) * 1.0
-            input_h, input_w = self.input_h, self.input_w
-            c = np.array([w / 2.0, h / 2.0], dtype=np.float32)
-
-        trans_input = get_affine_transform(c, s, 0, [input_w, input_h])
-        img = cv2.resize(img, (w, h))
-        inp = cv2.warpAffine(
-            img, trans_input, (input_w, input_h), flags=cv2.INTER_LINEAR
-        )
-
-        if not self.keep_res:
-            out_h = input_h // self.down_ratio
-            out_w = input_w // self.down_ratio
-            trans_output = get_affine_transform(c, s, 0, [out_w, out_h])
-
-        im_scale_w, im_scale_h = [input_w / w, input_h / h]
-
-        return {
-            "img": inp,
-            "img_size": [inp.shape[1], inp.shape[0]],
-            "scale_factors": [im_scale_w, im_scale_h],
-        }
-
-
-def compute_iou(box1, box2):
-    x1 = max(box1[0], box2[0])
-    y1 = max(box1[1], box2[1])
-    x2 = min(box1[2], box2[2])
-    y2 = min(box1[3], box2[3])
-    inter_area = max(0, x2 - x1 + 1) * max(0, y2 - y1 + 1)
-    box1_area = (box1[2] - box1[0] + 1) * (box1[3] - box1[1] + 1)
-    box2_area = (box2[2] - box2[0] + 1) * (box2[3] - box2[1] + 1)
-    iou = inter_area / float(box1_area + box2_area - inter_area)
-    return iou
-
-
-def is_box_mostly_inside(inner_box, outer_box, threshold=0.9):
-    x1 = max(inner_box[0], outer_box[0])
-    y1 = max(inner_box[1], outer_box[1])
-    x2 = min(inner_box[2], outer_box[2])
-    y2 = min(inner_box[3], outer_box[3])
-    inter_area = max(0, x2 - x1 + 1) * max(0, y2 - y1 + 1)
-    inner_box_area = (inner_box[2] - inner_box[0] + 1) * (inner_box[3] - inner_box[1] + 1)
-    return (inter_area / inner_box_area) >= threshold
-
-
-def non_max_suppression(boxes, scores, iou_threshold):
-    if len(boxes) == 0:
-        return []
-    x1 = boxes[:, 0]
-    y1 = boxes[:, 1]
-    x2 = boxes[:, 2]
-    y2 = boxes[:, 3]
-    areas = (x2 - x1 + 1) * (y2 - y1 + 1)
-    order = scores.argsort()[::-1]
-    keep = []
-    while order.size > 0:
-        i = order[0]
-        keep.append(i)
-        xx1 = np.maximum(x1[i], x1[order[1:]])
-        yy1 = np.maximum(y1[i], y1[order[1:]])
-        xx2 = np.minimum(x2[i], x2[order[1:]])
-        yy2 = np.minimum(y2[i], y2[order[1:]])
-
-        w = np.maximum(0.0, xx2 - xx1 + 1)
-        h = np.maximum(0.0, yy2 - yy1 + 1)
-        inter = w * h
-        iou = inter / (areas[i] + areas[order[1:]] - inter)
-        inds = np.where(iou <= iou_threshold)[0]
-        order = order[inds + 1]
-    return keep
-
-
-class DetPostProcess(BaseComponent):
-    """Save Result Transform"""
-
-    INPUT_KEYS = ["input_path", "boxes", "img_size"]
-    OUTPUT_KEYS = ["boxes"]
-    DEAULT_INPUTS = {"boxes": "boxes", "img_size": "ori_img_size"}
-    DEAULT_OUTPUTS = {"boxes": "boxes"}
-
-    def __init__(self, threshold=0.5, labels=None, layout_postprocess=False):
-        super().__init__()
-        self.threshold = threshold
-        self.labels = labels
-        self.layout_postprocess = layout_postprocess
-
-    def apply(self, boxes, img_size):
-        """apply"""
-        if isinstance(self.threshold, float):
-            expect_boxes = (boxes[:, 1] > self.threshold) & (boxes[:, 0] > -1)
-            boxes = boxes[expect_boxes, :]
-        elif isinstance(self.threshold, dict):
-            category_filtered_boxes = []
-            for cat_id in np.unique(boxes[:, 0]):
-                category_boxes = boxes[boxes[:, 0] == cat_id]
-                category_scores = category_boxes[:, 1]
-                category_threshold = self.threshold.get(int(cat_id), 0.5)
-                selected_indices = category_scores > category_threshold
-                category_filtered_boxes.append(category_boxes[selected_indices])
-            boxes = np.vstack(category_filtered_boxes) if category_filtered_boxes else np.array([])
-
-        if self.layout_postprocess:
-            filtered_boxes = []
-            ### Layout postprocess for NMS
-            for cat_id in np.unique(boxes[:, 0]):
-                category_boxes = boxes[boxes[:, 0] == cat_id]
-                category_scores = category_boxes[:, 1]
-                if len(category_boxes) > 0:
-                    nms_indices = non_max_suppression(category_boxes[:, 2:], category_scores, 0.5)
-                    category_boxes = category_boxes[nms_indices]
-                    keep_boxes = []
-                    for i, box in enumerate(category_boxes):
-                        if all(not is_box_mostly_inside(box[2:], other_box[2:]) for j, other_box in enumerate(category_boxes) if i != j):
-                            keep_boxes.append(box)
-                    filtered_boxes.extend(keep_boxes)
-            boxes = np.array(filtered_boxes)
-            ### Layout postprocess for removing boxes inside image category box
-            if self.labels and "image" in self.labels:
-                image_cls_id = self.labels.index('image')
-                if len(boxes) > 0:
-                    image_boxes = boxes[boxes[:, 0] == image_cls_id]
-                    other_boxes = boxes[boxes[:, 0] != image_cls_id]
-                    to_keep = []
-                    for box in other_boxes:
-                        keep = True
-                        for img_box in image_boxes:
-                            if (box[2] >= img_box[2] and box[3] >= img_box[3] and
-                                box[4] <= img_box[4] and box[5] <= img_box[5]):
-                                keep = False
-                                break
-                        if keep:
-                            to_keep.append(box)
-                    boxes = np.vstack([image_boxes, to_keep]) if to_keep else image_boxes
-            ### Layout postprocess for overlaps
-            final_boxes = []
-            while len(boxes) > 0:
-                current_box = boxes[0]
-                current_score = current_box[1]
-                overlaps = [current_box]
-                non_overlaps = []
-                for other_box in boxes[1:]:
-                    iou = compute_iou(current_box[2:], other_box[2:])
-                    if iou > 0.95:
-                        if other_box[1] > current_score:
-                            overlaps.append(other_box)
-                    else:
-                        non_overlaps.append(other_box)
-                best_box = max(overlaps, key=lambda x: x[1])
-                final_boxes.append(best_box)
-                boxes = np.array(non_overlaps)
-            boxes = np.array(final_boxes)
-
-        if boxes.shape[1] == 6:
-            """For Normal Object Detection"""
-            boxes = restructured_boxes(boxes, self.labels, img_size)
-        elif boxes.shape[1] == 10:
-            """Adapt For Rotated Object Detection"""
-            boxes = restructured_rotated_boxes(boxes, self.labels, img_size)
-        else:
-            """Unexpected Input Box Shape"""
-            raise ValueError(
-                f"The shape of boxes should be 6 or 10, instead of {boxes.shape[1]}"
-            )
-        result = {"boxes": boxes}
-        return result
-
-
-class CropByBoxes(BaseComponent):
-    """Crop Image by Box"""
-
-    YIELD_BATCH = False
-    INPUT_KEYS = ["input_path", "boxes"]
-    OUTPUT_KEYS = ["img", "box", "label"]
-    DEAULT_INPUTS = {"input_path": "input_path", "boxes": "boxes"}
-    DEAULT_OUTPUTS = {"img": "img", "box": "box", "label": "label"}
-
-    def __init__(self):
-        super().__init__()
-        self._reader = ImageReader(backend="opencv")
-
-    def apply(self, input_path, boxes):
-        output_list = []
-        img = self._reader.read(input_path)
-        for bbox in boxes:
-            label_id = bbox["cls_id"]
-            box = bbox["coordinate"]
-            label = bbox.get("label", label_id)
-            xmin, ymin, xmax, ymax = [int(i) for i in box]
-            img_crop = img[ymin:ymax, xmin:xmax]
-            output_list.append({"img": img_crop, "box": box, "label": label})
-
-        return output_list
-
-
-class DetPad(BaseComponent):
-
-    INPUT_KEYS = "img"
-    OUTPUT_KEYS = "img"
-    DEAULT_INPUTS = {"img": "img"}
-    DEAULT_OUTPUTS = {"img": "img"}
-
-    def __init__(self, size, fill_value=[114.0, 114.0, 114.0]):
-        """
-        Pad image to a specified size.
-        Args:
-            size (list[int]): image target size
-            fill_value (list[float]): rgb value of pad area, default (114.0, 114.0, 114.0)
-        """
-
-        super().__init__()
-        if isinstance(size, int):
-            size = [size, size]
-        self.size = size
-        self.fill_value = fill_value
-
-    def apply(self, img):
-        im = img
-        im_h, im_w = im.shape[:2]
-        h, w = self.size
-        if h == im_h and w == im_w:
-            return {"img": im}
-
-        canvas = np.ones((h, w, 3), dtype=np.float32)
-        canvas *= np.array(self.fill_value, dtype=np.float32)
-        canvas[0:im_h, 0:im_w, :] = im.astype(np.float32)
-        return {"img": canvas}

+ 0 - 89
paddlex/inference/components/task_related/instance_seg.py

@@ -1,89 +0,0 @@
-# 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
-
-import numpy as np
-from ....utils import logging
-from ..base import BaseComponent
-from .det import restructured_boxes
-
-
-import cv2
-import numpy as np
-
-
-def extract_masks_from_boxes(boxes, masks):
-    """
-    Extracts the portion of each mask that is within the corresponding box.
-    """
-    new_masks = []
-
-    for i, box in enumerate(boxes):
-        x_min, y_min, x_max, y_max = box["coordinate"]
-        x_min, y_min, x_max, y_max = map(
-            lambda x: int(round(x)), [x_min, y_min, x_max, y_max]
-        )
-
-        cropped_mask = masks[i][y_min:y_max, x_min:x_max]
-        new_masks.append(cropped_mask)
-
-    return new_masks
-
-
-class InstanceSegPostProcess(BaseComponent):
-    """Save Result Transform"""
-
-    INPUT_KEYS = [["boxes", "masks", "img_size"], ["class_id", "masks", "img_size"]]
-    OUTPUT_KEYS = ["img_path", "boxes", "masks"]
-    DEAULT_INPUTS = {"boxes": "boxes", "masks": "masks", "img_size": "ori_img_size"}
-    DEAULT_OUTPUTS = {
-        "boxes": "boxes",
-        "masks": "masks",
-    }
-
-    def __init__(self, threshold=0.5, labels=None):
-        super().__init__()
-        self.threshold = threshold
-        self.labels = labels
-
-    def apply(self, masks, img_size, boxes=None, class_id=None):
-        """apply"""
-        if boxes is not None:
-            expect_boxes = (boxes[:, 1] > self.threshold) & (boxes[:, 0] > -1)
-            boxes = boxes[expect_boxes, :]
-            boxes = restructured_boxes(boxes, self.labels, img_size)
-            masks = masks[expect_boxes, :, :]
-            masks = extract_masks_from_boxes(boxes, masks)
-            result = {"boxes": boxes, "masks": masks}
-        else:
-            mask_info = []
-            class_id = [list(item) for item in zip(class_id[0], class_id[1])]
-
-            selected_masks = []
-            for i, info in enumerate(class_id):
-                label_id = int(info[0])
-                if info[1] < self.threshold:
-                    continue
-                mask_info.append(
-                    {
-                        "label": self.labels[label_id],
-                        "score": info[1],
-                        "class_id": label_id,
-                    }
-                )
-                selected_masks.append(masks[i])
-            result = {"boxes": mask_info, "masks": selected_masks}
-
-        return result

+ 0 - 940
paddlex/inference/components/task_related/seal_det_warp.py

@@ -1,940 +0,0 @@
-# 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
-import cv2
-import copy
-import time
-
-from ....utils import logging
-
-
-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))
-    expand_y = int(0.5 * world_height * (ratio_height - 1))
-
-    pt_lefttop = [expand_x, expand_y]
-    pt_righttop = [expand_x + world_width, expand_y]
-    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])
-
-    img_crop_width = int(world_width * ratio_width)
-    img_crop_height = int(world_height * ratio_height)
-
-    M = cv2.getPerspectiveTransform(_points, pts_std)
-
-    dst_img = cv2.warpPerspective(
-        image,
-        M,
-        (img_crop_width, img_crop_height),
-        borderMode=cv2.BORDER_CONSTANT,  # BORDER_CONSTANT BORDER_REPLICATE
-        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
-        vcam_thy = 180
-        vcam_thx2 = 0
-
-        vcam_x = 0
-        vcam_y = 0
-        vcam_z = 100
-
-        radian = np.pi / 180
-
-        angle_z = radian * vcam_thz
-        angle_x1 = radian * vcam_thx1
-        angle_y = radian * vcam_thy
-        angle_x2 = radian * vcam_thx2
-
-        optic_x = vcam_x
-        optic_y = vcam_y
-        optic_z = vcam_z
-
-        fu = 100
-        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, 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)
-        )
-        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)
-        )
-        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, 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)
-        )
-        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)
-        )
-        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, 3] = optic_z
-        matT[3, 0] = 0
-        matT[3, 1] = 0
-        matT[3, 2] = 0
-        matT[3, 3] = 1
-
-        matS = np.zeros((4, 4))
-        matS[2, 3] = 0.5
-        matS[3, 2] = 0.5
-
-        self.ifu = 1 / fu
-        self.ifv = 1 / fv
-
-        self.matT = matT
-        self.matS = matS
-        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
-        :param points:
-        :param org_size:
-        :return:
-        """
-        org_w, org_h = org_size
-        _points = np.array(points).reshape(-1).tolist()
-        _points = np.array(_points[2:] + _points[:2]).reshape(-1, 2)
-
-        # convert to horizontal points
-        adjusted_points = np.zeros(_points.shape, dtype=np.float32)
-        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
-        )
-
-        # # convert to vertical points back
-        image_coord = _points.reshape(1, -1, 2)
-        world_coord = np.zeros(_world_coord.shape, dtype=np.float32)
-        world_coord[:, :, 0] = 0 - _world_coord[:, :, 1]
-        world_coord[:, :, 1] = _world_coord[:, :, 0]
-        world_coord[:, :, 2] = _world_coord[:, :, 2]
-        new_image_size = (_new_image_size[1], _new_image_size[0])
-
-        return image_coord, world_coord, new_image_size
-
-    def horizontal_text_process(self, points):
-        """
-        get image coordinate and world coordinate
-        :param points:
-        :return:
-        """
-        poly = np.array(points).reshape(-1)
-
-        dx_list = []
-        dy_list = []
-        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)
-            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)
-            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)
-        ]
-
-        height = np.around(np.mean(dy_list))
-
-        rect_coord = [0, 0]
-        for i in range(0, len(poly) // 4 - 1):
-            x = rect_coord[-2]
-            x += dx_list[i]
-            y = 0
-            rect_coord.append(x)
-            rect_coord.append(y)
-
-        rect_coord_half = copy.deepcopy(rect_coord)
-        for i in range(0, len(poly) // 4):
-            x = rect_coord_half[len(rect_coord_half) - 2 * i - 2]
-            y = height
-            rect_coord.append(x)
-            rect_coord.append(y)
-
-        np_rect_coord = np.array(rect_coord).reshape(-1, 2)
-        x_min = np.min(np_rect_coord[:, 0])
-        y_min = np.min(np_rect_coord[:, 1])
-        x_max = np.max(np_rect_coord[:, 0])
-        y_max = np.max(np_rect_coord[:, 1])
-        new_image_size = (int(x_max - x_min + 0.5), int(y_max - y_min + 0.5))
-        x_mean = (x_max - x_min) / 2
-        y_mean = (y_max - y_min) / 2
-        np_rect_coord[:, 0] -= x_mean
-        np_rect_coord[:, 1] -= y_mean
-        rect_coord = np_rect_coord.reshape(-1).tolist()
-
-        rect_coord = np.array(rect_coord).reshape(-1, 2)
-        world_coord = np.ones((len(rect_coord), 3)) * 0
-
-        world_coord[:, :2] = rect_coord
-
-        image_coord = np.array(poly).reshape(1, -1, 2)
-        world_coord = world_coord.reshape(1, -1, 3)
-
-        return image_coord, world_coord, new_image_size
-
-    def horizontal_text_estimate(self, points):
-        """
-        horizontal or vertical text
-        :param points:
-        :return:
-        """
-        pts = np.array(points).reshape(-1, 2)
-        x_min = int(np.min(pts[:, 0]))
-        y_min = int(np.min(pts[:, 1]))
-        x_max = int(np.max(pts[:, 0]))
-        y_max = int(np.max(pts[:, 1]))
-        x = x_max - x_min
-        y = y_max - y_min
-        is_horizontal_text = True
-        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
-
-        ppu = size[0] / 2 + 1e-6
-        ppv = size[1] / 2 + 1e-6
-
-        P = np.zeros((size[1], size[0], 3))
-
-        lu = np.array([i for i in range(size[0])])
-        lv = np.array([i for i in range(size[1])])
-        u, v = np.meshgrid(lu, lv)
-
-        yp = (v - ppv) * ifv
-        xp = (u - ppu) * ifu
-        angle_a = arctan(sqrt(xp * xp + yp * yp))
-        angle_b = arctan(yp / xp)
-
-        D0 = sin(angle_a) * cos(angle_b)
-        D1 = sin(angle_a) * sin(angle_b)
-        D2 = cos(angle_a)
-
-        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_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[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
-        )
-
-        return P
-
-    def world_to_image(self, image_size, world, intrinsic, distCoeffs, rotation, tvec):
-        r11 = rotation[0, 0]
-        r12 = rotation[0, 1]
-        r13 = rotation[0, 2]
-        r21 = rotation[1, 0]
-        r22 = rotation[1, 1]
-        r23 = rotation[1, 2]
-        r31 = rotation[2, 0]
-        r32 = rotation[2, 1]
-        r33 = rotation[2, 2]
-
-        t1 = tvec[0]
-        t2 = tvec[1]
-        t3 = tvec[2]
-
-        k1 = distCoeffs[0]
-        k2 = distCoeffs[1]
-        p1 = distCoeffs[2]
-        p2 = distCoeffs[3]
-        k3 = distCoeffs[4]
-        k4 = distCoeffs[5]
-        k5 = distCoeffs[6]
-        k6 = distCoeffs[7]
-
-        if len(distCoeffs) > 8:
-            s1 = distCoeffs[8]
-            s2 = distCoeffs[9]
-            s3 = distCoeffs[10]
-            s4 = distCoeffs[11]
-        else:
-            s1 = s2 = s3 = s4 = 0
-
-        if len(distCoeffs) > 12:
-            tx = distCoeffs[12]
-            ty = distCoeffs[13]
-        else:
-            tx = ty = 0
-
-        fu = intrinsic[0, 0]
-        fv = intrinsic[1, 1]
-        ppu = intrinsic[0, 2]
-        ppv = intrinsic[1, 2]
-
-        cos_tx = cos(tx)
-        cos_ty = cos(ty)
-        sin_tx = sin(tx)
-        sin_ty = sin(ty)
-
-        tao11 = cos_ty * cos_tx * cos_ty + sin_ty * cos_tx * sin_ty
-        tao12 = cos_ty * cos_tx * sin_ty * sin_tx - sin_ty * cos_tx * cos_ty * sin_tx
-        tao13 = -cos_ty * cos_tx * sin_ty * cos_tx + sin_ty * cos_tx * cos_ty * cos_tx
-        tao21 = -sin_tx * sin_ty
-        tao22 = cos_ty * cos_tx * cos_tx + sin_tx * cos_ty * sin_tx
-        tao23 = cos_ty * cos_tx * sin_tx - sin_tx * cos_ty * cos_tx
-
-        P = np.zeros((image_size[1], image_size[0], 2))
-
-        c3 = r31 * world[:, :, 0] + r32 * world[:, :, 1] + r33 * world[:, :, 2] + t3
-        c1 = r11 * world[:, :, 0] + r12 * world[:, :, 1] + r13 * world[:, :, 2] + t1
-        c2 = r21 * world[:, :, 0] + r22 * world[:, :, 1] + r23 * world[:, :, 2] + t2
-
-        x1 = c1 / c3
-        y1 = c2 / c3
-        x12 = x1 * x1
-        y12 = y1 * y1
-        x1y1 = 2 * x1 * y1
-        r2 = x12 + y12
-        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
-        )
-
-        x3 = tao11 * x2 + tao12 * y2 + tao13
-        y3 = tao21 * x2 + tao22 * y2 + tao23
-
-        P[:, :, 0] = fu * x3 + ppu
-        P[:, :, 1] = fv * y3 + ppv
-        P[c3 <= 0] = 0
-
-        return P
-
-    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 = image_map.astype(np.float32)
-        dst = cv2.remap(
-            image_data, image_map[:, :, 0], image_map[:, :, 1], interpolation
-        )
-        return dst
-
-    def calibrate(self, org_size, image_coord, world_coord):
-        """
-        calibration
-        :param org_size:
-        :param image_coord:
-        :param world_coord:
-        :return:
-        """
-        # flag = cv2.CALIB_RATIONAL_MODEL | cv2.CALIB_TILTED_MODEL  | cv2.CALIB_THIN_PRISM_MODEL
-        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
-        )
-        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,
-        )
-        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,
-                )
-                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,
-    ):
-        """
-        divide and conquer: homography
-        # ratio_width and ratio_height must be 1.0 here
-        """
-        _img_points = img_points.reshape(-1, 2)
-        _obj_points = obj_points.reshape(-1, 3)
-
-        homo_img_list = []
-        width_list = []
-        height_list = []
-        # divide and conquer
-        for i in range(len(_img_points) // 2 - 1):
-            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_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])
-                world_height = np.abs(new_obj_points[3, 1] - new_obj_points[0, 1])
-            else:
-                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_list.append(homo_img)
-            _h, _w = homo_img.shape[:2]
-            width_list.append(_w)
-            height_list.append(_h)
-
-        # stitching
-        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
-            st += w
-
-        if not is_horizontal_text:
-            # vertical rotation
-            rectified_image = np.rot90(rectified_image, 3)
-
-        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,
-    ):
-        _points = np.array(img_points).reshape(-1, 2).astype(np.float32)
-
-        expand_x = int(0.5 * world_width * (ratio_width - 1))
-        expand_y = int(0.5 * world_height * (ratio_height - 1))
-
-        pt_lefttop = [expand_x, expand_y]
-        pt_righttop = [expand_x + world_width, expand_y]
-        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])
-
-        img_crop_width = int(world_width * ratio_width)
-        img_crop_height = int(world_height * ratio_height)
-
-        M = cv2.getPerspectiveTransform(_points, pts_std)
-
-        dst_img = cv2.warpPerspective(
-            image,
-            M,
-            (img_crop_width, img_crop_height),
-            borderMode=cv2.BORDER_CONSTANT,  # BORDER_CONSTANT BORDER_REPLICATE
-            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",
-    ):
-        """
-        spatial transform for a poly text
-        :param image_data:
-        :param points: [x1,y1,x2,y2,x3,y3,...], clockwise order, (x1,y1) must be the top-left of first char.
-        :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 mode: 'calibration' or 'homography'. when homography, ratio_width and ratio_height must be 1.0
-        :return:
-        """
-        org_h, org_w = image_data.shape[:2]
-        org_size = (org_w, org_h)
-        self.image = image_data
-
-        is_horizontal_text = self.horizontal_text_estimate(points)
-        if is_horizontal_text:
-            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":
-            # 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,
-            )
-        else:
-            raise ValueError(
-                'mode must be ["calibration", "homography"], but got {}'.format(mode)
-            )
-
-        return dst, ret
-
-
-class AutoRectifier:
-    def __init__(self):
-        self.npoints = 10
-        self.curveTextRectifier = CurveTextRectifier()
-
-    @staticmethod
-    def get_rotate_crop_image(
-        img, points, interpolation=cv2.INTER_CUBIC, ratio_width=1.0, ratio_height=1.0
-    ):
-        """
-        crop or homography
-        :param img:
-        :param points:
-        :param interpolation:
-        :param ratio_width:
-        :param ratio_height:
-        :return:
-        """
-        h, w = img.shape[:2]
-        _points = np.array(points).reshape(-1, 2).astype(np.float32)
-
-        if len(_points) != 4:
-            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
-            expand_x = int(0.5 * dx * (ratio_width - 1))
-            expand_y = int(0.5 * dy * (ratio_height - 1))
-            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)
-
-            dst_img = img[y_min:y_max, x_min:x_max, :].copy()
-        else:
-            img_crop_width = int(
-                max(
-                    np.linalg.norm(_points[0] - _points[1]),
-                    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,
-            )
-
-        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
-            )
-            for i, p in enumerate(box):
-                if i != 0:
-                    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,
-                    )
-        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",
-    ):
-        """
-        rectification in strategies for a poly text
-        :param image_data:
-        :param points: [x1,y1,x2,y2,x3,y3,...], clockwise order, (x1,y1) must be the top-left of first char.
-        :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 mode: 'calibration' or 'homography'. when homography, ratio_width and ratio_height must be 1.0
-        :return:
-        """
-        _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
-                )
-                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,
-                    )
-                    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,
-                    )
-                    img_list += [_dst_img]
-                    loss_list += [_loss]
-
-                    min_loss = min(loss_list)
-                    dst_img = img_list[loss_list.index(min_loss)]
-
-                    if min_loss >= loss_thresh:
-                        logging.warning(
-                            "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
-                        )
-            except Exception as e:
-                logging.warning(f"Exception caught: {e}")
-                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
-            )
-
-        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",
-    ):
-        """
-        run for texts in an image
-        :param image_data: numpy.ndarray. The shape is [h, w, 3]
-        :param points_list: [[x1,y1,x2,y2,x3,y3,...], [x1,y1,x2,y2,x3,y3,...], ...], clockwise order, (x1,y1) must be the top-left of first char.
-        :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 mode: 'calibration' or 'homography'. when homography, ratio_width and ratio_height must be 1.0
-        :return: res: roi-image list, visualized_image: draw polys in original image
-        """
-        if image_data is None:
-            raise ValueError
-        if not isinstance(points_list, list):
-            raise ValueError
-        for points in points_list:
-            if not isinstance(points, list):
-                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
-                )
-            )
-
-        res = []
-        for points in points_list:
-            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

+ 0 - 40
paddlex/inference/components/task_related/seg.py

@@ -1,40 +0,0 @@
-# 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 numpy as np
-from skimage import measure, morphology
-
-from ..base import BaseComponent
-
-
-class Map_to_mask(BaseComponent):
-    """Map_to_mask"""
-
-    INPUT_KEYS = "pred"
-    OUTPUT_KEYS = "pred"
-    DEAULT_INPUTS = {"pred": "pred"}
-    DEAULT_OUTPUTS = {"pred": "pred"}
-
-    def apply(self, pred):
-        """apply"""
-        score_map = pred[0]
-        thred = 0.01
-        mask = score_map[0]
-        mask[mask > thred] = 255
-        mask[mask <= thred] = 0
-        kernel = morphology.disk(4)
-        mask = morphology.opening(mask, kernel)
-        mask = mask.astype(np.uint8)
-
-        return {"pred": mask[None, :, :]}

+ 0 - 191
paddlex/inference/components/task_related/table_rec.py

@@ -1,191 +0,0 @@
-# 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 numpy as np
-
-from ..base import BaseComponent
-
-__all__ = ["TableLabelDecode"]
-
-
-class TableLabelDecode(BaseComponent):
-    """decode the table model outputs(probs) to character str"""
-
-    ENABLE_BATCH = True
-
-    INPUT_KEYS = ["pred", "img_size", "ori_img_size"]
-    OUTPUT_KEYS = ["bbox", "structure", "structure_score"]
-    DEAULT_INPUTS = {
-        "pred": "pred",
-        "img_size": "img_size",
-        "ori_img_size": "ori_img_size",
-    }
-    DEAULT_OUTPUTS = {
-        "bbox": "bbox",
-        "structure": "structure",
-        "structure_score": "structure_score",
-    }
-
-    def __init__(self, model_name, merge_no_span_structure=True, dict_character=[]):
-        super().__init__()
-
-        if merge_no_span_structure:
-            if "<td></td>" not in dict_character:
-                dict_character.append("<td></td>")
-            if "<td>" in dict_character:
-                dict_character.remove("<td>")
-
-        self.model_name = model_name
-
-        dict_character = self.add_special_char(dict_character)
-        self.dict = {}
-        for i, char in enumerate(dict_character):
-            self.dict[char] = i
-        self.character = dict_character
-        self.td_token = ["<td>", "<td", "<td></td>"]
-
-    def add_special_char(self, dict_character):
-        """add_special_char"""
-        self.beg_str = "sos"
-        self.end_str = "eos"
-        dict_character = dict_character
-        dict_character = [self.beg_str] + dict_character + [self.end_str]
-        return dict_character
-
-    def get_ignored_tokens(self):
-        """get_ignored_tokens"""
-        beg_idx = self.get_beg_end_flag_idx("beg")
-        end_idx = self.get_beg_end_flag_idx("end")
-        return [beg_idx, end_idx]
-
-    def get_beg_end_flag_idx(self, beg_or_end):
-        """get_beg_end_flag_idx"""
-        if beg_or_end == "beg":
-            idx = np.array(self.dict[self.beg_str])
-        elif beg_or_end == "end":
-            idx = np.array(self.dict[self.end_str])
-        else:
-            assert False, "unsupported type %s in get_beg_end_flag_idx" % beg_or_end
-        return idx
-
-    def apply(self, pred, img_size, ori_img_size):
-        """apply"""
-        bbox_preds, structure_probs = [], []
-        for bbox_pred, stru_prob in pred:
-            bbox_preds.append(bbox_pred)
-            structure_probs.append(stru_prob)
-        bbox_preds = np.array(bbox_preds)
-        structure_probs = np.array(structure_probs)
-
-        bbox_list, structure_str_list, structure_score = self.decode(
-            structure_probs, bbox_preds, img_size, ori_img_size
-        )
-        structure_str_list = [
-            (
-                ["<html>", "<body>", "<table>"]
-                + structure
-                + ["</table>", "</body>", "</html>"]
-            )
-            for structure in structure_str_list
-        ]
-        return [
-            {"bbox": bbox, "structure": structure, "structure_score": structure_score}
-            for bbox, structure in zip(bbox_list, structure_str_list)
-        ]
-
-    def decode(self, structure_probs, bbox_preds, padding_size, ori_img_size):
-        """convert text-label into text-index."""
-        ignored_tokens = self.get_ignored_tokens()
-        end_idx = self.dict[self.end_str]
-
-        structure_idx = structure_probs.argmax(axis=2)
-        structure_probs = structure_probs.max(axis=2)
-
-        structure_batch_list = []
-        bbox_batch_list = []
-        batch_size = len(structure_idx)
-        for batch_idx in range(batch_size):
-            structure_list = []
-            bbox_list = []
-            score_list = []
-            for idx in range(len(structure_idx[batch_idx])):
-                char_idx = int(structure_idx[batch_idx][idx])
-                if idx > 0 and char_idx == end_idx:
-                    break
-                if char_idx in ignored_tokens:
-                    continue
-                text = self.character[char_idx]
-                if text in self.td_token:
-                    bbox = bbox_preds[batch_idx, idx]
-                    bbox = self._bbox_decode(
-                        bbox, padding_size[batch_idx], ori_img_size[batch_idx]
-                    )
-                    bbox_list.append(bbox.astype(int))
-                structure_list.append(text)
-                score_list.append(structure_probs[batch_idx, idx])
-            structure_batch_list.append(structure_list)
-            structure_score = np.mean(score_list)
-            bbox_batch_list.append(bbox_list)
-
-        return bbox_batch_list, structure_batch_list, structure_score
-
-    def decode_label(self, batch):
-        """convert text-label into text-index."""
-        structure_idx = batch[1]
-        gt_bbox_list = batch[2]
-        shape_list = batch[-1]
-        ignored_tokens = self.get_ignored_tokens()
-        end_idx = self.dict[self.end_str]
-
-        structure_batch_list = []
-        bbox_batch_list = []
-        batch_size = len(structure_idx)
-        for batch_idx in range(batch_size):
-            structure_list = []
-            bbox_list = []
-            for idx in range(len(structure_idx[batch_idx])):
-                char_idx = int(structure_idx[batch_idx][idx])
-                if idx > 0 and char_idx == end_idx:
-                    break
-                if char_idx in ignored_tokens:
-                    continue
-                structure_list.append(self.character[char_idx])
-
-                bbox = gt_bbox_list[batch_idx][idx]
-                if bbox.sum() != 0:
-                    bbox = self._bbox_decode(bbox, shape_list[batch_idx])
-                    bbox_list.append(bbox.astype(int))
-            structure_batch_list.append(structure_list)
-            bbox_batch_list.append(bbox_list)
-        return bbox_batch_list, structure_batch_list
-
-    def _bbox_decode(self, bbox, padding_shape, ori_shape):
-
-        if self.model_name == "SLANet":
-            w, h = ori_shape
-            bbox[0::2] *= w
-            bbox[1::2] *= h
-        else:
-            w, h = padding_shape
-            ori_w, ori_h = ori_shape
-            ratio_w = w / ori_w
-            ratio_h = h / ori_h
-            ratio = min(ratio_w, ratio_h)
-
-            bbox[0::2] *= w
-            bbox[1::2] *= h
-            bbox[0::2] /= ratio
-            bbox[1::2] /= ratio
-
-        return bbox

+ 0 - 895
paddlex/inference/components/task_related/text_det.py

@@ -1,895 +0,0 @@
-# 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
-import sys
-import cv2
-import copy
-import math
-import pyclipper
-import numpy as np
-from numpy.linalg import norm
-from PIL import Image
-from shapely.geometry import Polygon
-
-from ...utils.io import ImageReader
-from ....utils import logging
-from ..base import BaseComponent
-from .seal_det_warp import AutoRectifier
-
-
-__all__ = ["DetResizeForTest", "NormalizeImage", "DBPostProcess", "CropByPolys"]
-
-
-class DetResizeForTest(BaseComponent):
-    """DetResizeForTest"""
-
-    INPUT_KEYS = ["img"]
-    OUTPUT_KEYS = ["img", "img_shape"]
-    DEAULT_INPUTS = {"img": "img"}
-    DEAULT_OUTPUTS = {"img": "img", "img_shape": "img_shape"}
-
-    def __init__(self, **kwargs):
-        super().__init__()
-        self.resize_type = 0
-        self.keep_ratio = False
-        if "image_shape" in kwargs:
-            self.image_shape = kwargs["image_shape"]
-            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:
-            self.resize_type = 2
-            self.resize_long = kwargs.get("resize_long", 960)
-        else:
-            self.limit_side_len = 736
-            self.limit_type = "min"
-
-    def apply(self, img):
-        """apply"""
-        src_h, src_w, _ = img.shape
-        if sum([src_h, src_w]) < 64:
-            img = self.image_padding(img)
-
-        if self.resize_type == 0:
-            # 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)
-        else:
-            # img, shape = self.resize_image_type1(img)
-            img, [ratio_h, ratio_w] = self.resize_image_type1(img)
-        return {"img": img, "img_shape": np.array([src_h, src_w, ratio_h, ratio_w])}
-
-    def image_padding(self, im, value=0):
-        """padding image"""
-        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
-
-    def resize_image_type1(self, img):
-        """resize the image"""
-        resize_h, resize_w = self.image_shape
-        ori_h, ori_w = img.shape[:2]  # (h, w, c)
-        if self.keep_ratio is True:
-            resize_w = ori_w * resize_h / ori_h
-            N = math.ceil(resize_w / 32)
-            resize_w = N * 32
-        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, np.array([ori_h, ori_w])
-        return img, [ratio_h, ratio_w]
-
-    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
-        h, w, c = img.shape
-
-        # limit the max side
-        if self.limit_type == "max":
-            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":
-            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":
-            ratio = float(limit_side_len) / max(h, w)
-        else:
-            raise Exception("not support limit type, image ")
-        resize_h = int(h * ratio)
-        resize_w = int(w * ratio)
-
-        resize_h = max(int(round(resize_h / 32) * 32), 32)
-        resize_w = max(int(round(resize_w / 32) * 32), 32)
-
-        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:
-            logging.info(img.shape, resize_w, resize_h)
-            sys.exit(0)
-        ratio_h = resize_h / float(h)
-        ratio_w = resize_w / float(w)
-        return img, [ratio_h, ratio_w]
-
-    def resize_image_type2(self, img):
-        """resize image size"""
-        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]
-
-
-class NormalizeImage(BaseComponent):
-    """normalize image such as substract mean, divide std"""
-
-    INPUT_KEYS = ["img"]
-    OUTPUT_KEYS = ["img"]
-    DEAULT_INPUTS = {"img": "img"}
-    DEAULT_OUTPUTS = {"img": "img"}
-
-    def __init__(self, scale=None, mean=None, std=None, order="chw", **kwargs):
-        super().__init__()
-        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")
-
-    def apply(self, img):
-        """apply"""
-        from PIL import Image
-
-        if isinstance(img, Image.Image):
-            img = np.array(img)
-        assert isinstance(img, np.ndarray), "invalid input 'img' in NormalizeImage"
-        img = (img.astype("float32") * self.scale - self.mean) / self.std
-        return {"img": img}
-
-
-class DBPostProcess(BaseComponent):
-    """
-    The post process for Differentiable Binarization (DB).
-    """
-
-    INPUT_KEYS = ["pred", "img_shape"]
-    OUTPUT_KEYS = ["dt_polys", "dt_scores"]
-    DEAULT_INPUTS = {"pred": "pred", "img_shape": "img_shape"}
-    DEAULT_OUTPUTS = {"dt_polys": "dt_polys", "dt_scores": "dt_scores"}
-
-    def __init__(
-        self,
-        thresh=0.3,
-        box_thresh=0.7,
-        max_candidates=1000,
-        unclip_ratio=2.0,
-        use_dilation=False,
-        score_mode="fast",
-        box_type="quad",
-        **kwargs
-    ):
-        super().__init__()
-        self.thresh = thresh
-        self.box_thresh = box_thresh
-        self.max_candidates = max_candidates
-        self.unclip_ratio = unclip_ratio
-        self.min_size = 3
-        self.score_mode = score_mode
-        self.box_type = box_type
-        assert score_mode in [
-            "slow",
-            "fast",
-        ], "Score mode must be in [slow, fast] but got: {}".format(score_mode)
-
-        self.dilation_kernel = None if not use_dilation else np.array([[1, 1], [1, 1]])
-
-    def polygons_from_bitmap(self, pred, _bitmap, dest_width, dest_height):
-        """_bitmap: single map with shape (1, H, W), whose values are binarized as {0, 1}"""
-
-        bitmap = _bitmap
-        height, width = bitmap.shape
-
-        boxes = []
-        scores = []
-
-        contours, _ = cv2.findContours(
-            (bitmap * 255).astype(np.uint8), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE
-        )
-
-        for contour in contours[: self.max_candidates]:
-            epsilon = 0.002 * cv2.arcLength(contour, True)
-            approx = cv2.approxPolyDP(contour, epsilon, True)
-            points = approx.reshape((-1, 2))
-            if points.shape[0] < 4:
-                continue
-
-            score = self.box_score_fast(pred, points.reshape(-1, 2))
-            if self.box_thresh > score:
-                continue
-
-            if points.shape[0] > 2:
-                box = self.unclip(points, self.unclip_ratio)
-                if len(box) > 1:
-                    continue
-            else:
-                continue
-            box = box.reshape(-1, 2)
-
-            if len(box) > 0:
-                _, sside = self.get_mini_boxes(box.reshape((-1, 1, 2)))
-                if sside < self.min_size + 2:
-                    continue
-            else:
-                continue
-
-            box = np.array(box)
-            box[:, 0] = np.clip(np.round(box[:, 0] / width * dest_width), 0, dest_width)
-            box[:, 1] = np.clip(
-                np.round(box[:, 1] / height * dest_height), 0, dest_height
-            )
-            boxes.append(box)
-            scores.append(score)
-        return boxes, scores
-
-    def boxes_from_bitmap(self, pred, _bitmap, dest_width, dest_height):
-        """_bitmap: single map with shape (1, H, W), whose values are binarized as {0, 1}"""
-
-        bitmap = _bitmap
-        height, width = bitmap.shape
-
-        outs = cv2.findContours(
-            (bitmap * 255).astype(np.uint8), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE
-        )
-        if len(outs) == 3:
-            img, contours, _ = outs[0], outs[1], outs[2]
-        elif len(outs) == 2:
-            contours, _ = outs[0], outs[1]
-
-        num_contours = min(len(contours), self.max_candidates)
-
-        boxes = []
-        scores = []
-        for index in range(num_contours):
-            contour = contours[index]
-            points, sside = self.get_mini_boxes(contour)
-            if sside < self.min_size:
-                continue
-            points = np.array(points)
-            if self.score_mode == "fast":
-                score = self.box_score_fast(pred, points.reshape(-1, 2))
-            else:
-                score = self.box_score_slow(pred, contour)
-            if self.box_thresh > score:
-                continue
-
-            box = self.unclip(points, self.unclip_ratio).reshape(-1, 1, 2)
-            box, sside = self.get_mini_boxes(box)
-            if sside < self.min_size + 2:
-                continue
-            box = np.array(box)
-
-            box[:, 0] = np.clip(np.round(box[:, 0] / width * dest_width), 0, dest_width)
-            box[:, 1] = np.clip(
-                np.round(box[:, 1] / height * dest_height), 0, dest_height
-            )
-            boxes.append(box.astype(np.int16))
-            scores.append(score)
-        return np.array(boxes, dtype=np.int16), scores
-
-    def unclip(self, box, unclip_ratio):
-        """unclip"""
-        poly = Polygon(box)
-        distance = poly.area * unclip_ratio / poly.length
-        offset = pyclipper.PyclipperOffset()
-        offset.AddPath(box, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON)
-        try:
-            expanded = np.array(offset.Execute(distance))
-        except ValueError:
-            expanded = np.array(offset.Execute(distance)[0])
-        return expanded
-
-    def get_mini_boxes(self, contour):
-        """get mini boxes"""
-        bounding_box = cv2.minAreaRect(contour)
-        points = sorted(list(cv2.boxPoints(bounding_box)), key=lambda x: x[0])
-
-        index_1, index_2, index_3, index_4 = 0, 1, 2, 3
-        if points[1][1] > points[0][1]:
-            index_1 = 0
-            index_4 = 1
-        else:
-            index_1 = 1
-            index_4 = 0
-        if points[3][1] > points[2][1]:
-            index_2 = 2
-            index_3 = 3
-        else:
-            index_2 = 3
-            index_3 = 2
-
-        box = [points[index_1], points[index_2], points[index_3], points[index_4]]
-        return box, min(bounding_box[1])
-
-    def box_score_fast(self, bitmap, _box):
-        """box_score_fast: use bbox mean score as the mean score"""
-        h, w = bitmap.shape[:2]
-        box = _box.copy()
-        xmin = np.clip(np.floor(box[:, 0].min()).astype("int"), 0, w - 1)
-        xmax = np.clip(np.ceil(box[:, 0].max()).astype("int"), 0, w - 1)
-        ymin = np.clip(np.floor(box[:, 1].min()).astype("int"), 0, h - 1)
-        ymax = np.clip(np.ceil(box[:, 1].max()).astype("int"), 0, h - 1)
-
-        mask = np.zeros((ymax - ymin + 1, xmax - xmin + 1), dtype=np.uint8)
-        box[:, 0] = box[:, 0] - xmin
-        box[:, 1] = box[:, 1] - ymin
-        cv2.fillPoly(mask, box.reshape(1, -1, 2).astype(np.int32), 1)
-        return cv2.mean(bitmap[ymin : ymax + 1, xmin : xmax + 1], mask)[0]
-
-    def box_score_slow(self, bitmap, contour):
-        """box_score_slow: use polyon mean score as the mean score"""
-        h, w = bitmap.shape[:2]
-        contour = contour.copy()
-        contour = np.reshape(contour, (-1, 2))
-
-        xmin = np.clip(np.min(contour[:, 0]), 0, w - 1)
-        xmax = np.clip(np.max(contour[:, 0]), 0, w - 1)
-        ymin = np.clip(np.min(contour[:, 1]), 0, h - 1)
-        ymax = np.clip(np.max(contour[:, 1]), 0, h - 1)
-
-        mask = np.zeros((ymax - ymin + 1, xmax - xmin + 1), dtype=np.uint8)
-
-        contour[:, 0] = contour[:, 0] - xmin
-        contour[:, 1] = contour[:, 1] - ymin
-
-        cv2.fillPoly(mask, contour.reshape(1, -1, 2).astype(np.int32), 1)
-        return cv2.mean(bitmap[ymin : ymax + 1, xmin : xmax + 1], mask)[0]
-
-    def apply(self, pred, img_shape):
-        """apply"""
-        pred = pred[0][0, :, :]
-        segmentation = pred > self.thresh
-
-        src_h, src_w, ratio_h, ratio_w = img_shape
-        if self.dilation_kernel is not None:
-            mask = cv2.dilate(
-                np.array(segmentation).astype(np.uint8),
-                self.dilation_kernel,
-            )
-        else:
-            mask = segmentation
-        if self.box_type == "poly":
-            boxes, scores = self.polygons_from_bitmap(pred, mask, src_w, src_h)
-        elif self.box_type == "quad":
-            boxes, scores = self.boxes_from_bitmap(pred, mask, src_w, src_h)
-        else:
-            raise ValueError("box_type can only be one of ['quad', 'poly']")
-
-        return {"dt_polys": boxes, "dt_scores": scores}
-
-
-class CropByPolys(BaseComponent):
-    """Crop Image by Polys"""
-
-    INPUT_KEYS = ["input_path", "dt_polys"]
-    OUTPUT_KEYS = ["img"]
-    DEAULT_INPUTS = {"input_path": "input_path", "dt_polys": "dt_polys"}
-    DEAULT_OUTPUTS = {"img": "img"}
-
-    def __init__(self, det_box_type="quad"):
-        super().__init__()
-        self.det_box_type = det_box_type
-        self._reader = ImageReader(backend="opencv")
-
-    def apply(self, input_path, dt_polys):
-        """apply"""
-        img = self._reader.read(input_path)
-
-        if self.det_box_type == "quad":
-            dt_boxes = np.array(dt_polys)
-            output_list = []
-            for bno in range(len(dt_boxes)):
-                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]],
-                    }
-                )
-        elif self.det_box_type == "poly":
-            output_list = []
-            dt_boxes = dt_polys
-            for bno in range(len(dt_boxes)):
-                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]],
-                    }
-                )
-        else:
-            raise NotImplementedError
-
-        return output_list
-
-    def get_minarea_rect_crop(self, img, points):
-        """get_minarea_rect_crop"""
-        bounding_box = cv2.minAreaRect(np.array(points).astype(np.int32))
-        points = sorted(list(cv2.boxPoints(bounding_box)), key=lambda x: x[0])
-
-        index_a, index_b, index_c, index_d = 0, 1, 2, 3
-        if points[1][1] > points[0][1]:
-            index_a = 0
-            index_d = 1
-        else:
-            index_a = 1
-            index_d = 0
-        if points[3][1] > points[2][1]:
-            index_b = 2
-            index_c = 3
-        else:
-            index_b = 3
-            index_c = 2
-
-        box = [points[index_a], points[index_b], points[index_c], points[index_d]]
-        crop_img = self.get_rotate_crop_image(img, np.array(box))
-        return crop_img
-
-    def get_rotate_crop_image(self, img, points):
-        """
-        img_height, img_width = img.shape[0:2]
-        left = int(np.min(points[:, 0]))
-        right = int(np.max(points[:, 0]))
-        top = int(np.min(points[:, 1]))
-        bottom = int(np.max(points[:, 1]))
-        img_crop = img[top:bottom, left:right, :].copy()
-        points[:, 0] = points[:, 0] - left
-        points[:, 1] = points[:, 1] - top
-        """
-        assert len(points) == 4, "shape of points must be 4*2"
-        img_crop_width = int(
-            max(
-                np.linalg.norm(points[0] - points[1]),
-                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]),
-            )
-        )
-        pts_std = np.float32(
-            [
-                [0, 0],
-                [img_crop_width, 0],
-                [img_crop_width, img_crop_height],
-                [0, img_crop_height],
-            ]
-        )
-        M = cv2.getPerspectiveTransform(points, pts_std)
-        dst_img = cv2.warpPerspective(
-            img,
-            M,
-            (img_crop_width, img_crop_height),
-            borderMode=cv2.BORDER_REPLICATE,
-            flags=cv2.INTER_CUBIC,
-        )
-        dst_img_height, dst_img_width = dst_img.shape[0:2]
-        if dst_img_height * 1.0 / dst_img_width >= 1.5:
-            dst_img = np.rot90(dst_img)
-        return dst_img
-
-    def reorder_poly_edge(self, points):
-        """Get the respective points composing head edge, tail edge, top
-        sideline and bottom sideline.
-
-        Args:
-            points (ndarray): The points composing a text polygon.
-
-        Returns:
-            head_edge (ndarray): The two points composing the head edge of text
-                polygon.
-            tail_edge (ndarray): The two points composing the tail edge of text
-                polygon.
-            top_sideline (ndarray): The points composing top curved sideline of
-                text polygon.
-            bot_sideline (ndarray): The points composing bottom curved sideline
-                of text polygon.
-        """
-
-        assert points.ndim == 2
-        assert points.shape[0] >= 4
-        assert points.shape[1] == 2
-
-        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))]
-        return head_edge, tail_edge, sideline1, sideline2
-
-    def vector_slope(self, vec):
-        assert len(vec) == 2
-        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.
-
-        Args:
-            points (ndarray): The points composing a text polygon.
-            orientation_thr (float): The threshold for distinguishing between
-                head edge and tail edge among the horizontal and vertical edges
-                of a quadrangle.
-
-        Returns:
-            head_inds (list): The indexes of two points composing head edge.
-            tail_inds (list): The indexes of two points composing tail edge.
-        """
-
-        assert points.ndim == 2
-        assert points.shape[0] >= 4
-        assert points.shape[1] == 2
-        assert isinstance(orientation_thr, float)
-
-        if len(points) > 4:
-            pad_points = np.vstack([points, points[0]])
-            edge_vec = pad_points[1:] - pad_points[:-1]
-
-            theta_sum = []
-            adjacent_vec_theta = []
-            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]
-                )
-                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),
-            )
-            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
-            score += 0.35 * dist_score
-            if len(points) % 2 == 0:
-                position_score[(len(score) // 2 - 1)] += 1
-                position_score[-1] += 1
-            score += 0.1 * position_score
-            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.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
-                )
-
-            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)
-
-            if head_end > tail_end:
-                head_start, tail_start = tail_start, head_start
-                head_end, tail_end = tail_end, head_end
-            head_inds = [head_start, head_end]
-            tail_inds = [tail_start, tail_end]
-        else:
-            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]]
-            )
-
-            if vertical_len_sum > horizontal_len_sum * orientation_thr:
-                head_inds = horizontal_edge_inds[0]
-                tail_inds = horizontal_edge_inds[1]
-            else:
-                head_inds = vertical_edge_inds[0]
-                tail_inds = vertical_edge_inds[1]
-
-        return head_inds, tail_inds
-
-    def vector_angle(self, vec1, vec2):
-        if vec1.ndim > 1:
-            unit_vec1 = vec1 / (norm(vec1, axis=-1) + 1e-8).reshape((-1, 1))
-        else:
-            unit_vec1 = vec1 / (norm(vec1, axis=-1) + 1e-8)
-        if vec2.ndim > 1:
-            unit_vec2 = vec2 / (norm(vec2, axis=-1) + 1e-8).reshape((-1, 1))
-        else:
-            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])
-
-        index_a, index_b, index_c, index_d = 0, 1, 2, 3
-        if points[1][1] > points[0][1]:
-            index_a = 0
-            index_d = 1
-        else:
-            index_a = 1
-            index_d = 0
-        if points[3][1] > points[2][1]:
-            index_b = 2
-            index_c = 3
-        else:
-            index_b = 3
-            index_c = 2
-
-        box = [points[index_a], points[index_b], points[index_c], points[index_d]]
-        crop_img = self.get_rotate_crop_image(img, np.array(box))
-        return crop_img, box
-
-    def sample_points_on_bbox_bp(self, line, n=50):
-        """Resample n points on a line.
-
-        Args:
-            line (ndarray): The points composing a line.
-            n (int): The resampled points number.
-
-        Returns:
-            resampled_line (ndarray): The points composing the resampled line.
-        """
-        from numpy.linalg import norm
-
-        # 断言检查输入参数的有效性
-        assert line.ndim == 2
-        assert line.shape[0] >= 2
-        assert line.shape[1] == 2
-        assert isinstance(n, int)
-        assert n > 0
-
-        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)
-        current_edge_ind = 0
-        resampled_line = [line[0]]
-
-        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]
-            ):
-                current_edge_ind += 1
-            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
-            )
-            resampled_line.append(current_point)
-        resampled_line.append(line[-1])
-        resampled_line = np.array(resampled_line)
-        return resampled_line
-
-    def sample_points_on_bbox(self, line, n=50):
-        """Resample n points on a line.
-
-        Args:
-            line (ndarray): The points composing a line.
-            n (int): The resampled points number.
-
-        Returns:
-            resampled_line (ndarray): The points composing the resampled line.
-        """
-        assert line.ndim == 2
-        assert line.shape[0] >= 2
-        assert line.shape[1] == 2
-        assert isinstance(n, int)
-        assert n > 0
-
-        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
-            if length_list[i] < 0.9 * mean_length:
-                for g in group:
-                    if i in g:
-                        g.append(point_id)
-                        break
-            else:
-                g = [point_id]
-                group.append(g)
-
-        top_tail_len = norm(line[0] - line[-1])
-        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))
-        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格式
-        """
-        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)
-
-        def get_intersection(pD, pG):
-            return Polygon(pD).intersection(Polygon(pG)).area
-
-        cal_IoU = get_intersection_over_union(points, temp_box)
-
-        if cal_IoU >= 0.7:
-            points = self.sample_points_on_bbox_bp(points, 31)
-            return temp_crop_img
-
-        points_sample = self.sample_points_on_bbox(points)
-        points_sample = points_sample.astype(np.int32)
-        head_edge, tail_edge, top_line, bot_line = self.reorder_poly_edge(points_sample)
-
-        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
-        )
-        if sideline_mean_shift[1] > 0:
-            resample_bot_line, resample_top_line = resample_top_line, resample_bot_line
-        rectifier = AutoRectifier()
-        new_points = np.concatenate([resample_top_line, resample_bot_line])
-        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")
-        return np.array(img_crop[0], dtype=np.uint8)
-
-
-class SortBoxes(BaseComponent):
-
-    YIELD_BATCH = False
-
-    INPUT_KEYS = ["dt_polys"]
-    OUTPUT_KEYS = ["dt_polys"]
-    DEAULT_INPUTS = {"dt_polys": "dt_polys"}
-    DEAULT_OUTPUTS = {"dt_polys": "dt_polys"}
-
-    def apply(self, dt_polys):
-        """
-        Sort text boxes in order from top to bottom, left to right
-        args:
-            dt_boxes(array):detected text boxes with shape [4, 2]
-        return:
-            sorted boxes(array) with shape [4, 2]
-        """
-        dt_boxes = np.array(dt_polys)
-        num_boxes = dt_boxes.shape[0]
-        sorted_boxes = sorted(dt_boxes, key=lambda x: (x[0][1], x[0][0]))
-        _boxes = list(sorted_boxes)
-
-        for i in range(num_boxes - 1):
-            for j in range(i, -1, -1):
-                if abs(_boxes[j + 1][0][1] - _boxes[j][0][1]) < 10 and (
-                    _boxes[j + 1][0][0] < _boxes[j][0][0]
-                ):
-                    tmp = _boxes[j]
-                    _boxes[j] = _boxes[j + 1]
-                    _boxes[j + 1] = tmp
-                else:
-                    break
-        return {"dt_polys": _boxes}

+ 0 - 353
paddlex/inference/components/task_related/text_rec.py

@@ -1,353 +0,0 @@
-# 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
-import os.path as osp
-
-import re
-import numpy as np
-from PIL import Image
-import cv2
-import math
-import json
-import tempfile
-from tokenizers import Tokenizer as TokenizerFast
-
-from ....utils import logging
-from ..base import BaseComponent
-
-__all__ = [
-    "OCRReisizeNormImg",
-    "LaTeXOCRReisizeNormImg",
-    "CTCLabelDecode",
-    "LaTeXOCRDecode",
-]
-
-
-class OCRReisizeNormImg(BaseComponent):
-    """for ocr image resize and normalization"""
-
-    INPUT_KEYS = ["img", "img_size"]
-    OUTPUT_KEYS = ["img"]
-    DEAULT_INPUTS = {"img": "img", "img_size": "img_size"}
-    DEAULT_OUTPUTS = {"img": "img"}
-
-    def __init__(self, rec_image_shape=[3, 48, 320]):
-        super().__init__()
-        self.rec_image_shape = rec_image_shape
-
-    def resize_norm_img(self, img, max_wh_ratio):
-        """resize and normalize the img"""
-        imgC, imgH, imgW = self.rec_image_shape
-        assert imgC == img.shape[2]
-        imgW = int((imgH * max_wh_ratio))
-
-        h, w = img.shape[:2]
-        ratio = w / float(h)
-        if math.ceil(imgH * ratio) > imgW:
-            resized_w = imgW
-        else:
-            resized_w = int(math.ceil(imgH * ratio))
-        resized_image = cv2.resize(img, (resized_w, imgH))
-        resized_image = resized_image.astype("float32")
-        resized_image = resized_image.transpose((2, 0, 1)) / 255
-        resized_image -= 0.5
-        resized_image /= 0.5
-        padding_im = np.zeros((imgC, imgH, imgW), dtype=np.float32)
-        padding_im[:, :, 0:resized_w] = resized_image
-        return padding_im
-
-    def apply(self, img, img_size):
-        """apply"""
-        imgC, imgH, imgW = self.rec_image_shape
-        max_wh_ratio = imgW / imgH
-        w, h = img_size[:2]
-        wh_ratio = w * 1.0 / h
-        max_wh_ratio = max(max_wh_ratio, wh_ratio)
-        img = self.resize_norm_img(img, max_wh_ratio)
-        return {"img": img}
-
-
-class LaTeXOCRReisizeNormImg(BaseComponent):
-    """for ocr image resize and normalization"""
-
-    INPUT_KEYS = "img"
-    OUTPUT_KEYS = "img"
-    DEAULT_INPUTS = {"img": "img"}
-    DEAULT_OUTPUTS = {"img": "img"}
-
-    def __init__(self, rec_image_shape=[3, 48, 320]):
-        super().__init__()
-        self.rec_image_shape = rec_image_shape
-
-    def pad_(self, img, divable=32):
-        threshold = 128
-        data = np.array(img.convert("LA"))
-        if data[..., -1].var() == 0:
-            data = (data[..., 0]).astype(np.uint8)
-        else:
-            data = (255 - data[..., -1]).astype(np.uint8)
-        data = (data - data.min()) / (data.max() - data.min()) * 255
-        if data.mean() > threshold:
-            # To invert the text to white
-            gray = 255 * (data < threshold).astype(np.uint8)
-        else:
-            gray = 255 * (data > threshold).astype(np.uint8)
-            data = 255 - data
-
-        coords = cv2.findNonZero(gray)  # Find all non-zero points (text)
-        a, b, w, h = cv2.boundingRect(coords)  # Find minimum spanning bounding box
-        rect = data[b : b + h, a : a + w]
-        im = Image.fromarray(rect).convert("L")
-        dims = []
-        for x in [w, h]:
-            div, mod = divmod(x, divable)
-            dims.append(divable * (div + (1 if mod > 0 else 0)))
-        padded = Image.new("L", dims, 255)
-        padded.paste(im, (0, 0, im.size[0], im.size[1]))
-        return padded
-
-    def minmax_size_(
-        self,
-        img,
-        max_dimensions,
-        min_dimensions,
-    ):
-        if max_dimensions is not None:
-            ratios = [a / b for a, b in zip(img.size, max_dimensions)]
-            if any([r > 1 for r in ratios]):
-                size = np.array(img.size) // max(ratios)
-                img = img.resize(tuple(size.astype(int)), Image.BILINEAR)
-        if min_dimensions is not None:
-            # hypothesis: there is a dim in img smaller than min_dimensions, and return a proper dim >= min_dimensions
-            padded_size = [
-                max(img_dim, min_dim)
-                for img_dim, min_dim in zip(img.size, min_dimensions)
-            ]
-            if padded_size != list(img.size):  # assert hypothesis
-                padded_im = Image.new("L", padded_size, 255)
-                padded_im.paste(img, img.getbbox())
-                img = padded_im
-        return img
-
-    def norm_img_latexocr(self, img):
-        # CAN only predict gray scale image
-        shape = (1, 1, 3)
-        mean = [0.7931, 0.7931, 0.7931]
-        std = [0.1738, 0.1738, 0.1738]
-        scale = np.float32(1.0 / 255.0)
-        min_dimensions = [32, 32]
-        max_dimensions = [672, 192]
-        mean = np.array(mean).reshape(shape).astype("float32")
-        std = np.array(std).reshape(shape).astype("float32")
-
-        im_h, im_w = img.shape[:2]
-        if (
-            min_dimensions[0] <= im_w <= max_dimensions[0]
-            and min_dimensions[1] <= im_h <= max_dimensions[1]
-        ):
-            pass
-        else:
-            img = Image.fromarray(np.uint8(img))
-            img = self.minmax_size_(self.pad_(img), max_dimensions, min_dimensions)
-            img = np.array(img)
-            im_h, im_w = img.shape[:2]
-            img = np.dstack([img, img, img])
-        img = (img.astype("float32") * scale - mean) / std
-        img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
-        divide_h = math.ceil(im_h / 16) * 16
-        divide_w = math.ceil(im_w / 16) * 16
-        img = np.pad(
-            img, ((0, divide_h - im_h), (0, divide_w - im_w)), constant_values=(1, 1)
-        )
-        img = img[:, :, np.newaxis].transpose(2, 0, 1)
-        img = img.astype("float32")
-        return img
-
-    def apply(self, img):
-        """apply"""
-        img = self.norm_img_latexocr(img)
-        return {"img": img}
-
-
-class BaseRecLabelDecode(BaseComponent):
-    """Convert between text-label and text-index"""
-
-    INPUT_KEYS = ["pred"]
-    OUTPUT_KEYS = ["rec_text", "rec_score"]
-    DEAULT_INPUTS = {"pred": "pred"}
-    DEAULT_OUTPUTS = {"rec_text": "rec_text", "rec_score": "rec_score"}
-
-    ENABLE_BATCH = True
-
-    def __init__(self, character_str=None, use_space_char=True):
-        super().__init__()
-        self.reverse = False
-        character_list = (
-            list(character_str)
-            if character_str is not None
-            else list("0123456789abcdefghijklmnopqrstuvwxyz")
-        )
-        if use_space_char:
-            character_list.append(" ")
-
-        character_list = self.add_special_char(character_list)
-        self.dict = {}
-        for i, char in enumerate(character_list):
-            self.dict[char] = i
-        self.character = character_list
-
-    def pred_reverse(self, pred):
-        """pred_reverse"""
-        pred_re = []
-        c_current = ""
-        for c in pred:
-            if not bool(re.search("[a-zA-Z0-9 :*./%+-]", c)):
-                if c_current != "":
-                    pred_re.append(c_current)
-                pred_re.append(c)
-                c_current = ""
-            else:
-                c_current += c
-        if c_current != "":
-            pred_re.append(c_current)
-
-        return "".join(pred_re[::-1])
-
-    def add_special_char(self, character_list):
-        """add_special_char"""
-        return character_list
-
-    def decode(self, text_index, text_prob=None, is_remove_duplicate=False):
-        """convert text-index into text-label."""
-        result_list = []
-        ignored_tokens = self.get_ignored_tokens()
-        batch_size = len(text_index)
-        for batch_idx in range(batch_size):
-            selection = np.ones(len(text_index[batch_idx]), dtype=bool)
-            if is_remove_duplicate:
-                selection[1:] = text_index[batch_idx][1:] != text_index[batch_idx][:-1]
-            for ignored_token in ignored_tokens:
-                selection &= text_index[batch_idx] != ignored_token
-
-            char_list = [
-                self.character[text_id] for text_id in text_index[batch_idx][selection]
-            ]
-            if text_prob is not None:
-                conf_list = text_prob[batch_idx][selection]
-            else:
-                conf_list = [1] * len(selection)
-            if len(conf_list) == 0:
-                conf_list = [0]
-
-            text = "".join(char_list)
-
-            if self.reverse:  # for arabic rec
-                text = self.pred_reverse(text)
-
-            result_list.append((text, np.mean(conf_list).tolist()))
-        return result_list
-
-    def get_ignored_tokens(self):
-        """get_ignored_tokens"""
-        return [0]  # for ctc blank
-
-    def apply(self, pred):
-        """apply"""
-        preds = np.array(pred)
-        if isinstance(preds, tuple) or isinstance(preds, list):
-            preds = preds[-1]
-        preds_idx = preds.argmax(axis=2)
-        preds_prob = preds.max(axis=2)
-        text = self.decode(preds_idx, preds_prob, is_remove_duplicate=True)
-        return [{"rec_text": t[0], "rec_score": t[1]} for t in text]
-
-
-class CTCLabelDecode(BaseRecLabelDecode):
-    """Convert between text-label and text-index"""
-
-    def __init__(self, character_list=None, use_space_char=True):
-        super().__init__(character_list, use_space_char=use_space_char)
-
-    def apply(self, pred):
-        """apply"""
-        preds = np.array(pred)
-        preds_idx = preds.argmax(axis=-1).squeeze(axis=1)
-        preds_prob = preds.max(axis=-1).squeeze(axis=1)
-        text = self.decode(preds_idx, preds_prob, is_remove_duplicate=True)
-        return [{"rec_text": t[0], "rec_score": t[1]} for t in text]
-
-    def add_special_char(self, character_list):
-        """add_special_char"""
-        character_list = ["blank"] + character_list
-        return character_list
-
-
-class LaTeXOCRDecode(BaseComponent):
-    """Convert between latex-symbol and symbol-index"""
-
-    INPUT_KEYS = ["pred"]
-    OUTPUT_KEYS = ["rec_text"]
-    DEAULT_INPUTS = {"pred": "pred"}
-    DEAULT_OUTPUTS = {"rec_text": "rec_text"}
-
-    def __init__(self, character_list=None):
-        super().__init__()
-        character_list = character_list
-        temp_path = tempfile.gettempdir()
-        rec_char_dict_path = os.path.join(temp_path, "latexocr_tokenizer.json")
-        try:
-            with open(rec_char_dict_path, "w") as f:
-                json.dump(character_list, f)
-        except Exception as e:
-            print(f"创建 latexocr_tokenizer.json 文件失败, 原因{str(e)}")
-        self.tokenizer = TokenizerFast.from_file(rec_char_dict_path)
-
-    def post_process(self, s):
-        text_reg = r"(\\(operatorname|mathrm|text|mathbf)\s?\*? {.*?})"
-        letter = "[a-zA-Z]"
-        noletter = r"[\W_^\d]"
-        names = [x[0].replace(" ", "") for x in re.findall(text_reg, s)]
-        s = re.sub(text_reg, lambda match: str(names.pop(0)), s)
-        news = s
-        while True:
-            s = news
-            news = re.sub(r"(?!\\ )(%s)\s+?(%s)" % (noletter, noletter), r"\1\2", s)
-            news = re.sub(r"(?!\\ )(%s)\s+?(%s)" % (noletter, letter), r"\1\2", news)
-            news = re.sub(r"(%s)\s+?(%s)" % (letter, noletter), r"\1\2", news)
-            if news == s:
-                break
-        return s
-
-    def decode(self, tokens):
-        if len(tokens.shape) == 1:
-            tokens = tokens[None, :]
-
-        dec = [self.tokenizer.decode(tok) for tok in tokens]
-        dec_str_list = [
-            "".join(detok.split(" "))
-            .replace("Ġ", " ")
-            .replace("[EOS]", "")
-            .replace("[BOS]", "")
-            .replace("[PAD]", "")
-            .strip()
-            for detok in dec
-        ]
-        return [str(self.post_process(dec_str)) for dec_str in dec_str_list]
-
-    def apply(self, pred):
-        preds = np.array(pred[0])
-        text = self.decode(preds)
-        return {"rec_text": text[0]}

+ 0 - 43
paddlex/inference/components/task_related/warp.py

@@ -1,43 +0,0 @@
-# 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 numpy as np
-from ..base import BaseComponent
-
-
-class DocTrPostProcess(BaseComponent):
-    """normalize image such as substract mean, divide std"""
-
-    INPUT_KEYS = ["pred"]
-    OUTPUT_KEYS = ["doctr_img"]
-    DEAULT_INPUTS = {"pred": "pred"}
-    DEAULT_OUTPUTS = {"doctr_img": "doctr_img"}
-
-    def __init__(self, scale=None, **kwargs):
-        super().__init__()
-        if isinstance(scale, str):
-            scale = np.float32(scale)
-        self.scale = np.float32(scale if scale is not None else 255.0)
-
-    def apply(self, pred):
-        im = pred[0]
-        assert isinstance(im, np.ndarray), "invalid input 'im' in DocTrPostProcess"
-
-        im = im.squeeze()
-        im = im.transpose(1, 2, 0)
-        im *= self.scale
-        im = im[:, :, ::-1]
-        im = im.astype("uint8", copy=False)
-        result = {"doctr_img": im}
-        return result

+ 0 - 16
paddlex/inference/components/transforms/__init__.py

@@ -1,16 +0,0 @@
-# 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.
-
-from .image import *
-from .ts import *

+ 0 - 15
paddlex/inference/components/transforms/image/__init__.py

@@ -1,15 +0,0 @@
-# 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.
-
-from .common import *

+ 0 - 598
paddlex/inference/components/transforms/image/common.py

@@ -1,598 +0,0 @@
-# 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 ast
-import math
-from pathlib import Path
-from copy import deepcopy
-
-import numpy as np
-import cv2
-
-from .....utils.flags import (
-    INFER_BENCHMARK,
-    INFER_BENCHMARK_ITER,
-    INFER_BENCHMARK_DATA_SIZE,
-)
-from .....utils.cache import CACHE_DIR, temp_file_manager
-from ....utils.io import ImageReader, ImageWriter, PDFReader
-from ...base import BaseComponent
-from ..read_data import _BaseRead
-from . import funcs as F
-
-__all__ = [
-    "ReadImage",
-    "Flip",
-    "Crop",
-    "Resize",
-    "ResizeByLong",
-    "ResizeByShort",
-    "Pad",
-    "Normalize",
-    "ToCHWImage",
-    "PadStride",
-]
-
-
-def _check_image_size(input_):
-    """check image size"""
-    if not (
-        isinstance(input_, (list, tuple))
-        and len(input_) == 2
-        and isinstance(input_[0], int)
-        and isinstance(input_[1], int)
-    ):
-        raise TypeError(f"{input_} cannot represent a valid image size.")
-
-
-class ReadImage(_BaseRead):
-    """Load image from the file."""
-
-    INPUT_KEYS = ["img"]
-    OUTPUT_KEYS = ["img", "img_size", "ori_img", "ori_img_size"]
-    DEAULT_INPUTS = {"img": "img"}
-    DEAULT_OUTPUTS = {
-        "img": "img",
-        "input_path": "input_path",
-        "img_size": "img_size",
-        "ori_img": "ori_img",
-        "ori_img_size": "ori_img_size",
-    }
-
-    _FLAGS_DICT = {
-        "BGR": cv2.IMREAD_COLOR,
-        "RGB": cv2.IMREAD_COLOR,
-        "GRAY": cv2.IMREAD_GRAYSCALE,
-    }
-
-    SUFFIX = ["jpg", "png", "jpeg", "JPEG", "JPG", "bmp", "PDF", "pdf"]
-
-    def __init__(self, batch_size=1, format="BGR"):
-        """
-        Initialize the instance.
-
-        Args:
-            format (str, optional): Target color format to convert the image to.
-                Choices are 'BGR', 'RGB', and 'GRAY'. Default: 'BGR'.
-        """
-        super().__init__(batch_size)
-        self.format = format
-        flags = self._FLAGS_DICT[self.format]
-        self._img_reader = ImageReader(backend="opencv", flags=flags)
-        self._pdf_reader = PDFReader()
-        self._writer = ImageWriter(backend="opencv")
-
-    def apply(self, img):
-        """apply"""
-
-        def rand_data():
-            def parse_size(s):
-                res = ast.literal_eval(s)
-                if isinstance(res, int):
-                    return (res, res)
-                else:
-                    assert isinstance(res, (tuple, list))
-                    assert len(res) == 2
-                    assert all(isinstance(item, int) for item in res)
-                    return res
-
-            size = parse_size(INFER_BENCHMARK_DATA_SIZE)
-            return np.random.randint(0, 256, (*size, 3), dtype=np.uint8)
-
-        def process_ndarray(img):
-            with temp_file_manager.temp_file_context(suffix=".png") as temp_file:
-                img_path = Path(temp_file.name)
-                self._writer.write(img_path, img)
-                if self.format == "RGB":
-                    img = img[:, :, ::-1]
-                return {
-                    "input_path": img_path,
-                    "img": img,
-                    "img_size": [img.shape[1], img.shape[0]],
-                    "ori_img": deepcopy(img),
-                    "ori_img_size": deepcopy([img.shape[1], img.shape[0]]),
-                }
-
-        if INFER_BENCHMARK and img is None:
-            for _ in range(INFER_BENCHMARK_ITER):
-                yield [process_ndarray(rand_data()) for _ in range(self.batch_size)]
-
-        elif isinstance(img, np.ndarray):
-            yield [process_ndarray(img)]
-
-        elif isinstance(img, str):
-            file_path = img
-            file_path = self._download_from_url(file_path)
-            file_list = self._get_files_list(file_path)
-            batch = []
-            for file_path in file_list:
-                img = self._read(file_path)
-                batch.extend(img)
-                if len(batch) >= self.batch_size:
-                    yield batch
-                    batch = []
-            if len(batch) > 0:
-                yield batch
-        else:
-            raise TypeError(
-                f"ReadImage only supports the following types:\n"
-                f"1. str, indicating a image file path or a directory containing image files.\n"
-                f"2. numpy.ndarray.\n"
-                f"However, got type: {type(img).__name__}."
-            )
-
-    def _read(self, file_path):
-        if str(file_path).lower().endswith(".pdf"):
-            return self._read_pdf(file_path)
-        else:
-            return self._read_img(file_path)
-
-    def _read_img(self, img_path):
-        blob = self._img_reader.read(img_path)
-        if blob is None:
-            raise Exception("Image read Error")
-
-        if self.format == "RGB":
-            if blob.ndim != 3:
-                raise RuntimeError("Array is not 3-dimensional.")
-            # BGR to RGB
-            blob = blob[..., ::-1]
-        return [
-            {
-                "input_path": img_path,
-                "img": blob,
-                "img_size": [blob.shape[1], blob.shape[0]],
-                "ori_img": deepcopy(blob),
-                "ori_img_size": deepcopy([blob.shape[1], blob.shape[0]]),
-            }
-        ]
-
-    def _read_pdf(self, pdf_path):
-        img_list = self._pdf_reader.read(pdf_path)
-        return [
-            {
-                "input_path": pdf_path,
-                "img": img,
-                "img_size": [img.shape[1], img.shape[0]],
-                "ori_img": deepcopy(img),
-                "ori_img_size": deepcopy([img.shape[1], img.shape[0]]),
-            }
-            for img in img_list
-        ]
-
-
-class GetImageInfo(BaseComponent):
-    """Get Image Info"""
-
-    INPUT_KEYS = "img"
-    OUTPUT_KEYS = "img_size"
-    DEAULT_INPUTS = {"img": "img"}
-    DEAULT_OUTPUTS = {"img_size": "img_size"}
-
-    def __init__(self):
-        super().__init__()
-
-    def apply(self, img):
-        """apply"""
-        return {"img_size": [img.shape[1], img.shape[0]]}
-
-
-class Flip(BaseComponent):
-    """Flip the image vertically or horizontally."""
-
-    INPUT_KEYS = "img"
-    OUTPUT_KEYS = "img"
-    DEAULT_INPUTS = {"img": "img"}
-    DEAULT_OUTPUTS = {"img": "img"}
-
-    def __init__(self, mode="H"):
-        """
-        Initialize the instance.
-
-        Args:
-            mode (str, optional): 'H' for horizontal flipping and 'V' for vertical
-                flipping. Default: 'H'.
-        """
-        super().__init__()
-        if mode not in ("H", "V"):
-            raise ValueError("`mode` should be 'H' or 'V'.")
-        self.mode = mode
-
-    def apply(self, img):
-        """apply"""
-        if self.mode == "H":
-            img = F.flip_h(img)
-        elif self.mode == "V":
-            img = F.flip_v(img)
-        return {"img": img}
-
-
-class Crop(BaseComponent):
-    """Crop region from the image."""
-
-    INPUT_KEYS = "img"
-    OUTPUT_KEYS = ["img", "img_size"]
-    DEAULT_INPUTS = {"img": "img"}
-    DEAULT_OUTPUTS = {"img": "img", "img_size": "img_size"}
-
-    def __init__(self, crop_size, mode="C"):
-        """
-        Initialize the instance.
-
-        Args:
-            crop_size (list|tuple|int): Width and height of the region to crop.
-            mode (str, optional): 'C' for cropping the center part and 'TL' for
-                cropping the top left part. Default: 'C'.
-        """
-        super().__init__()
-        if isinstance(crop_size, int):
-            crop_size = [crop_size, crop_size]
-        _check_image_size(crop_size)
-
-        self.crop_size = crop_size
-
-        if mode not in ("C", "TL"):
-            raise ValueError("Unsupported interpolation method")
-        self.mode = mode
-
-    def apply(self, img):
-        """apply"""
-        h, w = img.shape[:2]
-        cw, ch = self.crop_size
-        if self.mode == "C":
-            x1 = max(0, (w - cw) // 2)
-            y1 = max(0, (h - ch) // 2)
-        elif self.mode == "TL":
-            x1, y1 = 0, 0
-        x2 = min(w, x1 + cw)
-        y2 = min(h, y1 + ch)
-        coords = (x1, y1, x2, y2)
-        if w < cw or h < ch:
-            raise ValueError(
-                f"Input image ({w}, {h}) smaller than the target size ({cw}, {ch})."
-            )
-        img = F.slice(img, coords=coords)
-        return {"img": img, "img_size": [img.shape[1], img.shape[0]]}
-
-
-class _BaseResize(BaseComponent):
-    _INTERP_DICT = {
-        "NEAREST": cv2.INTER_NEAREST,
-        "LINEAR": cv2.INTER_LINEAR,
-        "CUBIC": cv2.INTER_CUBIC,
-        "AREA": cv2.INTER_AREA,
-        "LANCZOS4": cv2.INTER_LANCZOS4,
-    }
-
-    def __init__(self, size_divisor, interp):
-        super().__init__()
-
-        if size_divisor is not None:
-            assert isinstance(
-                size_divisor, int
-            ), "`size_divisor` should be None or int."
-        self.size_divisor = size_divisor
-
-        try:
-            interp = self._INTERP_DICT[interp]
-        except KeyError:
-            raise ValueError(
-                "`interp` should be one of {}.".format(self._INTERP_DICT.keys())
-            )
-        self.interp = interp
-
-    @staticmethod
-    def _rescale_size(img_size, target_size):
-        """rescale size"""
-        scale = min(max(target_size) / max(img_size), min(target_size) / min(img_size))
-        rescaled_size = [round(i * scale) for i in img_size]
-        return rescaled_size, scale
-
-
-class Resize(_BaseResize):
-    """Resize the image."""
-
-    INPUT_KEYS = "img"
-    OUTPUT_KEYS = ["img", "img_size", "scale_factors"]
-    DEAULT_INPUTS = {"img": "img"}
-    DEAULT_OUTPUTS = {
-        "img": "img",
-        "img_size": "img_size",
-        "scale_factors": "scale_factors",
-    }
-
-    def __init__(
-        self, target_size, keep_ratio=False, size_divisor=None, interp="LINEAR"
-    ):
-        """
-        Initialize the instance.
-
-        Args:
-            target_size (list|tuple|int): Target width and height.
-            keep_ratio (bool, optional): Whether to keep the aspect ratio of resized
-                image. Default: False.
-            size_divisor (int|None, optional): Divisor of resized image size.
-                Default: None.
-            interp (str, optional): Interpolation method. Choices are 'NEAREST',
-                'LINEAR', 'CUBIC', 'AREA', and 'LANCZOS4'. Default: 'LINEAR'.
-        """
-        super().__init__(size_divisor=size_divisor, interp=interp)
-
-        if isinstance(target_size, int):
-            target_size = [target_size, target_size]
-        _check_image_size(target_size)
-        self.target_size = target_size
-
-        self.keep_ratio = keep_ratio
-
-    def apply(self, img):
-        """apply"""
-        target_size = self.target_size
-        original_size = img.shape[:2][::-1]
-
-        if self.keep_ratio:
-            h, w = img.shape[0:2]
-            target_size, _ = self._rescale_size((w, h), self.target_size)
-
-        if self.size_divisor:
-            target_size = [
-                math.ceil(i / self.size_divisor) * self.size_divisor
-                for i in target_size
-            ]
-
-        img_scale_w, img_scale_h = [
-            target_size[0] / original_size[0],
-            target_size[1] / original_size[1],
-        ]
-        img = F.resize(img, target_size, interp=self.interp)
-        return {
-            "img": img,
-            "img_size": [img.shape[1], img.shape[0]],
-            "scale_factors": [img_scale_w, img_scale_h],
-        }
-
-
-class ResizeByLong(_BaseResize):
-    """
-    Proportionally resize the image by specifying the target length of the
-    longest side.
-    """
-
-    INPUT_KEYS = "img"
-    OUTPUT_KEYS = ["img", "img_size"]
-    DEAULT_INPUTS = {"img": "img"}
-    DEAULT_OUTPUTS = {"img": "img", "img_size": "img_size"}
-
-    def __init__(self, target_long_edge, size_divisor=None, interp="LINEAR"):
-        """
-        Initialize the instance.
-
-        Args:
-            target_long_edge (int): Target length of the longest side of image.
-            size_divisor (int|None, optional): Divisor of resized image size.
-                Default: None.
-            interp (str, optional): Interpolation method. Choices are 'NEAREST',
-                'LINEAR', 'CUBIC', 'AREA', and 'LANCZOS4'. Default: 'LINEAR'.
-        """
-        super().__init__(size_divisor=size_divisor, interp=interp)
-        self.target_long_edge = target_long_edge
-
-    def apply(self, img):
-        """apply"""
-        h, w = img.shape[:2]
-        scale = self.target_long_edge / max(h, w)
-        h_resize = round(h * scale)
-        w_resize = round(w * scale)
-        if self.size_divisor is not None:
-            h_resize = math.ceil(h_resize / self.size_divisor) * self.size_divisor
-            w_resize = math.ceil(w_resize / self.size_divisor) * self.size_divisor
-
-        img = F.resize(img, (w_resize, h_resize), interp=self.interp)
-        return {"img": img, "img_size": [img.shape[1], img.shape[0]]}
-
-
-class ResizeByShort(_BaseResize):
-    """
-    Proportionally resize the image by specifying the target length of the
-    shortest side.
-    """
-
-    INPUT_KEYS = "img"
-    OUTPUT_KEYS = ["img", "img_size"]
-    DEAULT_INPUTS = {"img": "img"}
-    DEAULT_OUTPUTS = {"img": "img", "img_size": "img_size"}
-
-    def __init__(self, target_short_edge, size_divisor=None, interp="LINEAR"):
-        """
-        Initialize the instance.
-
-        Args:
-            target_short_edge (int): Target length of the shortest side of image.
-            size_divisor (int|None, optional): Divisor of resized image size.
-                Default: None.
-            interp (str, optional): Interpolation method. Choices are 'NEAREST',
-                'LINEAR', 'CUBIC', 'AREA', and 'LANCZOS4'. Default: 'LINEAR'.
-        """
-        super().__init__(size_divisor=size_divisor, interp=interp)
-        self.target_short_edge = target_short_edge
-
-    def apply(self, img):
-        """apply"""
-        h, w = img.shape[:2]
-        scale = self.target_short_edge / min(h, w)
-        h_resize = round(h * scale)
-        w_resize = round(w * scale)
-        if self.size_divisor is not None:
-            h_resize = math.ceil(h_resize / self.size_divisor) * self.size_divisor
-            w_resize = math.ceil(w_resize / self.size_divisor) * self.size_divisor
-
-        img = F.resize(img, (w_resize, h_resize), interp=self.interp)
-        return {"img": img, "img_size": [img.shape[1], img.shape[0]]}
-
-
-class Pad(BaseComponent):
-    """Pad the image."""
-
-    INPUT_KEYS = "img"
-    OUTPUT_KEYS = ["img", "img_size"]
-    DEAULT_INPUTS = {"img": "img"}
-    DEAULT_OUTPUTS = {"img": "img", "img_size": "img_size"}
-
-    def __init__(self, target_size, val=127.5):
-        """
-        Initialize the instance.
-
-        Args:
-            target_size (list|tuple|int): Target width and height of the image after
-                padding.
-            val (float, optional): Value to fill the padded area. Default: 127.5.
-        """
-        super().__init__()
-
-        if isinstance(target_size, int):
-            target_size = [target_size, target_size]
-        _check_image_size(target_size)
-        self.target_size = target_size
-
-        self.val = val
-
-    def apply(self, img):
-        """apply"""
-        h, w = img.shape[:2]
-        tw, th = self.target_size
-        ph = th - h
-        pw = tw - w
-
-        if ph < 0 or pw < 0:
-            raise ValueError(
-                f"Input image ({w}, {h}) smaller than the target size ({tw}, {th})."
-            )
-        else:
-            img = F.pad(img, pad=(0, ph, 0, pw), val=self.val)
-        return {"img": img, "img_size": [img.shape[1], img.shape[0]]}
-
-
-class PadStride(BaseComponent):
-    """padding image for model with FPN , instead PadBatch(pad_to_stride, pad_gt) in original config
-    Args:
-        stride (bool): model with FPN need image shape % stride == 0
-    """
-
-    INPUT_KEYS = "img"
-    OUTPUT_KEYS = "img"
-    DEAULT_INPUTS = {"img": "img"}
-    DEAULT_OUTPUTS = {"img": "img"}
-
-    def __init__(self, stride=0):
-        super().__init__()
-        self.coarsest_stride = stride
-
-    def apply(self, img):
-        """
-        Args:
-            im (np.ndarray): image (np.ndarray)
-        Returns:
-            im (np.ndarray):  processed image (np.ndarray)
-        """
-        im = img
-        coarsest_stride = self.coarsest_stride
-        if coarsest_stride <= 0:
-            return {"img": im}
-        im_c, im_h, im_w = im.shape
-        pad_h = int(np.ceil(float(im_h) / coarsest_stride) * coarsest_stride)
-        pad_w = int(np.ceil(float(im_w) / coarsest_stride) * coarsest_stride)
-        padding_im = np.zeros((im_c, pad_h, pad_w), dtype=np.float32)
-        padding_im[:, :im_h, :im_w] = im
-        return {"img": padding_im}
-
-
-class Normalize(BaseComponent):
-    """Normalize the image."""
-
-    INPUT_KEYS = "img"
-    OUTPUT_KEYS = "img"
-    DEAULT_INPUTS = {"img": "img"}
-    DEAULT_OUTPUTS = {"img": "img"}
-
-    def __init__(self, scale=1.0 / 255, mean=0.5, std=0.5, preserve_dtype=False):
-        """
-        Initialize the instance.
-
-        Args:
-            scale (float, optional): Scaling factor to apply to the image before
-                applying normalization. Default: 1/255.
-            mean (float|tuple|list, optional): Means for each channel of the image.
-                Default: 0.5.
-            std (float|tuple|list, optional): Standard deviations for each channel
-                of the image. Default: 0.5.
-            preserve_dtype (bool, optional): Whether to preserve the original dtype
-                of the image.
-        """
-        super().__init__()
-
-        self.scale = np.float32(scale)
-        if isinstance(mean, float):
-            mean = [mean]
-        self.mean = np.asarray(mean).astype("float32")
-        if isinstance(std, float):
-            std = [std]
-        self.std = np.asarray(std).astype("float32")
-        self.preserve_dtype = preserve_dtype
-
-    def apply(self, img):
-        """apply"""
-        old_type = img.dtype
-        # XXX: If `old_type` has higher precision than float32,
-        # we will lose some precision.
-        img = img.astype("float32", copy=False)
-        img *= self.scale
-        img -= self.mean
-        img /= self.std
-        if self.preserve_dtype:
-            img = img.astype(old_type, copy=False)
-        return {"img": img}
-
-
-class ToCHWImage(BaseComponent):
-    """Reorder the dimensions of the image from HWC to CHW."""
-
-    INPUT_KEYS = "img"
-    OUTPUT_KEYS = "img"
-    DEAULT_INPUTS = {"img": "img"}
-    DEAULT_OUTPUTS = {"img": "img"}
-
-    def apply(self, img):
-        """apply"""
-        img = img.transpose((2, 0, 1))
-        return {"img": img}

+ 0 - 58
paddlex/inference/components/transforms/image/funcs.py

@@ -1,58 +0,0 @@
-# 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 cv2
-
-
-def resize(im, target_size, interp):
-    """resize image to target size"""
-    w, h = target_size
-    im = cv2.resize(im, (w, h), interpolation=interp)
-    return im
-
-
-def flip_h(im):
-    """flip image horizontally"""
-    if len(im.shape) == 3:
-        im = im[:, ::-1, :]
-    elif len(im.shape) == 2:
-        im = im[:, ::-1]
-    return im
-
-
-def flip_v(im):
-    """flip image vertically"""
-    if len(im.shape) == 3:
-        im = im[::-1, :, :]
-    elif len(im.shape) == 2:
-        im = im[::-1, :]
-    return im
-
-
-def slice(im, coords):
-    """slice the image"""
-    x1, y1, x2, y2 = coords
-    im = im[y1:y2, x1:x2, ...]
-    return im
-
-
-def pad(im, pad, val):
-    """padding image by value"""
-    if isinstance(pad, int):
-        pad = [pad] * 4
-    if len(pad) != 4:
-        raise ValueError
-    chns = 1 if im.ndim == 2 else im.shape[2]
-    im = cv2.copyMakeBorder(im, *pad, cv2.BORDER_CONSTANT, value=(val,) * chns)
-    return im

+ 0 - 67
paddlex/inference/components/transforms/read_data.py

@@ -1,67 +0,0 @@
-# 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
-from pathlib import Path
-
-from ....utils.download import download
-from ....utils.cache import CACHE_DIR
-from ..base import BaseComponent
-
-
-class _BaseRead(BaseComponent):
-    """Load image from the file."""
-
-    NAME = "ReadCmp"
-    SUFFIX = []
-
-    def __init__(self, batch_size=1):
-        super().__init__()
-        self._batch_size = batch_size
-
-    @property
-    def batch_size(self):
-        return self._batch_size
-
-    @batch_size.setter
-    def batch_size(self, value):
-        if value <= 0:
-            raise ValueError("Batch size must be positive.")
-        self._batch_size = value
-
-    # XXX: auto download for url
-    def _download_from_url(self, in_path):
-        if in_path.startswith("http"):
-            file_name = Path(in_path).name
-            save_path = Path(CACHE_DIR) / "predict_input" / file_name
-            download(in_path, save_path, overwrite=True)
-            return save_path.as_posix()
-        return in_path
-
-    def _get_files_list(self, fp):
-        file_list = []
-        if fp is None or not os.path.exists(fp):
-            raise Exception(f"Not found any img file in path: {fp}")
-
-        if os.path.isfile(fp) and fp.split(".")[-1] in self.SUFFIX:
-            file_list.append(fp)
-        elif os.path.isdir(fp):
-            for root, dirs, files in os.walk(fp):
-                for single_file in files:
-                    if single_file.split(".")[-1] in self.SUFFIX:
-                        file_list.append(os.path.join(root, single_file))
-        if len(file_list) == 0:
-            raise Exception("Not found any file in {}".format(fp))
-        file_list = sorted(file_list)
-        return file_list

+ 0 - 15
paddlex/inference/components/transforms/ts/__init__.py

@@ -1,15 +0,0 @@
-# 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.
-
-from .common import *

+ 0 - 393
paddlex/inference/components/transforms/ts/common.py

@@ -1,393 +0,0 @@
-# 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.
-
-from pathlib import Path
-from copy import deepcopy
-import joblib
-import numpy as np
-import pandas as pd
-
-from .....utils.cache import CACHE_DIR, temp_file_manager
-from .....utils.download import download
-from .....utils.cache import CACHE_DIR
-from ....utils.io.readers import CSVReader
-from ....utils.io.writers import CSVWriter
-from ...base import BaseComponent
-from ..read_data import _BaseRead
-from .funcs import load_from_dataframe, time_feature
-
-
-__all__ = [
-    "ReadTS",
-    "BuildTSDataset",
-    "TSCutOff",
-    "TSNormalize",
-    "TimeFeature",
-    "TStoArray",
-    "BuildPadMask",
-    "ArraytoTS",
-    "TSDeNormalize",
-    "GetAnomaly",
-    "GetCls",
-]
-
-
-class ReadTS(_BaseRead):
-
-    INPUT_KEYS = ["ts"]
-    OUTPUT_KEYS = ["input_path", "ts", "ori_ts"]
-    DEAULT_INPUTS = {"ts": "ts"}
-    DEAULT_OUTPUTS = {"input_path": "input_path", "ts": "ts", "ori_ts": "ori_ts"}
-
-    SUFFIX = ["csv"]
-
-    def __init__(self, batch_size=1):
-        super().__init__(batch_size)
-        self._reader = CSVReader(backend="pandas")
-        self._writer = CSVWriter(backend="pandas")
-
-    def apply(self, ts):
-        if isinstance(ts, pd.DataFrame):
-            with temp_file_manager.temp_file_context(suffix=".csv") as temp_file:
-                input_path = Path(temp_file.name)
-                ts_path = input_path.as_posix()
-                self._writer.write(ts_path, ts)
-                yield {"input_path": input_path, "ts": ts, "ori_ts": deepcopy(ts)}
-        elif isinstance(ts, str):
-            ts_path = ts
-            ts_path = self._download_from_url(ts_path)
-            file_list = self._get_files_list(ts_path)
-            batch = []
-            for ts_path in file_list:
-                ts_data = self._reader.read(ts_path)
-                batch.append(
-                    {
-                        "input_path": Path(ts_path).name,
-                        "ts": ts_data,
-                        "ori_ts": deepcopy(ts_data),
-                    }
-                )
-                if len(batch) >= self.batch_size:
-                    yield batch
-                    batch = []
-            if len(batch) > 0:
-                yield batch
-        else:
-            raise TypeError(
-                f"ReadTS only supports the following types:\n"
-                f"1. str, indicating a CSV file path or a directory containing CSV files.\n"
-                f"2. pandas.DataFrame.\n"
-                f"However, got type: {type(ts).__name__}."
-            )
-
-
-class TSCutOff(BaseComponent):
-
-    INPUT_KEYS = ["ts", "ori_ts"]
-    OUTPUT_KEYS = ["ts", "ori_ts"]
-    DEAULT_INPUTS = {"ts": "ts", "ori_ts": "ori_ts"}
-    DEAULT_OUTPUTS = {"ts": "ts", "ori_ts": "ori_ts"}
-
-    def __init__(self, size):
-        super().__init__()
-        self.size = size
-
-    def apply(self, ts, ori_ts):
-        skip_len = self.size.get("skip_chunk_len", 0)
-        if len(ts) < self.size["in_chunk_len"] + skip_len:
-            raise ValueError(
-                f"The length of the input data is {len(ts)}, but it should be at least {self.size['in_chunk_len'] + self.size['skip_chunk_len']} for training."
-            )
-        ts_data = ts[-(self.size["in_chunk_len"] + skip_len) :]
-        return {"ts": ts_data, "ori_ts": ts_data}
-
-
-class TSNormalize(BaseComponent):
-
-    INPUT_KEYS = ["ts"]
-    OUTPUT_KEYS = ["ts"]
-    DEAULT_INPUTS = {"ts": "ts"}
-    DEAULT_OUTPUTS = {"ts": "ts"}
-
-    def __init__(self, scale_path, params_info):
-        super().__init__()
-        self.scaler = joblib.load(scale_path)
-        self.params_info = params_info
-
-    def apply(self, ts):
-        """apply"""
-        if self.params_info.get("target_cols", None) is not None:
-            ts[self.params_info["target_cols"]] = self.scaler.transform(
-                ts[self.params_info["target_cols"]]
-            )
-        if self.params_info.get("feature_cols", None) is not None:
-            ts[self.params_info["feature_cols"]] = self.scaler.transform(
-                ts[self.params_info["feature_cols"]]
-            )
-
-        return {"ts": ts}
-
-
-class TSDeNormalize(BaseComponent):
-
-    INPUT_KEYS = ["pred"]
-    OUTPUT_KEYS = ["pred"]
-    DEAULT_INPUTS = {"pred": "pred"}
-    DEAULT_OUTPUTS = {"pred": "pred"}
-
-    def __init__(self, scale_path, params_info):
-        super().__init__()
-        self.scaler = joblib.load(scale_path)
-        self.params_info = params_info
-
-    def apply(self, pred):
-        """apply"""
-        scale_cols = pred.columns.values.tolist()
-        pred[scale_cols] = self.scaler.inverse_transform(pred[scale_cols])
-        return {"pred": pred}
-
-
-class BuildTSDataset(BaseComponent):
-
-    INPUT_KEYS = ["ts", "ori_ts"]
-    OUTPUT_KEYS = ["ts", "ori_ts"]
-    DEAULT_INPUTS = {"ts": "ts", "ori_ts": "ori_ts"}
-    DEAULT_OUTPUTS = {"ts": "ts", "ori_ts": "ori_ts"}
-
-    def __init__(self, params_info):
-        super().__init__()
-        self.params_info = params_info
-
-    def apply(self, ts, ori_ts):
-        """apply"""
-        ts_data = load_from_dataframe(ts, **self.params_info)
-        return {"ts": ts_data, "ori_ts": ts_data}
-
-
-class TimeFeature(BaseComponent):
-
-    INPUT_KEYS = ["ts"]
-    OUTPUT_KEYS = ["ts"]
-    DEAULT_INPUTS = {"ts": "ts"}
-    DEAULT_OUTPUTS = {"ts": "ts"}
-
-    def __init__(self, params_info, size, holiday=False):
-        super().__init__()
-        self.freq = params_info["freq"]
-        self.size = size
-        self.holiday = holiday
-
-    def apply(self, ts):
-        """apply"""
-        if not self.holiday:
-            ts = time_feature(
-                ts,
-                self.freq,
-                ["hourofday", "dayofmonth", "dayofweek", "dayofyear"],
-                self.size["out_chunk_len"],
-            )
-        else:
-            ts = time_feature(
-                ts,
-                self.freq,
-                [
-                    "minuteofhour",
-                    "hourofday",
-                    "dayofmonth",
-                    "dayofweek",
-                    "dayofyear",
-                    "monthofyear",
-                    "weekofyear",
-                    "holidays",
-                ],
-                self.size["out_chunk_len"],
-            )
-        return {"ts": ts}
-
-
-class BuildPadMask(BaseComponent):
-
-    INPUT_KEYS = ["ts"]
-    OUTPUT_KEYS = ["ts"]
-    DEAULT_INPUTS = {"ts": "ts"}
-    DEAULT_OUTPUTS = {"ts": "ts"}
-
-    def __init__(self, input_data):
-        super().__init__()
-        self.input_data = input_data
-
-    def apply(self, ts):
-        if "features" in self.input_data:
-            ts["features"] = ts["past_target"]
-
-        if "pad_mask" in self.input_data:
-            target_dim = len(ts["features"])
-            max_length = self.input_data["pad_mask"][-1]
-            if max_length > 0:
-                ones = np.ones(max_length, dtype=np.int32)
-                if max_length != target_dim:
-                    target_ndarray = np.array(ts["features"]).astype(np.float32)
-                    target_ndarray_final = np.zeros(
-                        [max_length, target_dim], dtype=np.int32
-                    )
-                    end = min(target_dim, max_length)
-                    target_ndarray_final[:end, :] = target_ndarray
-                    ts["features"] = target_ndarray_final
-                    ones[end:] = 0.0
-                    ts["pad_mask"] = ones
-                else:
-                    ts["pad_mask"] = ones
-        return {"ts": ts}
-
-
-class TStoArray(BaseComponent):
-
-    INPUT_KEYS = ["ts"]
-    OUTPUT_KEYS = ["ts"]
-    DEAULT_INPUTS = {"ts": "ts"}
-    DEAULT_OUTPUTS = {"ts": "ts"}
-
-    def __init__(self, input_data):
-        super().__init__()
-        self.input_data = input_data
-
-    def apply(self, ts):
-        ts_list = []
-        input_name = list(self.input_data.keys())
-        input_name.sort()
-        for key in input_name:
-            ts_list.append(np.array(ts[key]).astype("float32"))
-
-        return {"ts": ts_list}
-
-
-class ArraytoTS(BaseComponent):
-
-    INPUT_KEYS = ["ori_ts", "pred"]
-    OUTPUT_KEYS = ["pred"]
-    DEAULT_INPUTS = {"ori_ts": "ori_ts", "pred": "pred"}
-    DEAULT_OUTPUTS = {"pred": "pred"}
-
-    def __init__(self, info_params):
-        super().__init__()
-        self.info_params = info_params
-
-    def apply(self, ori_ts, pred):
-        pred = pred[0]
-        if ori_ts.get("past_target", None) is not None:
-            ts = ori_ts["past_target"]
-        elif ori_ts.get("observed_cov_numeric", None) is not None:
-            ts = ori_ts["observed_cov_numeric"]
-        elif ori_ts.get("known_cov_numeric", None) is not None:
-            ts = ori_ts["known_cov_numeric"]
-        elif ori_ts.get("static_cov_numeric", None) is not None:
-            ts = ori_ts["static_cov_numeric"]
-        else:
-            raise ValueError("No value in ori_ts")
-
-        column_name = (
-            self.info_params["target_cols"]
-            if "target_cols" in self.info_params
-            else self.info_params["feature_cols"]
-        )
-        if isinstance(self.info_params["freq"], str):
-            past_target_index = ts.index
-            if past_target_index.freq is None:
-                past_target_index.freq = pd.infer_freq(ts.index)
-            future_target_index = pd.date_range(
-                past_target_index[-1] + past_target_index.freq,
-                periods=pred.shape[0],
-                freq=self.info_params["freq"],
-                name=self.info_params["time_col"],
-            )
-        elif isinstance(self.info_params["freq"], int):
-            start_idx = max(ts.index) + 1
-            stop_idx = start_idx + pred.shape[0]
-            future_target_index = pd.RangeIndex(
-                start=start_idx,
-                stop=stop_idx,
-                step=self.info_params["freq"],
-                name=self.info_params["time_col"],
-            )
-
-        future_target = pd.DataFrame(
-            np.reshape(pred, newshape=[pred.shape[0], -1]),
-            index=future_target_index,
-            columns=column_name,
-        )
-        return {"pred": future_target}
-
-
-class GetAnomaly(BaseComponent):
-
-    INPUT_KEYS = ["ori_ts", "pred"]
-    OUTPUT_KEYS = ["anomaly"]
-    DEAULT_INPUTS = {"ori_ts": "ori_ts", "pred": "pred"}
-    DEAULT_OUTPUTS = {"anomaly": "anomaly"}
-
-    def __init__(self, model_threshold, info_params):
-        super().__init__()
-        self.model_threshold = model_threshold
-        self.info_params = info_params
-
-    def apply(self, ori_ts, pred):
-        pred = pred[0]
-        if ori_ts.get("past_target", None) is not None:
-            ts = ori_ts["past_target"]
-        elif ori_ts.get("observed_cov_numeric", None) is not None:
-            ts = ori_ts["observed_cov_numeric"]
-        elif ori_ts.get("known_cov_numeric", None) is not None:
-            ts = ori_ts["known_cov_numeric"]
-        elif ori_ts.get("static_cov_numeric", None) is not None:
-            ts = ori_ts["static_cov_numeric"]
-        else:
-            raise ValueError("No value in ori_ts")
-        column_name = (
-            self.info_params["target_cols"]
-            if "target_cols" in self.info_params
-            else self.info_params["feature_cols"]
-        )
-
-        anomaly_score = np.mean(np.square(pred - np.array(ts)), axis=-1)
-        anomaly_label = (anomaly_score >= self.model_threshold) + 0
-
-        past_target_index = ts.index
-        past_target_index.name = self.info_params["time_col"]
-        anomaly_label = pd.DataFrame(
-            np.reshape(anomaly_label, newshape=[pred.shape[0], -1]),
-            index=past_target_index,
-            columns=["label"],
-        )
-        return {"anomaly": anomaly_label}
-
-
-class GetCls(BaseComponent):
-
-    INPUT_KEYS = ["pred"]
-    OUTPUT_KEYS = ["classification"]
-    DEAULT_INPUTS = {"pred": "pred"}
-    DEAULT_OUTPUTS = {"classification": "classification"}
-
-    def __init__(self):
-        super().__init__()
-
-    def apply(self, pred):
-        pred_ts = pred[0]
-        pred_ts -= np.max(pred_ts, axis=-1, keepdims=True)
-        pred_ts = np.exp(pred_ts) / np.sum(np.exp(pred_ts), axis=-1, keepdims=True)
-        classid = np.argmax(pred_ts, axis=-1)
-        pred_score = pred_ts[classid]
-        result = pd.DataFrame.from_dict({"classid": [classid], "score": [pred_score]})
-        result.index.name = "sample"
-        return {"classification": result}

+ 0 - 424
paddlex/inference/components/transforms/ts/funcs.py

@@ -1,424 +0,0 @@
-# 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
-from typing import Any, Callable, List, Optional, Sequence, Tuple, Union, Dict
-import numpy as np
-import pandas as pd
-import joblib
-import chinese_calendar
-from pandas.tseries.offsets import DateOffset, Easter, Day
-from pandas.tseries import holiday as hd
-from sklearn.preprocessing import StandardScaler
-
-
-MAX_WINDOW = 183 + 17
-EasterSunday = hd.Holiday("Easter Sunday", month=1, day=1, offset=[Easter(), Day(0)])
-NewYearsDay = hd.Holiday("New Years Day", month=1, day=1)
-SuperBowl = hd.Holiday("Superbowl", month=2, day=1, offset=DateOffset(weekday=hd.SU(1)))
-MothersDay = hd.Holiday(
-    "Mothers Day", month=5, day=1, offset=DateOffset(weekday=hd.SU(2))
-)
-IndependenceDay = hd.Holiday("Independence Day", month=7, day=4)
-ChristmasEve = hd.Holiday("Christmas", month=12, day=24)
-ChristmasDay = hd.Holiday("Christmas", month=12, day=25)
-NewYearsEve = hd.Holiday("New Years Eve", month=12, day=31)
-BlackFriday = hd.Holiday(
-    "Black Friday",
-    month=11,
-    day=1,
-    offset=[pd.DateOffset(weekday=hd.TH(4)), Day(1)],
-)
-CyberMonday = hd.Holiday(
-    "Cyber Monday",
-    month=11,
-    day=1,
-    offset=[pd.DateOffset(weekday=hd.TH(4)), Day(4)],
-)
-
-HOLIDAYS = [
-    hd.EasterMonday,
-    hd.GoodFriday,
-    hd.USColumbusDay,
-    hd.USLaborDay,
-    hd.USMartinLutherKingJr,
-    hd.USMemorialDay,
-    hd.USPresidentsDay,
-    hd.USThanksgivingDay,
-    EasterSunday,
-    NewYearsDay,
-    SuperBowl,
-    MothersDay,
-    IndependenceDay,
-    ChristmasEve,
-    ChristmasDay,
-    NewYearsEve,
-    BlackFriday,
-    CyberMonday,
-]
-
-
-def _cal_year(
-    x: np.datetime64,
-):
-    return x.year
-
-
-def _cal_month(
-    x: np.datetime64,
-):
-    return x.month
-
-
-def _cal_day(
-    x: np.datetime64,
-):
-    return x.day
-
-
-def _cal_hour(
-    x: np.datetime64,
-):
-    return x.hour
-
-
-def _cal_weekday(
-    x: np.datetime64,
-):
-    return x.dayofweek
-
-
-def _cal_quarter(
-    x: np.datetime64,
-):
-    return x.quarter
-
-
-def _cal_hourofday(
-    x: np.datetime64,
-):
-    return x.hour / 23.0 - 0.5
-
-
-def _cal_dayofweek(
-    x: np.datetime64,
-):
-    return x.dayofweek / 6.0 - 0.5
-
-
-def _cal_dayofmonth(
-    x: np.datetime64,
-):
-    return x.day / 30.0 - 0.5
-
-
-def _cal_dayofyear(
-    x: np.datetime64,
-):
-    return x.dayofyear / 364.0 - 0.5
-
-
-def _cal_weekofyear(
-    x: np.datetime64,
-):
-    return x.weekofyear / 51.0 - 0.5
-
-
-def _cal_holiday(
-    x: np.datetime64,
-):
-    return float(chinese_calendar.is_holiday(x))
-
-
-def _cal_workday(
-    x: np.datetime64,
-):
-    return float(chinese_calendar.is_workday(x))
-
-
-def _cal_minuteofhour(
-    x: np.datetime64,
-):
-    return x.minute / 59 - 0.5
-
-
-def _cal_monthofyear(
-    x: np.datetime64,
-):
-    return x.month / 11.0 - 0.5
-
-
-CAL_DATE_METHOD = {
-    "year": _cal_year,
-    "month": _cal_month,
-    "day": _cal_day,
-    "hour": _cal_hour,
-    "weekday": _cal_weekday,
-    "quarter": _cal_quarter,
-    "minuteofhour": _cal_minuteofhour,
-    "monthofyear": _cal_monthofyear,
-    "hourofday": _cal_hourofday,
-    "dayofweek": _cal_dayofweek,
-    "dayofmonth": _cal_dayofmonth,
-    "dayofyear": _cal_dayofyear,
-    "weekofyear": _cal_weekofyear,
-    "is_holiday": _cal_holiday,
-    "is_workday": _cal_workday,
-}
-
-
-def load_from_one_dataframe(
-    data: Union[pd.DataFrame, pd.Series],
-    time_col: Optional[str] = None,
-    value_cols: Optional[Union[List[str], str]] = None,
-    freq: Optional[Union[str, int]] = None,
-    drop_tail_nan: bool = False,
-    dtype: Optional[Union[type, Dict[str, type]]] = None,
-):
-
-    series_data = None
-    if value_cols is None:
-        if isinstance(data, pd.Series):
-            series_data = data.copy()
-        else:
-            series_data = data.loc[:, data.columns != time_col].copy()
-    else:
-        series_data = data.loc[:, value_cols].copy()
-
-    if time_col:
-        if time_col not in data.columns:
-            raise ValueError(
-                "The time column: {} doesn't exist in the `data`!".format(time_col)
-            )
-        time_col_vals = data.loc[:, time_col]
-    else:
-        time_col_vals = data.index
-
-    if np.issubdtype(time_col_vals.dtype, np.integer) and isinstance(freq, str):
-        time_col_vals = time_col_vals.astype(str)
-
-    if np.issubdtype(time_col_vals.dtype, np.integer):
-        if freq:
-            if not isinstance(freq, int) or freq < 1:
-                raise ValueError(
-                    "The type of `freq` should be `int` when the type of `time_col` is `RangeIndex`."
-                )
-        else:
-            freq = 1
-        start_idx, stop_idx = min(time_col_vals), max(time_col_vals) + freq
-        if (stop_idx - start_idx) / freq != len(data):
-            raise ValueError("The number of rows doesn't match with the RangeIndex!")
-        time_index = pd.RangeIndex(start=start_idx, stop=stop_idx, step=freq)
-    elif np.issubdtype(time_col_vals.dtype, np.object_) or np.issubdtype(
-        time_col_vals.dtype, np.datetime64
-    ):
-        time_col_vals = pd.to_datetime(time_col_vals, infer_datetime_format=True)
-        time_index = pd.DatetimeIndex(time_col_vals)
-        if freq:
-            if not isinstance(freq, str):
-                raise ValueError(
-                    "The type of `freq` should be `str` when the type of `time_col` is `DatetimeIndex`."
-                )
-        else:
-            # If freq is not provided and automatic inference fail, throw exception
-            freq = pd.infer_freq(time_index)
-            if freq is None:
-                raise ValueError(
-                    "Failed to infer the `freq`. A valid `freq` is required."
-                )
-            if freq[0] == "-":
-                freq = freq[1:]
-    else:
-        raise ValueError("The type of `time_col` is invalid.")
-    if isinstance(series_data, pd.Series):
-        series_data = series_data.to_frame()
-    series_data.set_index(time_index, inplace=True)
-    series_data.sort_index(inplace=True)
-    return series_data
-
-
-def load_from_dataframe(
-    df: pd.DataFrame,
-    group_id: str = None,
-    time_col: Optional[str] = None,
-    target_cols: Optional[Union[List[str], str]] = None,
-    label_col: Optional[Union[List[str], str]] = None,
-    observed_cov_cols: Optional[Union[List[str], str]] = None,
-    feature_cols: Optional[Union[List[str], str]] = None,
-    known_cov_cols: Optional[Union[List[str], str]] = None,
-    static_cov_cols: Optional[Union[List[str], str]] = None,
-    freq: Optional[Union[str, int]] = None,
-    fill_missing_dates: bool = False,
-    fillna_method: str = "pre",
-    fillna_window_size: int = 10,
-    **kwargs,
-):
-
-    dfs = []  # seperate multiple group
-    if group_id is not None:
-        group_unique = df[group_id].unique()
-        for column in group_unique:
-            dfs.append(df[df[group_id].isin([column])])
-    else:
-        dfs = [df]
-    res = []
-    if label_col:
-        if isinstance(label_col, str) and len(label_col) > 1:
-            raise ValueError("The length of label_col must be 1.")
-        target_cols = label_col
-    if feature_cols:
-        observed_cov_cols = feature_cols
-    for df in dfs:
-        target = None
-        observed_cov = None
-        known_cov = None
-        static_cov = dict()
-        if not any([target_cols, observed_cov_cols, known_cov_cols, static_cov_cols]):
-            target = load_from_one_dataframe(
-                df,
-                time_col,
-                [a for a in df.columns if a != time_col],
-                freq,
-            )
-
-        else:
-            if target_cols:
-                target = load_from_one_dataframe(
-                    df,
-                    time_col,
-                    target_cols,
-                    freq,
-                )
-
-            if observed_cov_cols:
-                observed_cov = load_from_one_dataframe(
-                    df,
-                    time_col,
-                    observed_cov_cols,
-                    freq,
-                )
-
-            if known_cov_cols:
-                known_cov = load_from_one_dataframe(
-                    df,
-                    time_col,
-                    known_cov_cols,
-                    freq,
-                )
-
-            if static_cov_cols:
-                if isinstance(static_cov_cols, str):
-                    static_cov_cols = [static_cov_cols]
-                for col in static_cov_cols:
-                    if col not in df.columns or len(np.unique(df[col])) != 1:
-                        raise ValueError(
-                            "static cov cals data is not in columns or schema is not right!"
-                        )
-                    static_cov[col] = df[col].iloc[0]
-        res.append(
-            {
-                "past_target": target,
-                "observed_cov_numeric": observed_cov,
-                "known_cov_numeric": known_cov,
-                "static_cov_numeric": static_cov,
-            }
-        )
-    return res[0]
-
-
-def _distance_to_holiday(holiday):
-    def _distance_to_day(index):
-        holiday_date = holiday.dates(
-            index - pd.Timedelta(days=MAX_WINDOW),
-            index + pd.Timedelta(days=MAX_WINDOW),
-        )
-        assert (
-            len(holiday_date) != 0
-        ), f"No closest holiday for the date index {index} found."
-        # It sometimes returns two dates if it is exactly half a year after the
-        # holiday. In this case, the smaller distance (182 days) is returned.
-        return float((index - holiday_date[0]).days)
-
-    return _distance_to_day
-
-
-def time_feature(dataset, freq, feature_cols, extend_points, inplace: bool = False):
-    """
-    Transform time column to time features.
-
-    Args:
-        dataset(TSDataset): Dataset to be transformed.
-        inplace(bool): Whether to perform the transformation inplace. default=False
-
-    Returns:
-        TSDataset
-    """
-    new_ts = dataset
-    if not inplace:
-        new_ts = dataset.copy()
-    # Get known_cov
-    kcov = new_ts["known_cov_numeric"]
-    if not kcov:
-        tf_kcov = new_ts["past_target"].index.to_frame()
-    else:
-        tf_kcov = kcov.index.to_frame()
-    time_col = tf_kcov.columns[0]
-    if np.issubdtype(tf_kcov[time_col].dtype, np.integer):
-        raise ValueError(
-            "The time_col can't be the type of numpy.integer, and it must be the type of numpy.datetime64"
-        )
-    if not kcov:
-        freq = freq if freq is not None else pd.infer_freq(tf_kcov[time_col])
-        extend_time = pd.date_range(
-            start=tf_kcov[time_col][-1],
-            freq=freq,
-            periods=extend_points + 1,
-            closed="right",
-            name=time_col,
-        ).to_frame()
-        tf_kcov = pd.concat([tf_kcov, extend_time])
-
-    for k in feature_cols:
-        if k != "holidays":
-            v = tf_kcov[time_col].apply(lambda x: CAL_DATE_METHOD[k](x))
-            v.index = tf_kcov[time_col]
-
-            if new_ts["known_cov_numeric"] is None:
-                new_ts["known_cov_numeric"] = pd.DataFrame(v.rename(k), index=v.index)
-            else:
-                new_ts["known_cov_numeric"][k] = v.rename(k).reindex(
-                    new_ts["known_cov_numeric"].index
-                )
-
-        else:
-            holidays_col = []
-            for i, H in enumerate(HOLIDAYS):
-                v = tf_kcov[time_col].apply(_distance_to_holiday(H))
-                v.index = tf_kcov[time_col]
-                holidays_col.append(k + "_" + str(i))
-                if new_ts["known_cov_numeric"] is None:
-                    new_ts["known_cov_numeric"] = pd.DataFrame(
-                        v.rename(k + "_" + str(i)), index=v.index
-                    )
-                else:
-                    new_ts["known_cov_numeric"][k + "_" + str(i)] = v.rename(k).reindex(
-                        new_ts["known_cov_numeric"].index
-                    )
-
-            scaler = StandardScaler()
-            scaler.fit(new_ts["known_cov_numeric"][holidays_col])
-            new_ts["known_cov_numeric"][holidays_col] = scaler.transform(
-                new_ts["known_cov_numeric"][holidays_col]
-            )
-    return new_ts

+ 0 - 1
paddlex/inference/models/base/__init__.py

@@ -12,5 +12,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from .pp_infer import BaseInfer
 from .predictor import BasePredictor, BasicPredictor

+ 0 - 15
paddlex/inference/models/base/pp_infer/__init__.py

@@ -1,15 +0,0 @@
-# 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.
-
-from .base_infer import BaseInfer

+ 0 - 17
paddlex/inference/models/base/pp_infer/base_infer.py

@@ -1,17 +0,0 @@
-# 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.
-
-
-class BaseInfer:
-    pass

+ 0 - 54
paddlex/inference/utils/process_hook.py

@@ -1,54 +0,0 @@
-# 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 inspect
-import functools
-from types import GeneratorType
-
-
-def batchable_method(func):
-    """batchable"""
-
-    @functools.wraps(func)
-    def _wrapper(self, input_, *args, **kwargs):
-        if isinstance(input_, list):
-            output = []
-            for ele in input_:
-                out = func(self, ele, *args, **kwargs)
-                output.append(out)
-            return output
-        else:
-            return func(self, input_, *args, **kwargs)
-
-    sig = inspect.signature(func)
-    if not len(sig.parameters) >= 2:
-        raise TypeError("The function to wrap should have at least two parameters.")
-    return _wrapper
-
-
-def generatorable_method(func):
-    """generatorable"""
-
-    @functools.wraps(func)
-    def _wrapper(self, input_, *args, **kwargs):
-        if isinstance(input_, GeneratorType):
-            for ele in input_:
-                yield func(self, ele, *args, **kwargs)
-        else:
-            yield func(self, input_, *args, **kwargs)
-
-    sig = inspect.signature(func)
-    if not len(sig.parameters) >= 2:
-        raise TypeError("The function to wrap should have at least two parameters.")
-    return _wrapper