基本思想:这里使用飞哥写的android代码,将其取出纯c++代码逻辑,自己尝试转了paddle+ocr模型,可以成功转换,不在详细阐述生成ncnn模型的过程和写后处理ocr识别过程,这里要实现的目的是使用c#调用ncnn的ocr工程,完成对应部门的业务支撑~
一、在window10+ncnn+clion+mingw32测试结果,对源代码进行稍微修改,不在此放出来了,只贴目录和识别结果 ,可以跳过直接使用vs开发18、window10+Clion2022+minGW编译opencv4.4.0+opencv_contrib4.4.0并测试_sxj731533730的博客-CSDN博客
1)打开CLion集成开发环境,在设置界面设置各种编码均为UTF-8。
然后,双击Shift,在弹出的搜索栏中,输入“Registry”,点击“Registry…”进入软件注册表界面,去掉“run.processes.with.pty”中默认的对勾,即不要选中该项。这样控制台程序的输出对中文的解析就是政策的
cmakelists.txt
cmake_minimum_required(VERSION 3.16)
project(untitled4)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
# Where to find CMake modules and OpenCV
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -o3 -lstdc++")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -o3 -lstdc++")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fopenmp ")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp ")
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
# add libs you need
include_directories(${CMAKE_SOURCE_DIR}/include)
#导入ncnn
add_library(libncnn STATIC IMPORTED)
set_target_properties(libncnn PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/lib/libncnn.a)
add_executable(untitled4 main.cpp common.cpp clipper.cpp)
target_link_libraries(untitled4 ${OpenCV_LIBS} libncnn)
测试图片
测试结果
D:\ncnn_ocr\cmake-build-debug\untitled4.exe
91.000000, 452.000000, 837.000000, 434.000000, 838.000000, 467.000000, 92.000000, 485.000000, 0.679223, 登机口于起飞前10分钟关闭GATESCLOSE10MINUTESBEFOREDEPARTURETIME
337.000000, 343.000000, 665.000000, 337.000000, 666.000000, 371.000000, 338.000000, 376.000000, 0.644384, ETKT7813699238489/1
56.000000, 103.000000, 327.000000, 96.000000, 329.000000, 131.000000, 57.000000, 138.000000, 0.698961, 航班FLIGHT日期DATE
74.000000, 134.000000, 331.000000, 127.000000, 333.000000, 162.000000, 75.000000, 169.000000, 0.714993, MU237903DEC
414.000000, 18.000000, 665.000000, 11.000000, 666.000000, 63.000000, 415.000000, 70.000000, 0.816461, BOArdING
149.000000, 21.000000, 361.000000, 15.000000, 362.000000, 72.000000, 151.000000, 78.000000, 0.831414, 登机牌
67.000000, 271.000000, 268.000000, 264.000000, 270.000000, 299.000000, 68.000000, 306.000000, 0.735916, zHaNGQIWEI
666.000000, 92.000000, 848.000000, 86.000000, 849.000000, 121.000000, 667.000000, 127.000000, 0.687389, 座位号SEATNO.
477.000000, 98.000000, 653.000000, 92.000000, 655.000000, 127.000000, 478.000000, 133.000000, 0.690239, 序号SERIALNO.
334.000000, 214.000000, 491.000000, 208.000000, 492.000000, 239.000000, 336.000000, 245.000000, 0.678569, TAIYUAN
338.000000, 233.000000, 488.000000, 229.000000, 490.000000, 260.000000, 340.000000, 264.000000, 0.689959, 身份识别IDNO.
673.000000, 162.000000, 821.000000, 158.000000, 823.000000, 193.000000, 674.000000, 197.000000, 0.646862, 登机时间,BDT
331.000000, 169.000000, 476.000000, 163.000000, 477.000000, 198.000000, 333.000000, 204.000000, 0.707408, 始发地FROM
480.000000, 169.000000, 618.000000, 165.000000, 620.000000, 196.000000, 481.000000, 200.000000, 0.680858, 登机口GATE
690.000000, 11.000000, 826.000000, 7.000000, 827.000000, 60.000000, 691.000000, 64.000000, 0.810896, PASS
331.000000, 100.000000, 463.000000, 96.000000, 464.000000, 127.000000, 333.000000, 131.000000, 0.698563, 舱位CLASS
453.000000, 289.000000, 582.000000, 285.000000, 583.000000, 322.000000, 455.000000, 326.000000, 0.696833, 票号TKTNO.
84.000000, 224.000000, 207.000000, 224.000000, 207.000000, 254.000000, 84.000000, 254.000000, 0.716215, FUZHOU
93.000000, 308.000000, 214.000000, 303.000000, 215.000000, 338.000000, 95.000000, 343.000000, 0.715540, 张祺伟
60.000000, 246.000000, 179.000000, 242.000000, 180.000000, 273.000000, 61.000000, 277.000000, 0.682952, 姓名NAME
58.000000, 173.000000, 173.000000, 169.000000, 175.000000, 204.000000, 60.000000, 208.000000, 0.707852, 目的地TO
63.000000, 338.000000, 169.000000, 334.000000, 170.000000, 366.000000, 64.000000, 371.000000, 0.725013, 票价FARE
89.000000, 201.000000, 175.000000, 197.000000, 176.000000, 229.000000, 91.000000, 233.000000, 0.691946, 福州
480.000000, 208.000000, 557.000000, 208.000000, 557.000000, 239.000000, 480.000000, 239.000000, 0.638389, LG11
505.000000, 126.000000, 572.000000, 126.000000, 572.000000, 162.000000, 505.000000, 162.000000, 0.731808, 035
728.000000, 131.000000, 768.000000, 131.000000, 768.000000, 154.000000, 728.000000, 154.000000, 0.474316, 12F
401.000000, 128.000000, 434.000000, 128.000000, 434.000000, 162.000000, 401.000000, 162.000000, 0.641305, W
795.000000, 126.000000, 826.000000, 126.000000, 826.000000, 154.000000, 795.000000, 154.000000, 0.543135, 西
代码目录
二、先在clion上改写一下代码,然后在用vs生成dll.改写过程需要使用c语言包裹一下,这里直接上最后的结果了
c#的主界面函数
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using System.IO;
using System.IO.Compression;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace WindowsFormsApp1
{
//
static class Program
{
[DllImport(@"D:\vs_project\Project2\x64\Release\Project2.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int init_model(StringBuilder model_param, StringBuilder bin_param, StringBuilder model_text, StringBuilder bin_text, StringBuilder texta);
[DllImport(@"D:\vs_project\Project2\x64\Release\Project2.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int detect_image(byte[] ImageBuffer, int height, int width, float boxscorethresh, float boxthresh, float unclipratio, StringBuilder data);
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
StringBuilder model_param = new StringBuilder("D:/vs_project/Project2/model/pdocrv2.0_det-op.param");
StringBuilder model_bin = new StringBuilder("D:/vs_project/Project2/model/pdocrv2.0_det-op.bin");
StringBuilder param_text = new StringBuilder("D:/vs_project/Project2/model/pdocrv2.0_rec-op.param");
StringBuilder bin_text = new StringBuilder("D:/vs_project/Project2/model/pdocrv2.0_rec-op.bin");
StringBuilder texta = new StringBuilder("D:/vs_project/Project2/model/paddleocr_keys.txt");
int ret = init_model(model_param, model_bin, param_text, bin_text, texta);
if (ret == 0)
{
Console.WriteLine("successfully\n");
}
else
{
Console.WriteLine("fail\n");
}
Mat frame = new Mat(@"D:/vs_project/Project2/1.jpg", ImreadModes.Color);
Bitmap bmp = frame.ToBitmap();
byte[] source = GetBGRValues(bmp);
StringBuilder textaa = new StringBuilder(10240);
detect_image(source, bmp.Height, bmp.Width, 0.3f, 0.2f, 2.0f, textaa);
Console.WriteLine(textaa);
Cv2.ImShow("Demo", frame);
// Console.WriteLine("show image");
// Cv2.WaitKey(0);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
public static byte[] GetBGRValues(Bitmap bmp)
{
var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
var bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
var rowBytes = bmpData.Width * Image.GetPixelFormatSize(bmp.PixelFormat) / 8;
var imgBytes = bmp.Height * rowBytes;
byte[] rgbValues = new byte[imgBytes];
IntPtr ptr = bmpData.Scan0;
for (var i = 0; i < bmp.Height; i++)
{
Marshal.Copy(ptr, rgbValues, i * rowBytes, rowBytes);
ptr += bmpData.Stride;
}
bmp.UnlockBits(bmpData);
return rgbValues;
}
public static Bitmap Byte2Bitmap(Byte[] data, int width, int height)
{
Bitmap image = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);
BitmapData bmData = image.LockBits(rect, ImageLockMode.ReadWrite, image.PixelFormat);
IntPtr ptr = bmData.Scan0;
for (int i = 0; i < image.Height; i++)
{
Marshal.Copy(data, i * image.Width * 3, ptr, image.Width * 3);
ptr = (IntPtr)(ptr.ToInt64() + bmData.Stride);
}
image.UnlockBits(bmData);
return image;
}
}
}
c++的vs界面工程
mian.cpp
#include <windows.h>//显示中文
#include <iostream>
#include "connect.h"
int main() {
cv::Mat image = cv::imread("D:/vs_project/Project2/1.jpg");
unsigned char* src = image.data;
const char* model_param = "D:/vs_project/Project2/model/pdocrv2.0_det-op.param";
const char* model_bin = "D:/vs_project/Project2/model/pdocrv2.0_det-op.bin";
const char* param_text = "D:/vs_project/Project2/model/pdocrv2.0_rec-op.param";
const char* bin_text = "D:/vs_project/Project2/model/pdocrv2.0_rec-op.bin";
const char* text = "D:/vs_project/Project2/model/paddleocr_keys.txt";
char result_text[10240] = { 0 };
float boxscorethresh = 0.4;
float boxthresh = 0.3;
float unclipratio = 2.0;
init_model(model_param, model_bin, param_text, bin_text, text);
detect_image(src, image.rows, image.cols, boxscorethresh, boxthresh, unclipratio, result_text);
SetConsoleOutputCP(65001);//显示中文
printf("%s\n", result_text);
return 0;
}
connect.h
//
// Created by 1 on 2023/6/17.
//
#ifndef UNTITLED4_CONNECT_H
#define UNTITLED4_CONNECT_H
#include "dbnet.h"
#include "crnnnet.h"
extern "C" __declspec(dllexport) int __stdcall init_model(const char* model_param, const char* bin_param, const char* model_param_text, const char* model_bin_text, const char* text);
extern "C" __declspec(dllexport) int __stdcall detect_image(unsigned char* ImageBuffer, int height, int width, float boxscorethresh, float boxthresh, float unclipratio, char* data);
#endif //UNTITLED4_CONNECT_H
connect.cpp
#pragma once
//
// Created by 1 on 2023/6/17.
//
#include "connect.h"
dbnet* item_dbnet = new dbnet();
crnnnet* item_crnnnet = new crnnnet();
int __stdcall init_model(const char* model_param, const char* bin_param, const char* model_param_text, const char* model_bin_text, const char* text)
{
int ret = item_dbnet->init_model(model_param, bin_param);
if (ret != 0) {
return -1;
}
ret = item_crnnnet->init_model(model_param_text, model_bin_text, text);
if (ret != 0) {
return -1;
}
return 0;
}
int __stdcall detect_image(unsigned char* ImageBuffer, int height, int width, float boxscorethresh, float boxthresh, float unclipratio, char* data)
{
cv::Mat rgb = cv::Mat(height, width, CV_8UC3, ImageBuffer);
std::vector<TextBox> objects = item_dbnet->getTextBoxes(rgb, boxscorethresh, boxthresh, unclipratio);
std::string textLines = item_crnnnet->getPartImages(rgb, objects);
//printf("%s\n",textLines.c_str());
memcpy(data, textLines.c_str(), textLines.size());
return 0;
}
c#的主函数,就不写界面了
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using System.IO;
using System.IO.Compression;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace WindowsFormsApp1
{
//
static class Program
{
[DllImport(@"D:\vs_project\Project2\x64\Release\Project2.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int init_model(StringBuilder model_param, StringBuilder bin_param, StringBuilder model_text, StringBuilder bin_text, StringBuilder texta);
[DllImport(@"D:\vs_project\Project2\x64\Release\Project2.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int detect_image(byte[] ImageBuffer, int height, int width, float boxscorethresh, float boxthresh, float unclipratio, StringBuilder data);
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
StringBuilder model_param = new StringBuilder("D:/vs_project/Project2/model/pdocrv2.0_det-op.param");
StringBuilder model_bin = new StringBuilder("D:/vs_project/Project2/model/pdocrv2.0_det-op.bin");
StringBuilder param_text = new StringBuilder("D:/vs_project/Project2/model/pdocrv2.0_rec-op.param");
StringBuilder bin_text = new StringBuilder("D:/vs_project/Project2/model/pdocrv2.0_rec-op.bin");
StringBuilder texta = new StringBuilder("D:/vs_project/Project2/model/paddleocr_keys.txt");
int ret = init_model(model_param, model_bin, param_text, bin_text, texta);
if (ret == 0)
{
Console.WriteLine("successfully\n");
}
else
{
Console.WriteLine("fail\n");
}
Mat frame = new Mat(@"D:/vs_project/Project2/1.jpg", ImreadModes.Color);
Bitmap bmp = frame.ToBitmap();
byte[] source = GetBGRValues(bmp);
StringBuilder textaa = new StringBuilder(10240);
detect_image(source, bmp.Height, bmp.Width, 0.3f, 0.2f, 2.0f, textaa);
Console.WriteLine(textaa);
Cv2.ImShow("Demo", frame);
// Console.WriteLine("show image");
// Cv2.WaitKey(0);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
public static byte[] GetBGRValues(Bitmap bmp)
{
var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
var bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
var rowBytes = bmpData.Width * Image.GetPixelFormatSize(bmp.PixelFormat) / 8;
var imgBytes = bmp.Height * rowBytes;
byte[] rgbValues = new byte[imgBytes];
IntPtr ptr = bmpData.Scan0;
for (var i = 0; i < bmp.Height; i++)
{
Marshal.Copy(ptr, rgbValues, i * rowBytes, rowBytes);
ptr += bmpData.Stride;
}
bmp.UnlockBits(bmpData);
return rgbValues;
}
public static Bitmap Byte2Bitmap(Byte[] data, int width, int height)
{
Bitmap image = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);
BitmapData bmData = image.LockBits(rect, ImageLockMode.ReadWrite, image.PixelFormat);
IntPtr ptr = bmData.Scan0;
for (int i = 0; i < image.Height; i++)
{
Marshal.Copy(data, i * image.Width * 3, ptr, image.Width * 3);
ptr = (IntPtr)(ptr.ToInt64() + bmData.Stride);
}
image.UnlockBits(bmData);
return image;
}
}
}
界面和运行结果,调用的c++生成的dll产生的文字检测数据
其他代码不放了,c#接收到结果,然后自己json格式化解析吧
successfully
[{"x0":91.0,"y0":452.0,"x1":837.0,"y1":434.0,"x2":838.0,"y2":467.0,"x3":92.0,"y3":485.0,"score":0.6764066815376282,"text":"登机口于起飞前10分钟关闭GATESCLOSE10MINUTESBEFOREDEPARTURETIME"},{"x0":337.0,"y0":343.0,"x1":665.0,"y1":337.0,"x2":666.0,"y2":371.0,"x3":338.0,"y3":376.0,"score":0.6362003087997437,"text":"ETKT7813699238489/1"},{"x0":56.0,"y0":103.0,"x1":327.0,"y1":96.0,"x2":329.0,"y2":131.0,"x3":57.0,"y3":138.0,"score":0.6969771981239319,"text":"航班FLIGHT日期DATE"},{"x0":72.0,"y0":133.0,"x1":333.0,"y1":126.0,"x2":334.0,"y2":163.0,"x3":74.0,"y3":170.0,"score":0.7076754570007324,"text":"MU237903DEC"},{"x0":414.0,"y0":18.0,"x1":665.0,"y1":11.0,"x2":666.0,"y2":63.0,"x3":415.0,"y3":70.0,"score":0.8130143880844116,"text":"BOArdING"},{"x0":149.0,"y0":21.0,"x1":361.0,"y1":15.0,"x2":362.0,"y2":72.0,"x3":151.0,"y3":78.0,"score":0.8278303146362305,"text":"登机牌"},{"x0":67.0,"y0":271.0,"x1":268.0,"y1":264.0,"x2":270.0,"y2":299.0,"x3":68.0,"y3":306.0,"score":0.7345755696296692,"text":"zHaNGQIWEI"},{"x0":666.0,"y0":92.0,"x1":849.0,"y1":86.0,"x2":851.0,"y2":121.0,"x3":667.0,"y3":127.0,"score":0.682906448841095,"text":"座位号SEATNO.T"},{"x0":477.0,"y0":98.0,"x1":653.0,"y1":92.0,"x2":655.0,"y2":127.0,"x3":478.0,"y3":133.0,"score":0.6870359182357788,"text":"序号SERIALNO."},{"x0":334.0,"y0":214.0,"x1":492.0,"y1":208.0,"x2":494.0,"y2":239.0,"x3":336.0,"y3":245.0,"score":0.6759243607521057,"text":"TAIYUANL"},{"x0":338.0,"y0":233.0,"x1":488.0,"y1":229.0,"x2":490.0,"y2":260.0,"x3":340.0,"y3":264.0,"score":0.6890138983726501,"text":"身份识别IDNO."},{"x0":673.0,"y0":162.0,"x1":821.0,"y1":158.0,"x2":823.0,"y2":193.0,"x3":674.0,"y3":197.0,"score":0.6356645226478577,"text":"登机时间,BDT"},{"x0":331.0,"y0":169.0,"x1":476.0,"y1":163.0,"x2":477.0,"y2":198.0,"x3":333.0,"y3":204.0,"score":0.7054211497306824,"text":"始发地FROM"},{"x0":688.0,"y0":11.0,"x1":826.0,"y1":7.0,"x2":827.0,"y2":60.0,"x3":690.0,"y3":64.0,"score":0.8068178296089172,"text":"PASS"},{"x0":480.0,"y0":169.0,"x1":618.0,"y1":165.0,"x2":620.0,"y2":196.0,"x3":481.0,"y3":200.0,"score":0.6808584332466125,"text":"登机口GATE"},{"x0":331.0,"y0":100.0,"x1":463.0,"y1":96.0,"x2":464.0,"y2":127.0,"x3":333.0,"y3":131.0,"score":0.6964778900146484,"text":"舱位CLASS"},{"x0":453.0,"y0":289.0,"x1":582.0,"y1":285.0,"x2":583.0,"y2":322.0,"x3":455.0,"y3":326.0,"score":0.6907559037208557,"text":"票号TKTNO."},{"x0":84.0,"y0":224.0,"x1":207.0,"y1":224.0,"x2":207.0,"y2":254.0,"x3":84.0,"y3":254.0,"score":0.7162151336669922,"text":"FUZHOU"},{"x0":93.0,"y0":308.0,"x1":214.0,"y1":303.0,"x2":215.0,"y2":338.0,"x3":95.0,"y3":343.0,"score":0.7120270729064941,"text":"张祺伟"},{"x0":60.0,"y0":246.0,"x1":180.0,"y1":242.0,"x2":182.0,"y2":273.0,"x3":61.0,"y3":277.0,"score":0.6744962930679321,"text":"姓名NAME"},{"x0":58.0,"y0":173.0,"x1":173.0,"y1":169.0,"x2":175.0,"y2":204.0,"x3":60.0,"y3":208.0,"score":0.7066438794136047,"text":"目的地TO"},{"x0":63.0,"y0":338.0,"x1":169.0,"y1":334.0,"x2":170.0,"y2":366.0,"x3":64.0,"y3":371.0,"score":0.7236731648445129,"text":"票价FARE"},{"x0":89.0,"y0":201.0,"x1":175.0,"y1":197.0,"x2":176.0,"y2":229.0,"x3":91.0,"y3":233.0,"score":0.6728214621543884,"text":"福州"},{"x0":480.0,"y0":208.0,"x1":557.0,"y1":208.0,"x2":557.0,"y2":239.0,"x3":480.0,"y3":239.0,"score":0.6250905394554138,"text":"LG11"},{"x0":505.0,"y0":126.0,"x1":572.0,"y1":126.0,"x2":572.0,"y2":162.0,"x3":505.0,"y3":162.0,"score":0.7296301126480103,"text":"035"},{"x0":728.0,"y0":131.0,"x1":768.0,"y1":131.0,"x2":768.0,"y2":154.0,"x3":728.0,"y3":154.0,"score":0.459035187959671,"text":"12F"},{"x0":401.0,"y0":128.0,"x1":434.0,"y1":128.0,"x2":434.0,"y2":162.0,"x3":401.0,"y3":162.0,"score":0.6323981285095215,"text":"W"},{"x0":795.0,"y0":126.0,"x1":826.0,"y1":126.0,"x2":826.0,"y2":154.0,"x3":795.0,"y3":154.0,"score":0.5345842242240906,"text":"西"}]
实验结果:链接:https://pan.baidu.com/s/1fLcMPKja5grOyAvgyZN30Q?pwd=4h99
提取码:4h99
参考代码:
GitHub - Tencent/ncnn: ncnn is a high-performance neural network inference framework optimized for the mobile platform
GitHub - FeiGeChuanShu/ncnn_paddleocr: Android paddleocr demo infer by ncnn