跳转至

yzm模块

超级鹰验证码识别

Chaojiying_Client

超级鹰验证码客服端

参考文档: http://www.chaojiying.com/api.php

Attributes:

Name Type Description
username str

超级鹰用户名

password str

超级鹰密码

soft_id str

超级鹰软件ID

Source code in src/cfun/yzm/chaojiying.py
class Chaojiying_Client:
    """
    超级鹰验证码客服端

    参考文档: http://www.chaojiying.com/api.php

    Attributes:
        username (str): 超级鹰用户名
        password (str): 超级鹰密码
        soft_id (str): 超级鹰软件ID
    """

    def __init__(self, username, password, soft_id):
        """初始化超级鹰客户端

        Args:
            username (str): 超级鹰用户名
            password (str): 超级鹰密码
            soft_id (str): 超级鹰软件ID
        """

        self.username = username
        password = password.encode("utf8")
        self.password = md5(password).hexdigest()
        self.soft_id = soft_id
        self.base_params = {
            "user": self.username,
            "pass2": self.password,
            "softid": self.soft_id,
        }
        self.headers = {
            "Connection": "Keep-Alive",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 Edg/135.0.0.0",
        }

    @staticmethod
    def calculate_md5(file_path: Path | str) -> str:
        """计算文件的 MD5 值

        Args:
            file_path (str): 文件路径

        Returns:
            str: 文件的 MD5 值
        """
        if isinstance(file_path, str):
            file_path = Path(file_path)

        assert file_path.exists(), f"文件不存在:{file_path}"
        assert file_path.is_file(), f"路径不是文件:{file_path}"
        assert file_path.stat().st_size > 0, f"文件大小为0:{file_path}"
        hash_md5 = hashlib.md5()
        with file_path.open("rb") as f:
            for chunk in iter(lambda: f.read(4096), b""):
                hash_md5.update(chunk)
        return hash_md5.hexdigest()

    @staticmethod
    def read_image(image_path: Path | str) -> bytes:
        """读取图片文件并返回字节

        Args:
            image_path (str): 图片路径

        Returns:
            bytes: 图片字节
        """
        if isinstance(image_path, str):
            image_path = Path(image_path)
        assert image_path.is_file(), f"路径不是文件:{image_path}"
        assert image_path.stat().st_size > 0, f"文件大小为0:{image_path}"
        assert Path(image_path).exists(), f"文件不存在:{image_path}"

        with open(image_path, "rb") as f:
            image = f.read()
        return image

    def PostPic(self, im, codetype) -> dict:
        """通过网络上传图片进行识别

        Args:
            im (bytes): 图片字节
            codetype (int): 题目类型 参考 http://www.chaojiying.com/price.html

        Returns:
            dict: 超级鹰返回的结果
        """
        params = {
            "codetype": codetype,
        }
        params.update(self.base_params)
        files = {"userfile": ("ccc.jpg", im)}
        r = requests.post(
            "http://upload.chaojiying.net/Upload/Processing.php",
            data=params,
            files=files,
            headers=self.headers,
        )
        return r.json()

    def PostPic_base64(self, base64_str, codetype) -> dict:
        """通过网络上传图片进行识别

        Args:
            base64_str (str): 图片的base64编码字符串
            codetype (int): 题目类型 参考 http://www.chaojiying.com/price.html

        Returns:
            dict: 超级鹰返回的结果
        """
        params = {"codetype": codetype, "file_base64": base64_str}
        params.update(self.base_params)
        r = requests.post(
            "http://upload.chaojiying.net/Upload/Processing.php",
            data=params,
            headers=self.headers,
        )
        return r.json()

    def ReportError(self, im_id) -> dict:
        """上传错误的图片ID进行反馈

        Args:
            im_id (str): 图片ID

        Returns:
            dict: 超级鹰返回的结果
        """
        params = {
            "id": im_id,
        }
        params.update(self.base_params)
        r = requests.post(
            "http://upload.chaojiying.net/Upload/ReportError.php",
            data=params,
            headers=self.headers,
        )
        return r.json()

    def parse_response(self, res: dict | str) -> list:
        """解析超级鹰返回的字符串格式

        Args:
            res (str | dict): 超级鹰返回的字符串 或者字典格式

        Returns:
            list: 解析后的列表
                [
                    {"name": "之", "coordinates": [207, 115]},
                    {"name": "成", "coordinates": [158, 86]},
                    {"name": "人", "coordinates": [126, 44]}
                ]

        Example:
            ```python
            input_str = '之,207,115|成,158,86|人,126,44' #或者直接输入字典格, 会提取里面的 pic_str字段
            output = parse_string_chaojiying(input_str)
            print(output)
            ```
        """

        if isinstance(res, dict):
            res = res["pic_str"]

        assert isinstance(res, str), "超级鹰识别失败,请检查图片是否正确"
        assert "|" in res, "超级鹰识别失败,请检查图片是否正确"
        items = res.split("|")

        result = []
        for item in items:
            # 将每个部分再以 "," 分隔
            parts = item.split(",")
            name = parts[0]  # 第一个部分是 name
            x = int(parts[1])  # 第二个部分是 x 坐标
            y = int(parts[2])

            # 创建字典对象并添加到列表
            result.append({"name": name, "coordinates": [x, y]})

        return result

    def get(self, image_path: str, codetype: int = 9800) -> list:
        """根据图片和题目类型获取超级鹰识别结果,

        根据图片和题目类型获取超级鹰识别结果, 暂时只针对 codetype=9800 进行处理,其他类型的暂未测试

        Args:
            image_path (str): 图片路径
            codetype (int): 题目类型 参考 http://www.chaojiying.com/price.html

        Returns:
            list: 扩展后的结果


        Example:
            ```python
            from cfun.yzm.chaojiying import Chaojiying_Client
            from cfun.yzm.dddocrtool import ImageDet
            chaojiying = Chaojiying_Client("****", "****", "***")
            image_path = Path(__file__).parent / "images" / "image_detect_01.png"
            result = chaojiying.get(image_path, 9800)
            print(result) #封装后的结果
            '''
            [
                {
                    "name": "之",
                    "coordinates": [207, 115],
                    "points": [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
                    "image_width": 100,
                    "image_height": 100
                },
                {
                    "name": "成",
                    "coordinates": [158, 86],
                    "points": [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
                    "image_width": 100,
                    "image_height": 100
                },
                {
                    "name": "人",
                    "coordinates": [126, 44],
                    "points": [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
                    "image_width": 100,
                    "image_height": 100
                }
            ]
            '''
            ```
        """
        im = self.read_image(image_path)
        res = self.PostPic(im, codetype)

        result = self.parse_response(res)
        extended_data = imgdet.extendCJYRecognition(result, image_path)

        # 计算图片的 md5 值
        md5_value = self.calculate_md5(image_path)
        # 将 md5 值添加到结果中
        for item in extended_data:
            item["rawimgmd5"] = md5_value
        return extended_data

__init__

__init__(username, password, soft_id)

初始化超级鹰客户端

Parameters:

Name Type Description Default
username str

超级鹰用户名

required
password str

超级鹰密码

required
soft_id str

超级鹰软件ID

required
Source code in src/cfun/yzm/chaojiying.py
def __init__(self, username, password, soft_id):
    """初始化超级鹰客户端

    Args:
        username (str): 超级鹰用户名
        password (str): 超级鹰密码
        soft_id (str): 超级鹰软件ID
    """

    self.username = username
    password = password.encode("utf8")
    self.password = md5(password).hexdigest()
    self.soft_id = soft_id
    self.base_params = {
        "user": self.username,
        "pass2": self.password,
        "softid": self.soft_id,
    }
    self.headers = {
        "Connection": "Keep-Alive",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 Edg/135.0.0.0",
    }

calculate_md5 staticmethod

calculate_md5(file_path: Path | str) -> str

计算文件的 MD5 值

Parameters:

Name Type Description Default
file_path str

文件路径

required

Returns:

Name Type Description
str str

文件的 MD5 值

Source code in src/cfun/yzm/chaojiying.py
@staticmethod
def calculate_md5(file_path: Path | str) -> str:
    """计算文件的 MD5 值

    Args:
        file_path (str): 文件路径

    Returns:
        str: 文件的 MD5 值
    """
    if isinstance(file_path, str):
        file_path = Path(file_path)

    assert file_path.exists(), f"文件不存在:{file_path}"
    assert file_path.is_file(), f"路径不是文件:{file_path}"
    assert file_path.stat().st_size > 0, f"文件大小为0:{file_path}"
    hash_md5 = hashlib.md5()
    with file_path.open("rb") as f:
        for chunk in iter(lambda: f.read(4096), b""):
            hash_md5.update(chunk)
    return hash_md5.hexdigest()

read_image staticmethod

read_image(image_path: Path | str) -> bytes

读取图片文件并返回字节

Parameters:

Name Type Description Default
image_path str

图片路径

required

Returns:

Name Type Description
bytes bytes

图片字节

Source code in src/cfun/yzm/chaojiying.py
@staticmethod
def read_image(image_path: Path | str) -> bytes:
    """读取图片文件并返回字节

    Args:
        image_path (str): 图片路径

    Returns:
        bytes: 图片字节
    """
    if isinstance(image_path, str):
        image_path = Path(image_path)
    assert image_path.is_file(), f"路径不是文件:{image_path}"
    assert image_path.stat().st_size > 0, f"文件大小为0:{image_path}"
    assert Path(image_path).exists(), f"文件不存在:{image_path}"

    with open(image_path, "rb") as f:
        image = f.read()
    return image

PostPic

PostPic(im, codetype) -> dict

通过网络上传图片进行识别

Parameters:

Name Type Description Default
im bytes

图片字节

required
codetype int

题目类型 参考 http://www.chaojiying.com/price.html

required

Returns:

Name Type Description
dict dict

超级鹰返回的结果

Source code in src/cfun/yzm/chaojiying.py
def PostPic(self, im, codetype) -> dict:
    """通过网络上传图片进行识别

    Args:
        im (bytes): 图片字节
        codetype (int): 题目类型 参考 http://www.chaojiying.com/price.html

    Returns:
        dict: 超级鹰返回的结果
    """
    params = {
        "codetype": codetype,
    }
    params.update(self.base_params)
    files = {"userfile": ("ccc.jpg", im)}
    r = requests.post(
        "http://upload.chaojiying.net/Upload/Processing.php",
        data=params,
        files=files,
        headers=self.headers,
    )
    return r.json()

PostPic_base64

PostPic_base64(base64_str, codetype) -> dict

通过网络上传图片进行识别

Parameters:

Name Type Description Default
base64_str str

图片的base64编码字符串

required
codetype int

题目类型 参考 http://www.chaojiying.com/price.html

required

Returns:

Name Type Description
dict dict

超级鹰返回的结果

Source code in src/cfun/yzm/chaojiying.py
def PostPic_base64(self, base64_str, codetype) -> dict:
    """通过网络上传图片进行识别

    Args:
        base64_str (str): 图片的base64编码字符串
        codetype (int): 题目类型 参考 http://www.chaojiying.com/price.html

    Returns:
        dict: 超级鹰返回的结果
    """
    params = {"codetype": codetype, "file_base64": base64_str}
    params.update(self.base_params)
    r = requests.post(
        "http://upload.chaojiying.net/Upload/Processing.php",
        data=params,
        headers=self.headers,
    )
    return r.json()

ReportError

ReportError(im_id) -> dict

上传错误的图片ID进行反馈

Parameters:

Name Type Description Default
im_id str

图片ID

required

Returns:

Name Type Description
dict dict

超级鹰返回的结果

Source code in src/cfun/yzm/chaojiying.py
def ReportError(self, im_id) -> dict:
    """上传错误的图片ID进行反馈

    Args:
        im_id (str): 图片ID

    Returns:
        dict: 超级鹰返回的结果
    """
    params = {
        "id": im_id,
    }
    params.update(self.base_params)
    r = requests.post(
        "http://upload.chaojiying.net/Upload/ReportError.php",
        data=params,
        headers=self.headers,
    )
    return r.json()

parse_response

parse_response(res: dict | str) -> list

解析超级鹰返回的字符串格式

Parameters:

Name Type Description Default
res str | dict

超级鹰返回的字符串 或者字典格式

required

Returns:

Name Type Description
list list

解析后的列表 [ {"name": "之", "coordinates": [207, 115]}, {"name": "成", "coordinates": [158, 86]}, {"name": "人", "coordinates": [126, 44]} ]

Example
1
2
3
input_str = '之,207,115|成,158,86|人,126,44' #或者直接输入字典格, 会提取里面的 pic_str字段
output = parse_string_chaojiying(input_str)
print(output)
Source code in src/cfun/yzm/chaojiying.py
def parse_response(self, res: dict | str) -> list:
    """解析超级鹰返回的字符串格式

    Args:
        res (str | dict): 超级鹰返回的字符串 或者字典格式

    Returns:
        list: 解析后的列表
            [
                {"name": "之", "coordinates": [207, 115]},
                {"name": "成", "coordinates": [158, 86]},
                {"name": "人", "coordinates": [126, 44]}
            ]

    Example:
        ```python
        input_str = '之,207,115|成,158,86|人,126,44' #或者直接输入字典格, 会提取里面的 pic_str字段
        output = parse_string_chaojiying(input_str)
        print(output)
        ```
    """

    if isinstance(res, dict):
        res = res["pic_str"]

    assert isinstance(res, str), "超级鹰识别失败,请检查图片是否正确"
    assert "|" in res, "超级鹰识别失败,请检查图片是否正确"
    items = res.split("|")

    result = []
    for item in items:
        # 将每个部分再以 "," 分隔
        parts = item.split(",")
        name = parts[0]  # 第一个部分是 name
        x = int(parts[1])  # 第二个部分是 x 坐标
        y = int(parts[2])

        # 创建字典对象并添加到列表
        result.append({"name": name, "coordinates": [x, y]})

    return result

get

get(image_path: str, codetype: int = 9800) -> list

根据图片和题目类型获取超级鹰识别结果,

根据图片和题目类型获取超级鹰识别结果, 暂时只针对 codetype=9800 进行处理,其他类型的暂未测试

Parameters:

Name Type Description Default
image_path str

图片路径

required
codetype int

题目类型 参考 http://www.chaojiying.com/price.html

9800

Returns:

Name Type Description
list list

扩展后的结果

Example
from cfun.yzm.chaojiying import Chaojiying_Client
from cfun.yzm.dddocrtool import ImageDet
chaojiying = Chaojiying_Client("****", "****", "***")
image_path = Path(__file__).parent / "images" / "image_detect_01.png"
result = chaojiying.get(image_path, 9800)
print(result) #封装后的结果
'''
[
    {
        "name": "之",
        "coordinates": [207, 115],
        "points": [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
        "image_width": 100,
        "image_height": 100
    },
    {
        "name": "成",
        "coordinates": [158, 86],
        "points": [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
        "image_width": 100,
        "image_height": 100
    },
    {
        "name": "人",
        "coordinates": [126, 44],
        "points": [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
        "image_width": 100,
        "image_height": 100
    }
]
'''
Source code in src/cfun/yzm/chaojiying.py
def get(self, image_path: str, codetype: int = 9800) -> list:
    """根据图片和题目类型获取超级鹰识别结果,

    根据图片和题目类型获取超级鹰识别结果, 暂时只针对 codetype=9800 进行处理,其他类型的暂未测试

    Args:
        image_path (str): 图片路径
        codetype (int): 题目类型 参考 http://www.chaojiying.com/price.html

    Returns:
        list: 扩展后的结果


    Example:
        ```python
        from cfun.yzm.chaojiying import Chaojiying_Client
        from cfun.yzm.dddocrtool import ImageDet
        chaojiying = Chaojiying_Client("****", "****", "***")
        image_path = Path(__file__).parent / "images" / "image_detect_01.png"
        result = chaojiying.get(image_path, 9800)
        print(result) #封装后的结果
        '''
        [
            {
                "name": "之",
                "coordinates": [207, 115],
                "points": [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
                "image_width": 100,
                "image_height": 100
            },
            {
                "name": "成",
                "coordinates": [158, 86],
                "points": [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
                "image_width": 100,
                "image_height": 100
            },
            {
                "name": "人",
                "coordinates": [126, 44],
                "points": [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
                "image_width": 100,
                "image_height": 100
            }
        ]
        '''
        ```
    """
    im = self.read_image(image_path)
    res = self.PostPic(im, codetype)

    result = self.parse_response(res)
    extended_data = imgdet.extendCJYRecognition(result, image_path)

    # 计算图片的 md5 值
    md5_value = self.calculate_md5(image_path)
    # 将 md5 值添加到结果中
    for item in extended_data:
        item["rawimgmd5"] = md5_value
    return extended_data

ddocrtool封装

ddddocr 目标检测工具类

这个类主要是对 ddddocr 进行封装, 方便使用

主要的功能:

  1. 目标检测,获取目标检测框

  2. 获取图片的宽高

  3. 将 ddddocr 返回的目标检测框转换为 xlabing 的格式 (xyxy格式)

  4. 删除太近边缘的框

  5. 检查坐标是否在给定的矩形范围内

  6. 扩展超级鹰识别的结果,增加目标检测框的坐标 (主要的功能)

  7. 检查框的格式是否正确

ImageDet

Source code in src/cfun/yzm/dddocrtool.py
class ImageDet:
    def __init__(self):
        self.det = ddddocr.DdddOcr(det=True, show_ad=False)

    def detection(self, image_path: str):
        with open(image_path, "rb") as f:
            image = f.read()
        bboxes = self.det.detection(image)
        # 返回的是一个list, 每个元素是一个框的坐标
        # eg: [[x1, y1, x2, y2], [x1, y1, x2, y2], ...] ,xyxy格式
        return bboxes

    def get_image_size(self, image_path: str | Path) -> tuple[int, int, None]:
        """通过 PIL 库获取图片的宽高

        Args:
            image_path (str | Path): 图片路径

        Returns:
            tuple(width, height, channels):  图片的宽高和通道数
        """
        if isinstance(image_path, Path):
            image_path = str(image_path)

        with Image.open(image_path) as img:
            width, height = img.size
        return width, height, None

    def toxlabeling(
        self, image_path: Optional[str] = None, bboxes: Optional[list] = None
    ) -> list:
        """把 ddddocr 返回的目标检测框转换为 xlabing 的格式 (xyxy格式)

        Args:
            image_path (str): 图片路径 (与 bboxes 二选一)
            bboxes (list): [[x1, y1, x2, y2], ...] (与 image_path 二选一), 这个坐标 左上,右下, 也就是 xyxy 的格式

        Returns:
            list: [[[x1, y1], [x2, y1], [x2, y2], [x1, y2]], ...]

        PS:
            1. 这个函数主要是把 ddddocr 返回的目标检测框转换为 xlabing 的格式, 也就是四个点的格式, 具体的格式是:

            ```
            [
                [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
                [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
                ...
            ]
            ```
        """
        if image_path is None and bboxes is None:
            raise ValueError("image_path 和 bboxes 必须二选一,不能都为 None")

        if image_path is not None and bboxes is not None:
            raise ValueError("image_path 和 bboxes 不能同时使用")

        if bboxes is not None:
            bboxes = self.detection(image_path)

        assert isinstance(bboxes, list)
        assert all(isinstance(bbox, list) and len(bbox) == 4 for bbox in bboxes)

        result = []
        for bbox in bboxes:
            x1, y1, x2, y2 = bbox
            result.append([[x1, y1], [x2, y1], [x2, y2], [x1, y2]])
        return result

    @staticmethod
    def _delete_bianyuan(bboxes, image_width, image_height, b=5):
        """删除太近边缘的框 (xyxy)

        Args:
            bboxes (list): [[x1, y1, x2, y2], ...], (左上角, 右下角),ddddocr 返回的格式
            image_width (int): 图片宽度
            image_height (int): 图片高度
            b (int): 边缘距离, 默认是 5

        Returns:
            list: 新的框列表,还是 [[x1, y1, x2, y2], ...] 的格式
        """
        assert all(isinstance(bbox, list) and len(bbox) == 4 for bbox in bboxes), (
            "bboxes 中的每个元素必须是一个列表且长度为4"
        )
        newbboxes = []
        for bbox in bboxes:
            x1, y1, x2, y2 = bbox
            # 检查框是否在边缘
            if x1 < b or y1 < b or x2 > image_width - b or y2 > image_height - b:
                continue
            newbboxes.append(bbox)
        bboxes = newbboxes
        return bboxes

    @staticmethod
    def _check_range(centerxy, box) -> bool:
        """检查坐标是否在给定的矩形范围内

        Args:
            centerxy (list): 坐标 [x, y]
            box (list): 矩形框的坐标 [x1, y1, x2, y2] (左上角, 右下角),ddddocr 返回的格式

        Returns:
            bool: True or False
        """
        assert len(box) == 4, "box must be a list of 4 points"
        assert len(centerxy) == 2, "corxy must be a list of 2 points"
        assert isinstance(box, list), "box must be a list"
        assert isinstance(centerxy, list), "corxy must be a list"
        minx = min(box[0], box[2])
        maxx = max(box[0], box[2])
        miny = min(box[1], box[3])
        maxy = max(box[1], box[3])
        x, y = centerxy

        if minx <= x <= maxx and miny <= y <= maxy:
            # 判断坐标是否在矩形范围内
            return True
        else:
            return False

    def extendCJYRecognition(self, ocrdata: list, image_path: str) -> list:
        """扩展超级鹰识别的结果. (也可以是其他平台的结果, 因为很多平台返回的结果都只含中心点坐标)

        Args:
            ocrdata (list of dict): 超级鹰识别的结果,每个元素是一个字典,格式如下:

                - name (str): 字符内容,例如 `"之"`。
                - coordinates (list of int): 坐标列表,格式如 `[x, y]`。

            image_path (str): 图片路径。

        Returns:
            list of dict: 扩展后的结果,每个字典包含以下字段:

                - name (str): 字符内容。
                - coordinates (list of int): 原始坐标。
                - points (list of list of float): 四个顶点坐标,格式如 `[[x1, y1], [x2, y1], [x2, y2], [x1, y2]]`。
                - image_width (int): 图片宽度。
                - image_height (int): 图片高度。

        Example:
            ```python

            '''
            超级鹰识别的结果是一个: '之,207,115|成,158,86|人,126,44'
                我们可以很轻松的转为:
                [
                    {
                        "name": "之",
                        "coordinates": [207, 115]
                    },
                    {
                        "name": "成",
                        "coordinates": [158, 86]
                    },
                    {
                        "name": "人",
                        "coordinates": [126, 44]
                    }
                ]
            以上只是返回中心点的坐标, 但是我希望得到目标检测框的坐标,即 四个定点的坐标,
            因此在这个结果的基础上,增加一个key, 叫做 points, 其值为 [[x1, y1], [x2, y1], [x2, y2], [x1, y2]]
            采用 xyxy 的格式, 这样就可以直接使用了. 这个目标检测的框如何产生呢? 这里采用ddddocr的目标检测框来进行扩展。
            '''
            from cfun.yzm.dddocrtool import ImageDet
            imgdet = ImageDet()
            ocrdata = [
                {"name": "之", "coordinates": [207, 115]},
                {"name": "成", "coordinates": [158, 86]},
                {"name": "人", "coordinates": [126, 44]}
            ]
            image_path = "restoredUnique2/0a19bbddea_乏宙泡瓜色.png"
            extended_data = imgdet.extendCJYRecognition(ocrdata, image_path)
            print(f"Extended Data: {extended_data}")
            '''
            [
                {
                    "name": "之",
                    "coordinates": [207, 115],
                    "points": [[x1, y1], [x2, y1], [x2, y2], [x1, y2]]
                    "image_width": 100,
                    "image_height": 100
                },
                {
                    "name": "成",
                    "coordinates": [158, 86],
                    "points": [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
                    "image_width": 100,
                    "image_height": 100
                },
                {
                    "name": "人",
                    "coordinates": [126, 44],
                    "points": [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
                    "image_width": 100,
                    "image_height": 100
                }
            ]
            '''
            ```
        """
        bboxes = self.detection(
            image_path
        )  # 返回的坐标,[[x1, y1, x2, y2], [x1, y1, x2, y2], ...] ,xyxy格式
        # 删除一些检测有问题的框, 比如框距离边缘太近的框
        image_width, image_height, _ = self.get_image_size(image_path)
        bboxes = self._delete_bianyuan(bboxes, image_width, image_height)

        # 这里需要判断一下, 如果框的中心点在框内, 则返回这个框的坐标
        for box in bboxes:
            x1, y1, x2, y2 = box
            for idata in ocrdata:
                xy = idata["coordinates"]
                if self._check_range(xy, box):
                    # 如果在框内, 则返回这个框的坐标
                    idata["points"] = [[x1, y1], [x2, y1], [x2, y2], [x1, y2]]
                    idata["image_width"] = image_width
                    idata["image_height"] = image_height
                    break
        # 移除没有 points 的框
        ocrdata = [idata for idata in ocrdata if "points" in idata]
        return deepcopy(ocrdata)

    @staticmethod
    def check_bboxes_four(bboxes: list) -> bool:
        """检查框的格式是否正确(四个点的格式)

        Args:
            bboxes (list): [[[x1, y1], [x2, y1], [x2, y2], [x1, y2]], ....]

        Returns:
            bool: True or False
        """
        if not isinstance(bboxes, list):
            return False
        if all(
            isinstance(bbox, list)
            and len(bbox) == 4
            and all(isinstance(point, list) and len(point) == 2 for point in bbox)
            for bbox in bboxes
        ):
            return True
        else:
            return False

    @staticmethod
    def check_bboxes_two(bboxes: list) -> bool:
        """检查框的格式是否正确(两个点的格式)

        Args:
            bboxes (list): [[x1, y1, x2, y2], ....]

        Returns:
            bool: True or False
        """
        if all(isinstance(bbox, list) and len(bbox) == 4 for bbox in bboxes):
            return True
        else:
            return False

get_image_size

get_image_size(
    image_path: str | Path,
) -> tuple[int, int, None]

通过 PIL 库获取图片的宽高

Parameters:

Name Type Description Default
image_path str | Path

图片路径

required

Returns:

Name Type Description
tuple (width, height, channels)

图片的宽高和通道数

Source code in src/cfun/yzm/dddocrtool.py
def get_image_size(self, image_path: str | Path) -> tuple[int, int, None]:
    """通过 PIL 库获取图片的宽高

    Args:
        image_path (str | Path): 图片路径

    Returns:
        tuple(width, height, channels):  图片的宽高和通道数
    """
    if isinstance(image_path, Path):
        image_path = str(image_path)

    with Image.open(image_path) as img:
        width, height = img.size
    return width, height, None

toxlabeling

toxlabeling(
    image_path: Optional[str] = None,
    bboxes: Optional[list] = None,
) -> list

把 ddddocr 返回的目标检测框转换为 xlabing 的格式 (xyxy格式)

Parameters:

Name Type Description Default
image_path str

图片路径 (与 bboxes 二选一)

None
bboxes list

[[x1, y1, x2, y2], ...] (与 image_path 二选一), 这个坐标 左上,右下, 也就是 xyxy 的格式

None

Returns:

Name Type Description
list list

[[[x1, y1], [x2, y1], [x2, y2], [x1, y2]], ...]

PS
  1. 这个函数主要是把 ddddocr 返回的目标检测框转换为 xlabing 的格式, 也就是四个点的格式, 具体的格式是:
1
2
3
4
5
[
    [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
    [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
    ...
]
Source code in src/cfun/yzm/dddocrtool.py
def toxlabeling(
    self, image_path: Optional[str] = None, bboxes: Optional[list] = None
) -> list:
    """把 ddddocr 返回的目标检测框转换为 xlabing 的格式 (xyxy格式)

    Args:
        image_path (str): 图片路径 (与 bboxes 二选一)
        bboxes (list): [[x1, y1, x2, y2], ...] (与 image_path 二选一), 这个坐标 左上,右下, 也就是 xyxy 的格式

    Returns:
        list: [[[x1, y1], [x2, y1], [x2, y2], [x1, y2]], ...]

    PS:
        1. 这个函数主要是把 ddddocr 返回的目标检测框转换为 xlabing 的格式, 也就是四个点的格式, 具体的格式是:

        ```
        [
            [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
            [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
            ...
        ]
        ```
    """
    if image_path is None and bboxes is None:
        raise ValueError("image_path 和 bboxes 必须二选一,不能都为 None")

    if image_path is not None and bboxes is not None:
        raise ValueError("image_path 和 bboxes 不能同时使用")

    if bboxes is not None:
        bboxes = self.detection(image_path)

    assert isinstance(bboxes, list)
    assert all(isinstance(bbox, list) and len(bbox) == 4 for bbox in bboxes)

    result = []
    for bbox in bboxes:
        x1, y1, x2, y2 = bbox
        result.append([[x1, y1], [x2, y1], [x2, y2], [x1, y2]])
    return result

extendCJYRecognition

extendCJYRecognition(
    ocrdata: list, image_path: str
) -> list

扩展超级鹰识别的结果. (也可以是其他平台的结果, 因为很多平台返回的结果都只含中心点坐标)

Parameters:

Name Type Description Default
ocrdata list of dict

超级鹰识别的结果,每个元素是一个字典,格式如下:

  • name (str): 字符内容,例如 "之"
  • coordinates (list of int): 坐标列表,格式如 [x, y]
required
image_path str

图片路径。

required

Returns:

Type Description
list

list of dict: 扩展后的结果,每个字典包含以下字段:

  • name (str): 字符内容。
  • coordinates (list of int): 原始坐标。
  • points (list of list of float): 四个顶点坐标,格式如 [[x1, y1], [x2, y1], [x2, y2], [x1, y2]]
  • image_width (int): 图片宽度。
  • image_height (int): 图片高度。
Example
'''
超级鹰识别的结果是一个: '之,207,115|成,158,86|人,126,44'
    我们可以很轻松的转为:
    [
        {
            "name": "之",
            "coordinates": [207, 115]
        },
        {
            "name": "成",
            "coordinates": [158, 86]
        },
        {
            "name": "人",
            "coordinates": [126, 44]
        }
    ]
以上只是返回中心点的坐标, 但是我希望得到目标检测框的坐标,即 四个定点的坐标,
因此在这个结果的基础上,增加一个key, 叫做 points, 其值为 [[x1, y1], [x2, y1], [x2, y2], [x1, y2]]
采用 xyxy 的格式, 这样就可以直接使用了. 这个目标检测的框如何产生呢? 这里采用ddddocr的目标检测框来进行扩展。
'''
from cfun.yzm.dddocrtool import ImageDet
imgdet = ImageDet()
ocrdata = [
    {"name": "之", "coordinates": [207, 115]},
    {"name": "成", "coordinates": [158, 86]},
    {"name": "人", "coordinates": [126, 44]}
]
image_path = "restoredUnique2/0a19bbddea_乏宙泡瓜色.png"
extended_data = imgdet.extendCJYRecognition(ocrdata, image_path)
print(f"Extended Data: {extended_data}")
'''
[
    {
        "name": "之",
        "coordinates": [207, 115],
        "points": [[x1, y1], [x2, y1], [x2, y2], [x1, y2]]
        "image_width": 100,
        "image_height": 100
    },
    {
        "name": "成",
        "coordinates": [158, 86],
        "points": [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
        "image_width": 100,
        "image_height": 100
    },
    {
        "name": "人",
        "coordinates": [126, 44],
        "points": [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
        "image_width": 100,
        "image_height": 100
    }
]
'''
Source code in src/cfun/yzm/dddocrtool.py
def extendCJYRecognition(self, ocrdata: list, image_path: str) -> list:
    """扩展超级鹰识别的结果. (也可以是其他平台的结果, 因为很多平台返回的结果都只含中心点坐标)

    Args:
        ocrdata (list of dict): 超级鹰识别的结果,每个元素是一个字典,格式如下:

            - name (str): 字符内容,例如 `"之"`。
            - coordinates (list of int): 坐标列表,格式如 `[x, y]`。

        image_path (str): 图片路径。

    Returns:
        list of dict: 扩展后的结果,每个字典包含以下字段:

            - name (str): 字符内容。
            - coordinates (list of int): 原始坐标。
            - points (list of list of float): 四个顶点坐标,格式如 `[[x1, y1], [x2, y1], [x2, y2], [x1, y2]]`。
            - image_width (int): 图片宽度。
            - image_height (int): 图片高度。

    Example:
        ```python

        '''
        超级鹰识别的结果是一个: '之,207,115|成,158,86|人,126,44'
            我们可以很轻松的转为:
            [
                {
                    "name": "之",
                    "coordinates": [207, 115]
                },
                {
                    "name": "成",
                    "coordinates": [158, 86]
                },
                {
                    "name": "人",
                    "coordinates": [126, 44]
                }
            ]
        以上只是返回中心点的坐标, 但是我希望得到目标检测框的坐标,即 四个定点的坐标,
        因此在这个结果的基础上,增加一个key, 叫做 points, 其值为 [[x1, y1], [x2, y1], [x2, y2], [x1, y2]]
        采用 xyxy 的格式, 这样就可以直接使用了. 这个目标检测的框如何产生呢? 这里采用ddddocr的目标检测框来进行扩展。
        '''
        from cfun.yzm.dddocrtool import ImageDet
        imgdet = ImageDet()
        ocrdata = [
            {"name": "之", "coordinates": [207, 115]},
            {"name": "成", "coordinates": [158, 86]},
            {"name": "人", "coordinates": [126, 44]}
        ]
        image_path = "restoredUnique2/0a19bbddea_乏宙泡瓜色.png"
        extended_data = imgdet.extendCJYRecognition(ocrdata, image_path)
        print(f"Extended Data: {extended_data}")
        '''
        [
            {
                "name": "之",
                "coordinates": [207, 115],
                "points": [[x1, y1], [x2, y1], [x2, y2], [x1, y2]]
                "image_width": 100,
                "image_height": 100
            },
            {
                "name": "成",
                "coordinates": [158, 86],
                "points": [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
                "image_width": 100,
                "image_height": 100
            },
            {
                "name": "人",
                "coordinates": [126, 44],
                "points": [[x1, y1], [x2, y1], [x2, y2], [x1, y2]],
                "image_width": 100,
                "image_height": 100
            }
        ]
        '''
        ```
    """
    bboxes = self.detection(
        image_path
    )  # 返回的坐标,[[x1, y1, x2, y2], [x1, y1, x2, y2], ...] ,xyxy格式
    # 删除一些检测有问题的框, 比如框距离边缘太近的框
    image_width, image_height, _ = self.get_image_size(image_path)
    bboxes = self._delete_bianyuan(bboxes, image_width, image_height)

    # 这里需要判断一下, 如果框的中心点在框内, 则返回这个框的坐标
    for box in bboxes:
        x1, y1, x2, y2 = box
        for idata in ocrdata:
            xy = idata["coordinates"]
            if self._check_range(xy, box):
                # 如果在框内, 则返回这个框的坐标
                idata["points"] = [[x1, y1], [x2, y1], [x2, y2], [x1, y2]]
                idata["image_width"] = image_width
                idata["image_height"] = image_height
                break
    # 移除没有 points 的框
    ocrdata = [idata for idata in ocrdata if "points" in idata]
    return deepcopy(ocrdata)

check_bboxes_four staticmethod

check_bboxes_four(bboxes: list) -> bool

检查框的格式是否正确(四个点的格式)

Parameters:

Name Type Description Default
bboxes list

[[[x1, y1], [x2, y1], [x2, y2], [x1, y2]], ....]

required

Returns:

Name Type Description
bool bool

True or False

Source code in src/cfun/yzm/dddocrtool.py
@staticmethod
def check_bboxes_four(bboxes: list) -> bool:
    """检查框的格式是否正确(四个点的格式)

    Args:
        bboxes (list): [[[x1, y1], [x2, y1], [x2, y2], [x1, y2]], ....]

    Returns:
        bool: True or False
    """
    if not isinstance(bboxes, list):
        return False
    if all(
        isinstance(bbox, list)
        and len(bbox) == 4
        and all(isinstance(point, list) and len(point) == 2 for point in bbox)
        for bbox in bboxes
    ):
        return True
    else:
        return False

check_bboxes_two staticmethod

check_bboxes_two(bboxes: list) -> bool

检查框的格式是否正确(两个点的格式)

Parameters:

Name Type Description Default
bboxes list

[[x1, y1, x2, y2], ....]

required

Returns:

Name Type Description
bool bool

True or False

Source code in src/cfun/yzm/dddocrtool.py
@staticmethod
def check_bboxes_two(bboxes: list) -> bool:
    """检查框的格式是否正确(两个点的格式)

    Args:
        bboxes (list): [[x1, y1, x2, y2], ....]

    Returns:
        bool: True or False
    """
    if all(isinstance(bbox, list) and len(bbox) == 4 for bbox in bboxes):
        return True
    else:
        return False