CTPN+CRNN算法端到端实现文字识别的实战开发

news2024/12/23 20:00:38

本文分享自华为云社区《CTPN+CRNN 算法端到端实现文字识别》,作者:HWCloudAI。

OCR介绍

光学字符识别(英语:Optical Character Recognition,OCR)是指对文本资料的图像文件进行分析识别处理,获取文字及版面信息的过程。发展时间较长,使用很普遍。OCR作为计算机视觉中较早使用深度学习技术的领域,有很多优秀的模型出现。普遍的深度学习下的OCR技术将文字识别过程分为:文本区域检测以及字符识别。

文本区域检测——CTPN模型

文字区域检测:将图片中出现的文本位置检测出来,可能存在不同语言,不同文字大小,不同角度倾斜,不同程度遮挡等情况。CTPN网络结合了CNN与LSTM深度网络,通过固定宽度的anchor提取proposal,能有效的检测出复杂场景的横向分布的文字区域,不定长度文本识别效果较好,是目前使用广泛的文字检测算法。

字符序列检测——CRNN模型

字符识别算法:将文本区域的字符识别出来。通过深度神经网络对目标区域进行特征提取,然后对固定特征进行提取和比对,得出识别结果。采用文本识别网络CRNN+CTC。CRNN全称为卷积循环神经网络,将特征提取,序列建模以及转录整合到统一的模型框架中。主要用于端到端地对不定长的文本序列进行识别,不用先对单个文字进行切割,而是将文本识别转化为时序依赖的序列学习问题,就是基于图像的序列识别。如下图,CRNN网络分为:卷积层、循环层和转录层三部分,CTC为无词典的转录方式, 不会被局限在预定义词汇范围中。

完整的端到端OCR流程

了解了文本区域检测以及字符识别后,下面详细讲解完整的端到端OCR流程:

(1)准备一张含有文字的原图;

(2)对原图进行文字位置的检测,检测结果可能是水平矩形框,也可能是倾斜矩形框;

(3)从原图中把文字框对应的图片切下来,并旋转正,得到水平的文字块切片图;

(4)对每个文字块切片图依次进行字符识别,每个切片图的识别结果汇总起来,就得到原图的文字识别结果。

因此完整的端到端OCR流程是:输入原图 -> 文字检测 -> 文字块切片 -> 字符识别 -> 识别结果汇总。

理论部分到此告一段落,下面开始在ModelArts中体验实战项目开发!

注意事项:

  1. 本案例使用框架**:** TensorFlow-1.8

  2. 本案例使用硬件规格**:** 8 vCPU + 64 GiB + 1 x Tesla V100-PCIE-32GB

  3. 进入运行环境方法:点此链接进入AI Gallery,点击Run in ModelArts按钮进入ModelArts运行环境,如需使用GPU,您可以在ModelArts JupyterLab运行界面右边的工作区进行切换

  4. 运行代码方法**:** 点击本页面顶部菜单栏的三角形运行按钮或按Ctrl+Enter键 运行每个方块中的代码

  5. JupyterLab的详细用法**:** 请参考《ModelAtrs JupyterLab使用指导》

  6. 碰到问题的解决办法**:** 请参考《ModelAtrs JupyterLab常见问题解决办法》

1. 下载代码和模型

本案例中已经将CTPN和CRNN的代码模型都整合到一起

import os
from modelarts.session import Session
sess = Session()

if sess.region_name == 'cn-north-1':
    bucket_path="modelarts-labs/notebook/DL_ocr_crnn_sequence_recognition/E2E_ocr.zip"
elif sess.region_name == 'cn-north-4':
    bucket_path="modelarts-labs-bj4/notebook/DL_ocr_crnn_sequence_recognition/E2E_ocr.zip"
else:
    print("请更换地区到北京一或北京四")

if not os.path.exists('E2E_ocr'):
    sess.download_data(bucket_path=bucket_path, path="./E2E_ocr.zip")

if os.path.exists('./E2E_ocr.zip'):
    status = os.system("unzip -q E2E_ocr.zip")
    if status == 0:
        os.system("rm E2E_ocr.zip")
       
Successfully download file modelarts-labs-bj4/notebook/DL_ocr_crnn_sequence_recognition/E2E_ocr.zip from OBS to local ./E2E_ocr.zip

2. CTPN相关模块导入

import shutil
import cv2
import numpy as np
import datetime
import os
import sys
import time
import json
import codecs
from PIL import Image
import tensorflow as tf
sys.path.append(os.getcwd() + '/E2E_ocr')
sys.path.append(os.getcwd() + '/E2E_ocr/CRNN/')
from collections import OrderedDict
from tensorflow.contrib import slim

from CTPN import data_provider as data_provider
from CTPN.model import mean_image_subtraction,Bilstm,lstm_fc,loss
from CTPN import vgg
from CTPN import model
from CTPN.utils.rpn_msr.proposal_layer import proposal_layer
from CTPN.utils.text_connector.detectors import TextDetector
from CTPN.utils.image import resize_image

/home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:519: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.

  _np_qint8 = np.dtype([("qint8", np.int8, 1)])

/home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:520: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.

  _np_quint8 = np.dtype([("quint8", np.uint8, 1)])

/home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:521: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.

  _np_qint16 = np.dtype([("qint16", np.int16, 1)])

/home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:522: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.

  _np_quint16 = np.dtype([("quint16", np.uint16, 1)])

/home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:523: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.

  _np_qint32 = np.dtype([("qint32", np.int32, 1)])

/home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:528: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.

  np_resource = np.dtype([("resource", np.ubyte, 1)])

3. CRNN相关模块安装与导入

!pip install -i https://pypi.tuna.tsinghua.edu.cn/simple keras==2.1.6
!pip install -i https://pypi.tuna.tsinghua.edu.cn/simple keras_applications==1.0.5

Requirement already satisfied: keras==2.1.6 in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages

Requirement already satisfied: numpy>=1.9.1 in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from keras==2.1.6)

Requirement already satisfied: six>=1.9.0 in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from keras==2.1.6)

Requirement already satisfied: scipy>=0.14 in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from keras==2.1.6)

Requirement already satisfied: pyyaml in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from keras==2.1.6)

Requirement already satisfied: h5py in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from keras==2.1.6)

[33mYou are using pip version 9.0.1, however version 21.0.1 is available.

You should consider upgrading via the 'pip install --upgrade pip' command.[0m

Requirement already satisfied: keras_applications==1.0.5 in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages

Requirement already satisfied: h5py in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from keras_applications==1.0.5)

Requirement already satisfied: keras>=2.1.6 in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from keras_applications==1.0.5)

Requirement already satisfied: numpy>=1.9.1 in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from keras_applications==1.0.5)

Requirement already satisfied: six in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from h5py->keras_applications==1.0.5)

Requirement already satisfied: pyyaml in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from keras>=2.1.6->keras_applications==1.0.5)

Requirement already satisfied: scipy>=0.14 in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from keras>=2.1.6->keras_applications==1.0.5)

[33mYou are using pip version 9.0.1, however version 21.0.1 is available.

You should consider upgrading via the 'pip install --upgrade pip' command.[0m

from keras.layers import Flatten, BatchNormalization, Permute, TimeDistributed, Dense, Bidirectional, GRU
from keras.layers import Input, Conv2D, MaxPooling2D, ZeroPadding2D,Lambda
from keras.models import Model
from keras.optimizers import SGD
from keras import backend as K

import keys as keys
from CRNN_model import decode

Using TensorFlow backend.

4. 加载CTPN模型

checkpoint_path = './E2E_ocr/models/checkpoints/'  # 训练模型保存路径
vgg_path = "./E2E_ocr/models/vgg_16.ckpt"          # vgg16预训练模型
image_path = './E2E_ocr/data/CTW-200'              # 训练集图片路径

CHECKPOINT_PATH = './E2E_ocr/models/checkpoints'   # 测试模型保存路径
os.environ['CUDA_VISIBLE_DEVICES'] = '0' #计算设备调用,空值为CPU计算,数字为GPU的序号

tf.reset_default_graph()
# 定义模型输入信息占位符
input_image = tf.placeholder(tf.float32, shape=[None, None, None, 3], name='input_image')
input_im_info = tf.placeholder(tf.float32, shape=[None, 3], name='input_im_info')
init_op = tf.initialize_all_variables()
# 定义模型训练步骤数
global_step = tf.variable_scope('global_step', [], initializer=tf.constant_initializer(0))

# 加载预训练模型
bbox_pred, cls_pred, cls_prob = model.model(input_image)
variable_averages = tf.train.ExponentialMovingAverage(0.997, global_step)
# 将变量存储到saver中
saver = tf.train.Saver(variable_averages.variables_to_restore())

ctpn_sess = tf.Session(config=tf.ConfigProto(allow_soft_placement=True))
with ctpn_sess.as_default():
    # 加载预训练模型权重信息
    ckpt_state = tf.train.get_checkpoint_state(CHECKPOINT_PATH)
    model_path = os.path.join(CHECKPOINT_PATH, os.path.basename(ckpt_state.model_checkpoint_path))
    saver.restore(ctpn_sess, model_path)
print('CTPN model load success')

WARNING:tensorflow:From /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages/tensorflow/python/util/tf_should_use.py:118: initialize_all_variables (from tensorflow.python.ops.variables) is deprecated and will be removed after 2017-03-02.

Instructions for updating:

Use `tf.global_variables_initializer` instead.
CTPN model load success

CTPN为了更好检测出文本区域,anchor为 宽度固定为16 , 高度为[11, 16, 23, 33, 48, 68, 97, 139, 198, 283] 的文本框,共10个anchor。

这样的设计是为了更好检测出文字区域的水平位置,在文字检测中,检测文字的水平范围比较垂直范围要更困难。将anchor的宽度固定,只检测10个高度的anchor,尤其在面对多个分离的文本的情况时,能够更好检测文字的范围。

不同的anchor得到了边界框,利用nms(非极大值抑制)进行边界框回归计算,最终得到细粒度的文本区域。

5. 加载CRNN模型

下图给出CRNN的结构参考:

characters = keys.alphabet[:]
nclass=len(characters)+1

input = Input(shape=(32, None, 1), name='the_input')
# CNN卷积层部分
m = Conv2D(64, kernel_size=(3, 3), activation='relu', padding='same', name='conv1')(input)
m = MaxPooling2D(pool_size=(2, 2), strides=(2, 2), name='pool1')(m)
m = Conv2D(128, kernel_size=(3, 3), activation='relu', padding='same', name='conv2')(m)
m = MaxPooling2D(pool_size=(2, 2), strides=(2, 2), name='pool2')(m)
m = Conv2D(256, kernel_size=(3, 3), activation='relu', padding='same', name='conv3')(m)
m = Conv2D(256, kernel_size=(3, 3), activation='relu', padding='same', name='conv4')(m)

m = ZeroPadding2D(padding=(0, 1))(m)
m = MaxPooling2D(pool_size=(2, 2), strides=(2, 1), padding='valid', name='pool3')(m)

m = Conv2D(512, kernel_size=(3, 3), activation='relu', padding='same', name='conv5')(m)
m = BatchNormalization(axis=1)(m)
m = Conv2D(512, kernel_size=(3, 3), activation='relu', padding='same', name='conv6')(m)
m = BatchNormalization(axis=1)(m)
m = ZeroPadding2D(padding=(0, 1))(m)
m = MaxPooling2D(pool_size=(2, 2), strides=(2, 1), padding='valid', name='pool4')(m)
m = Conv2D(512, kernel_size=(2, 2), activation='relu', padding='valid', name='conv7')(m)

m = Permute((2, 1, 3), name='permute')(m)
m = TimeDistributed(Flatten(), name='timedistrib')(m)
# RNN循环层部分
m = Bidirectional(GRU(256, return_sequences=True), name='blstm1')(m)
m = Dense(256, name='blstm1_out', activation='linear')(m)
m = Bidirectional(GRU(256, return_sequences=True), name='blstm2')(m)
y_pred = Dense(nclass, name='blstm2_out', activation='softmax')(m)

basemodel = Model(inputs=input, outputs=y_pred)
basemodel.load_weights('./E2E_ocr/CRNN/model_crnn.h5')
print("CRNN model load success")

CRNN model load success

6. 定义文字位置检测函数

from CTPN.utils.text_connector.text_connect_cfg import Config as TextLineCfg

def ctpn_text_detection(img_path):
    """
    CTPN文字位置检测函数
    :param img_path: 图片路径
    :return: img: 需要进行文字检测的图片
    :return: boxes: 图片上检测到的文字框
    """
    try:
        im = cv2.imread(img_path)[:, :, ::-1]
    except Exception as e:
        raise Exception("打开图片文件失败,图片路径:", img_path)
    img, (rh, rw) = resize_image(im)  #对图片进行形状调整
    h, w, c = img.shape
    im_info = np.array([h, w, c]).reshape([1, 3])
    #将图片信息传入模型得出预测结果,分别为文字区域坐标以及其得分
    bbox_pred_val, cls_prob_val = ctpn_sess.run([bbox_pred, cls_prob],feed_dict={input_image: [img],input_im_info: im_info})
    textsegs_total, _ = proposal_layer(cls_prob_val, bbox_pred_val, im_info)
    scores = textsegs_total[:, 0]
    textsegs = textsegs_total[:, 1:5]
    """文本框合并策略"""      
    TextLineCfg.MAX_HORIZONTAL_GAP = 50          # 两个框之间的距离小于50,才会被判定为临近框。该值越小,两个框之间要进行合并的要求就越高
    TextLineCfg.TEXT_PROPOSALS_MIN_SCORE = 0.7   # 单个小文本框的置信度,高于这个置信度的框才会被合并。该值越大,越多的框就会被丢弃掉
    TextLineCfg.TEXT_PROPOSALS_NMS_THRESH = 0.2  # 非极大值抑制阈值。该值越大,越多的框就会被丢弃掉
    TextLineCfg.MIN_V_OVERLAPS = 0.7             # 两个框之间的垂直重合度大于0.7,才会被判定为临近框。该值越大,两个在垂直方向上有偏差的框进行合并的可能性就越小
    textdetector = TextDetector(DETECT_MODE='H') # DETECT_MODE有两种取值:'H'和'O','H'模式适合检测水平文字,'O'模式适合检测有轻微倾斜的文字
    """文本框合并策略""" 
    boxes = textdetector.detect(textsegs, scores[:, np.newaxis], img.shape[:2])
    boxes = np.array(boxes, dtype=np.int)
    
    return img, boxes

7. 定义文字块切片函数

def img_transform_perspective(image, points, w_pad_rate=(0.0, 0.0), h_pad_rate=(0.0, 0.0)):
    """
    根据四个点进行透视变换,将四个点表示的四边形图变换成水平矩形图
    :param image: 原图
    :param points: 参考的四个点,坐标顺序是xmin, ymin, xmax, ymin, xmax, ymax, xmin, ymax
    :param w_pad_rate: 数组(rate1, rate2),对图像宽度左右两边的扩宽比例
    :param h_pad_rate: 数组(rate1, rate2),对图像宽度上下两边的扩宽比例
    :return: persp_img: 变换后的图
    :return: points2: 变换后的四点
    """
    if not isinstance(points, np.ndarray):
        points = np.array(points)
    points = points.reshape((4, 2))
    widths = np.linalg.norm(points[::2] - points[1::2], axis=1)  # points的4点组成的四边形的上下两边的长度
    width = int(round(widths.mean()))
    heights = np.linalg.norm(points[:2] - points[3:1:-1], axis=1)  # points的4点组成的四边形的左右两边的长度
    height = int(round(heights.mean()))

    points2 = np.array([[0, 0], [width - 1, 0],
                        [width - 1, height - 1], [0, height - 1]], np.float32)
    points2 += np.array([int(width * w_pad_rate[0]), int(height * h_pad_rate[0])]).reshape(1, 2)
    size = (int(width * (1 + w_pad_rate[0] + w_pad_rate[1])),
            int(height * (1 + h_pad_rate[0] + h_pad_rate[1])))

    mat = cv2.getPerspectiveTransform(points.astype(np.float32), points2)
    persp_img = cv2.warpPerspective(image, mat, size,
                                    borderMode=cv2.BORDER_CONSTANT,
                                    borderValue=(255, 255, 255))

    return persp_img, points2

8. 定义CRNN字符识别函数

def crnn_ocr(img):
    """
    CRNN字符识别函数
    :param img: 需要进行字符识别的图片
    :return: ocr_result: 图片的字符识别结果,数据类型为字符串
    """
    img = img.convert('L')
 
    img = img.convert('L')  # 图片灰度化
    
    scale = img.size[1] * 1.0 / 32  # 图片尺寸调整,把图片高度调整为32
    w = img.size[0] / scale
    w = int(w)
    img = img.resize((w, 32))
    img = np.array(img).astype(np.float32) / 255.0
    X = img.reshape((32, w, 1))
    X = np.array([X])
    y_pred = basemodel.predict(X)  # 预测
    ocr_result = decode(y_pred)  # 处理预测结果
  
    return ocr_result

9. 查看原图

img = Image.open('./E2E_ocr/test_dataset/text.png')
img

10. 开始图片测试

test_dir = './E2E_ocr/test_dataset'  # 待测试图片目录
save_results = True
output_dir = test_dir + '_output'
if not os.path.exists(output_dir):
    os.mkdir(output_dir)
ocr_results = OrderedDict()
files = os.listdir(test_dir)
for file_name in files:
    if not (file_name.endswith('jpg') or file_name.endswith('png')
         or file_name.endswith('JPG') or file_name.endswith('PNG')):
            continue
    print(file_name, 'ocr result:')
    file_path = os.path.join(test_dir, file_name)
    
    img, boxes = ctpn_text_detection(file_path)  # step1, 检测文字位置
    sorted_boxes = sorted(boxes.tolist(), key = lambda x: (x[1], x[0]))  # step2, 对文字框进行排序,优先按文字框左上顶点的y坐标升序排序,其次按x坐标升序排序
    for index, box in enumerate(sorted_boxes):
        cut_text_img, _ = img_transform_perspective(img, box[:8])  # step3, 从原图上切割出各个文字块,并将倾斜的文字块变换为水平矩形文字块
        ocr_result = crnn_ocr(Image.fromarray(cut_text_img))  # step4, 对每个文字块进行字符识别
        ocr_results[str(index)] = ocr_result
        print(str(index) + ',', ocr_result)
        
        if save_results:
            draw_img = img[:, :, ::-1].copy()
            for i, box in enumerate(boxes):
                cv2.polylines(draw_img, [box[:8].astype(np.int32).reshape((-1, 1, 2))], True, color=(0, 0, 255), thickness=2)
            cv2.imwrite(os.path.join(output_dir, file_name), draw_img)
            #将输出结果转为json格式
            with codecs.open(os.path.join(output_dir, file_name.split('.')[0] + '.json'), 'w', 'utf-8') as f:
                json.dump(ocr_results, f, indent=4, ensure_ascii=False)
print('end')

text.png ocr result:

0, A1正在改变我们的生活,

1, 正在改变我们身边的各行各业,

2, 但是这条通往智能世界的路并不平坦,

3, 其中一个巨大鸿沟就是AI人才的稀缺。

4, 在中国庞大的I从业群体,

5, A开发者缺口达百万级。

6, A1将成为全民普及性的技能,

7, 所以今天华为云El为大家带来《2020华为云AI实战营》免费课程,

8, 大幅降低A1拳习门]椤,

9, 帮助庞大的软件开发者群体快速拳握A1技能,

10, 把AI用起来。

end

点击关注,第一时间了解华为云新鲜技术~

 

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

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

相关文章

Java规则引擎Drools急速入门

文章目录1.Drools规则引擎简介2.Drools API开发步骤3.SpringBoot整合Drools案例4.Drools基础语法5.Drools条件语法部分6.Drools结果操作部分7.Drools内置属性部分8.Drools高级语法部分1.Drools规则引擎简介 (1)什么是规则引擎 ​ 全称为业务规则管理系…

类与对象(上篇)

类与对象面向过程和面向对象类的引入类的定义类的访问限定符及封装访问限定符封装类的作用域类的实例化类对象类对象的存储方式类成员函数的this指针this指针的引出this指针的特性面向过程和面向对象 C语言是面向过程,注重的是过程,先分析求解问题的步骤…

【计算机视觉】目标检测中Faster R-CNN、R-FCN、YOLO、SSD等算法的讲解(图文解释 超详细必看)

觉得有帮助请点赞关注收藏~~~ 一、基于候选区域的目标检测算法 基于候选区域的深度卷积神经网络(Region-based Convolutional Neural Networks)是一种将深度卷积神经网络和区域推荐相结合的物体检测方法,也可以叫做两阶段目标检测算法。第一…

Web大学生网页作业成品——环保垃圾分类网站设计与实现(HTML+CSS+JavaScript) web前端开发技术 web课程设计 网页规划与设计

🎀 精彩专栏推荐👇🏻👇🏻👇🏻 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业…

RFID标签让企业海量固定资产实现科学化管理

近年来,随着物联网、IoT、移动技术、云计算技术的成熟,越来越多的企业开始使用RFID标签管理企业海量的固定资产。优化固定资产标准化管理流程,有效管理和库存固定资产,进一步提高企业实物资产管理和库存效率。 包括资产申购、验收…

【操作系统】计算机大脑CPU

1.CPU组成机构和存储器层级 (1)CPU是计算机硬件系统的核心部件-大脑 结构:运算器控制器(两个部件里面有寄存器组)通过CPU内部的总线进行通信 (2)单核CPU架构 控制器Control Unit简称【CU】 …

Python使用Opencv图像处理方法完成手势识别(二)

Opencv完成手势识别根据坐标识别寻找最低点计算其他点与最低点的距离通过距离阈值判断手指根数和手势效果展现完整代码当我们把手近似出来后会得到一组轮廓的点坐标,我自己手势识别的思路就是根据点坐标来判断手势。根据坐标识别 寻找最低点 所谓寻找最低点&#…

浅谈Nacos注册中心集群分布式架构设计

前言 Nacos的压测性能是非常好的,这里是Nacos官方的压测报告。3节点(CPU 16核,内存32G)规模集群,压测容量服务数可达60W,实例注册数达110W,集群运行持续稳定,达到预期;注册/查询实例TPS达到 13…

ModStartBlog v6.3.0 任务调度重构,UEditor 升级

系统介绍 ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场拥有丰富的功能应用,支持后台一键快速安装,让开发者能快的实现业务功能开发。 系统完全开源,基于 Apache 2.0 开源协议。 功能特性 丰富的模块市场,后台一键…

跨设备链路聚合 M-LAG

M-LAG(Multichassis Link Aggregation Group)即跨设备链路聚合组,是一种实现跨设备链路聚合的机制,如下图所示,将SwitchA和SwitchB通过peer-link链路连接并以同一个状态和Switch进行链路聚合协商,从而把链路…

学校介绍静态HTML网页设计作品 DIV布局学校官网模板代码 DW大学网站制作成品下载 HTML5期末大作业

🎉精彩专栏推荐 💭文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (10…

iOS app上架app store流程详解​

前提条件​ 在有效期内的苹果开发者账号(类型为个人或者公司账号)。还有一种情况,就是你的Apple ID被添加到公司开发者账号团队里面,这样也是可以的,但是需要叫管理员给你开通相应的账号权限,如下截图&…

[附源码]Python计算机毕业设计Django在线图书销售系统

项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等等。 环境需要 1.运行环境:最好是python3.7.7,我…

Vue | 有关Vue2路由知识点的一些总结,以及Vue3路由做出了哪些调整?

目录 Vue2: 1. 路由: 2. 路由规则: 3. 实现切换(active-class可配置高亮样式) 4. 指定展示位置 5. 路由的query参数 6. params传参: 7. 多级路由 8. 路由的props配置 9. 的replace属性 10. 编…

Android databinding之BindingMethod与BindingMethods介绍与使用(五)

一、介绍 前面几篇文章已介绍了很多data binding的用法,今天我将会介绍一个新的方法绑定BindingMethod的用法。 BindingMethod,从名字可以看出是绑定方法的,绑定的一般都是和布局有关,通过绑定来提高布局可扩展性。 二、使用 Bin…

把盏言欢,款款而谈,ChatGPT结合钉钉机器人(outgoing回调)打造人工智能群聊/单聊场景,基于Python3.10

就像黑火药时代里突然诞生的核弹一样,OpenAI的ChatGPT语言模型的横空出世,是人工智能技术发展史上的一个重要里程碑。这是一款无与伦比、超凡绝伦的模型,能够进行自然语言推理和对话,并且具有出色的语言生成能力。 好吧&#xff…

资产扫描神器ARL增强改造

拉取项目首先从GitHub克隆到服务器上。git clone https://github.com/ki9mu/ARL-plus-docker/修改配置文件因为ARL在配置文件里设置了黑名单,有时候项目为GOV或者EDU之类的时候无法进行扫描,所以在这里修改一下配置文件就可以解除限制。cd ARL-plus-dock…

Spring Boot3.0 GA系列全新版本-全新体验-学习案例1

SpringBoot3.0 GA 2022-11-24这是伟大的一天,Spring Boot进入了3.0时代,并会叩开JDK全面升级的浪潮 文章目录SpringBoot3.0 GA技术支持一、新建第一个mode?1.1、打开IDEA,新建项目1.2、选择 版本 和 依赖1.3、查看项目结构1.4、配…

vscode配置remote ssh

1. 安装插件 设置界面 右键最左边tab栏: 主体: vscode 插件: Remote SSH Linux主体: vscode-server 插件: C/C, CMake, CMake Tools, CodeLLDB, Rainbow Brackets, vscode-proto3, SVN 虚拟化主体: multipass linux发行版(比如ubuntu) 补充: multipass实例本地端口…

【C++ unordered_set set 和 unordered_map 和 map】

文章目录前言简单介绍哈希表,哈希结构什么时候用哈希表unordered_map操作likou第一题 两数之和unordered_set 基础操作unordered_set 实现总结前言 今天重新打开力扣,看到以前的签到题两数之和,以前的方法是双指针暴力解法,偶然看…