add dll version and its usage
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
/* Copyright (C) 2011 阙荣文
|
||||
*
|
||||
* 这是一个开源免费软件,您可以自由的修改和发布.
|
||||
* 禁止用作商业用途.
|
||||
*
|
||||
* 联系原作者: querw@sina.com
|
||||
*/
|
||||
|
||||
// ATW.h: interface for the CBase64 class.
|
||||
// by Ted.Que - Que's C++ Studio
|
||||
// 2010-11-12
|
||||
// 转换字符编码
|
||||
#include "pch.h"
|
||||
#include "ATW.h"
|
||||
|
||||
|
||||
std::string __do_w_to_a_utf8(const wchar_t* pwszText, UINT uCodePage)
|
||||
{
|
||||
// 空指针输入
|
||||
if (pwszText == NULL) return "";
|
||||
|
||||
// 无法计算需要的长度.
|
||||
int nNeedSize = WideCharToMultiByte(uCodePage, 0, pwszText, -1, NULL, 0, NULL, NULL);
|
||||
if (0 == nNeedSize) return "";
|
||||
|
||||
// 分配空间,转换.
|
||||
char* pRet = new char[nNeedSize + 1]; // 虽然返回WideCharToMultiByte的长度是包含 null 字符的长度, 还是多+一个字符.
|
||||
memset(pRet, 0, nNeedSize + 1);
|
||||
|
||||
std::string strRet("");
|
||||
if (0 == WideCharToMultiByte(uCodePage, 0, pwszText, -1, pRet, nNeedSize, NULL, NULL))
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
strRet = pRet;
|
||||
}
|
||||
|
||||
delete[]pRet;
|
||||
return strRet;
|
||||
}
|
||||
|
||||
std::wstring __do_a_utf8_to_w(const char* pszText, UINT uCodePage)
|
||||
{
|
||||
// 空指针
|
||||
if (pszText == NULL) return L"";
|
||||
|
||||
// 计算长度
|
||||
int nNeedSize = MultiByteToWideChar(uCodePage, 0, pszText, -1, NULL, 0);
|
||||
if (0 == nNeedSize) return L"";
|
||||
|
||||
// 分配空间,转换
|
||||
std::wstring strRet(L"");
|
||||
wchar_t* pRet = new wchar_t[nNeedSize + 1];
|
||||
memset(pRet, 0, (nNeedSize + 1) * sizeof(wchar_t));
|
||||
if (0 == MultiByteToWideChar(uCodePage, 0, pszText, -1, pRet, nNeedSize))
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
strRet = pRet;
|
||||
}
|
||||
delete[]pRet;
|
||||
return strRet;
|
||||
}
|
||||
|
||||
std::string WtoA(const std::wstring& strText)
|
||||
{
|
||||
return __do_w_to_a_utf8(strText.c_str(), CP_ACP);
|
||||
}
|
||||
|
||||
std::string WtoA(const wchar_t* pwszText)
|
||||
{
|
||||
return __do_w_to_a_utf8(pwszText, CP_ACP);
|
||||
}
|
||||
|
||||
std::wstring AtoW(const std::string& strText)
|
||||
{
|
||||
return __do_a_utf8_to_w(strText.c_str(), CP_ACP);
|
||||
}
|
||||
|
||||
std::wstring AtoW(const char* pszText)
|
||||
{
|
||||
return __do_a_utf8_to_w(pszText, CP_ACP);
|
||||
}
|
||||
|
||||
std::string WtoUTF8(const std::wstring& strText)
|
||||
{
|
||||
return __do_w_to_a_utf8(strText.c_str(), CP_UTF8);
|
||||
}
|
||||
|
||||
std::string WtoUTF8(const wchar_t* pwszText)
|
||||
{
|
||||
return __do_w_to_a_utf8(pwszText, CP_UTF8);
|
||||
}
|
||||
|
||||
std::wstring UTF8toW(const std::string& strText)
|
||||
{
|
||||
return __do_a_utf8_to_w(strText.c_str(), CP_UTF8);
|
||||
}
|
||||
|
||||
std::wstring UTF8toW(const char* pszText)
|
||||
{
|
||||
return __do_a_utf8_to_w(pszText, CP_UTF8);
|
||||
}
|
||||
|
||||
std::string UTF8toA(const std::string& src)
|
||||
{
|
||||
return WtoA(UTF8toW(src));
|
||||
}
|
||||
|
||||
std::string UTF8toA(const char* src)
|
||||
{
|
||||
return WtoA(UTF8toW(src));
|
||||
}
|
||||
|
||||
std::string AtoUTF8(const std::string& src)
|
||||
{
|
||||
return WtoUTF8(AtoW(src));
|
||||
}
|
||||
|
||||
std::string AtoUTF8(const char* src)
|
||||
{
|
||||
return WtoUTF8(AtoW(src));
|
||||
}
|
||||
/*
|
||||
UTF-8 编码最多可以有6个字节
|
||||
|
||||
1字节 0xxxxxxx
|
||||
2字节 110xxxxx 10xxxxxx
|
||||
3字节 1110xxxx 10xxxxxx 10xxxxxx
|
||||
4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
|
||||
*/
|
||||
|
||||
// 返回值说明:
|
||||
// 0 -> 输入字符串符合UTF-8编码规则
|
||||
// -1 -> 检测到非法的UTF-8编码首字节
|
||||
// -2 -> 检测到非法的UTF-8字节编码的后续字节.
|
||||
|
||||
int IsTextUTF8(const char* pszSrc)
|
||||
{
|
||||
const unsigned char* puszSrc = (const unsigned char*)pszSrc; // 一定要无符号的,有符号的比较就不正确了.
|
||||
// 看看有没有BOM表示 EF BB BF
|
||||
if (puszSrc[0] != 0 && puszSrc[0] == 0xEF &&
|
||||
puszSrc[1] != 0 && puszSrc[1] == 0xBB &&
|
||||
puszSrc[2] != 0 && puszSrc[2] == 0xBF)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 如果没有 BOM标识
|
||||
bool bIsNextByte = false;
|
||||
int nBytes = 0; // 记录一个字符的UTF8编码已经占用了几个字节.
|
||||
const unsigned char* pCur = (const unsigned char*)pszSrc; // 指针游标用无符号字符型. 因为高位为1, 如果用 char 型, 会变为负数,不利于编程时候的比较操作.
|
||||
|
||||
while (pCur[0] != 0)
|
||||
{
|
||||
if (!bIsNextByte)
|
||||
{
|
||||
bIsNextByte = true;
|
||||
if ((pCur[0] >> 7) == 0)
|
||||
{
|
||||
bIsNextByte = false; nBytes = 1; bIsNextByte = false;
|
||||
} // 最高位为0, ANSI 兼容的.
|
||||
else if ((pCur[0] >> 5) == 0x06)
|
||||
{
|
||||
nBytes = 2;
|
||||
} // 右移5位后是 110 -> 2字节编码的UTF8字符的首字节
|
||||
else if ((pCur[0] >> 4) == 0x0E)
|
||||
{
|
||||
nBytes = 3;
|
||||
} // 右移4位后是 1110 -> 3字节编码的UTF8字符的首字节
|
||||
else if ((pCur[0] >> 3) == 0x1E)
|
||||
{
|
||||
nBytes = 4;
|
||||
} // 右移3位后是 11110 -> 4字节编码的UTF8字符的首字节
|
||||
else if ((pCur[0] >> 2) == 0x3E)
|
||||
{
|
||||
nBytes = 5;
|
||||
} // 右移2位后是 111110 -> 5字节编码的UTF8字符的首字节
|
||||
else if ((pCur[0] >> 1) == 0x7E)
|
||||
{
|
||||
nBytes = 6;
|
||||
} // 右移1位后是 1111110 -> 6字节编码的UTF8字符的首字节
|
||||
else
|
||||
{
|
||||
nBytes = -1; // 非法的UTF8字符编码的首字节
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((pCur[0] >> 6) == 0x02) // 首先,后续字节必须以 10xxx 开头
|
||||
{
|
||||
nBytes--;
|
||||
if (nBytes == 1) bIsNextByte = false; // 当 nBytes = 1时, 说明下一个字节应该是首字节.
|
||||
}
|
||||
else
|
||||
{
|
||||
nBytes = -2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 下跳一个字符
|
||||
pCur++;
|
||||
}
|
||||
|
||||
if (nBytes == 1) return 0;
|
||||
else return nBytes;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/* Copyright (C) 2011 阙荣文
|
||||
*
|
||||
* 这是一个开源免费软件,您可以自由的修改和发布.
|
||||
* 禁止用作商业用途.
|
||||
*
|
||||
* 联系原作者: querw@sina.com
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
1. 实现USC的压缩,Unicode <-> UTF-8.
|
||||
2. 实现多字节码 <-> Unicode
|
||||
|
||||
对于Unicode字符串,用 wstring 存储.
|
||||
对于非Unicode字符串用 string 存储. ANSI, GB2312 都一样
|
||||
对于UTF-8串,也用 string 存储, UTF-8的编码串中不会有null出现.
|
||||
*/
|
||||
|
||||
/*
|
||||
不要使用ATL 中的 USES_CONVERSION; A2W, A2T, W2A 等的宏, 由于这些宏都调用 alloca() 函数在函数栈中分配内存.
|
||||
虽然非常方便,函数返回后自动回收, 但是有溢出的危险, 函数栈只有 1M 的大小.
|
||||
|
||||
以下的函数使用的空间都是在堆中分配的,比较安全.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
#include "Windows.h"
|
||||
#endif
|
||||
|
||||
#if defined(_UNICODE) || defined(UNICODE)
|
||||
#define TtoA WtoA
|
||||
#define AtoT AtoW
|
||||
#define WtoT(a) (a)
|
||||
#define TtoW(a) (a)
|
||||
typedef std::wstring _tstring;
|
||||
#else
|
||||
#define TtoA(a) (a)
|
||||
#define AtoT(a) (a)
|
||||
#define WtoT WtoA
|
||||
#define TtoW AtoW
|
||||
typedef std::string _tstring;
|
||||
#endif
|
||||
|
||||
std::string WtoA(const wchar_t* pwszSrc);
|
||||
std::string WtoA(const std::wstring &strSrc);
|
||||
|
||||
std::wstring AtoW(const char* pszSrc);
|
||||
std::wstring AtoW(const std::string &strSrc);
|
||||
|
||||
std::string WtoUTF8(const wchar_t* pwszSrc);
|
||||
std::string WtoUTF8(const std::wstring &strSrc);
|
||||
|
||||
std::wstring UTF8toW(const char* pszSrc);
|
||||
std::wstring UTF8toW(const std::string &strSr);
|
||||
|
||||
std::string AtoUTF8(const char* src);
|
||||
std::string AtoUTF8(const std::string &src);
|
||||
|
||||
std::string UTF8toA(const char* src);
|
||||
std::string UTF8toA(const std::string &src);
|
||||
|
||||
// 检测一个以 null 结尾的字符串是否是UTF-8, 如果返回0, 也只表示这个串刚好符合UTF8的编码规则.
|
||||
// 返回值说明:
|
||||
// 1 -> 输入字符串符合UTF-8编码规则
|
||||
// -1 -> 检测到非法的UTF-8编码首字节
|
||||
// -2 -> 检测到非法的UTF-8字节编码的后续字节.
|
||||
int IsTextUTF8(const char* pszSrc);
|
||||
@@ -0,0 +1,263 @@
|
||||
#include "pch.h"
|
||||
#include "OCRConfig.h"
|
||||
#include "EasyOCR_Detector.h"
|
||||
|
||||
cv::Mat uns::EasyOCR_Detector::NormalizeMeanVariance(const cv::Mat& in)
|
||||
{
|
||||
cv::Mat img;
|
||||
in.convertTo(img, CV_32FC3);
|
||||
cv::Scalar mean(0.485f * 255, 0.456f * 255, 0.406f * 255);
|
||||
cv::Scalar var(0.229f * 255, 0.224f * 255, 0.225f * 255);
|
||||
img -= mean;
|
||||
img /= var;
|
||||
return img;
|
||||
}
|
||||
|
||||
void uns::EasyOCR_Detector::AdjustResultCoordinates(EOCRD_Rects& polys, float ratioW, float ratioH, float ratioNet)
|
||||
{
|
||||
for (auto& poly : polys)
|
||||
{
|
||||
for (auto& pt : poly)
|
||||
{
|
||||
pt.x *= ratioW * ratioNet;
|
||||
pt.y *= ratioH * ratioNet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void uns::EasyOCR_Detector::ResizeAspectRatio(const cv::Mat& src, cv::Mat& dst, float squareSize, float magRatio, float& ratio, cv::Size& heatmapSize)
|
||||
{
|
||||
int h = src.rows, w = src.cols, c = src.channels();
|
||||
float target = magRatio * std::max(h, w);
|
||||
if (target > squareSize)
|
||||
target = squareSize;
|
||||
ratio = target / std::max(h, w);
|
||||
|
||||
int targetH = int(h * ratio), targetW = int(w * ratio);
|
||||
cv::resize(src, dst, cv::Size(targetW, targetH));
|
||||
|
||||
int h32 = (targetH + 31) / 32 * 32;
|
||||
int w32 = (targetW + 31) / 32 * 32;
|
||||
cv::Mat canvas = cv::Mat::zeros(h32, w32, src.type());
|
||||
dst.copyTo(canvas(cv::Rect(0, 0, targetW, targetH)));
|
||||
dst = canvas;
|
||||
heatmapSize = cv::Size(w32 / 2, h32 / 2);
|
||||
}
|
||||
|
||||
bool uns::EasyOCR_Detector::GetDetBoxesCore(const cv::Mat& textmap, const cv::Mat& linkmap, float textThresh, float linkThresh, float lowText, EOCRD_Rects& boxes, cv::Mat& labels, std::vector<int>& mapper, bool estimateNumChars)
|
||||
{
|
||||
cv::Mat tmap = textmap.clone(), lmap = linkmap.clone();
|
||||
int H = tmap.rows, W = tmap.cols;
|
||||
// 1. 二值化 & 合并
|
||||
cv::Mat textScore, linkScore;
|
||||
cv::threshold(tmap, textScore, lowText, 1, cv::THRESH_BINARY);
|
||||
cv::threshold(lmap, linkScore, linkThresh, 1, cv::THRESH_BINARY);
|
||||
cv::Mat combined;
|
||||
cv::add(textScore, linkScore, combined);
|
||||
combined = cv::min(combined, 1);
|
||||
// 2. 连通域
|
||||
int nLabels = 0;
|
||||
cv::Mat stats, centroids;
|
||||
cv::Mat combined8u;
|
||||
combined.convertTo(combined8u, CV_8U);
|
||||
try
|
||||
{
|
||||
nLabels = cv::connectedComponentsWithStats(combined8u, labels, stats, centroids, 4);
|
||||
}
|
||||
catch (cv::Exception e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// 3. 遍历每个 label
|
||||
for (int k = 1; k < nLabels; ++k)
|
||||
{
|
||||
int area = stats.at<int>(k, cv::CC_STAT_AREA);
|
||||
if (area < 10)
|
||||
continue;
|
||||
// 文本阈值过滤
|
||||
cv::Mat mask = (labels == k);
|
||||
double maxVal;
|
||||
cv::minMaxLoc(tmap, nullptr, &maxVal, nullptr, nullptr, mask);
|
||||
if (maxVal < textThresh)
|
||||
continue;
|
||||
// 构建 segmap
|
||||
cv::Mat segmap = cv::Mat::zeros(H, W, CV_8UC1);
|
||||
segmap.setTo(255, labels == k);
|
||||
mapper.push_back(k);
|
||||
// 删除 link 区域
|
||||
segmap.setTo(0, (linkScore == 1) & (textScore == 0));
|
||||
// 膨胀
|
||||
int x = stats.at<int>(k, cv::CC_STAT_LEFT);
|
||||
int y = stats.at<int>(k, cv::CC_STAT_TOP);
|
||||
int wbox = stats.at<int>(k, cv::CC_STAT_WIDTH);
|
||||
int hbox = stats.at<int>(k, cv::CC_STAT_HEIGHT);
|
||||
int niter = int(std::sqrt(area * std::min(wbox, hbox) / float(wbox * hbox)) * 2);
|
||||
int sx = std::max(0, x - niter), sy = std::max(0, y - niter);
|
||||
int ex = std::min(W, x + wbox + niter + 1), ey = std::min(H, y + hbox + niter + 1);
|
||||
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(1 + niter, 1 + niter));
|
||||
cv::dilate(segmap(cv::Rect(sx, sy, ex - sx, ey - sy)), segmap(cv::Rect(sx, sy, ex - sx, ey - sy)), kernel);
|
||||
// 轮廓拟合最小外接矩形
|
||||
std::vector<cv::Point> pts;
|
||||
cv::findNonZero(segmap, pts);
|
||||
cv::RotatedRect rect = cv::minAreaRect(pts);
|
||||
cv::Point2f boxPts[4];
|
||||
rect.points(boxPts);
|
||||
std::vector<cv::Point2f> box(boxPts, boxPts + 4);
|
||||
// diamond->rect
|
||||
float wlen = (float)cv::norm(box[0] - box[1]);
|
||||
float hlen = (float)cv::norm(box[1] - box[2]);
|
||||
float ratio = std::max(wlen, hlen) / (std::min(wlen, hlen) + 1e-5f);
|
||||
if (std::abs(1 - ratio) <= 0.1f)
|
||||
{
|
||||
int minx = W, maxx = 0, miny = H, maxy = 0;
|
||||
for (auto& p : pts)
|
||||
{
|
||||
minx = std::min(minx, p.x); maxx = std::max(maxx, p.x);
|
||||
miny = std::min(miny, p.y); maxy = std::max(maxy, p.y);
|
||||
}
|
||||
box =
|
||||
{
|
||||
{ float(minx),float(miny) },
|
||||
{ float(maxx),float(miny) },
|
||||
{ float(maxx),float(maxy) },
|
||||
{ float(minx),float(maxy) }
|
||||
};
|
||||
}
|
||||
// 顺时针起点
|
||||
int start = 0;
|
||||
float minSum = box[0].x + box[0].y;
|
||||
for (int i = 1; i < 4; i++)
|
||||
{
|
||||
float s = box[i].x + box[i].y;
|
||||
if (s < minSum)
|
||||
{
|
||||
minSum = s;
|
||||
start = i;
|
||||
}
|
||||
}
|
||||
std::rotate(box.begin(), box.begin() + start, box.end());
|
||||
boxes.push_back(box);
|
||||
}
|
||||
return (!boxes.empty());
|
||||
}
|
||||
|
||||
uns::EasyOCR_Detector::EasyOCR_Detector()
|
||||
{
|
||||
ort_inited = false;
|
||||
ort_cpu_session = nullptr;
|
||||
model_path = G_OCRConfig.GetDetectModelPath();
|
||||
ort = OrtGetApiBase()->GetApi(ORT_API_VERSION);
|
||||
}
|
||||
|
||||
bool uns::EasyOCR_Detector::Init()
|
||||
{
|
||||
if (ort_inited)
|
||||
return true;
|
||||
if (!RecheckModelInfo())
|
||||
return false;
|
||||
try
|
||||
{
|
||||
ort_env = Ort::Env(ORT_LOGGING_LEVEL_WARNING, "EasyOCR_Detector");
|
||||
bool fallback_to_cpu = false;
|
||||
if (!OCRToolBox::AutoSelectEP(ort, ort_session_options, fallback_to_cpu))
|
||||
return false;
|
||||
OCRToolBox::InitOrtSessionOptions(ort_session_options);
|
||||
if ((G_OCRConfig.GetGPUUsage() == easyocr::GPUUsage::CPUOnly) || fallback_to_cpu) //使用CPU则初始化cpu session
|
||||
{
|
||||
ort_cpu_session = new Ort::Session(ort_env, model_path.c_str(), ort_session_options);
|
||||
//通过CPU session获取输入输出名
|
||||
OCRToolBox::GetInputOutputNames(ort_cpu_session, input_names, input_ns, output_names, output_ns);
|
||||
}
|
||||
else
|
||||
{
|
||||
//通过临时session获取输入输出名(CUDA:线程不安全)
|
||||
Ort::Session ort_session(ort_env, model_path.c_str(), ort_session_options);
|
||||
OCRToolBox::GetInputOutputNames(&ort_session, input_names, input_ns, output_names, output_ns);
|
||||
}
|
||||
ort_inited = true;
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool uns::EasyOCR_Detector::UnInit()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ort_cpu_session != nullptr)
|
||||
delete ort_cpu_session;
|
||||
ort_cpu_session = nullptr;
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool uns::EasyOCR_Detector::RecheckModelInfo()
|
||||
{
|
||||
if (model_path.empty())
|
||||
model_path = G_OCRConfig.GetDetectModelPath();
|
||||
return OCRToolBox::CheckFile(model_path);
|
||||
}
|
||||
|
||||
uns::EOCRD_Rects uns::EasyOCR_Detector::operator()(const cv::Mat& image)
|
||||
{
|
||||
// 0. check model
|
||||
if (!RecheckModelInfo())
|
||||
return {};
|
||||
try
|
||||
{
|
||||
// 1. resize + normalize
|
||||
cv::Mat resized;
|
||||
float ratio;
|
||||
cv::Size heatmapSize;
|
||||
ResizeAspectRatio(image, resized, 1280.0f, 1.5f, ratio, heatmapSize);
|
||||
cv::Mat input = NormalizeMeanVariance(resized);
|
||||
// 2. 构造成 NCHW tensor
|
||||
cv::dnn::blobFromImage(input, input);
|
||||
std::array<int64_t, 4> shape = { 1, 3, input.size[2], input.size[3] };
|
||||
Ort::MemoryInfo memInfo = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
|
||||
Ort::Value tensor = Ort::Value::CreateTensor<float>(memInfo, input.ptr<float>(), input.total(), shape.data(), shape.size());
|
||||
// 3. ONNX 推理
|
||||
auto outputs = ((ort_cpu_session != nullptr) ? ort_cpu_session->Run(Ort::RunOptions{nullptr}, input_names.data(), & tensor, 1, output_names.data(), 1) : Ort::Session(ort_env, model_path.c_str(), ort_session_options).Run(Ort::RunOptions{nullptr}, input_names.data(), & tensor, 1, output_names.data(), 1));
|
||||
std::vector<int64_t> outputShape = outputs[0].GetTensorTypeAndShapeInfo().GetShape();
|
||||
// 4. 拆分 score_text & score_link
|
||||
float* outData = outputs[0].GetTensorMutableData<float>();
|
||||
int H = int(outputShape[1]), W = int(outputShape[2]);
|
||||
cv::Mat score_text(H, W, CV_32F);
|
||||
cv::Mat score_link(H, W, CV_32F);
|
||||
for (int y = 0; y < H; ++y)
|
||||
{
|
||||
for (int x = 0; x < W; ++x)
|
||||
{
|
||||
int offset = (y * W + x) * 2;
|
||||
score_text.at<float>(y, x) = outData[offset + 0];
|
||||
score_link.at<float>(y, x) = outData[offset + 1];
|
||||
}
|
||||
}
|
||||
// --- 3. 得到 boxes/polys (heatmap 坐标系) ---
|
||||
EOCRD_Rects boxes, polys;
|
||||
cv::Mat labels;
|
||||
std::vector<int> mapper;
|
||||
if (!GetDetBoxesCore(score_text, score_link, textThreshold, linkThreshold, lowText, boxes, labels, mapper, false))
|
||||
return {};
|
||||
polys = boxes;
|
||||
// --- 4. 计算最终映射比例 ---
|
||||
float invRatio = 1.0f / ratio;
|
||||
float ratioNetW = float(resized.cols) / float(heatmapSize.width);
|
||||
float ratioNetH = float(resized.rows) / float(heatmapSize.height);
|
||||
// 通常 heatmapSize = (resized.cols/2, resized.rows/2),所以 ratioNetW/H ≈ 2
|
||||
// --- 5. 映射回原图 ---
|
||||
AdjustResultCoordinates(polys, invRatio, invRatio, ratioNetW);
|
||||
return polys;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include "OCRToolBox.h"
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <onnxruntime_cxx_api.h>
|
||||
|
||||
namespace uns
|
||||
{
|
||||
class EasyOCR_Detector
|
||||
{
|
||||
private:
|
||||
const float lowText = 0.4f;
|
||||
const float textThreshold = 0.7f;
|
||||
const float linkThreshold = 0.4f;
|
||||
|
||||
private:
|
||||
const OrtApi* ort;
|
||||
|
||||
Ort::Env ort_env;
|
||||
Ort::Session* ort_cpu_session;
|
||||
Ort::SessionOptions ort_session_options;
|
||||
|
||||
bool ort_inited;
|
||||
std::wstring model_path;
|
||||
|
||||
IONames input_names, output_names;
|
||||
IONamesStorage input_ns, output_ns;
|
||||
|
||||
private:
|
||||
cv::Mat NormalizeMeanVariance(const cv::Mat& in);
|
||||
void AdjustResultCoordinates(EOCRD_Rects& polys, float ratioW, float ratioH, float ratioNet = 2.0f);
|
||||
void ResizeAspectRatio(const cv::Mat& src, cv::Mat& dst, float squareSize, float magRatio, float& ratio, cv::Size& heatmapSize);
|
||||
bool GetDetBoxesCore(const cv::Mat& textmap, const cv::Mat& linkmap, float textThresh, float linkThresh, float lowText, EOCRD_Rects& boxes, cv::Mat& labels, std::vector<int>& mapper, bool estimateNumChars);
|
||||
|
||||
public:
|
||||
EasyOCR_Detector();
|
||||
EasyOCR_Detector(const EasyOCR_Detector&) = delete;
|
||||
|
||||
public:
|
||||
bool Init();
|
||||
bool UnInit();
|
||||
bool RecheckModelInfo();
|
||||
/// <summary>
|
||||
/// EasyOCR 文本检测函数
|
||||
/// </summary>
|
||||
/// <param name="image">待检测的图像(三通道BGR图像)</param>
|
||||
/// <returns>检测到的矩形框</returns>
|
||||
EOCRD_Rects operator()(const cv::Mat& image);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,389 @@
|
||||
#include "pch.h"
|
||||
#include "OCRCharset.h"
|
||||
#include "EasyOCR_Recognizer.h"
|
||||
|
||||
uns::EasyOCR_Recognizer::NormalizePAD::Size3i uns::EasyOCR_Recognizer::NormalizePAD::Size3i::operator=(const Size3i& obj)
|
||||
{
|
||||
d0 = obj.d0;
|
||||
d1 = obj.d1;
|
||||
d2 = obj.d2;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
uns::EasyOCR_Recognizer::NormalizePAD::NormalizePAD(Size3i max_size, const std::string& PAD_type)
|
||||
{
|
||||
this->max_size = max_size;
|
||||
this->PAD_type = PAD_type;
|
||||
max_width_half = max_size.d2 / 2; // 计算宽度的一半,用于可选操作
|
||||
}
|
||||
|
||||
cv::Mat uns::EasyOCR_Recognizer::NormalizePAD::operator()(const cv::Mat& input_img) const
|
||||
{
|
||||
// 将原图转换为32位浮点型并归一化到[0,1]
|
||||
cv::Mat img;
|
||||
input_img.convertTo(img, CV_32F, 1.0 / 255); // line 10: img = toTensor
|
||||
img = (img - 0.5f) / 0.5f; // line 11: img.sub_(0.5).div_(0.5)
|
||||
int h = img.rows; // 获取图像高度
|
||||
int w = img.cols; // 获取图像宽度
|
||||
int c = img.channels(); // 获取通道数,灰度图默认为1
|
||||
// 创建目标大小的全零Mat,类型为32F,尺寸为max_size.d1 x max_size.d2
|
||||
cv::Mat pad_img = cv::Mat::zeros(max_size.d1, max_size.d2, CV_32FC(c)); // line 13
|
||||
// 将原图像拷贝到pad_img的左上角区域,实现右侧填充
|
||||
img.copyTo(pad_img(cv::Rect(0, 0, w, h))); // line 14
|
||||
// 如果目标宽度大于原图宽度,则使用最后一列像素进行扩展填充
|
||||
if (max_size.d2 != w)
|
||||
{ // line 15
|
||||
cv::Mat last_col = img.col(w - 1);
|
||||
cv::Mat border;
|
||||
cv::repeat(last_col, 1, max_size.d2 - w, border); // 重复最后一列填充
|
||||
border.copyTo(pad_img(cv::Rect(w, 0, max_size.d2 - w, h)));
|
||||
}
|
||||
return pad_img; // 返回处理后的浮点张量
|
||||
}
|
||||
|
||||
cv::Mat uns::EasyOCR_Recognizer::AlignCollate::AdjustContrastGrey(const cv::Mat& img_in, double target) const
|
||||
{
|
||||
double contrast;
|
||||
int high, low;
|
||||
ContrastGrey(img_in, contrast, high, low);
|
||||
cv::Mat img = img_in.clone();
|
||||
if (contrast < target)
|
||||
{
|
||||
cv::Mat img_i;
|
||||
img.convertTo(img_i, CV_32S);
|
||||
double ratio = 200.0 / std::max(10, high - low);
|
||||
img_i = (img_i - low + 25) * ratio;
|
||||
// 将像素值限制在[0,255]范围,并转换回8位
|
||||
img_i.forEach<int>([] (int& pixel, const int*)
|
||||
{
|
||||
pixel = std::clamp(pixel, 0, 255);
|
||||
});
|
||||
img_i.convertTo(img, CV_8U);
|
||||
}
|
||||
return img;
|
||||
}
|
||||
|
||||
void uns::EasyOCR_Recognizer::AlignCollate::ContrastGrey(const cv::Mat& img, double& contrast, int& high, int& low) const
|
||||
{
|
||||
// 将Mat图像数据复制到一个连续的vector<int>中,以便排序
|
||||
std::vector<int> pixels;
|
||||
pixels.reserve(img.rows * img.cols); // 预分配空间以提高效率
|
||||
for (int i = 0; i < img.rows; ++i)
|
||||
{
|
||||
const uchar* row_ptr = img.ptr<uchar>(i);
|
||||
for (int j = 0; j < img.cols; ++j)
|
||||
pixels.push_back(static_cast<int>(row_ptr[j]));
|
||||
}
|
||||
// 对像素值进行排序,便于获取百分位数
|
||||
std::sort(pixels.begin(), pixels.end());
|
||||
// 计算90%的索引位置,与Python np.percentile保持一致
|
||||
int idx90 = static_cast<int>(0.9 * (pixels.size() - 1));
|
||||
int idx10 = static_cast<int>(0.1 * (pixels.size() - 1));
|
||||
high = pixels[idx90];
|
||||
low = pixels[idx10];
|
||||
// 计算contrast: (high - low) / max(10, high + low)
|
||||
contrast = double(high - low) / double(std::max(10, high + low));
|
||||
}
|
||||
|
||||
uns::EasyOCR_Recognizer::AlignCollate::AlignCollate(int imgH, int imgW, bool keep_ratio_with_pad, double adjust_contrast)
|
||||
{
|
||||
this->imgH = imgH;
|
||||
this->imgW = imgW;
|
||||
this->adjust_contrast = adjust_contrast;
|
||||
this->keep_ratio_with_pad = keep_ratio_with_pad;
|
||||
}
|
||||
|
||||
cv::Mat uns::EasyOCR_Recognizer::AlignCollate::operator()(const std::vector<cv::Mat>& batch) const
|
||||
{
|
||||
std::vector<cv::Mat> resized_images;
|
||||
|
||||
// 创建NormalizePAD实例,用于归一化和填充
|
||||
NormalizePAD transform({ 1, imgH, imgW });
|
||||
|
||||
for (const cv::Mat& image : batch)
|
||||
{
|
||||
cv::Mat working;
|
||||
if (adjust_contrast > 0)
|
||||
{
|
||||
cv::Mat grey;
|
||||
if (image.channels() > 1)
|
||||
cv::cvtColor(image, grey, cv::COLOR_BGR2GRAY);
|
||||
else
|
||||
grey = image;
|
||||
working = AdjustContrastGrey(grey, adjust_contrast);
|
||||
}
|
||||
else
|
||||
working = image;
|
||||
int w = working.cols;
|
||||
int h = working.rows;
|
||||
double ratio = double(w) / h;
|
||||
int resized_w = static_cast<int>(std::ceil(imgH * ratio));
|
||||
if (resized_w > imgW)
|
||||
resized_w = imgW;
|
||||
cv::Mat resized;
|
||||
cv::resize(working, resized, cv::Size(resized_w, imgH), 0, 0, cv::INTER_CUBIC);
|
||||
cv::Mat tensor = transform(resized);
|
||||
resized_images.push_back(tensor);
|
||||
}
|
||||
cv::Mat blob;
|
||||
cv::dnn::blobFromImages(resized_images, blob);
|
||||
return blob;
|
||||
}
|
||||
|
||||
float uns::EasyOCR_Recognizer::CustomMean(const VecFloat& x)
|
||||
{
|
||||
size_t N = x.size();
|
||||
if (N == 0)
|
||||
return 0.0f;
|
||||
// 1. 计算所有元素的乘积
|
||||
double prod = 1.0;
|
||||
for (float v : x)
|
||||
if (v != 0)
|
||||
prod *= static_cast<double>(v);
|
||||
// 2. 计算指数 2.0 / sqrt(N)
|
||||
double exponent = 2.0 / std::sqrt(static_cast<double>(N));
|
||||
// 3. 返回 prod 的 exponent 次幂
|
||||
return static_cast<float>(std::pow(prod, exponent));
|
||||
}
|
||||
|
||||
cv::Mat uns::EasyOCR_Recognizer::Preprocess(const cv::Mat& img) const
|
||||
{
|
||||
if (img.empty())
|
||||
return {}; //此处不适合抛出异常,使用空图像终止后级的处理即可
|
||||
cv::Mat gray;
|
||||
int ch = img.channels();
|
||||
// case 2: BGR 彩色图(3 通道)
|
||||
if (ch == 3)
|
||||
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
|
||||
// case 3: RGBA 彩色图(4 通道)
|
||||
else if (ch == 4)
|
||||
{
|
||||
// 去掉 alpha 通道,把 BGRA → GRAY
|
||||
cv::Mat bgr;
|
||||
cv::cvtColor(img, gray, cv::COLOR_BGRA2GRAY);
|
||||
}
|
||||
else // image 本身可能是 (h×w) 或者 (h×w×1),对我们来说都当灰度处理
|
||||
gray = img;
|
||||
int width = gray.cols;
|
||||
int height = gray.rows;
|
||||
int model_height = 64, model_width = 0;
|
||||
float ratio = static_cast<float>(width) / static_cast<float>(height);
|
||||
cv::Mat resized;
|
||||
if (ratio < 1.0f)
|
||||
{
|
||||
// 垂直文本情况,使用 calculate_ratio 保证高度为 model_height
|
||||
float adj_ratio = CalculateRatio(width, height);
|
||||
model_width = static_cast<int>(model_height * adj_ratio);
|
||||
cv::resize(gray, resized, cv::Size(model_height, model_width), 0, 0, cv::INTER_LINEAR);
|
||||
ratio = adj_ratio;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 横向文本情况,高度为 model_height
|
||||
model_width = static_cast<int>(model_height * ratio);
|
||||
cv::resize(gray, resized, cv::Size(model_width, model_height), 0, 0, cv::INTER_LINEAR);
|
||||
}
|
||||
AlignCollate alignCollate(model_height, model_width, true, 0.5);
|
||||
return alignCollate({ resized });
|
||||
}
|
||||
|
||||
float uns::EasyOCR_Recognizer::CalculateRatio(int width, int height) const
|
||||
{
|
||||
float ratio = static_cast<float>(width) / static_cast<float>(height);
|
||||
if (ratio < 1.0f)
|
||||
ratio = 1.0f / ratio;
|
||||
return ratio;
|
||||
}
|
||||
|
||||
uns::VecFloat uns::EasyOCR_Recognizer::SoftMAX(const float* logits, int C) const
|
||||
{
|
||||
// 找到最大值以稳定数值
|
||||
float m = logits[0];
|
||||
for (int i = 1; i < C; ++i)
|
||||
m = std::max(m, logits[i]);
|
||||
// 计算 exp(logit - m)
|
||||
std::vector<float> exps(C);
|
||||
float sum = 0.f;
|
||||
for (int i = 0; i < C; ++i)
|
||||
{
|
||||
exps[i] = std::exp(logits[i] - m);
|
||||
sum += exps[i];
|
||||
}
|
||||
// 归一化
|
||||
for (int i = 0; i < C; ++i)
|
||||
exps[i] /= (sum > 1e-6f ? sum : 1e-6f);
|
||||
return exps;
|
||||
}
|
||||
|
||||
void uns::EasyOCR_Recognizer::PostprocessONNXOutput(const Ort::Value& outputs, int N, int T, int C, VecInt& out_indices, VecFloat& out_probs, const VecInt ignore_idx)
|
||||
{
|
||||
// 指针访问底层数据
|
||||
const float* data = outputs.GetTensorData<float>();
|
||||
out_indices.clear();
|
||||
out_probs.clear();
|
||||
// 临时存储每步概率
|
||||
std::vector<float> probs;
|
||||
probs.reserve(C);
|
||||
// 遍历每个样本、每个时间步
|
||||
for (int n = 0; n < N; ++n)
|
||||
{
|
||||
for (int t = 0; t < T; ++t)
|
||||
{
|
||||
// logits 起始位置: ((n * T) + t) * C
|
||||
const float* logits = data + ((size_t)n * T + t) * C;
|
||||
// 1) Softmax
|
||||
probs = SoftMAX(logits, C);
|
||||
// 2) 忽略 ignore_idx
|
||||
if (!ignore_idx.empty())
|
||||
for (const auto& idx : ignore_idx)
|
||||
probs[idx] = 0.f;
|
||||
// 3) 再次归一化
|
||||
float sum = 0.f;
|
||||
for (int c = 0; c < C; ++c)
|
||||
sum += probs[c];
|
||||
if (sum > 1e-6f)
|
||||
{
|
||||
for (int c = 0; c < C; ++c)
|
||||
probs[c] /= sum;
|
||||
}
|
||||
// 4) 取最大索引
|
||||
int best = 0;
|
||||
float best_prob = 0.0f;
|
||||
for (int c = 1; c < C; ++c)
|
||||
{
|
||||
if (probs[c] > probs[best])
|
||||
{
|
||||
best = c;
|
||||
best_prob = probs[c];
|
||||
}
|
||||
}
|
||||
out_indices.push_back(best);
|
||||
out_probs.push_back(best_prob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uns::EasyOCR_Recognizer::EasyOCR_Recognizer()
|
||||
{
|
||||
ort_inited = false;
|
||||
ort_cpu_session = nullptr;
|
||||
model_path = G_OCRConfig.GetRecognizeModelPath();
|
||||
ort = OrtGetApiBase()->GetApi(ORT_API_VERSION);
|
||||
}
|
||||
|
||||
bool uns::EasyOCR_Recognizer::Init()
|
||||
{
|
||||
if (ort_inited)
|
||||
return true;
|
||||
if (!RecheckModelInfo())
|
||||
return false;
|
||||
try
|
||||
{
|
||||
ort_env = Ort::Env(ORT_LOGGING_LEVEL_WARNING, "EasyOCR_Recognizer");
|
||||
bool fallback_to_cpu = false;
|
||||
if (!OCRToolBox::AutoSelectEP(ort, ort_session_options, fallback_to_cpu))
|
||||
return false;
|
||||
OCRToolBox::InitOrtSessionOptions(ort_session_options);
|
||||
if ((G_OCRConfig.GetGPUUsage() == easyocr::GPUUsage::CPUOnly) || fallback_to_cpu) //使用CPU则初始化cpu session
|
||||
{
|
||||
ort_cpu_session = new Ort::Session(ort_env, model_path.c_str(), ort_session_options);
|
||||
//通过CPU session获取输入输出名
|
||||
OCRToolBox::GetInputOutputNames(ort_cpu_session, input_names, input_ns, output_names, output_ns);
|
||||
}
|
||||
else
|
||||
{
|
||||
//通过临时session获取输入输出名(CUDA:线程不安全)
|
||||
Ort::Session ort_session(ort_env, model_path.c_str(), ort_session_options);
|
||||
OCRToolBox::GetInputOutputNames(&ort_session, input_names, input_ns, output_names, output_ns);
|
||||
}
|
||||
ort_inited = true;
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool uns::EasyOCR_Recognizer::UnInit()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ort_cpu_session != nullptr)
|
||||
delete ort_cpu_session;
|
||||
ort_cpu_session = nullptr;
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool uns::EasyOCR_Recognizer::RecheckModelInfo()
|
||||
{
|
||||
if (model_path.empty())
|
||||
model_path = G_OCRConfig.GetRecognizeModelPath();
|
||||
return OCRToolBox::CheckFile(model_path);
|
||||
}
|
||||
|
||||
uns::EOCR_Result uns::EasyOCR_Recognizer::operator()(const cv::Mat& image)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!RecheckModelInfo())
|
||||
return { L"", -1.0f };
|
||||
cv::Mat input = Preprocess(image);
|
||||
if (input.empty())
|
||||
return { L"", 0.0f };
|
||||
std::array<int64_t, 4> inputShape = { 1, 1, input.size[2], input.size[3] };
|
||||
Ort::MemoryInfo memInfo = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
|
||||
Ort::Value inputTensor = Ort::Value::CreateTensor<float>(memInfo, input.ptr<float>(), input.total(), inputShape.data(), inputShape.size());
|
||||
auto outputs = ((ort_cpu_session != nullptr) ? ort_cpu_session->Run(Ort::RunOptions{nullptr}, input_names.data(), & inputTensor, 1, output_names.data(), 1) : Ort::Session(ort_env, model_path.c_str(), ort_session_options).Run(Ort::RunOptions{nullptr}, input_names.data(), & inputTensor, 1, output_names.data(), 1));
|
||||
// 输出 shape: [1, T, C]
|
||||
auto& outVal = outputs.front();
|
||||
auto info = outVal.GetTensorTypeAndShapeInfo();
|
||||
auto shape = info.GetShape(); // {1, T, C}
|
||||
int N = (int)shape[0], T = (int)shape[1], C = (int)shape[2];
|
||||
float* data = outVal.GetTensorMutableData<float>();
|
||||
// greedy pick & softmax
|
||||
std::vector<int> indices(T);
|
||||
std::vector<float> maxProbs(T);
|
||||
PostprocessONNXOutput(outputs[0], N, T, C, indices, maxProbs);
|
||||
// 解码
|
||||
std::wstring text = OCRCharset::GetString(indices);
|
||||
// 置信度
|
||||
float conf = CustomMean(maxProbs);
|
||||
return { text, conf };
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return { L"", -2.0f };
|
||||
}
|
||||
}
|
||||
|
||||
uns::EOCR_ResultSet uns::EasyOCR_Recognizer::operator()(const cv::Mat& full_image, const EOCRD_Rects& rects)
|
||||
{
|
||||
if (!RecheckModelInfo())
|
||||
return {};
|
||||
try
|
||||
{
|
||||
EOCR_ResultSet result_set;
|
||||
for (size_t i = 0; i < rects.size(); ++i)
|
||||
{
|
||||
// 将多边形转为最小外接矩形并裁剪
|
||||
cv::Rect rect = cv::boundingRect(rects[i]);
|
||||
rect &= cv::Rect(0, 0, full_image.cols, full_image.rows); // 裁剪到图像范围
|
||||
cv::Mat crop = full_image(rect);
|
||||
if (crop.empty())
|
||||
continue;
|
||||
auto [text, conf] = (*this)(crop);
|
||||
result_set.insert({ i, { text, conf } });
|
||||
}
|
||||
return result_set;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "OCRConfig.h"
|
||||
#include "OCRToolBox.h"
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <onnxruntime_cxx_api.h>
|
||||
|
||||
namespace uns
|
||||
{
|
||||
class EasyOCR_Recognizer
|
||||
{
|
||||
public:
|
||||
class NormalizePAD
|
||||
{
|
||||
public:
|
||||
struct Size3i // 自定义3维尺寸结构,表示通道、高度、宽度
|
||||
{
|
||||
int d0, d1, d2;
|
||||
|
||||
Size3i operator=(const Size3i& obj);
|
||||
};
|
||||
|
||||
private:
|
||||
Size3i max_size; // 最大尺寸
|
||||
int max_width_half; // 宽度一半,用于可选的中心裁剪等功能
|
||||
std::string PAD_type; // 填充类型,支持"right"或"left"
|
||||
|
||||
public:
|
||||
NormalizePAD(Size3i max_size, const std::string& PAD_type = "right");
|
||||
|
||||
public:
|
||||
cv::Mat operator()(const cv::Mat& input_img) const;
|
||||
};
|
||||
|
||||
class AlignCollate
|
||||
{
|
||||
private:
|
||||
int imgH; // 目标图像高度
|
||||
int imgW; // 目标图像宽度
|
||||
bool keep_ratio_with_pad; // 保持长宽比并填充标志(暂未使用)
|
||||
double adjust_contrast; // 对比度调整目标值
|
||||
|
||||
private:
|
||||
cv::Mat AdjustContrastGrey(const cv::Mat& img_in, double target = 0.4) const;
|
||||
void ContrastGrey(const cv::Mat& img, double& contrast, int& high, int& low) const;
|
||||
|
||||
public:
|
||||
AlignCollate(int imgH = 32, int imgW = 100, bool keep_ratio_with_pad = false, double adjust_contrast = 0.0);
|
||||
|
||||
public:
|
||||
cv::Mat operator()(const std::vector<cv::Mat>& batch) const;
|
||||
};
|
||||
|
||||
private:
|
||||
const OrtApi* ort;
|
||||
|
||||
Ort::Env ort_env;
|
||||
Ort::Session* ort_cpu_session;
|
||||
Ort::SessionOptions ort_session_options;
|
||||
|
||||
bool ort_inited;
|
||||
std::wstring model_path;
|
||||
|
||||
IONames input_names, output_names;
|
||||
IONamesStorage input_ns, output_ns;
|
||||
|
||||
private:
|
||||
float CustomMean(const VecFloat& x);
|
||||
cv::Mat Preprocess(const cv::Mat& img) const;
|
||||
float CalculateRatio(int width, int height) const;
|
||||
VecFloat SoftMAX(const float* logits, int C) const;
|
||||
void PostprocessONNXOutput(const Ort::Value& outputs, int N, int T, int C, VecInt& out_indices, VecFloat& out_probs, const VecInt ignore_idx = {});
|
||||
|
||||
public:
|
||||
EasyOCR_Recognizer();
|
||||
|
||||
public:
|
||||
bool Init();
|
||||
bool UnInit();
|
||||
bool RecheckModelInfo();
|
||||
/// <summary>
|
||||
/// EasyOCR 文本识别函数
|
||||
/// </summary>
|
||||
/// <param name="image">仅包含文本的图像(三通道BGR图像)</param>
|
||||
/// <returns>文本及其置信度</returns>
|
||||
EOCR_Result operator()(const cv::Mat& image);
|
||||
/// <summary>
|
||||
/// EasyOCR 文本识别函数
|
||||
/// </summary>
|
||||
/// <param name="image">完整的待检测图像(三通道BGR图像)</param>
|
||||
/// <param name="rects">EasyOCR_Detector检测到的文本位置矩形</param>
|
||||
/// <returns>若干个 [文本及其置信度] 的组合</returns>
|
||||
EOCR_ResultSet operator()(const cv::Mat& full_image, const EOCRD_Rects& rects);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef LIBEASYOCRCPP_EXPORTS
|
||||
#define LIBEASYOCRCPP_API _declspec(dllexport)
|
||||
#else
|
||||
#define LIBEASYOCRCPP_API _declspec(dllimport)
|
||||
#ifdef _DEBUG
|
||||
#pragma comment(lib, "LibEasyOCR-CPPd.lib")
|
||||
#else
|
||||
#pragma comment(lib, "LibEasyOCR-CPP.lib")
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,135 @@
|
||||
#include "pch.h"
|
||||
#include "ATW.h"
|
||||
#include "OCRConfig.h"
|
||||
#include "LibEasyOCR-CPP.h"
|
||||
#include "EasyOCR_Detector.h"
|
||||
#include "EasyOCR_Recognizer.h"
|
||||
|
||||
uns::EasyOCR_Detector G_Detector;
|
||||
uns::EasyOCR_Recognizer G_Recognizer;
|
||||
|
||||
uns::OCRConfig& uns::EasyOCR_CPP::GlobalOCRConfig()
|
||||
{
|
||||
return G_OCRConfig;
|
||||
}
|
||||
|
||||
void uns::EasyOCR_CPP::SetRecognitionModel(const std::wstring& reco_model, easyocr::CharsetType language)
|
||||
{
|
||||
G_OCRConfig.SetLanguage(language);
|
||||
G_OCRConfig.SetRecognizeModelPath(reco_model);
|
||||
}
|
||||
|
||||
bool uns::EasyOCR_CPP::CleanupOCR()
|
||||
{
|
||||
bool detect = G_Detector.UnInit();
|
||||
bool recognize = G_Recognizer.UnInit();
|
||||
return (detect && recognize);
|
||||
}
|
||||
|
||||
bool uns::EasyOCR_CPP::InitDetectionModel()
|
||||
{
|
||||
return G_Detector.Init();
|
||||
}
|
||||
|
||||
bool uns::EasyOCR_CPP::InitRecognitionModel()
|
||||
{
|
||||
return G_Recognizer.Init();
|
||||
}
|
||||
|
||||
uns::easyocr::EOCRD_Rects uns::EasyOCR_CPP::Detect(const cv::Mat& img)
|
||||
{
|
||||
if (img.empty() || (!G_Detector.RecheckModelInfo()))
|
||||
return {};
|
||||
return G_Detector(img);
|
||||
}
|
||||
|
||||
uns::easyocr::EOCR_Result uns::EasyOCR_CPP::Recognize(const cv::Mat& img)
|
||||
{
|
||||
if (img.empty() || (!G_Recognizer.RecheckModelInfo()))
|
||||
return {};
|
||||
return G_Recognizer(img);
|
||||
}
|
||||
|
||||
uns::easyocr::EOCR_ResultSet uns::EasyOCR_CPP::Recognize(const cv::Mat& img, const easyocr::EOCRD_Rects& rects)
|
||||
{
|
||||
if (img.empty() || (!G_Recognizer.RecheckModelInfo()))
|
||||
return {};
|
||||
return G_Recognizer(img, rects);
|
||||
}
|
||||
|
||||
uns::easyocr::EOCR_ResultSet uns::EasyOCR_CPP::FullAuto(const cv::Mat& img)
|
||||
{
|
||||
if (img.empty() || (!G_Detector.RecheckModelInfo()) || (!G_Recognizer.RecheckModelInfo()))
|
||||
return {};
|
||||
auto rects = G_Detector(img);
|
||||
if (rects.empty())
|
||||
return {};
|
||||
else
|
||||
return G_Recognizer(img, rects);
|
||||
}
|
||||
|
||||
std::string uns::EOCR_SupportTools::WtoA(const std::wstring& wstr)
|
||||
{
|
||||
return ::WtoA(wstr);
|
||||
}
|
||||
|
||||
std::wstring uns::EOCR_SupportTools::AtoW(const std::string& str)
|
||||
{
|
||||
return ::AtoW(str);
|
||||
}
|
||||
|
||||
std::string uns::EOCR_SupportTools::AtoUTF8(const std::string& str)
|
||||
{
|
||||
return ::AtoUTF8(str);
|
||||
}
|
||||
|
||||
std::string uns::EOCR_SupportTools::UTF8toA(const std::string& utf8)
|
||||
{
|
||||
return ::UTF8toA(utf8);
|
||||
}
|
||||
|
||||
cv::Mat uns::EOCR_SupportTools::ReadImage(const std::string& file, bool blur, int blur_size)
|
||||
{
|
||||
if (!uns::OCRToolBox::CheckFile(::AtoW(file)))
|
||||
return {};
|
||||
try
|
||||
{
|
||||
if (!blur)
|
||||
return cv::imread(file);
|
||||
else
|
||||
{
|
||||
cv::Mat img = cv::imread(file);
|
||||
if (img.empty())
|
||||
return {};
|
||||
cv::blur(img, img, cv::Size(blur_size, blur_size));
|
||||
return img;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
cv::Mat uns::EOCR_SupportTools::ReadImage(const std::wstring& file, bool blur, int blur_size)
|
||||
{
|
||||
if (!uns::OCRToolBox::CheckFile(file))
|
||||
return {};
|
||||
try
|
||||
{
|
||||
if (!blur)
|
||||
return cv::imread(::WtoA(file));
|
||||
else
|
||||
{
|
||||
cv::Mat img = cv::imread(::WtoA(file));
|
||||
if (img.empty())
|
||||
return {};
|
||||
cv::blur(img, img, cv::Size(blur_size, blur_size));
|
||||
return img;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "OCRConfig.h"
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include "LibEasyOCR-CPP-Export.h"
|
||||
|
||||
namespace uns
|
||||
{
|
||||
namespace easyocr
|
||||
{
|
||||
using EOCR_Result = std::pair<std::wstring, float>;
|
||||
using EOCR_ResultSet = std::map<size_t, EOCR_Result>;
|
||||
using EOCRD_Rects = std::vector<std::vector<cv::Point2f>>;
|
||||
}
|
||||
|
||||
class LIBEASYOCRCPP_API EasyOCR_CPP
|
||||
{
|
||||
public:
|
||||
static OCRConfig& GlobalOCRConfig();
|
||||
static void SetRecognitionModel(const std::wstring& reco_model, easyocr::CharsetType language);
|
||||
|
||||
static bool CleanupOCR();
|
||||
static bool InitDetectionModel();
|
||||
static bool InitRecognitionModel();
|
||||
|
||||
static easyocr::EOCRD_Rects Detect(const cv::Mat& img);
|
||||
static easyocr::EOCR_Result Recognize(const cv::Mat& img);
|
||||
static easyocr::EOCR_ResultSet Recognize(const cv::Mat& img, const easyocr::EOCRD_Rects& rects);
|
||||
|
||||
static easyocr::EOCR_ResultSet FullAuto(const cv::Mat& img);
|
||||
};
|
||||
|
||||
class LIBEASYOCRCPP_API EOCR_SupportTools
|
||||
{
|
||||
public:
|
||||
static std::string WtoA(const std::wstring& wstr);
|
||||
static std::wstring AtoW(const std::string& str);
|
||||
static std::string AtoUTF8(const std::string& str);
|
||||
static std::string UTF8toA(const std::string& utf8);
|
||||
|
||||
static cv::Mat ReadImage(const std::string& file, bool blur = false, int blur_size = 3);
|
||||
static cv::Mat ReadImage(const std::wstring& file, bool blur = false, int blur_size = 3);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{1900cf83-6bce-41c9-b022-0645107abd57}</ProjectGuid>
|
||||
<RootNamespace>LibEasyOCRCPP</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<IncludePath>F:\vcpkg\installed\x64-windows\include\opencv4;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<IncludePath>F:\vcpkg\installed\x64-windows\include\opencv4;$(IncludePath)</IncludePath>
|
||||
<TargetName>$(ProjectName)d</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;LIBEASYOCRCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;LIBEASYOCRCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;LIBEASYOCRCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;LIBEASYOCRCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ATW.h" />
|
||||
<ClInclude Include="EasyOCR_Detector.h" />
|
||||
<ClInclude Include="EasyOCR_Recognizer.h" />
|
||||
<ClInclude Include="framework.h" />
|
||||
<ClInclude Include="LibEasyOCR-CPP-Export.h" />
|
||||
<ClInclude Include="LibEasyOCR-CPP.h" />
|
||||
<ClInclude Include="OCRCharset.h" />
|
||||
<ClInclude Include="OCRConfig.h" />
|
||||
<ClInclude Include="OCRToolBox.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ATW.cpp" />
|
||||
<ClCompile Include="dllmain.cpp" />
|
||||
<ClCompile Include="EasyOCR_Detector.cpp" />
|
||||
<ClCompile Include="EasyOCR_Recognizer.cpp" />
|
||||
<ClCompile Include="LibEasyOCR-CPP.cpp" />
|
||||
<ClCompile Include="OCRCharset.cpp" />
|
||||
<ClCompile Include="OCRConfig.cpp" />
|
||||
<ClCompile Include="OCRToolBox.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="源文件">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="头文件">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="资源文件">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="framework.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LibEasyOCR-CPP.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ATW.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="EasyOCR_Detector.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="EasyOCR_Recognizer.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="OCRCharset.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="OCRConfig.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="OCRToolBox.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LibEasyOCR-CPP-Export.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="dllmain.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ATW.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="EasyOCR_Detector.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="EasyOCR_Recognizer.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="OCRCharset.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="OCRConfig.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="OCRToolBox.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LibEasyOCR-CPP.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace uns
|
||||
{
|
||||
class OCRCharset
|
||||
{
|
||||
private:
|
||||
static std::map<int, std::wstring> en_charmap;
|
||||
static std::map<int, std::wstring> ch_en_charmap;
|
||||
|
||||
public:
|
||||
OCRCharset() = delete;
|
||||
OCRCharset(const OCRCharset&) = delete;
|
||||
|
||||
public:
|
||||
static std::wstring GetChar(int index);
|
||||
static std::wstring GetString(const std::vector<int>& indexs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#include "pch.h"
|
||||
#include "OCRConfig.h"
|
||||
|
||||
uns::OCRConfig uns::G_OCRConfig;
|
||||
|
||||
uns::OCRConfig::OCRConfig()
|
||||
{
|
||||
language = easyocr::CharsetType::EN;
|
||||
gpu_usage = easyocr::GPUUsage::PreferGPU;
|
||||
}
|
||||
|
||||
uns::OCRConfig::OCRConfig(const std::wstring& detect_model, const std::wstring& reco_model, easyocr::CharsetType language, easyocr::GPUUsage gpu)
|
||||
{
|
||||
gpu_usage = gpu;
|
||||
this->language = language;
|
||||
detect_model_path = detect_model;
|
||||
recognize_model_path = reco_model;
|
||||
}
|
||||
|
||||
uns::easyocr::GPUUsage uns::OCRConfig::GetGPUUsage() const
|
||||
{
|
||||
return gpu_usage;
|
||||
}
|
||||
|
||||
uns::easyocr::CharsetType uns::OCRConfig::GetLanguage() const
|
||||
{
|
||||
return language;
|
||||
}
|
||||
|
||||
std::wstring uns::OCRConfig::GetDetectModelPath() const
|
||||
{
|
||||
return detect_model_path;
|
||||
}
|
||||
|
||||
std::wstring uns::OCRConfig::GetRecognizeModelPath() const
|
||||
{
|
||||
return recognize_model_path;
|
||||
}
|
||||
|
||||
void uns::OCRConfig::SetGPUUsage(easyocr::GPUUsage usage)
|
||||
{
|
||||
gpu_usage = usage;
|
||||
}
|
||||
|
||||
void uns::OCRConfig::SetLanguage(easyocr::CharsetType type)
|
||||
{
|
||||
language = type;
|
||||
}
|
||||
|
||||
void uns::OCRConfig::SetDetectModelPath(const std::wstring& path)
|
||||
{
|
||||
detect_model_path = path;
|
||||
}
|
||||
|
||||
void uns::OCRConfig::SetRecognizeModelPath(const std::wstring& path)
|
||||
{
|
||||
recognize_model_path = path;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "LibEasyOCR-CPP-Export.h"
|
||||
|
||||
namespace uns
|
||||
{
|
||||
namespace easyocr
|
||||
{
|
||||
enum class LIBEASYOCRCPP_API GPUUsage
|
||||
{
|
||||
ForceGPU, // 强制使用GPU(失败报错)
|
||||
PreferGPU, // 优先GPU,失败回退
|
||||
CPUOnly // 仅使用CPU
|
||||
};
|
||||
|
||||
enum class LIBEASYOCRCPP_API CharsetType
|
||||
{
|
||||
EN, //英语
|
||||
EN_CH //英语和中文
|
||||
};
|
||||
}
|
||||
|
||||
class LIBEASYOCRCPP_API OCRConfig
|
||||
{
|
||||
private:
|
||||
easyocr::GPUUsage gpu_usage;
|
||||
easyocr::CharsetType language;
|
||||
std::wstring detect_model_path;
|
||||
std::wstring recognize_model_path;
|
||||
|
||||
public:
|
||||
OCRConfig();
|
||||
OCRConfig(const OCRConfig& obj) = delete;
|
||||
OCRConfig(const std::wstring& detect_model, const std::wstring& reco_model, easyocr::CharsetType language, easyocr::GPUUsage gpu = easyocr::GPUUsage::PreferGPU);
|
||||
|
||||
public:
|
||||
easyocr::GPUUsage GetGPUUsage() const;
|
||||
easyocr::CharsetType GetLanguage() const;
|
||||
|
||||
std::wstring GetDetectModelPath() const;
|
||||
std::wstring GetRecognizeModelPath() const;
|
||||
|
||||
public:
|
||||
void SetGPUUsage(easyocr::GPUUsage usage);
|
||||
void SetLanguage(easyocr::CharsetType type);
|
||||
void SetDetectModelPath(const std::wstring& path);
|
||||
void SetRecognizeModelPath(const std::wstring& path);
|
||||
};
|
||||
|
||||
extern OCRConfig G_OCRConfig;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
#include "pch.h"
|
||||
#include "OCRToolBox.h"
|
||||
#include "OCRConfig.h"
|
||||
|
||||
bool uns::OCRToolBox::CheckFile(const std::wstring& file)
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
try
|
||||
{
|
||||
if (file.empty())
|
||||
return false;
|
||||
if (!fs::exists(file))
|
||||
return false;
|
||||
if (!fs::is_regular_file(file))
|
||||
return false;
|
||||
auto perms = fs::status(file).permissions();
|
||||
return ((perms & fs::perms::owner_read) != fs::perms::none && (perms & fs::perms::owner_write) != fs::perms::none);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void uns::OCRToolBox::InitOrtSessionOptions(Ort::SessionOptions& se_opt)
|
||||
{
|
||||
se_opt.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL);
|
||||
se_opt.EnableCpuMemArena(); // 启用内存池
|
||||
se_opt.EnableMemPattern(); // 启用内存模式优化
|
||||
}
|
||||
|
||||
bool uns::OCRToolBox::AutoSelectEP(const OrtApi* ort, Ort::SessionOptions& se_opt, bool& fallback_to_cpu)
|
||||
{
|
||||
fallback_to_cpu = false;
|
||||
if (G_OCRConfig.GetGPUUsage() == easyocr::GPUUsage::CPUOnly)
|
||||
return true;
|
||||
try
|
||||
{
|
||||
OrtStatusPtr status = OrtSessionOptionsAppendExecutionProvider_CUDA(se_opt, 0);
|
||||
if (status)
|
||||
{
|
||||
ort->ReleaseStatus(status);
|
||||
if (G_OCRConfig.GetGPUUsage() == easyocr::GPUUsage::ForceGPU)
|
||||
return false;
|
||||
se_opt = Ort::SessionOptions(); //Reset Session Options (Fallback to CPU)
|
||||
fallback_to_cpu = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return true; //GPU Enable Successful
|
||||
}
|
||||
catch (const Ort::Exception&)
|
||||
{
|
||||
se_opt = Ort::SessionOptions(); //Reset Session Options (Fallback to CPU)
|
||||
fallback_to_cpu = true;
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void uns::OCRToolBox::GetInputOutputNames(Ort::Session* ort_session, IONames& input_names, IONamesStorage& input_ns, IONames& output_names, IONamesStorage& output_ns)
|
||||
{
|
||||
input_ns.clear();
|
||||
output_ns.clear();
|
||||
input_names.clear();
|
||||
output_names.clear();
|
||||
|
||||
if (ort_session == nullptr)
|
||||
return;
|
||||
|
||||
auto input_name_alloc = ort_session->GetInputNameAllocated(0, Ort::AllocatorWithDefaultOptions());
|
||||
std::string input_name = input_name_alloc.get();
|
||||
input_ns = { input_name };
|
||||
for (auto& name : input_ns)
|
||||
input_names.push_back(name.c_str());
|
||||
|
||||
auto output_name_alloc = ort_session->GetOutputNameAllocated(0, Ort::AllocatorWithDefaultOptions());
|
||||
std::string output_name = output_name_alloc.get();
|
||||
output_ns = { output_name };
|
||||
for (auto& name : output_ns)
|
||||
output_names.push_back(name.c_str());
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <filesystem>
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <onnxruntime_cxx_api.h>
|
||||
|
||||
namespace uns
|
||||
{
|
||||
using VecInt = std::vector<int>;
|
||||
using VecFloat = std::vector<float>;
|
||||
using IONames = std::vector<const char*>;
|
||||
using IONamesStorage = std::vector<std::string>;
|
||||
using EOCR_Result = std::pair<std::wstring, float>;
|
||||
using EOCR_ResultSet = std::map<size_t, EOCR_Result>;
|
||||
using EOCRD_Rects = std::vector<std::vector<cv::Point2f>>;
|
||||
|
||||
class OCRToolBox
|
||||
{
|
||||
public:
|
||||
static bool CheckFile(const std::wstring& file);
|
||||
static void InitOrtSessionOptions(Ort::SessionOptions& se_opt);
|
||||
static bool AutoSelectEP(const OrtApi* ort, Ort::SessionOptions& se_opt, bool& fallback_to_cpu);
|
||||
static void GetInputOutputNames(Ort::Session* ort_session, IONames& input_names, IONamesStorage& input_ns, IONames& output_names, IONamesStorage& output_ns);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// dllmain.cpp : 定义 DLL 应用程序的入口点。
|
||||
#include "pch.h"
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
case DLL_PROCESS_DETACH:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
|
||||
// Windows 头文件
|
||||
#include <windows.h>
|
||||
@@ -0,0 +1,5 @@
|
||||
// pch.cpp: 与预编译标头对应的源文件
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
// 当使用预编译的头时,需要使用此源文件,编译才能成功。
|
||||
@@ -0,0 +1,13 @@
|
||||
// pch.h: 这是预编译标头文件。
|
||||
// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
|
||||
// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
|
||||
// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
|
||||
// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。
|
||||
|
||||
#ifndef PCH_H
|
||||
#define PCH_H
|
||||
|
||||
// 添加要在此处预编译的标头
|
||||
#include "framework.h"
|
||||
|
||||
#endif //PCH_H
|
||||
Reference in New Issue
Block a user