信息发布→ 登录 注册 退出

【PaddleHub模型贡献】一行代码实现水表的数字表盘分割

发布时间:2025-07-21

点击量:
本文介绍将水表数字表盘分割模型贡献到PaddleHub的方法。先安装必要库,复现模型:准备数据集,配置GPU,定义图像预处理流程和数据集,用DeepLabv3p训练模型并导出。接着转换模型为PaddleHub模型,补充代码实现旋转剪裁等功能,最后测试安装与调用,实现水表数字表盘分割。

☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

【PaddleHub模型贡献】一行代码实现水表的数字表盘分割

一、安装必要的库

In [3]
!pip install paddlex -i https://mirror.baidu.com/pypi/simple
!pip install --upgrade paddlepaddle-gpu -i https://pypi.tuna.tsinghua.edu.cn/simple
!pip install --upgrade paddlehub==2.0.1 -i https://pypi.tuna.tsinghua.edu.cn/simple
   

二、模型训练

项目作者使用PaddleX做的语义分割,因为作者没有直接公开训练好的模型,所以这里我们先按照作者的思路复现模型。

1.准备表盘数据集

In [ ]
!unzip -oq /home/aistudio/data/data73852/water.zip
   

2. 模型训练

2.1 配置GPU

In [ ]
# 设置使用0号GPU卡(如无GPU,执行此代码后仍然会使用CPU训练模型)import matplotlib
matplotlib.use('Agg') 
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'import paddlex as pdx
   

2.2 定义图像预处理流程transforms

定义数据处理流程,其中训练和测试需分别定义,训练过程包括了部分测试过程中不需要的数据增强操作,如在本示例中,训练过程使用了RandomHorizontalFlip和RandomPaddingCrop两种数据增强方式,更多图像预处理流程transforms的使用可参见paddlex.seg.transforms。

In [ ]
from paddlex.seg import transforms
train_transforms = transforms.Compose([
    transforms.RandomHorizontalFlip(),
    transforms.Resize(target_size=512),
    transforms.RandomPaddingCrop(crop_size=500),
    transforms.Normalize()
])
eval_transforms = transforms.Compose([
    transforms.Resize(512),
    transforms.Normalize()
])
   

2.3 定义数据集Dataset

实例分割使用SegDataset格式的数据集,因此采用pdx.datasets.SegDataset来加载数据集,该接口的介绍可参见文档pdx.datasets.SegDataset。

In [ ]
train_dataset = pdx.datasets.SegDataset(
    data_dir='water',
    file_list='water/train.txt',
    label_list='water/class_names.txt',
    transforms=train_transforms,
    shuffle=True)
eval_dataset = pdx.datasets.SegDataset(
    data_dir='water',
    file_list='water/val.txt',
    label_list='water/class_names.txt',
    transforms=eval_transforms)
       
2025-03-11 14:54:48 [INFO]	150 samples in file water/train.txt
2025-03-11 14:54:48 [INFO]	11 samples in file water/val.txt
       

2.4 模型开始训练

使用本数据集在P40上训练,如有GPU,模型的训练过程预估为13分钟左右;如无GPU,则预估为5小时左右。更多训练模型的参数可参见文档paddlex.seg.DeepLabv3p。模型训练过程每间隔save_interval_epochs轮会保存一次模型在save_dir目录下,同时在保存的过程中也会在验证数据集上计算相关指标,具体相关日志参见文档。

In [ ]
num_classes = len(train_dataset.labels)
model = pdx.seg.DeepLabv3p(num_classes=num_classes)
model.train(
    num_epochs=40,
    train_dataset=train_dataset,
    train_batch_size=4,
    eval_dataset=eval_dataset,
    learning_rate=0.01,
    save_interval_epochs=1,    # pretrain_weights='output/deeplab4/best_model',
    save_dir='output/water')
   

最后一轮的输出如下所示:

2025-03-11 15:02:56 [INFO]	[TRAIN] Epoch=40/40, Step=1/37, loss=0.010831, lr=0.000362, time_each_step=0.18s, eta=0:0:10
2025-03-11 15:02:56 [INFO]	[TRAIN] Epoch=40/40, Step=3/37, loss=0.010944, lr=0.000344, time_each_step=0.2s, eta=0:0:10
2025-03-11 15:02:57 [INFO]	[TRAIN] Epoch=40/40, Step=5/37, loss=0.009099, lr=0.000326, time_each_step=0.22s, eta=0:0:10
2025-03-11 15:02:57 [INFO]	[TRAIN] Epoch=40/40, Step=7/37, loss=0.011186, lr=0.000308, time_each_step=0.24s, eta=0:0:10
2025-03-11 15:02:57 [INFO]	[TRAIN] Epoch=40/40, Step=9/37, loss=0.008269, lr=0.00029, time_each_step=0.25s, eta=0:0:10
2025-03-11 15:02:58 [INFO]	[TRAIN] Epoch=40/40, Step=11/37, loss=0.011792, lr=0.000272, time_each_step=0.25s, eta=0:0:10
2025-03-11 15:02:58 [INFO]	[TRAIN] Epoch=40/40, Step=13/37, loss=0.010976, lr=0.000254, time_each_step=0.26s, eta=0:0:9
2025-03-11 15:02:58 [INFO]	[TRAIN] Epoch=40/40, Step=15/37, loss=0.01399, lr=0.000236, time_each_step=0.26s, eta=0:0:9
2025-03-11 15:02:58 [INFO]	[TRAIN] Epoch=40/40, Step=17/37, loss=0.009998, lr=0.000217, time_each_step=0.26s, eta=0:0:8
2025-03-11 15:02:58 [INFO]	[TRAIN] Epoch=40/40, Step=19/37, loss=0.012266, lr=0.000198, time_each_step=0.26s, eta=0:0:8
2025-03-11 15:02:58 [INFO]	[TRAIN] Epoch=40/40, Step=21/37, loss=0.011713, lr=0.00018, time_each_step=0.13s, eta=0:0:5
2025-03-11 15:02:58 [INFO]	[TRAIN] Epoch=40/40, Step=23/37, loss=0.010291, lr=0.00016, time_each_step=0.11s, eta=0:0:5
2025-03-11 15:02:58 [INFO]	[TRAIN] Epoch=40/40, Step=25/37, loss=0.010211, lr=0.000141, time_each_step=0.09s, eta=0:0:4
2025-03-11 15:02:59 [INFO]	[TRAIN] Epoch=40/40, Step=27/37, loss=0.02097, lr=0.000121, time_each_step=0.08s, eta=0:0:4
2025-03-11 15:02:59 [INFO]	[TRAIN] Epoch=40/40, Step=29/37, loss=0.008198, lr=0.000101, time_each_step=0.07s, eta=0:0:3
2025-03-11 15:02:59 [INFO]	[TRAIN] Epoch=40/40, Step=31/37, loss=0.010346, lr=8.1e-05, time_each_step=0.06s, eta=0:0:3
2025-03-11 15:02:59 [INFO]	[TRAIN] Epoch=40/40, Step=33/37, loss=0.009331, lr=6e-05, time_each_step=0.06s, eta=0:0:3
2025-03-11 15:02:59 [INFO]	[TRAIN] Epoch=40/40, Step=35/37, loss=0.01259, lr=3.8e-05, time_each_step=0.06s, eta=0:0:3
2025-03-11 15:02:59 [INFO]	[TRAIN] Epoch=40/40, Step=37/37, loss=0.013072, lr=1.4e-05, time_each_step=0.06s, eta=0:0:3
2025-03-11 15:02:59 [INFO]	[TRAIN] Epoch 40 finished, loss=0.011522, lr=0.000195 .
2025-03-11 15:02:59 [INFO]	Start to evaluating(total_samples=11, total_steps=3)...
100%|██████████| 3/3 [00:02<00:00,  1.00it/s]
2021-03-11 15:03:02 [INFO]	[EVAL] Finished, Epoch=40, miou=0.814756, category_iou=[0.99168644 0.63782582], oacc=0.991806, category_acc=[0.99431391 0.84710874], kappa=0.774722, category_F1-score=[0.99582587 0.77886893] .
2021-03-11 15:03:03 [INFO]	Model saved in output/water/epoch_40.
2021-03-11 15:03:03 [INFO]	Current evaluated best model in eval_dataset is epoch_35, miou=0.8284633456567256
   

3.模型导出

模型训练时会自动保存模型参数,我们需要把训练模型导出成可预测模型。

In [ ]
!paddlex --export_inference --model_dir=output/water/best_model --save_dir=./inference_model
       
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/setuptools/depends.py:2: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
  import imp
W0311 15:49:28.613981   782 device_context.cc:362] Please NOTE: device: 0, GPU Compute Capability: 7.0, Driver API Version: 11.0, Runtime API Version: 10.1
W0311 15:49:28.618839   782 device_context.cc:372] device: 0, cuDNN Version: 7.6.
2025-03-11 15:49:32 [INFO]	Model[DeepLabv3p] loaded.
2025-03-11 15:49:32 [INFO]	Model for inference deploy saved in ./inference_model.
       

三、封装Module

下面正式开始模型转换!

1.模型转换

PaddleX模型可以快速转换成PaddleHub模型,只需要用下面这一句命令即可:

In [ ]
!hub convert --model_dir inference_model \
              --module_name WatermeterSegmentation \
              --module_version 1.0.0 \
              --output_dir outputs
   

转换成功后的模型保存在outputs文件夹下,我们解压一下:

In [ ]
!gzip -dfq /home/aistudio/outputs/WatermeterSegmentation.tar.gz
!tar -xf /home/aistudio/outputs/WatermeterSegmentation.tar
   

2.补充代码

刚刚转换的模型其实已经是PaddleHub的Module了,但是原项目中,作者做了一些图片的裁剪等操作,把数字提取出来了,因此,我们需要把这部分代码补充进去。

完整的module.py文件内容如下:

from __future__ import absolute_importfrom __future__ import divisionimport osimport cv2import argparseimport base64import paddlex as pdxfrom math import *import time, math, reimport numpy as npimport paddlehub as hubfrom paddlehub.module.module import moduleinfo, runnable, servingdef base64_to_cv2(b64str):
    data = base64.b64decode(b64str.encode('utf8'))
    data = np.fromstring(data, np.uint8)
    data = cv2.imdecode(data, cv2.IMREAD_COLOR)    return datadef cv2_to_base64(image):
    # return base64.b64encode(image)
    data = cv2.imencode('.jpg', image)[1]    return base64.b64encode(data.tostring()).decode('utf8')def read_images(paths):
    images = []    for path in paths:
        images.append(cv2.imread(path))    return images'''旋转图像并剪裁'''def rotate(
        img,  # 图片
        pt1, pt2, pt3, pt4,
        imgOutSrc):
    # print(pt1,pt2,pt3,pt4)
    withRect = math.sqrt((pt4[0] - pt1[0]) ** 2 + (pt4[1] - pt1[1]) ** 2)  # 矩形框的宽度
    heightRect = math.sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) **2)    # print("矩形的宽度",withRect, "矩形的高度", heightRect)
    angle = acos((pt4[0] - pt1[0]) / withRect) * (180 / math.pi)  # 矩形框旋转角度
    # print("矩形框旋转角度", angle)

    if withRect > heightRect:        if pt4[1]>pt1[1]:            # print("顺时针旋转")
            pass
        else:            # print("逆时针旋转")
            angle=-angle    else:        # print("逆时针旋转")
        angle=90 - angle

    height = img.shape[0]  # 原始图像高度
    width = img.shape[1]   # 原始图像宽度
    rotateMat = cv2.getRotationMatrix2D((width / 2, height / 2), angle, 1)  # 按angle角度旋转图像
    heightNew = int(width * fabs(sin(radians(angle))) + height * fabs(cos(radians(angle))))
    widthNew = int(height * fabs(sin(radians(angle))) + width * fabs(cos(radians(angle))))

    rotateMat[0, 2] += (widthNew - width) / 2
    rotateMat[1, 2] += (heightNew - height) / 2
    imgRotation = cv2.warpAffine(img, rotateMat, (widthNew, heightNew), borderValue=(255, 255, 255))    # cv2.imwrite("imgRotation.jpg", imgRotation) 

    # 旋转后图像的四点坐标
    [[pt1[0]], [pt1[1]]] = np.dot(rotateMat, np.array([[pt1[0]], [pt1[1]], [1]]))
    [[pt3[0]], [pt3[1]]] = np.dot(rotateMat, np.array([[pt3[0]], [pt3[1]], [1]]))
    [[pt2[0]], [pt2[1]]] = np.dot(rotateMat, np.array([[pt2[0]], [pt2[1]], [1]]))
    [[pt4[0]], [pt4[1]]] = np.dot(rotateMat, np.array([[pt4[0]], [pt4[1]], [1]]))    # 处理反转的情况
    if pt2[1]>pt4[1]:
        pt2[1],pt4[1]=pt4[1],pt2[1]    if pt1[0]>pt3[0]:
        pt1[0],pt3[0]=pt3[0],pt1[0]

    imgOut = imgRotation[int(pt2[1]):int(pt4[1]), int(pt1[0]):int(pt3[0])]
    cv2.imwrite(imgOutSrc, imgOut) # 裁减得到的旋转矩形框@moduleinfo(
    name='WatermeterSegmentation',    type='CV/semantic_segmentatio',
    author='郑博培、彭兆帅',
    author_email='2733821739@qq.com',
    summary='Digital dial segmentation of water meter',
    version='1.0.0')class MODULE(hub.Module):
    def _initialize(self, **kwargs):
        self.default_pretrained_model_path = os.path.join(
            self.directory, 'assets')
        self.model = pdx.deploy.Predictor(self.default_pretrained_model_path,
                                          **kwargs)    def predict(self,
                images=None,
                paths=None,
                data=None,
                batch_size=1,
                use_gpu=False,
                **kwargs):

        all_data = images if images is not None else read_images(paths)
        total_num = len(all_data)
        loop_num = int(np.ceil(total_num / batch_size))

        res = []        for iter_id in range(loop_num):
            batch_data = list()
            handle_id = iter_id * batch_size            for image_id in range(batch_size):                try:
                    batch_data.append(all_data[handle_id + image_id])                except IndexError:                    break
            out = self.model.batch_predict(batch_data, **kwargs)
            res.extend(out)        return res    def cutPic(self, picUrl):
        # seg = hub.Module(name='WatermeterSegmentation')
        image_name = picUrl
        im = cv2.imread(image_name)
        result = self.predict(images=[im])        # 将多边形polygon转矩形
        contours, hier = cv2.findContours(result[0]['label_map'], cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) 
        print(type(contours[0]))
        n = 0
        m = 0
        for index,contour in enumerate(contours):            if len(contour) > n:
                n = len(contour)
                m = index

        image = cv2.imread(image_name)        # 获取最小的矩形
        rect = cv2.minAreaRect(contours[m])
        box = np.int0(cv2.boxPoints(rect))        # 获取到矩形的四个点
        tmp = cv2.drawContours(image, [box], 0, (0, 0, 255), 3)
        imgOutSrc = 'result.jpg'
        rotate(image, box[0], box[1], box[2], box[3], imgOutSrc)
        res = []
        res.append(imgOutSrc)        return res    @serving
    def serving_method(self, images, **kwargs):
        """
        Run as a service.
        """
        images_decode = [base64_to_cv2(image) for image in images]
        results = self.predict(images_decode, **kwargs)
        res = []        for result in results:            if isinstance(result, dict):                # result_new = dict()
                for key, value in result.items():                    if isinstance(value, np.ndarray):
                        result[key] = cv2_to_base64(value)                    elif isinstance(value, np.generic):
                        result[key] = np.asscalar(value)            elif isinstance(result, list):                for index in range(len(result)):                    for key, value in result[index].items():                        if isinstance(value, np.ndarray):
                            result[index][key] = cv2_to_base64(value)                        elif isinstance(value, np.generic):
                            result[index][key] = np.asscalar(value)            else:                raise RuntimeError('The result cannot be used in serving.')
            res.append(result)        return res    @runnable
    def run_cmd(self, argvs):
        """
        Run as a command.
        """
        self.parser = argparse.ArgumentParser(
            description="Run the {} module.".format(self.name),
            prog='hub run {}'.format(self.name),
            usage='%(prog)s',
            add_help=True)
        self.arg_input_group = self.parser.add_argument_group(
            title="Input options", description="Input data. Required")
        self.arg_config_group = self.parser.add_argument_group(
            title="Config options",
            description=            "Run configuration for controlling module behavior, not required.")
        self.add_module_config_arg()
        self.add_module_input_arg()
        args = self.parser.parse_args(argvs)
        results = self.predict(
            paths=[args.input_path],
            use_gpu=args.use_gpu)        return results    def add_module_config_arg(self):
        """
        Add the command config options.
        """
        self.arg_config_group.add_argument(            '--use_gpu',            type=bool,
            default=False,            help="whether use GPU or not")    def add_module_input_arg(self):
        """
        Add the command input options.
        """
        self.arg_input_group.add_argument(            '--input_path', type=str, help="path to image.")if __name__ == '__main__':
    module = MODULE(directory='./new_model')
    images = [cv2.imread('./cat.jpg'), cv2.imread('./cat.jpg'), cv2.imread('./cat.jpg')]
    res = module.predict(images=images)
   

3.模型测试

首先安装我们刚刚写好的Module:

In [ ]
!hub install WatermeterSegmentation
       
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/setuptools/depends.py:2: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
  import imp
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/__init__.py:107: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  from collections import MutableMapping
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/rcsetup.py:20: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  from collections import Iterable, Mapping
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/colors.py:53: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  from collections import Sized[2025-03-11 16:42:50,225] [    INFO] - Successfully uninstalled WatermeterSegmentation[2025-03-11 16:42:50,441] [    INFO] - Successfully installed WatermeterSegmentation-1.0.0
       

模型调用:

In [4]
import cv2import paddlehub as hub

seg = hub.Module(name='WatermeterSegmentation')
res = seg.cutPic(picUrl="water/images/val/20250521105032.png")
       
[2025-03-11 17:13:36,113] [ WARNING] - The _initialize method in HubModule will soon be deprecated, you can use the __init__() to handle the initialization of the object
       
       

预测结果如下。

输入图片:

       

最终将截取的图片显示效果如下:

       


相关文章: AI创作利器集结,五大工具助力效率与创意飞跃,ai欧美形象  SEO与SEM:提升网站流量与转化率的关键策略  文心一言引领长文本创作,开拓AI艺术新篇章,mac ai破解方法  天工AI与豆包智能助手,性能对决,谁主沉浮?,ai写作软件怎么复制文字  AI绘画新境界,文心一言开启照片转绘画,ai的阴影在哪  豆包科技赋能原神,AI智能体在中的创新实践,ai挑战秀  文心一言4.0深度评测,全面解析试用攻略,ai7电影网欧美三级  豆包AI违规操作揭秘,违规次数与封号之谜,爱ai小视频完整版免费  豆包AI声音克隆技术,引领个性化声音模仿新潮流的TF力量,ai里面没有色板  科技与文化交融盛宴,文心一言3月16日绽放,ai写作公文哪个好用  AI写作新秀,文心一言实力解析与对比评测,ai文档写作是真的吗  怎么降低文章的AI生成率:打造更真实、更有价值的内容  AI模型化应用全攻略,从搭建到部署的实战手册,ai写作神器怎么改文案  豆包AI,全能助手,聊天与表格制作两不误,ai大模型私有化部署  AI大模型驱动写作新,查重技术面临哪些?,糠豆广场舞 ai  文心一言全新升级,揭秘内容创作新亮点,ai图案凹凸  文心一言,重塑未来人工智能写作的助手,ai锁定图片的快捷键  下载卡通AI模型,引领个性化虚拟形象新风尚,如何有效使用ai提升写作技能与能力  华为引领智能语音交互新,大模型赋能AI语音突破,ai做小怪物  文心一言概念股热潮来袭,市场关注与投资机遇解读,ai奇闻  打造高效AI车位检测统,从理论到实操手册,AI偏小  文心一言,引领高效办公新时代,ai稿纸  国内AI大模型上线,开启产业创新与发展新,AI度晓晓二级  东海帝皇AI模型,深度学习之光,引领智慧创新之旅,美国ai写作软件免费吗  AI算法模型的深度解析,哪一款ai写作工具更好  AI助力手工模型汽车制作,简易教程大揭秘,ai智能业务  文心一言应用攻略,释放创意潜能,ai怎么取消投影  AI未来,重塑人类生活图景,ai野咖啡  AI大模型,从萌芽到的发展历程,德ai  掌上AI新体验,探索端大模型的无限可能,苹果能用的免费ai写作  AI模型评测,性能与局限性深度剖析,可畏ai美图  AI量化交易模型软件精选指南,助投资者金融市场扬帆远航,王者ai挑战20关  AI赋能投资,盘点股票的智能模型与未来市场助手揭秘,ai大数据结合竞价图解  文心一言诞生记,揭秘背后的故事与历史时刻,ai植入字体  揭秘文心一言AIPPT,AI写作助手实操技巧大解析,ai换脸免会员  文心一言,财务领域的智能应用与创新探索,mo ai ni  全I大模型技术新格局,领跑,排名再刷新,ai科幻画主题  文心一言回复删除攻略,告别困扰,畅享无忧交流,某某ai合成  文心一言,穿越时空的恋爱奇缘,测ai17  深度学习巨无霸,AI模型参数量级排行榜大揭秘,ai薄纱  AI赋能办公,构建高效表格模型的AI软件揭秘,漂亮的ai  Azure AI模型,驱动智能时代变革的力量,ai后代  文心一言4.0重磅升级,AI写作助手引领行业新潮流,字母矛盾ai  揭秘清华AI模型,接口全解析与应用场景深度解析,正则ai课  网站站内信功能,让用户沟通更加高效与便捷  文心一言,揭开人工智能写作助手的面纱,敬请揭晓上线时间!,ai创新点  未来科技:AI工具为生活赋能,打造智能未来  ERNIE-4.0 8K,文心一言开启自然语言处理新时代,ai渐变对齐  让爬虫技术飞起来!5个适合练手的爬虫网站推荐  SEO与SEM推广:如何用互联网营销提升品牌曝光与业绩 

标签:# 文档  # 这部  # 会在  # 只需  # 两种  # 如有  # 不需要  # 这一  # 如无  # python  # 矩形框  # 接口  # 封装  # red  # cos  # ai  # qq  # git  
在线客服
服务热线

服务热线

400 8905 500

微信咨询
二维码
返回顶部
×二维码

截屏,微信识别二维码

打开微信

微信号已复制,请打开微信添加咨询详情!