【课程总结】Day11(中):手势图像识别实战(Vgg16和ResNet)

news2024/11/20 6:16:41

前言

在上一章《【课程总结】Day11(上):手势图像识别实战(LeNet模型)》课程中,我们通过使用LeNet模型实现了手势识别。在本章内容中,我们将搭建Vgg模型和ResNet模型,并应用到手势识别中。

Vgg模型

Vgg简介

VGG是一种深度卷积神经网络模型,由牛津大学的研究团队提出。它在2014年的ImageNet图像分类比赛中获得了第二名的好成绩,被广泛应用于计算机视觉领域。

论文地址:https://arxiv.org/abs/1409.1556

Vgg网络结构图

查看Vgg的论文可以看到Vgg的网络构成如下:

Vgg网络有不同的版本,本次我们使用Vgg16,即为图中D列对应的网络结构。该网络结构换另外一种展示方式如下图所示:

输入层

输入层是一张224×224的RGB图像,通道数为3,通道数代表RGB三个通道的像素值。

第一次卷积

经过2层的64×3×3卷积核,输出输出层为64×224×224

  • 卷积核大小为3×3,步长为1,填充为1
  • 激活函数为ReLU
  • 卷积次数为2
第一次MaxPooling

经过最大池化层,图像尺寸减半,输出尺寸为64×112×112

  • 池化核大小为2×2,步长为2
第二次卷积

经过2层的128×3×3卷积核,输出尺寸为128×112×112

  • 卷积核大小为3×3,步长为1,填充为1
  • 激活函数为ReLU
  • 卷积次数为2
第二次MaxPooling

经过最大池化层,图像尺寸减半,输出尺寸为128×56×56

  • 池化核大小为2×2,步长为2
第三次卷积

经过3层256×3×3卷积核,输出尺寸为256×56×56

  • 卷积核大小为3×3,步长为1,填充为1
  • 激活函数为ReLU
  • 卷积次数为3
第三次MaxPooling

经过最大池化层,图像尺寸减半,输出尺寸为256×28×28

  • 池化核大小为2×2,步长为2
第四次卷积

经过3层512×3×3卷积核,输出尺寸为512×28×28

  • 卷积核大小为3×3,步长为1,填充为1
  • 激活函数为ReLU
  • 卷积次数为3
第四次MaxPooling

经过最大池化层,图像尺寸减半,输出尺寸为512×14×14

  • 池化核大小为2×2,步长为2
第五次卷积

经过3层512×3×3卷积核,输出尺寸为512×14×14

  • 卷积核大小为3×3,步长为1,填充为1
  • 激活函数为ReLU
  • 卷积次数为3
第五次MaxPooling

经过最大池化层,图像尺寸减半,输出尺寸为512×7×7

  • 池化核大小为2×2,步长为2
全连接层

将feature map展平,输出一维尺寸为512×7×7=25088

全链接4096

经过2层的1×1×4096的全连接层,输出尺寸为4096

  • 激活函数为ReLU
  • 全链接次数为2
全链接1000

经过1×1×1000的全连接层,输出尺寸为1000,最后通过softmax函数进行分类,输出1000个预测结果。

1000由最终分类数量决定,当年比赛需要分1000类

Vgg网络特点

  • 小卷积核组:作者通过堆叠多个33的卷积核(少数使用11)来替代大的卷积核,以减少所需参数;
  • 小池化核:相比较于AlexNet使用的33的池化核,VGG全部为22的池化核;
  • 网络更深特征图更宽:卷积核专注于扩大通道数,池化专注于缩小高和宽,使得模型更深更宽的同时,计算量的增加不断放缓;

总结来说,Vgg这种设计可以增加网络的深度,提高特征提取的效果,同时减少了参数数量,降低了过拟合的风险。

Vgg网络搭建

import torch
from torch import nn


class ConvBlock(nn.Module):
    """
        一层卷积:
            - 卷积层
            - 批规范化层
            - 激活层
    """
    def __init__(self, in_channels, out_channels, 
                 kernel_size=3, stride=1, padding=1):
        super().__init__()
        self.conv = nn.Conv2d(in_channels=in_channels, out_channels=out_channels,
                             kernel_size=kernel_size, stride=stride,padding=padding)
        self.bn = nn.BatchNorm2d(num_features=out_channels)
        self.relu = nn.ReLU()
    
    def forward(self, x):
        x = self.conv(x)
        x = self.bn(x)
        x = self.relu(x)
        return x

class Vgg16(nn.Module):
    def __init__(self, n_classes=1000):
        super().__init__()
        # 1, 特征抽取部分
        self.feature_extractor = nn.Sequential(
            
            # stage1
            
            # 卷积1
            ConvBlock(in_channels=3, 
                      out_channels=64, 
                      kernel_size=3,
                      stride=1,
                      padding=1),
            # 卷积2
            ConvBlock(in_channels=64, 
                      out_channels=64, 
                      kernel_size=3,
                      stride=1,
                      padding=1),
            # 池化
            nn.MaxPool2d(kernel_size=2, stride=2, padding=0),
            
            # stage2
            
            # 卷积1
            ConvBlock(in_channels=64, 
                      out_channels=128, 
                      kernel_size=3,
                      stride=1,
                      padding=1),
            # 卷积2
            ConvBlock(in_channels=128, 
                      out_channels=128, 
                      kernel_size=3,
                      stride=1,
                      padding=1),
            # 池化
            nn.MaxPool2d(kernel_size=2, stride=2, padding=0),
            
            # stage3
            
            # 卷积1
            ConvBlock(in_channels=128, 
                      out_channels=256, 
                      kernel_size=3,
                      stride=1,
                      padding=1),
            # 卷积2
            ConvBlock(in_channels=256, 
                      out_channels=256, 
                      kernel_size=3,
                      stride=1,
                      padding=1),
            # 卷积3
            ConvBlock(in_channels=256, 
                      out_channels=256, 
                      kernel_size=3,
                      stride=1,
                      padding=1),
            # 池化
            nn.MaxPool2d(kernel_size=2, stride=2, padding=0),
            
            # stage4
            
            # 卷积1
            ConvBlock(in_channels=256, 
                      out_channels=512, 
                      kernel_size=3,
                      stride=1,
                      padding=1),
            # 卷积2
            ConvBlock(in_channels=512, 
                      out_channels=512, 
                      kernel_size=3,
                      stride=1,
                      padding=1),
            # 卷积3
            ConvBlock(in_channels=512, 
                      out_channels=512, 
                      kernel_size=3,
                      stride=1,
                      padding=1),
            # 池化
            nn.MaxPool2d(kernel_size=2, stride=2, padding=0),
            
            
            # stage5
            
            # 卷积1
            ConvBlock(in_channels=512, 
                      out_channels=512, 
                      kernel_size=3,
                      stride=1,
                      padding=1),
            # 卷积2
            ConvBlock(in_channels=512, 
                      out_channels=512, 
                      kernel_size=3,
                      stride=1,
                      padding=1),
            # 卷积3
            ConvBlock(in_channels=512, 
                      out_channels=512, 
                      kernel_size=3,
                      stride=1,
                      padding=1),
            # 池化
            nn.MaxPool2d(kernel_size=2, stride=2, padding=0),
        )
        
        # 2, 分类
        self.classifier = nn.Sequential(
            nn.Flatten(),
            nn.Linear(in_features=7 * 7 * 512, out_features=4096),
            nn.ReLU(),
            nn.Linear(in_features=4096, out_features=4096),
            nn.ReLU(),
            nn.Linear(in_features=4096, out_features=n_classes)
        )
        
    def forward(self, x):
        # 1, 提取特征
        x = self.feature_extractor(x)
        # 2, 分类输出
        x = self.classifier(x)
        return x

手势识别Vgg的应用

在【课程总结】Day11(上):手势图像识别实战(LeNet模型)中,我们已经实现了整体的流程如下:

  1. 数据预处理
    1.1 数据读取
    1.2 数据切分
    1.3 数据规范化
  2. 批量化打包数据
  3. 模型搭建
  4. 筹备训练
  5. 训练模型
    5.1 定义监控指标和方法
    5.2 实现训练过程
    5.3 开始训练

相比LeNet模型的使用过程,我们只需要对模型搭建训练模型模型预测部分进行适当修改即可。

模型搭建

将上述Vgg16模型代码封装到models.py中,然后在训练代码中如下引用即可:

from models import Vgg16
model = Vgg16(n_classes=10)
模型训练

在train()的方法中,修改训练后的模型名称

        # ....(以上代码省略)
        # 保存模型
        if cur_test_acc < test_acc:
            cur_test_acc = test_acc
            # 保存最好模型
            torch.save(obj=model.state_dict(), f="vgg16_best.pt")
        # 保存最后模型
        torch.save(obj=model.state_dict(), f="vgg16_last.pt") 
        

运行结果:

模型预测

在streamlit的前端页面中,修改模型加载部分的代码:


    # 2, 加载模型
    m1 = Vgg16()
    m1.to(device=device)
    # 加载权重
    m1.load_state_dict(state_dict=torch.load(f="vgg16_best.pt", map_location=device),
                    strict=False)
    if not isinstance(m1, Vgg16):
        raise ValueError("模型加载失败")

运行结果:

ResNet模型

ResNet简介

ResNet 网络是在 2015年 由微软实验室中的何凯明等几位大神提出,斩获当年ImageNet竞赛中分类任务第一名,目标检测第一名。该网络结构中残差思想,解决了梯度消失和梯度爆炸问题,使得神经网络更多层卷积成为可能。

ResNet引入背景

在ResNet提出之前,所有的神经网络都是通过卷积层和池化层的叠加组成的。
人们认为卷积层和池化层的层数越多,获取到的图片特征信息越全,学习效果也就越好。但是在实际的试验中发现,随着网络层数的增加,梯度在反向传播过程中逐渐变得非常小,导致深层网络难以训练的现象。

备注:关于梯度消失的详细论述不是本章重点,如须了解更详细内容,可以查看
CSDN:ResNet50超详细解析!!!

残差思想

为了避免梯度消失问题,ResNet的关键创新是引入了residual结构(残差结构),将输入信号直接跳过一到多层,与后续层的输出相加,而不是简单地经过一层层的变换。这种设计使得网络可以更轻松地学习残差映射,有助于减轻梯度消失问题。

所谓的残差结构是在正常网络的结构中,增加一个分支结构,这样网络的输出便不再是F\left ( x \right ),而是F\left ( x \right )+x

实线残差结构 VS 虚线残差结构

ResNet由实线残差结构和虚线残差结构两种:

以深层(50/101/152)为例,实线和虚线残差结构如下图所示:

残差结构代码实现

虚线残差结构
import torch
from torch import nn


class ConvBlock(nn.Module):
    """
        虚线块,每一个大的重复逻辑块前面,第一个短接块就是这个
        实现逻辑:
            y = F(x) + Conv(x)
    """
    def __init__(self, in_channels, out_channels, stride):
        # 调用父类初始化方法
        super().__init__()
                
        # 1,核心处理逻辑
        self.stage = nn.Sequential(
            # 1  1 * 1
            nn.Conv2d(in_channels=in_channels,
                     out_channels=out_channels[0],
                     kernel_size=1,
                     stride=stride,
                     padding=0,
                     bias=False),
            nn.BatchNorm2d(num_features=out_channels[0]),
            nn.ReLU(),
            # 2  3 * 3
            nn.Conv2d(in_channels=out_channels[0],
                     out_channels=out_channels[1],
                     kernel_size=3,
                     padding=1,
                     stride=1,
                     bias=False),
            nn.BatchNorm2d(num_features=out_channels[1]),
            nn.ReLU(),
            # 3  1 * 1
            nn.Conv2d(in_channels=out_channels[1],
                     out_channels=out_channels[2],
                     kernel_size=1,
                     stride=1,
                     padding=0,
                     bias=False),
            nn.BatchNorm2d(num_features=out_channels[2]))
        
        # 2,短路层
        self.shortcut = nn.Sequential(
            nn.Conv2d(in_channels=in_channels,
                                   out_channels=out_channels[2],
                                   kernel_size=1,
                                   stride=stride,
                                   padding=0,
                                   bias=False),
            nn.BatchNorm2d(num_features=out_channels[2])
        )
        
        # 3,最后的激活
        self.relu = nn.ReLU()
    
    def forward(self, x):
        # 1,短接处理
        s = self.shortcut(x)
        
        # 2,核心处理
        h = self.stage(x)
        
        # 3,两部分相加 add
        h = h + s
        
        # 4,输出 激活
        o = self.relu(h)
        
        return o
    
实线残差结构
    
class IdentityBlock(nn.Module):
    """
        实线块
            y = F(x) + x
    """
    def __init__(self, in_channels, out_channels):
        super().__init__()
        
        self.stage = nn.Sequential(
            # 1:1 x 1
            nn.Conv2d(in_channels=in_channels,
                     out_channels=out_channels[0],
                     kernel_size=1,
                     padding=0,
                     stride=1,
                     bias=False),
            nn.BatchNorm2d(num_features=out_channels[0]),
            nn.ReLU(),
            
            # 2:3 x 3
            nn.Conv2d(in_channels=out_channels[0],
                     out_channels=out_channels[1],
                     kernel_size=3,
                     padding=1,
                     stride=1,
                     bias=False),
            nn.BatchNorm2d(num_features=out_channels[1]),
            nn.ReLU(),
            
            # 3:1 x 1
            nn.Conv2d(in_channels=out_channels[1],
                     out_channels=out_channels[2],
                     kernel_size=1,
                     padding=0,
                     stride=1,
                     bias=False),
            nn.BatchNorm2d(num_features=out_channels[2])
        )
        
        self.relu = nn.ReLU()
    
    def forward(self, x):
        h = x + self.stage(x)
        o = self.relu(h)  
        return o

对比实线残差结构和虚线残差结构,会发现其主要的不同点在于:
虚线残差结构:

    def forward(self, x):
        # 1,短接处理
        s = self.shortcut(x)
        # 2,核心处理
        h = self.stage(x)
        # 3,两部分相加 add
        h = h + s
        # 4,输出 激活
        o = self.relu(h)
        return o

实线残差结构:

    def forward(self, x):
        h = x + self.stage(x)
        o = self.relu(h)  
        return o

虚线残差结构和实线残差结构都是有效的残差连接方式,有助于解决梯度消失问题,提高深度神经网络的训练效果。

ResNet50

基于引入残差结构的思想,ResNet50模型结构如下图所示:

代码实现
class ResNet50(nn.Module):
    """
        自定义 ResNet50
    """
    def __init__(self, n_classes=1000):
        super(ResNet50, self).__init__()
        self.stage1 = nn.Sequential(
            nn.Conv2d(in_channels=3, 
                     out_channels=64,
                     kernel_size=7,
                     padding=3,
                     stride=2,
                     bias=False),
            nn.BatchNorm2d(num_features=64),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3,
                        stride=2,
                        padding=1)
        )
        self.stage2 = nn.Sequential(
            ConvBlock(in_channels=64,
                     out_channels=(64, 64, 256),
                     stride=1),
            IdentityBlock(in_channels=256,
                         out_channels=(64, 64, 256)),
            IdentityBlock(in_channels=256,
                         out_channels=(64, 64, 256)),
        )
        
        self.stage3 = nn.Sequential(
            ConvBlock(in_channels=256,
                      out_channels=(128, 128, 512),
                      stride=2),
            IdentityBlock(in_channels=512,
                         out_channels=(128, 128, 512)),
            IdentityBlock(in_channels=512,
                         out_channels=(128, 128, 512)),
            IdentityBlock(in_channels=512,
                         out_channels=(128, 128, 512))
        )
        
        self.stage4 = nn.Sequential(
            ConvBlock(in_channels=512,
                      out_channels=(256, 256, 1024),
                      stride=2),
            IdentityBlock(in_channels=1024,
                         out_channels=(256, 256, 1024)),
            IdentityBlock(in_channels=1024,
                         out_channels=(256, 256, 1024)),
            IdentityBlock(in_channels=1024,
                         out_channels=(256, 256, 1024)),
            IdentityBlock(in_channels=1024,
                         out_channels=(256, 256, 1024)),
            IdentityBlock(in_channels=1024,
                         out_channels=(256, 256, 1024))
        )
        self.stage5 = nn.Sequential(
            ConvBlock(in_channels=1024,
                      out_channels=(512, 512, 2048),
                      stride=2),
            IdentityBlock(in_channels=2048,
                         out_channels=(512, 512, 2048)),
            IdentityBlock(in_channels=2048,
                         out_channels=(512, 512, 2048))
        )
        self.pool = nn.AdaptiveAvgPool2d(output_size=(1, 1))
        
        self.fc = nn.Linear(in_features=2048,
                           out_features=n_classes)
    def forward(self, x):
        h = self.stage1(x)
        h = self.stage2(h)
        h = self.stage3(h)
        h = self.stage4(h)
        h = self.stage5(h)
        h = self.pool(h)
        h = h.view(h.size(0), -1)
        o = self.fc(h)
        return o

手势识别ResNet50的应用

类比Vgg16的手势识别的代码实现,我们修改以下相关内容

模型搭建

将上述Vgg16模型代码封装到models.py中,然后在训练代码中如下引用即可:

from models import ResNet50
model = ResNet50(n_classes=10)
模型训练

在train()的方法中,修改训练后的模型名称

        # ....(以上代码省略)
        # 保存模型
        if cur_test_acc < test_acc:
            cur_test_acc = test_acc
            # 保存最好模型
            torch.save(obj=model.state_dict(), f="resnet50_best.pt")
        # 保存最后模型
        torch.save(obj=model.state_dict(), f="resnet50_last.pt") 
        

运行结果:

对比LeNet、Vgg16、ResNet50对比:

  • Vgg16模型文件最大,LeNet模型文件最小,ResNet50模型文件适中
  • Vgg16训练提升速度最快,在第4轮就已经达到90%的准确率

内容小结

  • VGG是一种深度卷积神经网络模型,由牛津大学的研究团队提出。
  • VGG的主要特点是更多层的卷积,同时采用了3x3卷积核,来替代大的卷积核,以减少所需参数。
  • ResNet50是一种深度残差网络模型,由Microsoft Research团队提出。
  • ResNet50中最核心的思想是引入残差结构,通过残差结构解决了梯度消失问题,从而提高了深度神经网络的训练效果。

参考资料

CSDN:VGG网络讲解——小白也能懂

CSDN:VGG 经典神经网络学习笔记 (附代码)

CSDN:ResNet50超详细解析!!!

知乎:ResNet(深度残差网络)原理及代码实现(基于Pytorch)

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

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

相关文章

黑马点评06短信登录-用户请求和会话管理过程

用户请求发送&#xff1a; 用户的浏览器向服务器发送请求&#xff08;例如&#xff0c;访问网页或提交表单&#xff09;。请求头包含之前存储在浏览器中的Cookie&#xff0c;其中包括会话ID&#xff08;Session ID&#xff09;。 服务器接收请求&#xff1a; 服务器接收到用户的…

智慧城市:数据可视化如何提升城市管理

数据可视化在智慧城市中有何应用&#xff1f;随着城市化进程的加快和信息技术的飞速发展&#xff0c;智慧城市的建设成为全球各大城市追求的目标。而数据可视化技术作为智慧城市的重要组成部分&#xff0c;通过将复杂的数据转化为直观、易理解的图表和图形&#xff0c;极大地提…

c#关键字 ArgumentOutOfRangeException .? IEnumerable string.Join

c# ArgumentOutOfRangeException ArgumentOutOfRangeException 是 C# 中表示某个参数值超出了方法或属性定义的有效范围时引发的一个异常。这个异常通常在尝试访问数组、集合、字符串等的无效索引&#xff0c;或者当传递给方法或属性的参数不在其有效范围内时发生。 例如&…

Rill Data:实时数据分析的未来

欢迎来到 Rill Rill是从数据湖到仪表板的最快路径。 rilldata 与大多数 BI 工具不同&#xff0c;Rill 带有自己的嵌入式内存数据库。数据和计算位于同一位置&#xff0c;查询以毫秒为单位返回。 因此&#xff0c;您可以即时透视、切片和深入研究数据。 下载 Rill 开始建模数…

写程序100道41-50

41.定义一个Father和Child类&#xff0c;并进行测试。 要求如下&#xff1a; (1)Father类为外部类&#xff0c;类中定义一个私有的String类型的属性name&#xff0c;name的值为“Join”。 (2)Child类为Father类的内部类&#xff0c;其中定义一个readName()方法&#xff0c;方…

【数据结构初阶】--- 归并排序

归并排序 递归递归的思路归并的步骤&#xff1a;代码示例 非递归快排为什么可以用栈模拟&#xff1a;归并可以用栈模拟吗&#xff1f;非递归的思路初版代码示例问题&#xff1a;越界 时间复杂度针对递归的优化小区间优化 递归 递归的思路 归并的前提是需要两个有序的区间&…

LLM大模型算法学习资源持续整理

文章目录 waytoagiLLM101llm-coursellm-cookbook waytoagi 飞书文档写的AGI知识库。 https://www.waytoagi.com/ LLM101 karpathy更新中的大模型教程&#xff1a; https://github.com/karpathy/LLM101n llm-course Course to get into Large Language Models (LLMs) wi…

【前端】实现时钟网页

【前端】实现时钟网页 文章目录 【前端】实现时钟网页项目介绍代码效果图 项目介绍 时钟显示在网页中央&#xff0c;并且使网页能够切换白天和夜晚两种模式。搭建基本的html结构&#xff0c;动态得到实时的时&#xff0c;分&#xff0c;秒 通过Date()函数获得。将得到的数字根…

单片机学习记录

一&#xff0c;单片机及开发板介绍 1&#xff0c;基本介绍 单片机&#xff0c;英文Micro Controller Unit&#xff0c;简称MCU内部集成了CPU、RAM、ROM、定时器、中断系统、通讯接口等一系列电脑的常用硬件功能单片机的任务是信息采集(依靠传感器)、处理(依靠CPU)和硬件设备(…

SpringBoot整合拦截器和日期转换器

一、SpringBoot整合拦截器 1.添加拦截器 package com.by.interceptor;import com.by.pojo.User; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest; import java…

程序员如何用ChatGPT解决常见编程问题:实例解析

引言 在现代编程的世界中&#xff0c;技术进步日新月异&#xff0c;程序员们面临着各种各样的挑战和问题。解决这些问题的过程中&#xff0c;找到合适的工具至关重要。ChatGPT作为一种先进的人工智能语言模型&#xff0c;能够帮助程序员迅速、高效地解决常见的编程问题。本文将…

【Liunx-后端开发软件安装】Liunx安装FDFS并整合nginx

【Liunx-后端开发软件安装】Liunx安装nacos 文章中涉及的相关fdfs相关软件安装包请点击下载&#xff1a; https://download.csdn.net/download/weixin_49051190/89471122 一、简介 FastDFS是一个开源的轻量级分布式文件系统&#xff0c;它对文件进行管理&#xff0c;功能包括…

揭秘循环购模式:消费即投资,边消费边赚钱!

大家好&#xff0c;我是你们的电商顾问吴军。今天&#xff0c;我将带大家走进一个引人瞩目的商业模式——循环购模式。你可能会好奇&#xff0c;为何有商家能如此慷慨&#xff0c;消费1000元就送2000元&#xff1f;每天还有额外的现金收入&#xff1f;这背后究竟隐藏着怎样的秘…

Cadence计算器函数leafValue

与getData结合使用 leafValue( getData(“/output” ?result “dc”) 转自eetop https://bbs.eetop.cn/thread-931912-1-1.html

20240627 每日AI必读资讯

&#x1f50d;挑战英伟达&#xff01;00 后哈佛辍学小哥研发史上最快 AI 芯片 - 3名大学辍学生创立、目前仅35 名员工、刚筹集1.2 亿美元的团队&#xff1a;Etched。 - 史上最快Transformer芯片诞生了&#xff01; - 用Sohu跑Llama 70B&#xff0c;推理性能已超B200十倍&…

让AI保持怪异

让AI保持怪异 Anthropic的创意技术专家和员工设计师凯尔图尔曼(Kyle Turman)分享了一种深深引起共鸣的观点。他说(转述原话):“人工智能实际上真的很奇怪&#xff0c;我认为人们对这一点的认识还不够。”这引发了我向小组提出的问题:我们是否有消毒人工智能固有的陌生感的风险?…

办公软件汇总

1、OCR 1.1 pearOCR pearOCR 是一个免费的免费在线文字提取OCR工具网站。PearOCR界面简洁&#xff0c;所有过程均在网页端完成,无需下载任何软件&#xff0c;点开即用。官方地址&#xff1a;https://pearocr.com/ 参考&#xff1a;9款文字识别&#xff08;OCR&#xff09;工具…

魔众一物一码溯源防伪系统——守护品牌,守护信任!

在这个充满竞争的市场上&#xff0c;如何确保你的产品不被仿冒&#xff0c;如何赢得消费者的信任&#xff1f;魔众一物一码溯源防伪系统&#xff0c;为你提供一站式解决方案&#xff0c;守护你的品牌&#xff0c;守护消费者的信任&#xff01; &#x1f50d;魔众一物一码溯源防…

玩机进阶教程----MTK芯片使用Maui META修复基带 改写参数详细教程步骤解析

目前mtk芯片与高通芯片在主流机型 上使用比较普遍。但有时候版本更新或者误檫除分区等等原因会导致手机基带和串码丢失的故障。mtk芯片区别与高通。在早期mtk芯片中可以使用工具SN_Writer_Tool读写参数。但一些新版本机型兼容性不太好。今天使用另外一款工具来演示mtk芯片改写参…

企业如何通过数据资产入表与融资加速数字化转型

数据作为五大生产要素之一&#xff0c;是数字经济发展的基础。如何对数据资产进行确权、核算和变现&#xff0c;已成为数字经济时代的难点和热点。随着“数据资产入表”的提出与实践&#xff0c;这一领域迎来了新的变化与机遇。 一、什么是数据资产入表 在我国&#xff0c;数据…