飞桨黑客松 OpenVINO™ 任务获奖者经验分享 | 基于OpenVINO™ 与PaddleOCR的结构化输出Pipeline...

news2024/11/17 16:39:37

点击蓝字

关注我们,让开发变得更有趣

作者 | 张一乔

排版 | 李擎

401918fd42e9948f5cf75c616be457b1.png

 OpenVINO™

1. 黑客松活动介绍

01

第四季飞桨黑客松(PaddlePaddle Hackathon Season 4)是由百度联合合作伙伴共同举办的开源深度学习框架类黑客松活动。本次活动旨在为全球开发者提供一个交流、合作和创新的机会,推动人工智能技术的发展和应用。

02

英特尔作为本比赛的主要合作伙伴,推出了 OpenVINO™ 系列任务。

其中,205号赛题需要开发者向OpenVINO™ Notebooks (https://github.com/openvinotoolkit/openvino_notebooks) 中提交一个Notebook,用于构建基于PaddlePaddle和OpenVINO™ 的项目示例。

具体来说,首先需要在OpenVINO™ Notebooks (https://github.com/openvinotoolkit/openvino_notebooks)中提交  Issue 以详细描述自己打算开发的项目内容,以及预期的开发进展。当对应的工作人员 review 该 issue 并通过后,即可进行notebook的开发并提交 pull requests,提交PR后需要根据审稿人的意见进行修改,当PR合入要求后,该PR将会被Approve并合入仓库。

相比于其他任务,本任务更加偏向于模型应用,同样,对参赛者的英文表达也有一定的要求。

OpenVINO™

2. 项目介绍

“表” 是生活中的随处可见的一种设备。

常见的“表”包括了家用电表,水表等设备;除此之外,还有工频场强计等“表”。由于受到区域因素以及技术因素的制约,并非每种“表”都能够进行数据的自动采集,导致只能通过人工手动抄表。这种数据采集工作较为费事和枯燥,同时,长时间工作也会导致工作人员疲劳,从而产生抄录错误。通过人工智能技术构造自动化的抄表流程能够极大的克服上述问题,提高工作效率。

当前大部分抄表相关的项目都需要在特定领域的数据集上进行微调,但实际上抄表工作的抄录内容多为文字,对于特定的表而言,其布局和展示内容较为单一,因此可以基于一些配置值,在无需微调的情况下,实现抄表工作。

本项目提供了有一种无需额外训练的抄表器,只需要人为指定一些和布局有关的配置信息,即可实现表中数据的记录。

3.OpenVINO™ 与 OpenVINO™  NoteBook

OpenVINO™ 是一个用于优化和部署 AI 推理的开源工具包。使用 OpenVINO™ 可以显著提升深度学习在计算机视觉、自动语音识别、自然语言处理和其他常见任务中的性能。能够使主流框架(如TensorFlow,PyTorch等)训练的模型在运行时减少资源需求,并在从边缘到云的一系列英特尔®平台上高效部署。

OpenVINO™ 在仓库 OpenVINO™ Notebooks (https://github.com/openvinotoolkit/openvino_notebooks) 中提供了一些可以运行的 Jupyter* Notebooks,用于学习和尝试使用 OpenVINO™ 工具套件。这些 Notebooks 旨在向各位开发者提供 OpenVINO™ 基础知识的介绍,并教会大家如何利用我们的 API 来优化深度学习推理。

4. PaddleOCR

PaddleOCR 是 PaddlePaddle 的文字识别套件。迄今为止,PaddleOCR 已经提供了许多复用性强的预训练模型。

在本项目中使用的预训练模型是 Chinese and English ultra-lightweight PP-OCR model (9.4M)。更多的信息请参考 PaddleOCR Github(https://github.com/PaddlePaddle/PaddleOCR)或 PaddleOCR Gitee(https://gitee.com/PaddlePaddle/PaddleOCR)。

一个标准的 OCR 流程包括了文字检测和文字识别,对于本项目来说,文字检测工作会通过非深度学习的方式进行处理,因此,本项目只使用PaddleOCR中的文字识别模型。

5. 技术方案概览

本项目面对的应用场景是抄录特定的一类表的信息。因此,可以通过人工配置表面的尺寸,以及各个元素在表中的布局,从而跳过 OCR 模型对文字区域的检测,而后可以进一步对识别结果进行结构化输出。

在整个Pipeline中,无需额外针对业务场景对模型进行训练或配置,从深度学习的层面上完全依赖于开源模型,只是需要人工地进行一些预处理或后处理。从而可以极大的降低OCR模型的应用门槛,也减轻了对数据准备和标注的依赖。

项目的总体流程如下: 

Step 1

 配置图片中屏幕区域的坐标值。(这些坐标值也可以通过 cv2 的拐点检测或深度学习进行获取)

Step 2

 对图片进行预处理(仿射变换)

Step 3

配置待识别的元素对应的坐标,并裁剪对应的区域。

Step 4

如有需要,可以对裁剪下来的区域进行预处理。

Step 5

基于 OpenVINO™ 进行文字识别。

Step 6

结构化输出信息

Step 7

如有需要,对输出结果进行进一步精炼。

c3ccf97035c74879d4c4e53809f84191.png

6. 图片输入处理

由于本项目是一个零微调的项目,因此,为了保证识别模型的有效性,需要人工对齐输入信息。

- 修正倾斜的图片,将图片中的屏幕区域修正到指定的大小

- 根据从说明书等地方获取到的设备信息,设定待识别的区域在屏幕上的布局。

通过下面代码,可以将图片从倾斜状态修正。

# 配置坐标信息
# The coordinates of the corners of the screen in case 1


POINTS = [[1121, 56],    # Left top
          [3242, 183],   # right top
          [3040, 1841],  # right bottom
          [1000, 1543]]   # left bottom
          
# The size of the screen in case 1
DESIGN_SHAPE = (1300, 1000)


# 配置坐标信息
# The coordinates of the corners of the screen in case 1
POINTS = [[1121, 56],    # Left top
          [3242, 183],   # right top
          [3040, 1841],  # right bottom
          [1000, 1543]]   # left bottom


# The size of the screen in case 1
DESIGN_SHAPE = (1300, 1000)


# 配置坐标信息
# The coordinates of the corners of the screen in case 1
POINTS = [[1121, 56],    # Left top
          [3242, 183],   # right top
          [3040, 1841],  # right bottom
          [1000, 1543]]   # left bottom
          
# The size of the screen in case 1
DESIGN_SHAPE = (1300, 1000)

f582aa57cb8ec08bf2ac81bf848d0079.png

a642a97846e64447921e50ce7c639c26.png

7. 基于 OpenVINO™ 加载 PaddleOCR 识别模型进行预测

7.1. 加载模型

使用 OpenVINO™ 加载 Paddle 模型无需经过任何转换,只需要

1)创建环境

2)读取模型

3)生成推理接口

假设当前模型保存在 ch_PP-OCRv3_rec_infer 目录下,则可以通过以下代码一键加载模型:

from openvino.runtime import Core


# Initialize OpenVINO Runtime for text recognition.
core = Core()


# Read the model and corresponding weights from a file.
rec_model_file_path = "ch_PP-OCRv3_rec_infer/inference.pdmodel"
rec_model = core.read_model(model=rec_model_file_path)


# Assign dynamic shapes to every input layer on the last dimension.
for input_layer in rec_model.inputs:
    input_shape = input_layer.partial_shape
    input_shape[3] = -1
    rec_model.reshape({input_layer: input_shape})


rec_compiled_model = core.compile_model(model=rec_model, device_name="CPU")


# Get input and output nodes.
rec_input_layer = rec_compiled_model.input(0)
rec_output_layer = rec_compiled_model.output(0)

7.2. 文字识别

依旧对于上述示例图片,希望结构化输出以下内容:[{"Info_Probe":""}, {"Freq_Set":""}, {"Freq_Main":""}, {"Val_Total":""},{"Val_X":""}, {"Val_Y":""}, {"Val_Z":""}, {"Unit":""}, {"Field":""}]。输出示例如下图所示:

465103c18756f7faac40bd4ffdffa6c0.png

首先,需要基于仿射变换的结果,配置各个元素在图片上的布局。这个配置对于同一批表来说是固定的

# features and layout information
DESIGN_LAYOUT = {'Info_Probe':[14, 36, 410, 135],  # feature_name, xmin, ymin, xmax, ymax
                 'Freq_Set':[5, 290, 544, 406],
                 'Val_Total':[52, 419, 1256, 741],
                 'Val_X':[19, 774, 433, 882],
                 'Val_Y':[433, 773, 874, 884],
                 'Val_Z':[873, 773, 1276, 883],
                 'Unit':[1064, 291, 1295, 403],
                 'Field':[5, 913, 243, 998]}

然后,需要配置文字识别预处理函数。

首先,需要将传入的检测框转化为图片,对于本项目,检测框即为整个图片,但我们依然可以复用这个模块。

def prep_for_rec(dt_boxes, frame):
    """
    Preprocessing of the detected bounding boxes for text recognition


    Parameters:
        dt_boxes: detected bounding boxes from text detection 
        frame: original input frame 
    """
    ori_im = frame.copy()
    img_crop_list = [] 
    for bno in range(len(dt_boxes)):
        tmp_box = copy.deepcopy(dt_boxes[bno])
        img_crop = get_rotate_crop_image(ori_im, tmp_box)
        img_crop_list.append(img_crop)
        
    img_num = len(img_crop_list)
    # 计算对应的缩放比例
    width_list = []
    for img in img_crop_list:
        width_list.append(img.shape[1] / float(img.shape[0]))
    
    # 对文字进行排序以加速推理
    indices = np.argsort(np.array(width_list))
    return img_crop_list, img_num, indices

当完成裁剪之后,可以将图片分装成一个个batch,以成批次地进行预测:

def batch_text_box(img_crop_list, img_num, indices, beg_img_no, batch_num):
    """
    Batch for text recognition


    Parameters:
        img_crop_list: processed bounding box images with detected bounding box
        img_num: number of bounding boxes from text detection
        indices: sorting for bounding boxes to speed up text recognition
        beg_img_no: the beginning number of bounding boxes for each batch of text recognition inference
        batch_num: number of images in each batch
    """
    norm_img_batch = []
    max_wh_ratio = 0
    end_img_no = min(img_num, beg_img_no + batch_num)
    for ino in range(beg_img_no, end_img_no):
        h, w = img_crop_list[indices[ino]].shape[0:2]
        wh_ratio = w * 1.0 / h
        max_wh_ratio = max(max_wh_ratio, wh_ratio)
    for ino in range(beg_img_no, end_img_no):
        norm_img = resize_norm_img(img_crop_list[indices[ino]], max_wh_ratio)
        norm_img = norm_img[np.newaxis, :]
        norm_img_batch.append(norm_img)


    norm_img_batch = np.concatenate(norm_img_batch)
    norm_img_batch = norm_img_batch.copy()
return norm_img_batch

完成识别后,需要将识别结果转化为汉字,这一步需要一个映射表,可以在 PaddleOCR 或者 OpenVINO™ Notebook 中找到。

class RecLabelDecode(object):
    """ Convert between text-label and text-index """


    def __init__(self,
                 character_dict_path=None,
                 character_type='ch',
                 use_space_char=False):
        # 初始化函数


        
    def __call__(self, preds, label=None, *args, **kwargs):
        # 对字符进行解码


# 解码函数,用于将id转换为对应地文字
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):
            char_list = []
            conf_list = []
            for idx in range(len(text_index[batch_idx])):
                # 将id转换为对应地文字
            text = ''.join(char_list)
            result_list.append((text, np.mean(conf_list)))
        return result_list

之后就可以识别与输出啦!

下面以Freq_Set为例,进行文字识别

# 输出结构体
struct_result = {} 


# Crop imgs according the layout information
xmin, ymin, xmax, ymax = DESIGN_LAYOUT['Freq_Set']
crop_img = img[ymin:ymax, xmin:xmax]


h = ymax - ymin  # height of crop_img
w = xmax - xmin  # width of crop_img
dt_boxes = [np.array([[0,0],[w,0],[w,h],[0,h]],dtype='float32')]
batch_num = 1


# since the input img is cropped, we do not need a detection model to find the position of texts
# Preprocess detection results for recognition.
img_crop_list, img_num, indices = prep_for_rec(dt_boxes, crop_img)


# txts are the recognized text results
rec_res = [['', 0.0]] * img_num
txts = [] 


for beg_img_no in range(0, img_num):


    # Recognition starts from here.
    norm_img_batch = batch_text_box(
        img_crop_list, img_num, indices, beg_img_no, batch_num)


    # Run inference for text recognition. 
    rec_results = rec_compiled_model([norm_img_batch])[rec_output_layer]


    # Postprocessing recognition results.
    rec_result = text_decoder(rec_results)
    for rno in range(len(rec_result)):
        rec_res[indices[beg_img_no + rno]] = rec_result[rno]   
    if rec_res:
        txts = [rec_res[i][0] for i in range(len(rec_res))] 


# record the recognition result
struct_result['Freq_Set'] = txts[0]
print(txts[0])

8. 结构化输出与后处理

上面的逻辑已经完成了使用 OpenVINO™ 加载 PaddleOCR 并进行预测,但实际上由于整个模型没有进行微调,所以对于当前的业务场景来说可能不够完美,这个时候可以通过一些简单的逻辑进行处理,比如,对于示例图片中,右上角区域中,存在文字“100Hz 实时值”,对于检测模型而言,“Hz”可能被识别为“H2”等字样,对于识别结果“100H2 实时值”而言,可以通过将“H2”替换为“Hz”提高最后的输出效果。

简单来说,对于示例图片的这种表,可以定义如下后处理函数:

# Post-processing, fix some error made in recognition
def post_processing(results, post_configration):
    """
    Postprocessing function for correcting the recognition errors.
    Parameters:
        results (Dict): The result directory.
        post_configration (Dict): The configuration directory.
    """
    for key in results.keys():
        if len(post_configration[key]) == 0:
            continue  # nothing to do
        for post_item in post_configration[key]:
            key_word = post_item[0]
            if key_word == 'MP':  # mapping
                source_word = post_item[1]
                target_word = post_item[2]
                if source_word in results[key]:
                    results[key] = target_word
            elif key_word == 'RP':  # removing
                source_word = post_item[1]
                target_word = post_item[2]
                results[key] = results[key].replace(source_word, target_word)
            elif key_word == 'AD':  # add point
                add_position = post_item[1]
                results[key] = results[key][:add_position] + '.' + results[key][add_position:]
    return results

定义了辅助函数后,即可通过配置值进行后处理。辅助函数中共有三种配置的预处理类型,分别是映射、替换和增加小数点。以识别结果“深头:LF-01”为例,“映射”(MP)操作意味着只要文字包含“LF”,即将整段文字替换为“探头:LF-01”。以识别结果“100H2 实时值”为例,“替换”(RP)操作意味着将文字中的“H2”替换为“Hz”。以识别结果“613475”为例,如果已知最后两位必然为小数位,并且小数点极小容易被漏检,增加小数点(AD)操作可以快捷地在指定位上增加小数点。

下述代码给出了使用配置值进行后处理地示例。

# Congiguration for postprocessing of the results
RESULT_POST = {"Info_Probe":[['MP', 'LF', '探头:LF-01']],  # words need to be mapped
               "Freq_Set":[['RP', '实时值', ''], ['RP', ' ', ''], ['RP', 'H2', 'HZ']],  # words need to be replace
               "Val_Total":[['RP', 'H2', 'Hz']],
               "Val_X":[['RP', 'X', ''], ['RP', ':', '']], 
               "Val_Y":[['RP', 'Y', ''], ['RP', ':', '']], 
               "Val_Z":[['RP', 'Z', ''], ['RP', ':', '']], 
               "Unit":[['MP', 'T', 'μT'],['MP', 'kV', 'kV/m'],['MP', 'kv', 'kV/m'],['MP', 'vm', 'V/m'],['MP', 'Vm', 'V/m'],['MP', 'A', 'A/m']], 
               "Field":[]}  # nothing need to do


# Postprocessing, to fix some error made in recognition
struct_result = post_processing(struct_result, RESULT_POST)


# Print result
print(struct_result)

9. 全流程一键运行

将上述代码组合起来,可以进行全流程的运行~

# 为了避免因为图片模糊导致的漏检,配置一个输出模板,从而让每个图片输出格式都一致
# Output template in case 1
RESULT_TEMP = {"Info_Probe":"探头:---", 
               "Freq_Set":"", 
               "Val_Total":"无探头", 
               "Val_X":"", 
               "Val_Y":"", 
               "Val_Z":"", 
               "Unit":"A/m", 
               "Field":"常规"}
# the input of recognition should be image, DESIGN information, compiled_model
def main_function(img, DESIGN_LAYOUT, RESULT_TEMP, preprocess_function=None):
    # 根据配置信息获取输出结构的格式
    struct_result = copy.deepcopy(RESULT_TEMP)


    # 逐个访问各个检测目标
    for key in DESIGN_LAYOUT.keys():
        # 裁剪各个区域的图片,并进行预处理
# 对预处理后的图片进行识别
        
# 返回识别结果
    return struct_result

下面是调用示例:

# 读取图片并进行图片预处理
img = cv2.imread('example1.jpg')
img = pre_processing(img, POINTS, DESIGN_SHAPE)


# 进行文字识别
struct_result = main_function(img, DESIGN_LAYOUT, RESULT_TEMP)


# 对识别结果进行精炼
instruct_result = post_processing(struct_result, RESULT_POST)

10. 总结

本文介绍了一种基于开源OCR模型(https://github.com/PaddlePaddle/PaddleOCR)进行读表的Pipeline,这个Pipeline能够在不进行模型微调的前提下完成抄表工作,并按照格式输出内容。

整个Pipeline专注于OCR模型的调用,但仍可以拼接一些检测模型或边缘检测方法,用于自动确定屏幕区域,以降低人力,提高效率。

完整的源码地址见:https://github.com/openvinotoolkit/openvino_notebooks/pull/1030

https://github.com/openvinotoolkit/openvino_notebooks/tree/main/notebooks/203-meter-reader

--END--

你也许想了解(点击蓝字查看)⬇️➡️ OpenVINO™ DevCon 2023重磅回归!英特尔以创新产品激发开发者无限潜能➡️ 5周年更新 | OpenVINO™  2023.0,让AI部署和加速更容易➡️ OpenVINO™5周年重头戏!2023.0版本持续升级AI部署和加速性能➡️ OpenVINO™2023.0实战 | 在 LabVIEW 中部署 YOLOv8 目标检测模型➡️ 开发者实战系列资源包来啦!➡️ 以AI作画,祝她节日快乐;简单三步,OpenVINO™ 助你轻松体验AIGC
➡️ 还不知道如何用OpenVINO™作画?点击了解教程。➡️ 几行代码轻松实现对于PaddleOCR的实时推理,快来get!➡️ 使用OpenVINO 在“端—边—云”快速实现高性能人工智能推理➡️ 图片提取文字很神奇?试试三步实现OCR!➡️【Notebook系列第六期】基于Pytorch预训练模型,实现语义分割任务➡️使用OpenVINO™ 预处理API进一步提升YOLOv5推理性能
扫描下方二维码立即体验 
OpenVINO™ 工具套件 2023.0

点击 阅读原文 立即体验OpenVINO 2023.0

e45d5a7a61a87d832b9d2c8dfa019940.png

文章这么精彩,你有没有“在看

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/734335.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Django_模型类详解(七)

目录 一、定义属性 字段类型 选项 二、查询集 两大特性 查询集的缓存 限制查询集 三、条件查询 条件运算符 1) 查询相等 2) 模糊查询 3) 空查询 4) 范围查询 5) 比较查询 6) 日期查询 F对象 Q对象 聚合函数 四、关联查询 通过对象执行关联查询 通过模型类执…

线程池介绍

1、什么是线程池 例子: 10年前单核CPU电脑,假的多线程,像马戏团小丑玩多个球,CPU需要来回切换。 现在是多核电脑,多个线程各自跑在独立的CPU上,不用切换效率高。 线程池的优势: 线程池做的工作…

七、VPN技术之密码学基础(密码体制、对称加密算法、非对称加密算法)

更多网络基础内容可见: 网络基础学习目录及各章节指引 7.1 密码学基础 7.1.1 基础概念 密码:对文本进行编码,使偷窥者无法识别的算法。是一套编码方案,一种特殊的报文编码和相应的解码方式的结合体。 加密之前的原始报文称为明文,使用密码之后的报文叫密文。一个简单的例…

elk高并发架构

1.前言 普通的elk架构只适合数据量小的情景,而且也不安全,在瞬时数据量大的情况下可能会导致logstash崩溃,从而导致数据的丢失,对于数据安全有较高要求,可以在架构中加入消息队列,既可以防止瞬时的大流量并…

有过JVM调优经验吗【面试题】

写作目的 JVM其实比较偏理论的,日常工作中很少遇到。但是面试他问,所以需要自己mock一下场景进行准备这个问题的回复。 本次分析的场景的元空间太小导致频繁FGC的问题。 源码&启动参数 gitee下载源码 启动-调优前 nohup java -XX:MetaspaceS…

【MySQL】SQL索引失效的几种场景及优化

MySQL中提高性能的一个最有效的方式是对数据表设计合理的索引。索引提供了高效访问数据的方法,并且加快查询的速度, 因此索引对查询的速度有着至关重要的影响。 使用索引可以快速地定位表中的某条记录,从而提高数据库查询的速度,…

C++之函数模板高级用法(一百五十四)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…

两个好用到爆的Python模块,建议收藏!

在日常开发工作中,经常会遇到这样的一个问题:要对数据中的某个字段进行匹配,但这个字段有可能会有微小的差异。比如同样是招聘岗位的数据,里面省份一栏有的写“广西”,有的写“广西壮族自治区”,甚至还有写…

基于单片机的智能鞋柜的设计与实现

功能介绍 以51单片机作为主控系统;通过DHT11温湿度采集;通过按键设置逻辑处理;通过LED紫外线消毒;通过继电器控制风扇进行换气除湿;通过继电器控制加热片进行加热;整个电路以5v供电; 电路图 PCB 源代码 #i…

nodejs 读取xlsx 文件转json 格式(包含表格时间类型)

需求概要:从xlsx 文件中读取内容转化成想要的json 格式,用于web 读取数据 newDoc.xlsx文档内容大概: 本内容主要是更新前端公告内容, const xlsx require(node-xlsx) const fs require(fs) const moment require(moment)//转换…

双非本大二上岸大厂——念念不忘,必有回响

⭐️前言⭐️ 博主就读于一所普通的学校(双非本),在大二下学期3月份开始网上投递简历,历时近百余天,投递简历500,面试近40余场,最终在6月份学期末,斩获了两个大厂offer(北…

最小栈——力扣155

方法&#xff1a;辅助栈 这些函数中只有求最小值函数需要借助辅助栈 代码如下&#xff1a; class MinStack {stack<int> x_stack;stack<int> min_stack; public:MinStack() {min_stack.push(INT_MAX);}void push(int val) {x_stack.push(val);min_stack.push(…

使用Java计算课程绩点、课程学分绩点、总绩点

1、定义实体类 实体类中包括属性表 名称释义xuefen该课程学分chengji该课程取得的成绩xuefenjidian该课程取得的学分绩点xuefen该课程取得的学分 其中有式子&#xff1a; j i d i a n ( c h e n g j i − 50 ) 10.0 jidian \frac{(chengji-50)}{10.0} jidian10.0(chengji−…

【Azure】解析 Microsoft Defender for Cloud:云安全的保护与管理

你在使用自己的电脑的时候&#xff0c;作为安全防护你可能直接装个杀毒软件&#xff0c;或者什么xx管家之类的&#xff0c;那么你是否有想过&#xff0c;如果我有一套云服务之后&#xff0c;我应该如何进行安全防护呢&#xff1f;本文带你了解在 Azure 云中的安全防护体系&…

同余最短路

同余最短路就是把每一个同余类当成一个结点&#xff0c;在同余类之间建边&#xff0c;然后跑最短路 答案统计的时候对每个同余类单独计算贡献 题意&#xff1a; 思路&#xff1a; 答案可以对模X的所有同余类计算贡献 设dis[i]为在模X意义下&#xff0c;Y和Z之后%X余数为i的…

数据库练习

数据库练习 建立三张表&#xff0c;以及表中的联系 由于学生表中存在外键&#xff0c;所以我们需要先创建课程表和班级表 课程表 mysql> create table course(-> course_id int primary key auto_increment comment 课程编号,-> course_name varchar(10) not null…

【C++初阶】C++入门——引用

文章目录 一、引用的概念二、共用同一块空间验证三、引用的特性3.1 引用在定义时必须初始化3.2 一个变量可以有多个引用3.3 引用不能改变 四、引用的使用场景4.1 做参数4.2 做返回值 五、传值、传引用效率比较六、常引用6.1 权限放大——不被允许6.2 权限平移6.3 权限缩小6.4 赋…

MYSQL索引为啥要用B+树储存数据呢

首先我们来分析一下需求 MYSQL索引需要怎样的数据结构 为了防止数据因为特(duan)殊(kai)情(dian)况(yuan)丢失,我们的数据肯定是要持久化的,也就是保存在硬件(磁盘)里面,而我们知道 磁盘相对于内存来讲 速度要慢了几万倍 甚至即使万倍 所以我们必须减少磁盘的I/O操作 再有呢…

电子时钟制作(瑞萨RA)(10)----电容触摸配置

概述 这篇文档将创建一个使用 e2 studio 集成 QE 的电容式触摸应用示例。 硬件准备 首先需要准备一个开发板&#xff0c;这里我准备的是芯片型号R7FA2E1A72DFL的开发板&#xff1a; 视频教程 https://www.bilibili.com/video/BV14h4y1E7py/ 电子时钟制作(10)----电容触摸配…

python接口自动化(二十三)--unittest断言——上(详解)

简介 在测试用例中&#xff0c;执行完测试用例后&#xff0c;最后一步是判断测试结果是 pass 还是 fail&#xff0c;自动化测试脚本里面一般把这种生成测试结果的方法称为断言&#xff08;assert&#xff09;。用 unittest 组件测试用例的时候&#xff0c;断言的方法还是很多的…