Python深度学习基于Tensorflow(15)OCR验证码 文本检测与识别实例

news2025/1/12 23:36:31

文章目录

    • 文本检测
    • 文本识别
      • CTC层
      • 生成验证码并制作数据集
      • 建立模型
      • 模型推理
    • 参考

文本检测

文本检测和目标检测类似,其不同之处在于文本目标具有序列特征,有连续性,可以通过结合 Faster R-CNNLSTM 的方式进行文本检测,如 CTPN 网络,其网络结构来自论文: Detecting Text in Natural Image with Connectionist Text Proposal Network (arxiv.org);

![[Pasted image 20240603151528.png]]

CNN学习的是感受野内的空间信息,LSTM学习的是序列特征。对于文本序列检测,显然既需要CNN抽象空间特征,也需要序列特征(毕竟文字是连续的)。

详细可以查看场景文字检测—CTPN原理与实现 - 知乎 (zhihu.com),CTPN网络的缺点很明显,只能识别行排列文本,如果文本充满艺术排列效果就不是很好;

这里有其他更好的方法进行文本检测:

  1. FCENet:[2104.10442] Fourier Contour Embedding for Arbitrary-Shaped Text Detection (arxiv.org)
  2. DBNet:[1911.08947] Real-time Scene Text Detection with Differentiable Binarization (arxiv.org)
  3. DBNet++:[2202.10304] Real-Time Scene Text Detection with Differentiable Binarization and Adaptive Scale Fusion (arxiv.org)

详细介绍可以看这个网站:天天教程 (foobarweb.net)

文本识别

在检测到文本位置后,我们提取出文本图片,将文本图片转化为同一大小格式,接着我们需要执行将文本图片转化为字符串任务;

文章中使用的是 CNN+RNN+CTC(CRNN+CTC) 架构,具体如下图所示

![[Pasted image 20240603210003.png]]

CTC层

CTC (Connectionist Temporal Classification),其被设计用来解决输入序列和输出序列难以一一对应的问题。

我们在特征图展开为特征序列这一步骤中,将原始特征图切割成不同的小块,理想情况是不同小块分别顺序对应目标字符串中某一字符,由于图片的无规则性以及感受野的存在,这种情况是不现实的,目标字符串中的字符很有可能被不同的块分别对应,这会导致字符串重复的情况出现,因此我们需要对齐预测序列和真实序列,Sequence Modeling with CTC (distill.pub)详细介绍了这一效果;

CTC 本质上是一个 softmax 矩阵,其 row nums 由预测的字符类别数量决定,其 col nums 又特征图切割成不同的小块的数量决定,这里 col nums 又称为时间步 T , 假设我们预测的字符串长度为 true_string_length ,由于每一时间步只能预测一个字符再加上空字符和重复字符的出现,我们必须要保证时间步 T 要大于 true_string_length

对重复字符进行删除,如下图1所示,这会导致两个问题:

  1. 通常,强制每个输入步骤与某些输出对齐是没有意义的。例如,在语音识别中,输入可能会出现一段沉默而没有相应的输出。
  2. 我们无法产生连续多个字符的输出。考虑对齐方式 [h, h, e, l, l, l, o]。折叠重复将产生“helo”而不是“hello”。

![[Pasted image 20240603214024.png]]

这里引入一个空字符: ϵ \epsilon ϵ,利用该字符去间隔重复字符,然后进行删除就可以解决上面两个问题,具体效果如上图2所示;

如何具体实施这一操作呢,这里我们可以对 y 进行处理,对 y 做一个简单的变换: π 0 = [ ϵ , y 1 , ϵ , y 2 , ϵ , … , y n , ϵ ] \pi_0=[\epsilon, y_1,\epsilon,y_2,\epsilon,\dots,y_n,\epsilon] π0=[ϵ,y1,ϵ,y2,ϵ,,yn,ϵ]
得到其概率为 p ( π 0 ∣ X ) p(\pi_0|X) p(π0X),由于 y 的变换有很多,如果 y 1 y_1 y1 不等于 y 2 y_2 y2 ,那么有许多的新变换,如 π 1 = [ ϵ , y 1 , y 2 , ϵ , ϵ , … , y n , ϵ ] \pi_1=[\epsilon, y_1,y_2,\epsilon,\epsilon,\dots,y_n,\epsilon] π1=[ϵ,y1,y2,ϵ,ϵ,,yn,ϵ] 等等

这里将所有的变换的概率值作为损失,得到损失如下: L = ∑ i − log ⁡ p ( y i ∣ X i ) = ∑ i ∑ j − log ⁡ p ( π i j ∣ X i ) \mathcal{L} = \sum_i -\log p(y_i|X_i)=\sum_i \sum_j-\log p(\pi_{ij}|X_i) L=ilogp(yiXi)=ijlogp(πijXi)
由于变换很多,单一计算非常困难,这里我们可以使用动态规划进行简化,详细请看:Sequence Modeling with CTC (distill.pub)

![[Pasted image 20240603220041.png]]

tensorflowCTC 损失计算的接口,接口如下:

tf.nn.ctc_loss(
    labels,
    inputs,
    sequence_length,
    preprocess_collapse_repeated=False,
    ctc_merge_repeated=True,
    ignore_longer_outputs_than_inputs=False,
    time_major=True
)

生成验证码并制作数据集

captcha 是 python 用来生成随机验证码的一个库,可以使用 pip install captcha 安装

定义两个函数 random_captcha_textgen_captcha_text_and_image 分别执行随机生成验证码文本和验证码图片生成任务;

import os
import random
from rich.progress import track
from captcha.image import ImageCaptcha


def random_captcha_text(char_set=None, captcha_size=5):
    """随机生成 number 和 alphabet 组合的字符串"""
    if char_set is None:
        number = [ '1', '2', '3', '4', '5', '6', '7', '8', '9']
        alphabet = [ 'a', 'd',  'h', 'j', 'k', 'q', 's', 't', 'y']
        char_set = number + alphabet

    captcha_text = []
    for i in range(captcha_size):
        c = random.choice(char_set)
        captcha_text.append(c)
    return ''.join(captcha_text)


def gen_captcha_text_and_image(width=200, height=50, char_set=None, save_path='./captcha_imgs/'):
    """随机生成验证码并保存在./captcha_imgs/文件目录下"""
    os.makedirs(save_path, exist_ok=True)
    ic = ImageCaptcha(width=width, height=height)
    captcha_text = random_captcha_text(char_set)
    img= ic.create_captcha_image(captcha_text,color='red', background='white')
    # create_noise_curve方法将上面生成的验证码 img 画上干扰线
    img = ic.create_noise_curve(img, color='black')
    img.save(save_path+captcha_text+".png")
    return captcha_text, img

利用 gen_captcha_text_and_image 生成3000个验证码图片作为数据集图片,下一步开始制作数据集

nums = 3000
for step in track(range(nums)):
    gen_captcha_text_and_image()

# Working... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:14

数据集制作

import os
import random
from pathlib import Path
import tensorflow as tf
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split

def process_data(img_path, label):
    """dataset --> process_dataset"""
    img = tf.io.read_file(img_path)
    img = tf.io.decode_png(img, channels=1)
    img = tf.image.convert_image_dtype(img, tf.float32)
    
    label = tf.strings.unicode_split(label, input_encoding="UTF-8")
    label = char_num(label)
    # 固定 TensorSpec
    img = tf.reshape(img, [50, 200, 1])
    label = tf.reshape(label, [5])
    return img, label

# 存储验证码文件夹 ./captcha_imgs/
data_dir = Path('./captcha_imgs/')
image_paths = list(map(str, list(data_dir.glob("*.png"))))
labels = [image_path.split(os.path.sep)[1].split('.png')[0] for image_path in image_paths]

# image_paths[0], labels[0]  --> ('captcha_imgs\\1113s.png', '1113s')

characters = sorted(list(set(char for label in labels for char in label)))
# ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'd', 'h', 'j', 'k', 'q', 's', 't', 'y']

# 定义两个转换,一个是将char转化为num,一个是将num转化为char
char_num = tf.keras.layers.StringLookup(vocabulary=characters, invert=False)
num_char = tf.keras.layers.StringLookup(vocabulary=characters, invert=True )

# 切割数据集
X_train, X_test, y_train, y_test = train_test_split(image_paths, labels, test_size=0.33, random_state=42)

# 定义超参数
batch_size = 16

# 制作数据集
train_data = tf.data.Dataset.from_tensor_slices((X_train, y_train))
test_data = tf.data.Dataset.from_tensor_slices((X_test, y_test))
train_data = train_data.map(process_data).batch(batch_size).prefetch(buffer_size=tf.data.AUTOTUNE).cache()
test_data = test_data.map(process_data).batch(batch_size)

可视化数据代码

def plot_16_images():
    plt.figure(figsize=(10, 4))
    imgs, labels = train_data.take(1).get_single_element()
    for ix in range(imgs.shape[0]):
        plt.subplot(4, 4, ix+1)
        plt.imshow(imgs[ix])
        plt.title(tf.strings.reduce_join(num_char(labels[ix])).numpy().decode('utf-8'))
        plt.axis('off')
    plt.tight_layout()
    plt.show()

plot_16_images()

得到结果如下

![[Pasted image 20240603235836.png]]

建立模型

模型架构如图,现在使用代码实现该模型架构;

![[Pasted image 20240603210003.png]]

实现模型架构代码如下:

import tensorflow as tf

class CustomModel(tf.keras.models.Model):
    def __init__(self):
        super(CustomModel, self).__init__()
        self.conv_1 = tf.keras.layers.Conv2D(32, 3, activation='relu', padding='same')
        self.conv_2 = tf.keras.layers.Conv2D(64, 3, activation='relu', padding='same')
        self.max_pool_1 = tf.keras.layers.MaxPooling2D((2,2))
        self.max_pool_2 = tf.keras.layers.MaxPooling2D((2,2))
        self.blstm_1 = tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(128, return_sequences=True, dropout=0.25))
        self.blstm_2 = tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(64, return_sequences=True, dropout=0.25))
        self.dense_1 = tf.keras.layers.Dense(64, activation='relu')
        self.dense_2 = tf.keras.layers.Dense(len(characters) + 2, activation='softmax')
        self.dropout = tf.keras.layers.Dropout(0.2)
    
    def call(self, x):
        x = tf.transpose(x, perm=[0, 2, 1, 3])
        x = self.conv_1(x)
        x = self.max_pool_1(x)
        x = self.conv_2(x)
        x = self.max_pool_2(x)
        x = tf.reshape(x, [tf.shape(x)[0], tf.shape(x)[1], -1])
        x = self.dense_1(x)
        x = self.dropout(x)
        x = self.blstm_1(x)
        x = self.blstm_2(x)
        x = self.dense_2(x)
        return x

def custom_loss(y_true, y_pred):
    batch_len = tf.cast(tf.shape(y_true)[0], dtype="int64")
    input_length = tf.cast(tf.shape(y_pred)[1], dtype="int64")
    label_length = tf.cast(tf.shape(y_true)[1], dtype="int64")

    # 1 慢一些
    # input_length = input_length * tf.ones(shape=(batch_len), dtype="int64")
    # label_length = label_length * tf.ones(shape=(batch_len), dtype="int64")
    # loss = tf.nn.ctc_loss(y_true, y_pred, label_length, input_length, logits_time_major=False)

    # 2 快一些
    input_length = input_length * tf.ones(shape=(batch_len, 1), dtype="int64")
    label_length = label_length * tf.ones(shape=(batch_len, 1), dtype="int64")
    loss = tf.keras.backend.ctc_batch_cost(y_true, y_pred, input_length, label_length)
    
    return loss

model = CustomModel()
# build the model
model(train_data.take(1).get_single_element()[0])

# summary the model
model.summary()

compile 模型并开始训练:

model.compile(
    optimizer='adam',
    loss=custom_loss
)

model.fit(train_data, validation_data=test_data, epochs=200)

得到训练过程如下:

Epoch 1/200
169/169 [==============================] - 21s 61ms/step - loss: 22.0665 - val_loss: 16.1782
Epoch 2/200
169/169 [==============================] - 7s 39ms/step - loss: 16.1598 - val_loss: 16.1217
.........................................................................................
169/169 [==============================] - 7s 39ms/step - loss: 15.4558 - val_loss: 15.6200
Epoch 27/200
169/169 [==============================] - 7s 39ms/step - loss: 15.4595 - val_loss: 15.6041
Epoch 28/200
169/169 [==============================] - 7s 40ms/step - loss: 15.4209 - val_loss: 15.4314
Epoch 29/200
169/169 [==============================] - 7s 40ms/step - loss: 15.1606 - val_loss: 14.4924
Epoch 30/200
169/169 [==============================] - 7s 40ms/step - loss: 14.1823 - val_loss: 12.9583
Epoch 31/200
.........................................................................................
Epoch 42/200
169/169 [==============================] - 7s 40ms/step - loss: 0.6783 - val_loss: 0.2937
Epoch 43/200
169/169 [==============================] - 7s 40ms/step - loss: 0.6130 - val_loss: 0.2544
Epoch 44/200
169/169 [==============================] - 7s 39ms/step - loss: 0.4716 - val_loss: 0.2368
.........................................................................................
Epoch 194/200
169/169 [==============================] - 7s 40ms/step - loss: 0.0463 - val_loss: 0.1134
Epoch 195/200
169/169 [==============================] - 7s 40ms/step - loss: 0.0439 - val_loss: 0.0840
Epoch 196/200
169/169 [==============================] - 7s 41ms/step - loss: 0.0767 - val_loss: 0.1057
Epoch 197/200
169/169 [==============================] - 7s 41ms/step - loss: 0.0326 - val_loss: 0.0906
Epoch 198/200
169/169 [==============================] - 7s 41ms/step - loss: 0.0224 - val_loss: 0.0844
Epoch 199/200
169/169 [==============================] - 7s 41ms/step - loss: 0.0701 - val_loss: 0.1003
Epoch 200/200
169/169 [==============================] - 7s 40ms/step - loss: 0.0477 - val_loss: 0.0911

模型推理

当模型训练完毕后,模型的输出并不是目标字符串,仍然是一个 softmax 矩阵,因此我们需要对该矩阵继续进行操作;

当我们训练好一个RNN模型时,给定一个输入序列X,我们需要找到最可能的输出,也就是求解
Y ∗ = a r g m a x k P ( Y / X ) Y^*=\underset{k}{argmax} P(Y/X) Y=kargmaxP(Y/X)
求解最可能的输出有两种方案,一种是Greedy Search,第二种是Beam Search

  1. Greedy Search:每个时间片均取该时间片概率最高的节点作为输出
  2. Beam Search:Beam Search是寻找全局最优值和Greedy Search在查找时间和模型精度的一个折中。一个简单的beam search在每个时间片计算所有可能假设的概率,并从中选出最高的几个作为一组。然后再从这组假设的基础上产生概率最高的几个作为一组假设,依次进行,直到达到最后一个时间片。
def decode_batch_predictions(X, mode='greedy'):
    """ mode 有两种模式 beam 和 greedy 一般来说 greedy 效果要好一些"""
    y_pred = model(X)

    if mode == 'beam':
        input_length = tf.cast(tf.shape(y_pred)[1], dtype="int64")
        zz = tf.nn.ctc_beam_search_decoder(tf.transpose(y_pred, perm=[1,0,2]),  [50]*16)[0][0]
        zz = tf.strings.reduce_join(num_char(tf.sparse.to_dense(zz)), axis=-1).numpy()
        zz = [s.decode('utf-8').replace('[UNK]', '')[:5] for s in list(zz)]
        
    elif mode == 'greedy':
        input_length = tf.cast(tf.shape(y_pred)[1], dtype="int64")
        zz = tf.nn.ctc_greedy_decoder(tf.transpose(y_pred, perm=[1,0,2]),  [50]*16)[0][0]
        zz = tf.strings.reduce_join(num_char(tf.sparse.to_dense(zz)), axis=-1).numpy()
        zz = [s.decode('utf-8').replace('[UNK]', '')[:5] for s in list(zz)]

    return zz

可视化如下

def plot_16_images_pred():
    plt.figure(figsize=(10, 4))
    imgs, labels = test_data.take(1).get_single_element()
    pred_labels = decode_batch_predictions(imgs, mode='greedy')
    labels = [zz.decode('utf-8') for zz in tf.strings.reduce_join(num_char(labels), axis=-1).numpy()]
    for ix in range(imgs.shape[0]):
            plt.subplot(4, 4, ix+1)
            plt.imshow(imgs[ix])
            plt.title(f'pred:{pred_labels[ix]}-real:{labels[ix]}')
            plt.axis('off')
    plt.tight_layout()
    plt.show()
    return pred_labels

plot_16_images_pred()

![[Pasted image 20240604012816.png]]

完毕!

参考

  1. 场景文字检测—CTPN原理与实现 - 知乎 (zhihu.com)
  2. Detecting Text in Natural Image with Connectionist Text Proposal Network (arxiv.org)
  3. CTC Loss 数学原理讲解:Connectionist Temporal Classification-CSDN博客
  4. Sequence Modeling with CTC (distill.pub)
  5. 一文读懂CRNN+CTC文字识别 - 知乎 (zhihu.com)
  6. CTC(Connectionist Temporal Classification)介绍 - PilgrimHui - 博客园 (cnblogs.com)

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

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

相关文章

【算法】MT2 棋子翻转

✨题目链接: MT2 棋子翻转 ✨题目描述 在 4x4 的棋盘上摆满了黑白棋子,黑白两色棋子的位置和数目随机,其中0代表白色,1代表黑色;左上角坐标为 (1,1) ,右下角坐标为 (4,4) 。 现在依次有一些翻转操作&#…

数据库讲解---(关系规范化)【二】

目录 前言 一.函数依赖相关 1.1函数依赖集F的逻辑蕴涵 1.2函数依赖集闭包 1.3函数依赖的推理规则 1.3.1独立推理规则 自反律 增广律 传递律 1.3.2其他推理规则 合并规则 分解规则 伪传递规则 二.数据集闭包与F逻辑蕴涵的充要条件 2.1属性集闭包 2.2F逻辑蕴涵的…

基于Kubernetes和DeepSpeed进行分布式训练的实战教程

目录 ​编辑 一、前期准备 二、部署和配置训练任务 三、编写和运行训练代码 四、监控和调优 五、代码实现 5.1. Dockerfile 5. 2. DeepSpeed 配置文件 (ds_config.json) 5.3. Kubernetes 部署文件 (deployment.yaml) 5.4. PyTorch 训练脚本 (train.py) 注意事项&am…

HTML5常用标签表单from

form表单标签 <!-- form表单其实就是一种&#xff1a;客户端和服务端数据交流一种方式机制。1&#xff1a; 服务端&#xff0c;提供数据接受地址&#xff08;gin/beego/inris&#xff09;比如&#xff1a;http://localhost:8080/toLogin2: 因为浏览器&#xff0c;在提交数据…

算法类学习笔记 —— 典型卷积神经网络

文章目录 介绍LetNet填充&步长&通道数填充步长通道数卷积层池化层全连接层激活函数常见的激活函数Sigmoid函数tanh函数ReLU激活函数LReLUPReLUSwish softmax分类 AlexNetVGGNetGoogleNetResNetDenseNetSENet 介绍 现有的卷积神经网络的结构可以按照下图机型分类&#x…

沃可趣产品增PC版,员工社区登上大屏幕

作为企业内部沟通与协作的枢纽&#xff0c;员工互动社区在促进信息流通、增强团队凝聚力方面扮演着关键角色。 沃可趣&#xff0c;这一匠心打造的员工互动社区&#xff0c;融汇了工作、学习与社交的精髓&#xff0c;为职场人构筑了一站式互动天地。 为了满足更广泛的工作场景…

基于Python+FFMPEG环境下载B站歌曲

题主环境 WSL on Windows10 命令如下 # python3.9 pip install --pre yutto yutto --batch https://www.bilibili.com/video/BV168411o7Bh --audio-only ls | grep aac | xargs -I {} ffmpeg -i {} -acodec libmp3lame {}.mp3WinAmp

[word] word图片环绕方式怎么设置? #经验分享#笔记#媒体

word图片环绕方式怎么设置&#xff1f; 在文档中图片排版是很常见的&#xff0c;在图片排版的过程中我们如何利用小技巧快速处理呢&#xff1f;下面给大家分享word图片环绕方式怎么设置的操作方法&#xff0c;一起来学习下吧&#xff01; 1、修改图片环绕方式 在Word文档中图…

JCR一区级 | Matlab实现TCN-BiLSTM-MATT时间卷积双向长短期记忆神经网络多特征分类预测

JCR一区级 | Matlab实现TCN-BiLSTM-MATT时间卷积双向长短期记忆神经网络多特征分类预测 目录 JCR一区级 | Matlab实现TCN-BiLSTM-MATT时间卷积双向长短期记忆神经网络多特征分类预测分类效果基本介绍程序设计参考资料 分类效果 基本介绍 1.JMatlab实现TCN-BiLSTM-MATT时间卷积双…

AI辅助论文:探索AI查重与AI降重技术

在科研领域&#xff0c;AI写作工具如同新一代的科研利器&#xff0c;它们能够极大提高文献查阅、思路整理和表达优化的效率&#xff0c;本质上促进了科研工作的进步。AI写作工具不仅快速获取并整理海量信息&#xff0c;还帮助我们精确提炼中心思想&#xff0c;显著提升论文写作…

使用Obfuscar 混淆WPF(Net6)程序

Obfuscar 是.Net 程序集的基本混淆器&#xff0c;它使用大量的重载将.Net程序集中的元数据&#xff08;方法&#xff0c;属性、事件、字段、类型和命名空间的名称&#xff09;重命名为最小集。详细使用方式参见&#xff1a;Obfuscar 在NetFramework框架进行的WPF程序的混淆比较…

电商数据采集决策智慧:深度解析数据采集与应用||电商API数据采集接口的接入与应用

引言 在数字化时代&#xff0c;数据已成为电商企业最宝贵的资产之一。通过有效的数据采集&#xff0c;企业能够洞察市场动态、理解消费者需求、优化运营策略&#xff0c;从而在激烈的市场竞争中脱颖而出。本文将深入探讨电商数据采集的重要性、常用方法以及应用实践。 一、电…

无锡哲讯携手SAP,赋能装备制造业数字化转型

在当今快速发展的工业4.0时代&#xff0c;装备制造业作为国民经济的重要支柱&#xff0c;正面临着前所未有的机遇与挑战。无锡哲讯智能科技有限公司凭借其深厚的行业经验和专业的SAP实施能力&#xff0c;为装备制造业提供全面的数字化解决方案&#xff0c;助力企业实现智能化、…

背包问题(第k优解问题)

这篇博客先说一道洛谷蓝题&#xff08;实际难度其实可能也就是在橙题左右&#xff0c;难度不大&#xff0c;请放心食用&#xff09; 1.背包问题的第k优解 首先&#xff0c;我们知道背包问题的最优解&#xff0c;我们可以通过状态转移方程来求出最优解 状态转移方程&#xff…

创业项目TensorLink开源了,没有显卡也可以玩大模型、SD。

显卡是很贵的、也是稀缺的。但是AI又是很火的&#xff0c;每个人都不想错过这个机会&#xff0c;公司也一样。 假设你是公司或者团队的负责人&#xff0c; 为了拥抱AI&#xff0c;先要解决显卡算力问题。如果要给每个人都配置一个显卡&#xff0c;哪怕是消费卡&#xff0c;也是…

Git - 详解 创建一个新仓库 / 推送现有文件夹 / 推送现有的 Git 仓库 到私有Gitlab

文章目录 【推送现有文件夹】详细步骤指令说明Git 全局设置设置Git全局用户名设置Git全局电子邮件地址 推送现有文件夹1. 进入现有文件夹2. 初始化Git仓库并设置初始分支为main3. 添加远程仓库4. 添加所有文件到暂存区5. 提交更改6. 推送代码到远程仓库并设置上游分支 创建一个…

麦克风什么牌子的音质效果好?揭秘最好的无线麦克风品牌排行

最近几年可以说全民短视频也不为过&#xff0c;越来越多人开始通过用手机拍摄短视频、vlog记录自己的生活&#xff0c;而领夹式无线麦克风的需求也开始激增。毕竟一个好的视频除了要有巧妙的构思和清晰稳定的拍摄外&#xff0c;干净的声音也是必不可少的部分。 要知道短视频归根…

pytorch构建模型训练数据集

pytorch构建模型训练数据集 pytorch构建模型训练数据集1.AlexNet:1.1.导入必要的库&#xff1a;1.2.数据预处理和增强&#xff1a;1.3.加载数据集&#xff1a;1.4.划分测试集和训练集&#xff1a;1.5.创建数据加载器&#xff1a;1.6.加载AlexNet模型&#xff1a;1.7.修改模型以…

【Python报错】已解决AttributeError: list object has no attribute ’shape‘ ( Solved )

解决Python报错&#xff1a;AttributeError: ‘list’ object has no attribute ‘shape’ (Solved) 在Python中&#xff0c;AttributeError表明你试图访问的对象没有你请求的属性或方法。如果你遇到了AttributeError: list object has no attribute shape的错误&#xff0c;这…

OpenMV学习笔记3——画图函数汇总

画图&#xff0c;即在摄像头对应位置画出图形&#xff0c;对于需要反馈信息的程序来说很直观。就如上一篇文章颜色识别当中的例子一样&#xff0c;我们在识别出的色块上画出矩形方框&#xff0c;并在中间标出十字&#xff0c;可以直观的看到OpenMV现在识别出的色块。 目录 一…