用 pytorch 训练端对端验证码识别神经网络并进行 C++ 移植

文章目录

  • 前言
  • 安装
    • 安装 pytorch
    • 安装 libtorch
    • 安装 opencv(C++)
  • 准备
  • 数据集
    • 获取训练数据
      • 下载
      • 标定
    • 编码
      • 预分析
    • 数据集封装格式
  • 神经网络搭建
  • 神经网络训练
  • 神经网络测试
  • 神经网络预测
  • C++ 移植
    • 模型转换
      • 通过跟踪转换为 Torch Script
      • 通过注解转换为 Torch Script
    • 编写 C++ 代码
    • 编译环境搭建
      • C++ 库管理
      • 方法一:手动配置 visual studio 环境
      • 方法二:cmake 配置环境
    • python 调用 C++ 程序

前言

训练和测试验证码来自中南大学教务系统登录验证码:http://csujwc.its.csu.edu.cn/

部分代码参考自该 github 项目:dee1024/pytorch-captcha-recognition

标记好的训练数据
链接:https://pan.baidu.com/s/1xGmvY8FuKm9jP2HW48wI3A
提取码:fana

训练好的神经网络模型( TrainNetwork.py 得到的模型)
链接:https://pan.baidu.com/s/1-cZCLNuYs-1MOu1NI42UOw
提取码:4wsv

环境

  • 操作系统:Windows 10
  • CPU:Intel i5-9300
  • GPU:GTX 1650
  • 深度学习框架:pytorch 1.6,libtorch 1.6
  • CUDA 11
  • python 3.8
  • opencv 4.4.0(C++)
  • visual studio 2019

安装

安装 pytorch

pytorch 是 torch 的 python 版本,也是我们常用的深度学习框架。

首先进入官网:https://pytorch.org/

在安装栏根据自己的环境配置选择相应的 pytorch 版本,获得安装指令。

在这里插入图片描述

例如 Windows 的 pip 安装:在命令行输入

pip install torch===1.6.0 torchvision===0.7.0 -f https://download.pytorch.org/whl/torch_stable.html

后回车等待一段时间即可完成安装。

如何查看 CUDA 版本?

如果是英伟达的显卡,打开英伟达显卡的控制面板。点击 系统信息 -> 组件,即可看到 CUDA 版本。

例如我的电脑的 CUDA 版本为 11.0.228,但是安装低版本 CUDA (10.1) 的 pytorch 也同样可以使用。

在这里插入图片描述

安装 libtorch

libtorch 是 torch 的 C++ 版本。libtorch 的版本必须与 pytorch 一致

同样是 pytorch 的安装栏,Package 选择 libtorchLangugae 选择 C++/Java

获得如下的下载链接:

在这里插入图片描述

我选择的是 Release version,点击相应链接下载完毕后解压在合适的目录下。

安装 opencv(C++)

opencv 主要用来进行 C++ 程序的图像处理。

和 libtorch 安装类似,在下载页面 https://opencv.org/releases/ 选择相应的操作系统。例如选择 Windows 系统,跟随安装程序选择合适的安装目录即可完成安装。

准备

该项目使用到的需要额外安装的 python 库:

  • numpy
  • requests
  • matplotlib:绘图
  • skimage:负责图像处理

新建一个目录,在目录下新建:

  • TrainImages 目录:存放训练数据
  • TestImages 目录:存放检验数据
  • Network.py:神经网络定义
  • code.py:编码规则
  • Dataset.py:数据集定义
  • TrainNetwork.py:神经网络训练
  • TestNetwork.py:神经网络测试

数据集

获取训练数据

下载

采用 python 脚本进行批量下载,下载的图片用时间戳命名。

提供一个下载脚本,将其复制到项目的主目录再运行:

import requests
import timeCaptchaUrl = 'http://csujwc.its.csu.edu.cn/verifycode.servlet'
root = '.\\TrainImages\\' #图片保存目录
DownloadNumber = 0def GetOneCaptcha(time_label):global DownloadNumbertry:r = requests.get(CaptchaUrl)r.raise_for_status()with open(root + '_' + time_label + '.png','wb') as f:f.write(r.content)DownloadNumber = DownloadNumber + 1except:passdef Download(CaptchaNumber):while(DownloadNumber < CaptchaNumber):GetOneCaptcha(str(int(time.time()*1000)))print("已下载 {0:>4} 个".format(DownloadNumber),end = '\r')print("\n下载完毕")if __name__ == "__main__":   Download(100) #下载100张

标定

人工标记(自己标定了600多张,眼睛都要花了),标记的效果大概这样:

在这里插入图片描述

编码

预分析

经过大量验证码数据分析可以得出中南大学教务系统验证码具有如下规律:

  • 包含四个字符
  • 字符分为小写英文字母和数字
  • 数字包括 1,2,3
  • 英文字母包括 b,c,m,n,v,x,z

所以神经网络需要对一个字符作十分类,总共四个字符,输出包含四十个元素的一维特征向量。

在这里插入图片描述

端对端神经网络输入图片直接输出结果,输出的每10个元素中最大的(代表概率最大)为神经网络的预测字符。

code.py 文件内容如下,负责 array 类型和字符串类型的转换。

import numpy as npchar_table = ['1', '2', '3', 'b', 'c', 'm', 'n', 'v', 'x', 'z']def encode(raw_string):  # 编码out_code = np.zeros(40)for i, c in enumerate(raw_string):out_code[i * 10 + char_table.index(c)] = 1return out_codedef decode(raw_code):  # 解码out_string = ''raw_code = raw_code.reshape(4, 10)raw_code = np.argmax(raw_code, 1)for i in raw_code:out_string += char_table[i]return out_string

例程

string = 'mn3z'
code = encode(string)
string = decode(code)
print(code)
print(string)

运行结果

[0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 1. 0.0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
mn3z

数据集封装格式

Dataset.py 文件内容如下,自己定义的数据集需要继承 pytorch 的 torch.utils.data.Dataset 类,而且必须重写 __len____getitem__ 方法

  • __init__folder 参数为数据集所在的目录名称,transform 参数为数据的变换方式,这里为转换Tensor 类型。

    • train_image_file_paths 为含有目录内所有图片路径的列表
  • __len__获取数据集长度。根据前面的获取的train_image_file_paths 即可知道数据集长度。

  • __getitem__根据索引获取数据,类似于列表。返回包含 Tensor 类型数据的元组

    • skimage.io.imread()skimage 库的图像读取函数as_grey 参数表示是否转换为灰度图(即图片三通道转单通道)。

    • skimage.transform.resize()skimage 库的图像变形函数,在这里图像被变形为 45x45 大小。

      • 注意:如果使用其他图像处理库(例如 Pillow)的图像变形函数得出的图像可能不一样,最好在训练,测试,预测时统一使用一个图像处理库。
    • label 为图片的标签编码,例如图片 1b13_1589636262331.png 通过 split('_')[0] 即可得到标签,在经过前面提到的 encode() 函数编码。

    • 输出的 Tensor 类型数据通过 .float() 方法全部转为浮点型

  • torch.utils.data.Dataloader 为 pytorch 的数据集装载方法,数据集必须经过装载才能输入进神经网络。batch_size 参数为小批量训练每批的大小(pytorch 采用小批量训练),shuffle 参数表示是否打乱数据集。

  • get_train_data_loader()get_test_data_loader() 函数用来获取训练集和测试集,通过 batch_size 参数定义批量大小。

import os
import torch
from torch.utils.data import DataLoader,Dataset
import torchvision.transforms as transforms
from skimage import io,transform
import codeclass mydataset(Dataset):def __init__(self, folder, transform=None):self.train_image_file_paths = [os.path.join(folder, image_file) for image_file in os.listdir(folder)] #获取含有目录内所有图片路径的列表self.transform = transformdef __len__(self):return len(self.train_image_file_paths)def __getitem__(self, idx):image_root = self.train_image_file_paths[idx] #图片路径image_name = image_root.split(os.path.sep)[-1] #图片名称image = io.imread(image_root,as_gray = True) #读取图片并转为灰度图image = transform.resize(image,(45,45)) #将图片转为 45x45 大小if self.transform is not None:image = self.transform(image) #转换图片label = code.encode(image_name.split('_')[0]) #编码return image.float(), torch.from_numpy(label).float()Transform = transforms.Compose([transforms.ToTensor() #转换为 Tensor 类型
])def get_train_data_loader(batch_size = 16): #获取训练集dataset = mydataset('.\\TrainImages', transform = Transform)return DataLoader(dataset, batch_size, shuffle = True)def get_test_data_loader(batch_size = 1): #获取测试集dataset = mydataset('.\\TestImages', transform = Transform)return DataLoader(dataset, batch_size, shuffle = True)

神经网络搭建

神经网络的总体结构如图,为典型的 CNN 网络。

该 CNN 网络部分结构与 AlexNet 相似,输入N x 45 x 45(N 为输入图片个数)的灰度图像,前向环节按照从左到右顺序分别是卷积层,池化层,卷积层,池化层,全连接层。其中,图像经过卷积层后还要经过批归一化,经过池化层后采用 ReLU 作为激活函数。在训练时加入了 Dropout 环节以增强神经网络的泛化能力。

输出 N x 40(N 为输入图片个数)one - hot 标签,每 10 个元素为一组代表一个字符,这 10 个元素代表了每个类别的概率,后续需要经过处理来输出最终的结果。

在这里插入图片描述

在这里插入图片描述

Network.py 文件内容如下,自己定义的神经网络需要继承 pytorch 的 torch.nn.Module 类。而且必须重写 __init__forward 方法

  • __init__:负责神经网络的初始化,num_classnum_char 分别为字符种类数和字符个数,由上分析可知分别为 10 和 4。下面的一些方法涉及到深度学习的知识,最好有相关的基础。

    • torch.nn.Sequential():pytorch 的一个封装神经网络环节的方法。

    • torch.nn.Conv2d():卷积层。

    • torch.nn.BatchNorm2d():批归一化层。

      请参考该博客:https://blog.csdn.net/vict_wang/article/details/88075861

    • torch.nn.Dropout(0.5)

      在 Dropout 环节中,神经网络将每个隐藏神经元的输出设置为零(概率为 0.5)。 以这种方式“脱落”的神经元不会对前向传播做出贡献,也不会参与反向传播。

      因此,每次出现输入时,神经网络都会对不同的体系结构进行采样,但是所有这些体系结构都会共享权重。由于神经元不能依靠特定其他神经元的存在,因此该技术减少了神经元的复杂共适应。 因此,它被迫学习更强大的功能,这些功能可与其他神经元的许多不同随机子集结合使用。

    • torch.nn.MaxPool2d()

      池化函数使用某一位置的相邻输出的总体统计特征来代替网络在该位置的输出,最大池化函数给出相邻矩形区域内的最大值。不管采用什么样的池化函数,当输入作出少量平移时,池化能够帮助输入的表示近似不变。对于平移的不变性是指当我们对输入进行少量平移时,经过池化函数后的大多数输出并不会发生改变。

    • torch.nn.RELU()

      在神经网络中,线性整流作为神经元的激活函数,定义了该神经元在线性变换之后的非线性输出结果。换言之,对于进入神经元的来自上一层神经网络的输入向量,使用线性整流激活函数的神经元会输出
      m a x ( 0 , w T x + b ) max(0,w^Tx+b) max(0,wTx+b)
      至下一层神经元或作为整个神经网络的输出(取决现神经元在网络结构中所处位置)。

    • torch.nn.Linear

      全连接层在整个卷积神经网络中起到 “分类器”的作用。全连接层连接前面卷积池化后得到的所有特征,将输出值赋予分类器。

  • forward:神经网络的前向传播,正是通过该方法神经网络把输入进的图片数据转换为输出向量(即识别结果)。pytorch 要求输入的数据维度为 N x C x H x W,N:输入的图片个数,C:图片的通道数。

#神经网络定义import torch
import torch.nn as nnclass Net(nn.Module):def __init__(self,num_class = 10,num_char = 4):super(Net,self).__init__()self.num_class = num_class #标签个数self.num_char = num_char #字符个数self.conv1 = nn.Sequential(nn.Conv2d(1,16,6),nn.BatchNorm2d(16),nn.Dropout(0.5),nn.MaxPool2d(2,2),nn.ReLU())self.conv2 = nn.Sequential(nn.Conv2d(16,8,3),nn.BatchNorm2d(8),nn.Dropout(0.5),nn.MaxPool2d(2,2),nn.ReLU())self.fc = nn.Linear(8*9*9,self.num_class*self.num_char)def forward(self,x):x = self.conv1(x)x = self.conv2(x)x = x.view(x.size(0),-1)x = self.fc(x)return x

神经网络训练

TrainNetwork.py 文件内容如下,负责神经网络的训练。

  • num_epochs:训练轮数,即重复输入训练集的次数。

  • learning_rate:学习率,梯度下降法里的重要参数,越小梯度下降越慢,太大会导致神经网络发散。

  • torch.device:pytorch 的设备设置,表示数据是在 CPU 上计算还是在 GPU 上计算。

    通过 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 获取电脑的可用设备。

    通过 .to(device) 方法将数据或神经网络模型移动到设定好的设备上,例如移动到 GPU 上就可以加快神经网络的训练速度。

  • 训练时要将神经网络置于训练模式:.train(),此时神经网络的 Dropout 环节会开启。

  • torch.nn.MultiLabelSoftMarginLoss:多标签分类损失函数,参数为神经网络的预测标签和图片实际标签。通过预测出的数据和实际数据的差异计算出损失值,损失值代表了神经网络预测的准确性。越小神经网络在数据集上的准确性表现得越好。

    数学形式如下
    l o s s ( x , y ) = − ∑ i y [ i ] ∗ log ⁡ ( ( 1 + e x p ( − x [ i ] ) ) − 1 ) + ( 1 − y [ i ] ) ∗ log ⁡ ( e x p ( − x [ i ] ) 1 + e x p ( − x [ i ] ) ) ) loss(x,y)=-\sum_i{y[i]*\log{((1+exp(-x[i]))^{-1})}}+(1-y[i])*\log{}(\frac{exp(-x[i])}{1+exp(-x[i])})) loss(x,y)=iy[i]log((1+exp(x[i]))1)+(1y[i])log(1+exp(x[i])exp(x[i])))

  • torch.optim.Adam:Adam 优化算法,参数为神经网络的参数 cnn.parameters() 和学习率 learning_rate

    pytorch 可以自动进行梯度计算。神经网络每进行一次损失值计算,优化器清空保存的梯度然后执行后向传播得到新的梯度,并基于此执行优化算法。

    请参考该博客:https://www.cnblogs.com/yifdu25/p/8183587.html

  • torch.save(cnn.state_dict(), ".\\model.pt"):保存神经网络的部分数据

import torch
import torch.nn as nn
from torch.autograd import Variable
import Dataset
import code
from Network import Net#训练参数
num_epochs = 30 #训练轮数
learning_rate = 0.001 #学习率def main():device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')cnn = Net() #初始化神经网络cnn.to(device) cnn.train() #训练模式print('初始化神经网络')criterion = nn.MultiLabelSoftMarginLoss() #损失函数optimizer = torch.optim.Adam(cnn.parameters(), lr=learning_rate) #优化器#训练神经网络train_dataloader = Dataset.get_train_data_loader(batch_size=16)for epoch in range(num_epochs):for i, (images, labels) in enumerate(train_dataloader):images = Variable(images).to(device)labels = Variable(labels.float()).to(device)predict_labels = cnn(images)loss = criterion(predict_labels, labels)optimizer.zero_grad()loss.backward()optimizer.step()if (i+1) % 10 == 0:print("当前轮数:", epoch, "当前步数:", i, "损失值:", loss.item())if (i+1) % 20 == 0:torch.save(cnn.state_dict(), ".\\model.pt")print("保存模型")torch.save(cnn.state_dict(), ".\\model.pt")print("保存最后的模型")if __name__ == '__main__':main()

运行结果

初始化神经网络
当前轮数: 0 当前步数: 9 损失值: 0.3719463050365448
当前轮数: 0 当前步数: 19 损失值: 0.3350425958633423
保存模型
当前轮数: 0 当前步数: 29 损失值: 0.30613207817077637
………………
当前轮数: 29 当前步数: 9 损失值: 0.006565847899764776
当前轮数: 29 当前步数: 19 损失值: 0.002701024990528822
保存模型
当前轮数: 29 当前步数: 29 损失值: 0.005611632019281387
保存最后的模型

神经网络测试

TestNetwork.py 文件内容如下,负责神经网络训练后的测试。

将神经网络的输出标签和图片实际标签比较得出神经网络的识别是否正确,在比较时需要通过 code.decode(predict_label.data.numpy()) 将神经网络的输出结果(Tensor 类型)转换字符串形式。

测试时要将神经网络置于计算模式:.eval(),此时神经网络的 Dropout 环节会关闭。

import torch
import Dataset
from Network import Net
import codedef test_network(dataloader):net = Net() #初始化神经网络net.load_state_dict(torch.load('.\\model.pt')) #加载模型net.eval() #计算模式output_list = [] #存放预测结果for i, (image, label) in enumerate(dataloader):predict_label = net(image)label = code.decode(label.data.numpy())predict_label = code.decode(predict_label.data.numpy())if predict_label == label:output_list.append(1)else:output_list.append(0)if (i+1)%10 == 0:acc = sum(output_list) / len(output_list)print("{} 张正确率:{} %".format(i+1,acc*100))acc = sum(output_list) / len(output_list)print("总正确率:{} %".format(acc*100))if __name__ == '__main__':test_network(Dataset.get_test_data_loader(batch_size=1))#获取测试集

运行结果

10 张正确率:100.0 %
20 张正确率:100.0 %
30 张正确率:100.0 %
………………
170 张正确率:98.82352941176471 %
180 张正确率:98.88888888888889 %
190 张正确率:98.94736842105263 %
200 张正确率:99.0 %
总正确率:99.0 %

可以看到正确率已经非常高了。

神经网络预测

由于有现成的验证码 URL,所以采用在线预测。提供一个预测脚本:

# pytorch 预测import torch
from skimage import io,transform,color
from Network import Net
import matplotlib.pyplot as plt
import codedef predict(img_path):   net = Net()net.load_state_dict(torch.load('.\\model.pt'))net.eval()for i in range(25):img = io.imread(img_path)plt.subplot(5,5,i+1)plt.imshow(img)img = color.rgb2gray(img)img = transform.resize(img,(45,45))img = torch.from_numpy(img).float().view(1,1,45,45)out = net(img).view(1,-1).data.numpy()output = code.decode(out)plt.title(output)plt.axis('off')plt.show()if __name__ == '__main__':        predict('http://csujwc.its.csu.edu.cn/verifycode.servlet')

神经网络输入单个图片时同样也要进行图像处理转换为 1 x 1 x 45 x 45 的维度。

这里调用了 matplotlib 库来同时显示图片和预测结果。

预测结果如下

在这里插入图片描述

C++ 移植

pytorch 进行神经网络的搭建和训练固然很方便,但是当部署到实际中来时就显得效率一般。例如网络爬虫需要识别验证码以进行登录,运行 python 程序的话光是加载 torch 库就要花一段时间,这对于不需要一次进行大量图片识别的网络爬虫是一种拖累。所以需要编写 C++ 程序来加载神经网路模型以提高程序运行的效率。

部分来自官方教程:https://pytorch.org/tutorials/advanced/cpp_export.html

模型转换

pytorch 提供了一种统一的模型描述语言 Torch Script 供其他编程语言程序加载,下面介绍两种将我们训练出来的模型转换为 Torch Script 的方法,也可以参考这篇博客:https://blog.csdn.net/xxradon/article/details/86504906

通过跟踪转换为 Torch Script

将模型的实例以及示例输入传递给 torch.jit.trace 函数。

这个方法适用于对任意类型输入有固定格式输出的神经网络。

import torch
from Network import Netnet = Net()
net.load_state_dict(torch.load('.\\model.pt'))
example = torch.rand(1, 1, 45, 45)
scrpit_net = torch.jit.trace(net,example)
script_net.save('.\\model_script.pt')

跟踪器有可能生成警告,因为有就地赋值(torch.rand())。

通过注解转换为 Torch Script

这个方法适用于对不同类型输入有不同格式输出的神经网络。

import torch
from Network import Netnet = Net()
net.load_state_dict(torch.load('.\\model.pt'))
scrpit_net = torch.jit.script(net)
script_net.save('.\\model_script.pt')

编写 C++ 代码

源.cpp 文件内容如下,调用了 opencv 库和 libtorch 库。

  • decode() 解码函数,类似于前面 python 的解码函数。

  • C++ 程序通过主函数的入口参数 argc(参数个数) 和 argv[](参数集) 获取传入程序的参数,例如在windows 命令行中输入:

    program.exe arg1 arg2
    

    即可向程序传入两个参数:arg1arg2,在程序中读取 argv[0]argv[1] 即可知道这两个参数的值。

    在本程序中传入的参数为前面保存的神经网络模型的路径和图片的路径

  • torch::jit::script::Module module = torch::jit::load(argv[1]) 通过模型路径读取模型并创建神经网络,十分简便。

  • cv::imread() 为 opencv 的图像读取函数,输入图片路径即可返回 Mat 类型的图像数据矩阵。

  • cv::resize() 为 opencv 的图像变形函数,在这里使图片变形为 45 x 45。

  • cv::cvtColor() 为 opencv 的图像色域改变函数,opencv 读取的图像通道和我们常见的 RGB 通道不同,它是 BGR,在这里我们只需要将它转变为单通道(即灰度图)。

  • image.convertTo(image, CV_32FC1);Mat 类型的数据类型转换方法,在这里转换为32为浮点型单通道。因为在前面的 pytorch 节我们知道输入给神经网络的 Tensor 类型的数据都经过 .float() 方法转换为了32位浮点型。

  • cv::vconcat() 为 opencv 的图像拼接函数,在这里是在图像个数维上进行拼接。

  • 使用 write 标记 images 变量是否已赋值,因为我们要将所有的输入图片拼接成一个整体再转换为 Tensor 传给神经网络以加快程序运行速度。

  • torch::from_blob() 为 libtorch 提供的 Mat 类型转 Tensor 类型的函数接口,我们可以看到最终输入给神经网络的数据维度为 N x 1 x 45 x 45 (N 为图片个数),和前面的 pytorch 输入一致。

  • std::vector<torch::jit::IValue> inputs;

    inputs.push_back(input_tensors);

    auto outputs = module.forward(inputs).toTensor();

    这三条语句为我在网上查到的一种固定写法,大概是要将 Tensor 类型的数据放在一种叫向量的数据结构里才能传递给神经网络。

  • 最总通过 std::cout 直接输出识别结果,多图片时输出结果以单空格隔开。

也不知道为什么,发现把模型置于计算模式时(module.eval();)输出结果是错误的。

/*
神经网络调用程序,根据命令行参数直接输出识别结果,可一次识别多张,上限100张
参数顺序:神经网络模型路径,图片1路径,……,图片n路径
*/#include <torch/script.h>
#include <opencv2/opencv.hpp>
#include <iostream>
#include <memory>std::string decode(at::Tensor code) //解码函数
{int char_num = 4; //字符个数int a;std::string str = ""; //输出字符串std::string table[10] = {"1", "2", "3", "b", "c", "m", "n", "v", "x", "z"}; //编码对照表code = code.view({ -1, 10 });code = torch::argmax(code, 1);for (int i = 0; i < char_num; i++){a = code[i].item().toInt();str += table[a];}return str;
}int main(int argc, const char *argv[])
{if (argc < 3 || argc > 102){std::cerr << "调用错误!" << std::endl;return -1;}try{int image_num = argc - 2; //读取的图片数量bool write = false;torch::jit::script::Module module = torch::jit::load(argv[1]); //读取模型cv::Mat images; //保存图片数据for (int i = 0; i < image_num; i++){cv::Mat image = cv::imread(argv[i + 2]); //读取图片cv::resize(image, image, cv::Size(45, 45)); //变形成为45 x 45cv::cvtColor(image, image, cv::COLOR_BGR2GRAY); //转成灰度图image.convertTo(image, CV_32FC1); //转换为32位浮点型if (!write){images = image;write = true;}else{cv::vconcat(images, image, images); //拼接图像}}auto input_tensors = torch::from_blob(images.data, {image_num, 1, 45, 45 }); //将mat转成tensorstd::vector<torch::jit::IValue> inputs;inputs.push_back(input_tensors);auto outputs = module.forward(inputs).toTensor();std::cout << decode(outputs[0]);for (int i = 1; i < image_num; i++){auto result = decode(outputs[i]);//解码std::cout << ' ' << result;}return 0;}catch (...)  //捕获任意异常{std::cerr << "程序执行出错!" << std::endl;return -1;}
}

编译环境搭建

下面介绍两种编译环境的搭载方法,一种是直接在 visual studio 中配置,一种是官方推荐的用 cmake 配置 visual studio 环境。

确保已安装 visual studio 的 C++ 部件,下面所有方法都以 visual studio 2019 为 IDE,程序编译类型为 x64 release

C++ 库管理

在前面我们已安装了 libtorch 和 opencv,以 libtorch 1.6.0 (release) 和 opencv 4.4.0 为例,假设它们放在一个文件夹:D:\CplusLib\

在这里插入图片描述

程序需要的动态链接库位置:

  • libtorchD:\CplusLib\libtorch\lib
  • opencvD:\CplusLib\opencv\build\x64\vc15\bin

接下来我们需要将这些动态链接库的路径添加进环境变量,以便 C++ 程序通过环境变量找寻这些动态链接库。

以 Windows 10 系统为例,右键 此电脑,选择 属性 -> 高级系统设置 -> 环境变量 打开界面。

在这里插入图片描述

有两种环境变量:用户变量和系统变量,一种只针对一个用户有效,另一种对所有用户都有效。

我们添加系统变量的 path 项,双击 path,点击 新建 输入路径:

在这里插入图片描述

点击 确定 -> 确定

方法一:手动配置 visual studio 环境

直接在 visual studio 环境中配置要一个一个去写包含的库的路径和动态链接库的名称。我是采用属性表的形式直接让工程项目读取,这样每创建一个新工程就不用重复配置了。

以上面的库安装路径为基础提供每个库的属性表:

  • libtorch(release)libtorch.Cpp.x64.user.props

    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"><ImportGroup Label="PropertySheets" /><PropertyGroup Label="UserMacros" /><PropertyGroup><IncludePath>D:\CplusLib\libtorch\include;D:\CplusLib\libtorch\include\torch;$(IncludePath)</IncludePath><LibraryPath>D:\CplusLib\libtorch\lib;$(LibraryPath)</LibraryPath></PropertyGroup><ItemDefinitionGroup><Link><AdditionalDependencies>asmjit.lib;c10.lib;c10_cuda.lib;caffe2_detectron_ops_gpu.lib;caffe2_module_test_dynamic.lib;caffe2_nvrtc.lib;clog.lib;cpuinfo.lib;dnnl.lib;fbgemm.lib;libprotobuf-lite.lib;libprotobuf.lib;libprotoc.lib;mkldnn.lib;torch.lib;torch_cpu.lib;torch_cuda.lib;%(AdditionalDependencies)</AdditionalDependencies></Link></ItemDefinitionGroup><ItemGroup />
    </Project>
    
  • opencv (release)opencv.Cpp.x64.user.props

    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"><ImportGroup Label="PropertySheets" /><PropertyGroup Label="UserMacros" /><PropertyGroup><IncludePath>D:\CplusLib\opencv\build\include;D:\CplusLib\opencv\build\include\opencv2;$(IncludePath)</IncludePath><LibraryPath>D:\CplusLib\opencv\build\x64\vc15\lib;$(LibraryPath)</LibraryPath></PropertyGroup><ItemDefinitionGroup><Link><AdditionalDependencies>opencv_world440.lib;%(AdditionalDependencies)</AdditionalDependencies></Link></ItemDefinitionGroup><ItemGroup />
    </Project>
    

其中IncludePath 是库的包含路径,LibraryPath 是链接库路径,AdditionalDependencies 是链接库名称。

如何导入属性表

在 visual studio 中点击 属性管理器,右键 Release|x64 -> 添加现有属性表。最后效果如图:

在这里插入图片描述

新建 C++ 项目,将我们前面编写的 C++ 文件添加进来,按照如图配置:

在这里插入图片描述

点击 本地 Windows调试器 即可完成编译。

方法二:cmake 配置环境

首先安装 cmake:https://cmake.org/download/

比如 Windows 64位系统选择 cmake-3.18.2-win64-x64.msi,跟随安装程序即可完成安装。

新建一个目录 D:\CaptchaRecognize\,在目录下新建 CMakeLists.txt

cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(CaptchaRecognize)SET(CMAKE_BUILE_TYPE RELEASE)INCLUDE_DIRECTORIES(
D:/CplusLib/libtorch/include
D:/CplusLib/libtorch/include/torch
D:/CplusLib/opencv/build/include
D:/CplusLib/opencv/build/include/opencv2
)SET(TORCH_LIBRARIES D:/CplusLib/libtorch/lib)
SET(OpenCV_LIBS D:/CplusLib/opencv/build/x64/vc15/lib)LINK_DIRECTORIES(
${TORCH_LIBRARIES}
${OpenCV_LIBS}
)add_executable(CaptchaRecognize 源.cpp)target_link_libraries(CaptchaRecognize
asmjit.lib
c10.lib
c10_cuda.lib
caffe2_detectron_ops_gpu.lib
caffe2_module_test_dynamic.lib
caffe2_nvrtc.lib
clog.lib
cpuinfo.lib
dnnl.lib
fbgemm.lib
libprotobuf-lite.lib
libprotobuf.lib
libprotoc.lib
mkldnn.lib
torch.lib
torch_cpu.lib
torch_cuda.lib
opencv_world440.lib
)set_property(TARGET CaptchaRecognize PROPERTY CXX_STANDARD 14)

该文件中的一些属性和属性表的内容相似。将 源.cpp 复制到该目录下,新建目录 build

在这里插入图片描述

打开 Cmake 进行如下配置:

在这里插入图片描述

点击 configure -> finish,然后点击 Generate 生成 visual studio 工程。

build 目录中找到工程文件并打开,打开 解决方案资源管理器,右键 CaptchaRecognize -> 设为启动项目

在这里插入图片描述

编译属性

在这里插入图片描述
点击 本地 Windows调试器 即可完成编译。

python 调用 C++ 程序

该 C++ 程序的命令行调用示例

CaptchaRecognize.exe model_script.pt pic1.png pic2.png

在 python 中通过 os.popen('command').read() 即可像命令行一样调用 C++ 程序并读取程序的输出结果。

例如

import osresult = os.popen('CaptchaRecognize.exe model_script.pt pic1.png pic2.png').read() #调用 C++ 程序
result = result.split(' ')

由前面可知 C++ 程序输出的结果由单空格隔开,通过 .split(' ') 可得到含所有图片识别结果的列表。

这篇文章花了我很长时间,如果觉得不错不妨点个赞哦

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【工具】SecureCR-8.5下载、安装激活和使用教程

起初我参考的文章&#xff1a;【工具】SecureCR-8.5下载、安装激活和使用教程&#xff08;包含常用设置&#xff09;_securecrt激活_SecureCode的博客-CSDN博客 但是不行啊&#xff0c;执行到13步的时候报错&#xff1a; 问了作者也没有回应&#xff0c;直到我参考了&#xff…

神经网络的发展历史

神经网络的发展历史可以追溯到上世纪的数学理论和生物学研究。以下是神经网络发展史的详细概述&#xff1a; 1. 早期的神经元模型&#xff1a; 1943年&#xff0c;Warren McCulloch和Walter Pitts提出了一种神经元模型&#xff0c;被称为MCP神经元模型&#xff0c;它模拟了生…

车载视频如何转换视频格式

当你收集了多种视频想在车内进行播放&#xff0c;它们可能不会自动播放。你有可能会在屏幕上看到一条消息&#xff0c;显示“文件格式不受支持”&#xff0c;这是因为这些视频可能采用了你的汽车无法识别的格式。 那我们如何才可以转换为车载播放器上运行的最重要且最广泛使用…

Yakit工具篇:专项漏洞检测的配置和使用

简介&#xff08;来自官方文档&#xff09; 专项漏洞检测是针对特定应用程序或系统进行的安全漏洞扫描技术&#xff0c;旨在检测与该应用程序或系统相关的安全漏洞。 Yakit通过对常见的中间件、CMS、框架、组件进行总结、归纳&#xff0c;并针对这些组件对其常见的高危漏洞进…

IP协议(上)

目录 一、初步认识IP协议 二、认识IP地址 三、协议报头格式 1.报头和有效载荷分离 2.20字节的固定数据 四、网段划分 1.一个小例子 2.认识IP地址的划分 3.数据的传输过程 4.特殊的IP地址 5.通信运营商 &#xff08;1&#xff09;通信运营商的作用 &#xff08;2&a…

Spring Security认证架构介绍

在之前的Spring Security&#xff1a;总体架构中&#xff0c;我们讲到Spring Security整个架构是通过Bean容器和Servlet容器对过滤器的支持来实现的。我们将从过滤器出发介绍Spring Security的Servlet类型的认证架构。 1.AbstractAuthenticationProcessingFilter AbstractAut…

Day6力扣打卡

打卡记录 统计无向图中无法互相到达点对数&#xff08;并查集 / DFS&#xff09; 链接 并查集 思路&#xff1a;用并查集将连通区域的连在一起&#xff0c;再遍历所有点&#xff0c;用hash表存储不同连通块的元素个数&#xff0c;然后 乘积和 便是答案。 注意&#xff1a; /…

计算机视觉基础(5)——特征点及其描述子

前言 本文我们将学习到特征点及其描述子。在特征点检测中&#xff0c;我们将学习角点检测和SIFT关键点检测器&#xff0c;角点检测以哈里斯角点检测器为例进行说明&#xff0c;SIFT将从高斯拉普拉斯算子和高斯差分算子展开。在描述子部分&#xff0c;我们将分别学习SIFT描述子和…

水库大坝安全监测方案,筑牢水库安全防线!

方案背景 党的十九届五中全会提出&#xff1a;“统筹发展和安全、加快病险水库除险加固”&#xff1b;国务院常务会议明确“十四五”期间&#xff0c;水库除险加固和运行管护要消除存量隐患&#xff0c;实现常态化管理&#xff1b;到2025年前&#xff0c;完成新出现病险水库的…

UVM-什么是UVM方法学

概念简介 百度对UVM的解释如下&#xff1a; 通用验证方法学&#xff08;Universal Verification Methodology, UVM&#xff09;是一个以SystemVerilog类库为主体的验证平台开发框架&#xff0c;验证工程师可以利用其可重用组件构建具有标准化层次结构和接口的功能验证环境 UVM…

我国跨境电商行业研究报告(2022)

我国跨境电商行业研究报告 我国跨境电商规模突飞猛进&#xff0c;2022年进出口规模超2万亿元&#xff0c;2023年上半年跨境电商出口8210亿元&#xff0c;增长19.9%。全国跨境电商主体已超10万家&#xff0c;近年来涌现出一批上市公司&#xff0c;以及广州希音等全球独角兽企业。…

【了解一下,Elastic Search的检索】

文章目录 **1.1.ES**1.1.1.elasticsearch的作用**1.1.2.ELK栈****2.索引库操作****2.1.mapping映射属性****2.2.索引库的CRUD** **3. 文档操作** **基于IDEA操作ES****索引操作****文档操作** DSL查询文档**1.1.DSL查询分类****1.2. 全文检索查询****1.3. 精准查询****1.4. 地理…

淘宝商品详情API接口(标题|主图|SKU|价格|销量|库存..)

一、应用场景 淘宝商品详情接口的应用场景非常广泛&#xff0c;以下是其中几个例子&#xff1a; 商家用于展示商品信息&#xff1a;淘宝详情接口可以被用于商家的自主店铺或第三方电商平台上&#xff0c;方便展示商品详细信息。 商品价格比对&#xff1a;淘宝详情接口可以用于…

2.4 如何在FlinkSQL使用DataGen(数据生成器)

1、DataGen SQL 连接器 FLinkSQL中可以使用内置的DataGen SQL 连接器来生成测试数据 官网链接&#xff1a;DataGen SQL 连接器 2、随机数数据生成器 随机数数据生成器支持随机生成 char、varchar、binary、varbinary、string 类型的数据 它是一个无界流的数据生成器 -- TO…

VSCode使用记录

一、安装 从官网 https://code.visualstudio.com 下载相应安装包 二、扩展&#xff1a;商店 Chinese (Simplified) (简体中文) Language Pack for Visual Studio CodeLive Serveropen in browserGitLens — Git superchargedRemote - SSHPrettier - Code formatterESLintpxt…

10个最流行的土木工程BIM软件

建筑信息模型 (BIM) 是一种数字化流程&#xff0c;最近在土木工程领域受到广泛关注。 它是一种设计、构建和管理项目的协作方法。 它涉及创建和使用建筑物的详细数字表示&#xff0c;包括 3D 模型、数据和有关项目的信息。 BIM 在参与项目的不同利益相关者之间提供实时协作&…

H5随机短视频滑动版带打赏源码,可封装APP软件或嵌入式观看

H5随机短视频滑动版带打赏源码&#xff0c;可封装APP软件或嵌入式观看&#xff0c;网站引流必备源码&#xff01; 数据来源抖音和快手官方短视频链接&#xff0c;无任何违规内容&#xff01;可自行添加广告等等&#xff01; 手机端完美支持滑动屏幕观看&#xff08;向上或向右…

UVM 验证方法学之interface学习系列文章(八)《interface不小心引入X态问题》

前面的文章学习,想必大家都对interface 有了深入了解。大家可不要骄傲哦,俗话说:小心驶得万年船。今天,再给大家介绍一个工作中,不是经常遇到,但是一旦遇到,会让你纠结很久的事情。 前面文章提到,随着验证复杂度的不断增加,interface 的bind 的操作,是必不可少的用法…

Git Bash(一)Windows下安装及使用

目录 一、简介1.1 什么是Git&#xff1f;1.2 Git 的主要特点1.3 什么是 Git Bash&#xff1f; 二、下载三、安装3.1 同意协议3.2 选择安装位置3.3 其他配置&#xff08;【Next】 即可&#xff09;3.4 安装完毕3.5 打开 Git Bash 官网地址&#xff1a; https://www.git-scm.com/…

【论文解读】The Power of Scale for Parameter-Efficient Prompt Tuning

一.介绍 1.1 promote tuning 和 prefix tuning 的关系 “前缀调优”的简化版 1.2 大致实现 冻结了整个预训练模型&#xff0c;并且只允许每个下游任务附加k个可调令牌到输入文本。这种“软提示”是端到端训练的&#xff0c;可以压缩来自完整标记数据集的信号&#xff0c;使…