Commit 441457e7 authored by liucong's avatar liucong
Browse files

精简代码

parent 62415db5
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
# 设置项目名 # 设置项目名
project(MIGraphX_Samples) project(CRNN)
# 设置编译器 # 设置编译器
set(CMAKE_CXX_COMPILER g++) set(CMAKE_CXX_COMPILER g++)
...@@ -12,7 +12,6 @@ set(CMAKE_BUILD_TYPE release) ...@@ -12,7 +12,6 @@ set(CMAKE_BUILD_TYPE release)
# 添加头文件路径 # 添加头文件路径
set(INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Src/ set(INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Src/
${CMAKE_CURRENT_SOURCE_DIR}/Src/Utility/ ${CMAKE_CURRENT_SOURCE_DIR}/Src/Utility/
${CMAKE_CURRENT_SOURCE_DIR}/Src/OCR/Crnn/
$ENV{DTKROOT}/include/ $ENV{DTKROOT}/include/
${CMAKE_CURRENT_SOURCE_DIR}/depend/include/) ${CMAKE_CURRENT_SOURCE_DIR}/depend/include/)
include_directories(${INCLUDE_PATH}) include_directories(${INCLUDE_PATH})
...@@ -37,10 +36,9 @@ link_libraries(${LIBRARY}) ...@@ -37,10 +36,9 @@ link_libraries(${LIBRARY})
# 添加源文件 # 添加源文件
set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Src/main.cpp set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Src/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Src/Sample.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Src/Crnn.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Src/OCR/Crnn/Crnn.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Src/Utility/CommonUtility.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Src/Utility/CommonUtility.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Src/Utility/Filesystem.cpp) ${CMAKE_CURRENT_SOURCE_DIR}/Src/Utility/Filesystem.cpp)
# 添加可执行目标 # 添加可执行目标
add_executable(MIGraphX_Samples ${SOURCE_FILES}) add_executable(CRNN ${SOURCE_FILES})
# CRNN文本识别
本示例基于CRNN文本识别模型实现了两种MIGraphX推理:静态推理和动态shape推理。静态推理时CRNN模型只接受固定尺寸的图像输入,而动态shape推理则可以输入不同尺寸的图像进行识别。本份文档首先对CRNN算法进行简介,然后介绍了如何导出静态和动态的CRNN onnx格式模型,最后介绍了两种模式下的推理流程,并给出静态推理和动态推理的结果。
## 模型简介
CRNN是文本识别领域的一种经典算法。该算法的主要思想是认为文本识别需要对序列进行预测,所以采用了预测序列常用的RNN网络。算法通过CNN提取图片特征,然后采用RNN对序列进行预测,最终使用CTC方法得到最终结果。模型的主要结构包括基于CNN的图像特征提取模块以及基于双向LSTM的文字序列特征提取模块,网络结构如下图所示。
<img src="./CRNN_01.jpg" alt="CRNN_01" />
本示例采用了如下的开源实现:https://github.com/meijieru/crnn.pytorch, 作者提供了CRNN的预训练模型,下载CRNN的预训练模型保存到CRNN工程的weights目录。
## 模型转换
本示例采用的开源实现未提供CRNN导出onnx模型的程序,所以需要在CRNN工程下创建export.py文件,在文件中添加导出crnn.onnx模型的程序,该程序根据参数输入的不同,可以分别导出满足CRNN动态和静态两种推理所需的onnx模型,代码如下。
```
import torch
import models.crnn as crnn
import argparse
def export_onnx(modelpath, is_dynamic):
model = crnn.CRNN(32, 1, 37, 256)
model.load_state_dict(torch.load(modelpath))
dummy_input = torch.randn(1, 1, 32, 100)
input_names = ["input"]
output_names = ["output"]
if is_dynamic:
dynamic_axes = {"input": [3]}
torch.onnx.export(model,
dummy_input,
"crnn_dynamic.onnx",
verbose=True,
dynamic_axes=dynamic_axes,
input_names=input_names,
output_names=output_names)
else:
torch.onnx.export(model,
dummy_input,
"crnn.onnx",
verbose=True,
dynamic_axes=None,
input_names=input_names,
output_names=output_names)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('--weights', nargs='+', type=str, default='./weights/crnn.pth', help='model.pt path(s)')
parser.add_argument('--dynamic', action="store_true", help='ONNX/TF/TensorRT: dynamic axes')
opt = parser.parse_args()
export_onnx(opt.weights, opt.dynamic)
```
export.py程序中dummy_input表示onnx模型的输入尺寸,数据排布为NCHW。在导出静态模型时,模型的输入尺寸是固定的,本示例为了和开源实现中提供的demo示例保持一致设为(1, 1, 32, 100)。在导出用于动态shape推理的模型时,将CRNN模型的NCH维度固定(1, 1, 32),W维度设为动态维度dynamic_axes。
通过添加导出onnx模型程序后,按照如下步骤可将crnn.pth转化为onnx格式:
1. 安装DTK版的Pytorch,下载地址:https://cancon.hpccube.com:65024/4/main/pytorch
2. 安装依赖
```
# 进入migraphx samples工程根目录
cd <path_to_migraphx_samples>
# 进入示例程序目录
cd Python/Ocr/CRNN
# 环境配置
pip install -r requirements.txt
```
3. 静态模型转换
```
python export.py --weights ./weights/crnn.pth
```
4. 动态模型转换
```
python export.py --weights ./weights/crnn.pth --dynamic
```
模型导出后,可以通过netron工具:https://netron.app/ 进行模型可视化,检查模型的输入输出。**动态模型通过设置既可以执行动态推理,也可以执行静态推理,本示例中的静态推理和动态shape推理则是基于导出的动态模型实现。**
## 模型初始化
本示例使用导出的动态CRNN模型同时执行静态和动态推理。模型加载成功后,模型初始化主要调用MIGraphX的parse_onnx()、get_parameter_shapes()、compile()函数实现对模型的加载解析、获取模型输入属性和编译处理。静态推理和动态推理在这个过程中主要区别在于parse_onnx()函数的输入不同。
1. 静态推理
```
ErrorCode Crnn::Initialize(InitializationParameterOfOcr initializationParameterOfOcr, bool dynamic)
{
...
migraphx::onnx_options onnx_options;
onnx_options.map_input_dims["input"]={1,1,32,100};
net = migraphx::parse_onnx(modelPath, onnx_options);
...
}
```
静态推理将模型的输入设为一个固定的尺寸{1,1,32,100},每次执行静态推理都必须将输入图像resize到设定的尺寸。
2. 动态shape推理
```
ErrorCode Crnn::Initialize(InitializationParameterOfOcr initializationParameterOfOcr, bool dynamic)
{
...
migraphx::onnx_options onnx_options;
onnx_options.map_input_dims["input"]={1,1,32,512};
net = migraphx::parse_onnx(modelPath, onnx_options);
...
}
```
动态shape推理需要设定一个最大shape,本示例设为{1,1,32,512},并在parse_onnx()函数对crnn动态模型解析时作为参数输入。注意每次输入图像的尺寸可以不像静态推理一样需要固定,但是输入尺寸必须要小于设定的最大shape。
## 预处理
静态推理将待识别的文本图像输入模型前,需要对图像做如下预处理:
- 转换为单通道图像
- resize到模型初始化设定的尺寸(100, 32)
- 将像素值归一化到[-1, 1]
- 转换数据排布为NCHW
动态推理将待识别的文本图像输入模型前,需要对图像做如下预处理:
- 转换为单通道图像
- resize到(widthRaw, 32),其中widthRaw为待识别图像原宽度
- 将像素值归一化到[-1, 1]
- 转换数据排布为NCHW
```
ErrorCode Crnn::Infer(const cv::Mat &srcImage, std::vector<char> &resultsChar, bool raw, bool dynamic)
{
...
cv::Mat inputImage, inputBlob;
cv::cvtColor(srcImage, inputImage, CV_BGR2GRAY);
int height, width, widthRaw;
widthRaw = inputImage.cols;
if(dynamic)
{
cv::resize(inputImage, inputImage, cv::Size(widthRaw, 32));
height = inputImage.rows, width = inputImage.cols;
}
else
{
cv::resize(inputImage, inputImage, cv::Size(100, 32));
height = inputImage.rows, width = inputImage.cols;
}
// 数据排布转换
inputBlob = cv::dnn::blobFromImage(inputImage);
for(int i=0; i<width * height; i++)
{
*((float*)inputBlob.data+i) = ((*((float*)inputBlob.data+i))/255.f - 0.5)/0.5;
}
...
}
```
对比两种推理模式的预处理方式可以看出,动态推理的输入图像的尺寸是不固定的,静态推理需要固定尺寸的输入。
## 推理
### 静态推理
静态推理只需在文本图像经过预处理之后,输入到模型中执行推理,就可以得到推理结果。
```
ErrorCode Crnn::Infer(const cv::Mat &srcImage, std::vector<char> &resultsChar, bool raw)
{
...
// 输入数据
std::unordered_map<std::string, migraphx::shape> inputData;
inputData[inputName]= migraphx::argument{inputShape, (float*)inputBlob.data};
// 推理
std::vector<migraphx::argument> inferenceResults = net.eval(inputData);
// 获取推理结果
std::vector<cv::Mat> outs;
migraphx::argument result = inferenceResults[0];
// 转换为cv::Mat
migraphx::shape outputShape = result.get_shape();
int shape[]={outputShape.lens()[0],outputShape.lens()[1],outputShape.lens()[2]};
cv::Mat out(3,shape,CV_32F);
memcpy(out.data,result.data(),sizeof(float)*outputShape.elements());
outs.push_back(out);
...
}
```
inferenceResults是crnn.onnx模型的MIGraphX推理结果,CRNN模型包含一个输出,所以result等于inferenceResults[0],result包含三个维度:outputShape.lens()[0]=26可以认为是对应原图的26份纵向分割,也就是这张图片要被从左到右预测的次数,每次预测输出一个特征向量,outputShape.lens()[1]=1表示特征向量的宽度,outputShape.lens()[2]=37表示特征向量的长度。其中37=10+26+1,10、26、1分别表示10个数字(0123456789)、26个英文字母(abcdefghijklmnopqrstuvwxyz)和一个空格“-”。获取MIGraphX推理结果之后需要进行后处理得到最终文本识别的结果。后处理包括两个步骤:
1. 第一步通过判断每次预测输出的特征向量中得分最高字符对应的位置索引信息maxIdx,并将其保存在数组predChars中
```
ErrorCode Crnn::Infer(const cv::Mat &srcImage, std::vector<char> &resultsChar, bool raw)
{
...
std::vector<int> predChars;
const std::string alphabet = "-0123456789abcdefghijklmnopqrstuvwxyz";
//获取字符索引序列
for(uint i = 0; i < outs[0].size[0]; i++)
{
cv::Mat scores = Mat(1,outs[0].size[2],CV_32F,outs[0].ptr<float>(i));
cv::Point charIdPoint;
double maxCharScore;
cv::minMaxLoc(scores, 0, &maxCharScore, 0, &charIdPoint);
int maxIdx = charIdPoint.x;
predChars.push_back(maxIdx);
}
...
}
```
2. 第二步根据predChars保存的字符索引信息从alphabet中提取对应的字符,并且当布尔值raw为真时,数组resultsChar保存包括空格、重复字符在内的所有输出,当布尔值raw为假时,需要做去除空格和重复字符处理,数组resultsChar保存最终的文本识别结果。
```
ErrorCode Crnn::Infer(const cv::Mat &srcImage, std::vector<char> &resultsChar, bool raw)
{
...
//字符转录处理
for(uint i=0; i<predChars.size(); i++)
{
if(raw)
{
resultsChar.push_back(alphabet[predChars[i]]);
}
else
{
if(predChars[i] != 0)
{
if(!(i > 0 && predChars[i-1]==predChars[i]))
{
resultsChar.push_back(alphabet[predChars[i]]);
}
}
}
}
...
}
```
### 动态shape推理
动态shape推理需要处理多张图像,每次输入新图像执行与静态推理一致的操作,相关过程定义在Sample.cpp文件中,代码如下:
```
void Sample_Crnn_Dynamic()
{
...
// 读取多张测试图像
std::vector<cv::Mat> srcImages;
cv::String folder = "../Resource/Images/DynamicPic";
std::vector<cv::String> imagePathList;
cv::glob(folder,imagePathList);
for (int i = 0; i < imagePathList.size(); ++i)
{
cv:: Mat srcImage=cv::imread(imagePathList[i], 1);
srcImages.push_back(srcImage);
}
// 获取推理结果
LOG_INFO(stdout,"========== Ocr Results ==========\n");
for(int i=0; i<srcImages.size(); ++i)
{
// 执行动态shape推理
std::vector<char> resultSim;
crnn.Infer(srcImages[i],resultSim, false, true);
// 打印输出结果
for(int i = 0; i < resultSim.size(); i++)
{
std::cout << resultSim.at(i);
}
std::cout << std::endl;
}
...
}
```
# CRNN文本识别
本示例构建了CRNN的Python推理示例,利用MIGraphX框架推理可以正确的获得文本字符识别结果。
## 模型简介
CRNN是文本识别领域的一种经典算法。该算法的主要思想是认为文本识别需要对序列进行预测,所以采用了预测序列常用的RNN网络。算法通过CNN提取图片特征,然后采用RNN对序列进行预测,最终使用CTC方法得到最终结果。模型的主要结构包括基于CNN的图像特征提取模块以及基于双向LSTM的文字序列特征提取模块,网络结构如下图所示。
<img src="./CRNN_01.jpg" alt="CRNN_01" />
本示例采用了如下的开源实现:https://github.com/meijieru/crnn.pytorch, 作者提供了CRNN的预训练模型,下载CRNN的预训练模型保存到CRNN工程的weights目录。
## 模型转换
本示例采用的开源实现未提供CRNN导出onnx模型的程序,所以需要在CRNN工程下创建export.py文件,在文件中添加导出crnn.onnx模型的程序,该程序根据参数输入的不同,可以分别导出满足CRNN动态和静态两种推理所需的onnx模型,代码如下。
```
import torch
import models.crnn as crnn
import argparse
def export_onnx(modelpath, is_dynamic):
model = crnn.CRNN(32, 1, 37, 256)
model.load_state_dict(torch.load(modelpath))
dummy_input = torch.randn(1, 1, 32, 100)
input_names = ["input"]
output_names = ["output"]
if is_dynamic:
dynamic_axes = {"input": [3]}
torch.onnx.export(model,
dummy_input,
"crnn_dynamic.onnx",
verbose=True,
dynamic_axes=dynamic_axes,
input_names=input_names,
output_names=output_names)
else:
torch.onnx.export(model,
dummy_input,
"crnn.onnx",
verbose=True,
dynamic_axes=None,
input_names=input_names,
output_names=output_names)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('--weights', nargs='+', type=str, default='./weights/crnn.pth', help='model.pt path(s)')
parser.add_argument('--dynamic', action="store_true", help='ONNX/TF/TensorRT: dynamic axes')
opt = parser.parse_args()
export_onnx(opt.weights, opt.dynamic)
```
export.py程序中dummy_input表示onnx模型的输入尺寸,数据排布为NCHW。在导出静态模型时,模型的输入尺寸是固定的,本示例为了和开源实现中提供的demo示例保持一致设为(1, 1, 32, 100)。在导出用于动态shape推理的模型时,将CRNN模型的NCH维度固定(1, 1, 32),W维度设为动态维度dynamic_axes。
通过添加导出onnx模型程序后,按照如下步骤可将crnn.pth转化为onnx格式:
1. 安装DTK版的Pytorch,下载地址:https://cancon.hpccube.com:65024/4/main/pytorch
2. 安装依赖
```
# 进入migraphx samples工程根目录
cd <path_to_migraphx_samples>
# 进入示例程序目录
cd Python/Ocr/CRNN
# 环境配置
pip install -r requirements.txt
```
3. 静态模型转换
```
python export.py --weights ./weights/crnn.pth
```
4. 动态模型转换
```
python export.py --weights ./weights/crnn.pth --dynamic
```
模型导出后,可以通过netron工具:https://netron.app/ 进行模型可视化,检查模型的输入输出。
## 预处理
将待识别的文本图像输入模型前,需要对图像做如下预处理:
1. 转换为单通道图像
2. resize到100x32
3. 将像素值归一化到[-1, 1]
4. 转换数据排布为NCHW
本示例代码采用了OpenCV的blobFromImage()函数实现了预处理操作,blobFromImage处理输入图像的顺序为:首先将输入图像resize到inputSize,然后减去均值mean,最后乘以缩放系数scalefactor并转换为NCHW排布,blob是预处理之后的输出图像。
```
def prepare_input(self, image):
img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
inputSize = (self.inputShape[3], self.inputShape[2])
blob = cv2.dnn.blobFromImage(img_gray, scalefactor=1 / 127.5, size=inputSize, mean=127.5)
```
其中self.inputShape通过MIGraphX对CRNN模型进行解析获取:
```
class Crnn:
def __init__(self, path):
# 解析推理模型
self.model = migraphx.parse_onnx(path)
# 获取模型的输入name
self.inputName = self.model.get_parameter_names()[0]
# 获取模型的输入尺寸
self.inputShape = self.model.get_parameter_shapes()[self.inputName].lens()
...
```
## 推理
待识别文本图像经过预处理之后,将其输入到crnn.onnx模型中执行推理,利用migraphx推理计算得到CRNN模型的输出。
```
def infer(self, image):
inputImage = self.prepare_input(image)
# 执行推理
results = self.model.run({self.model.get_parameter_names()[0]: inputImage})
# 获取第一个输出节点的数据,migraphx.argument类型
result=results[0]
result=np.array(result)
text = self.decode(result)
final_text = self.map_rule(text)
return text, final_text
```
经过推理获取CRNN模型的输出结果,由于该模型只有一个输出,所以输出结果等于results[0],该结果是一个argument类型,维度为[26,1,37]。为了便于对其进行后处理将其转化为numpy数据类型。后处理主要包括两个步骤:
1、第一步通过解码获取模型预测的文本序列,解码过程首先获取输出特征向量中最高得分字符对应的位置索引信息,然后从alphabet中获取对应字符保存在text中。
```
def decode(self, scores):
alphabet = "-0123456789abcdefghijklmnopqrstuvwxyz"
text = ""
# 获取模型预测的文本序列
for i in range(scores.shape[0]):
c = np.argmax(scores[i][0])
text += alphabet[c]
return text
```
其中scores.shape[0]表示输入图像从左到右预测字符的次数,本示例中次数为scores.shape[0]=26,每次预测输出一个特征向量,使用np.argmax()判断最高得分字符对应的位置索引信息c,然后根据c从alphabet中获取对应字符,并将其保存到text中。
2、第二步通过对预测得到的文本序列进行去除空格和重复字符处理,从而得到最终的预测结果。
```
def map_rule(self, text):
char_list = []
for i in range(len(text)):
if i == 0:
if text[i] != '-':
char_list.append(text[i])
else:
if text[i] != '-' and (not (text[i] == text[i - 1])):
char_list.append(text[i])
return ''.join(char_list)
```
...@@ -67,7 +67,7 @@ class Crnn: ...@@ -67,7 +67,7 @@ class Crnn:
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('--imgpath', type=str, default='../Resource/Images/text.jpg', help="image path") parser.add_argument('--imgpath', type=str, default='../Resource/Images/text.jpg', help="image path")
parser.add_argument('--modelpath', type=str, default='../Resource/Models/Ocr/CRNN/crnn.onnx', help="onnx filepath") parser.add_argument('--modelpath', type=str, default='../Resource/Models/crnn.onnx', help="onnx filepath")
args = parser.parse_args() args = parser.parse_args()
crnn = Crnn(args.modelpath) crnn = Crnn(args.modelpath)
......
...@@ -8,7 +8,11 @@ CRNN是文本识别领域的一种经典算法,该算法的主要思想是认 ...@@ -8,7 +8,11 @@ CRNN是文本识别领域的一种经典算法,该算法的主要思想是认
CRNN模型的主要结构包括基于CNN的图像特征提取模块以及基于双向LSTM的文字序列特征提取模块。 CRNN模型的主要结构包括基于CNN的图像特征提取模块以及基于双向LSTM的文字序列特征提取模块。
## 构建安装 ## python版本推理
下面介绍如何运行python代码示例,具体推理代码解析,在Doc/Tutorial_Python.md中有详细说明。
### 拉取镜像
在光源可拉取推理的docker镜像,CRNN 模型推理推荐的镜像如下: 在光源可拉取推理的docker镜像,CRNN 模型推理推荐的镜像如下:
...@@ -16,6 +20,33 @@ CRNN模型的主要结构包括基于CNN的图像特征提取模块以及基于 ...@@ -16,6 +20,33 @@ CRNN模型的主要结构包括基于CNN的图像特征提取模块以及基于
docker pull image.sourcefind.cn:5000/dcu/admin/base/custom:ort1.14.0_migraphx3.0.0-dtk22.10.1 docker pull image.sourcefind.cn:5000/dcu/admin/base/custom:ort1.14.0_migraphx3.0.0-dtk22.10.1
``` ```
### 推理示例
CRNN工程的Python推理示例仅提供静态推理,可使用如下命令运行:
```
# 进入python示例目录
cd ./Python
# 安装依赖
pip install -r requirements.txt
# 运行程序
python Crnn_infer_migraphx.py
```
该python示例输入样本图像与C++在静态推理中一致,文本识别结果为:
```
a-----v--a-i-l-a-bb-l-e--- => available
```
## C++版本推理
下面介绍如何运行C++代码示例,具体推理代码解析,在Doc/Tutorial_Cpp.md目录中有详细说明。
参考Python版本推理中的构建安装,在光源中拉取推理的docker镜像。
### 安装Opencv依赖 ### 安装Opencv依赖
```python ```python
...@@ -59,16 +90,14 @@ export LD_LIBRARY_PATH=<path_to_migraphx_samples>/depend/lib/:$LD_LIBRARY_PATH ...@@ -59,16 +90,14 @@ export LD_LIBRARY_PATH=<path_to_migraphx_samples>/depend/lib/:$LD_LIBRARY_PATH
source ~/.bashrc source ~/.bashrc
``` ```
## 推理 ### 推理示例
### C++版本推理
成功编译CRNN工程后,在build目录下输入如下命令运行该示例: 成功编译CRNN工程后,在build目录下输入如下命令运行该示例:
1. 静态推理 1. 静态推理
``` ```
./MIGraphX_Samples a ./MIGraphX_Samples 0
``` ```
静态推理示例输入样本图像为: 静态推理示例输入样本图像为:
...@@ -88,7 +117,7 @@ a-----v--a-i-l-a-bb-l-e--- => available ...@@ -88,7 +117,7 @@ a-----v--a-i-l-a-bb-l-e--- => available
export MIGRAPHX_DYNAMIC_SHAPE=1 export MIGRAPHX_DYNAMIC_SHAPE=1
# 执行动态推理 # 执行动态推理
./MIGraphX_Samples b ./MIGraphX_Samples 1
``` ```
文本识别结果: 文本识别结果:
...@@ -100,27 +129,6 @@ waiting ...@@ -100,27 +129,6 @@ waiting
recognition recognition
``` ```
### python版本推理
CRNN工程的Python推理示例仅提供静态推理,可使用如下命令运行:
```
# 进入python示例目录
cd ./Python
# 安装依赖
pip install -r requirements.txt
# 运行程序
python Crnn_infer_migraphx.py
```
该python示例输入样本图像与C++在静态推理中一致,文本识别结果为:
```
a-----v--a-i-l-a-bb-l-e--- => available
```
## 历史版本 ## 历史版本
​ https://developer.hpccube.com/codes/modelzoo/crnn_migraphx ​ https://developer.hpccube.com/codes/modelzoo/crnn_migraphx
......
...@@ -3,6 +3,6 @@ ...@@ -3,6 +3,6 @@
<!--CRNN动态文本识别 --> <!--CRNN动态文本识别 -->
<CrnnDynamic> <CrnnDynamic>
<ModelPath>"../Resource/Models/Ocr/CRNN/crnn_dynamic.onnx"</ModelPath> <ModelPath>"../Resource/Models/crnn_dynamic.onnx"</ModelPath>
</CrnnDynamic> </CrnnDynamic>
</opencv_storage> </opencv_storage>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment