168 lines
5.0 KiB
C++
168 lines
5.0 KiB
C++
// DLLUsageDemo.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
|
||
//
|
||
|
||
#include <set>
|
||
#include <iostream>
|
||
#include "LibEasyOCR-CPP.h"
|
||
#include <opencv2/highgui/highgui.hpp>
|
||
#include <opencv2/imgproc/imgproc.hpp>
|
||
#include <range/v3/view/enumerate.hpp>
|
||
#include <opencv2/photo/photo.hpp>
|
||
#include <opencv2/features2d.hpp>
|
||
|
||
static const std::set<char> validate_vin_chars = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','J','K','L','M','N','P','R','S','T','U','V','W','X','Y','Z' };
|
||
|
||
std::string PreProcessVIN(const std::string& vin)
|
||
{
|
||
std::string res;
|
||
for (const auto& chr : vin)
|
||
{
|
||
if ((chr == 'i') || (chr == 'I'))
|
||
res.push_back('1');
|
||
else if ((chr == 'o') || (chr == 'O'))
|
||
res.push_back('0');
|
||
else if (chr == 'q')
|
||
res.push_back('9');
|
||
else if (chr == 'Q')
|
||
res.push_back('0');
|
||
else if (validate_vin_chars.count(toupper(chr)) >= 1)
|
||
res.push_back(toupper(chr));
|
||
}
|
||
return res;
|
||
}
|
||
|
||
void RecognizeSingleImage(const std::string& img, int image_no)
|
||
{
|
||
cv::Mat image = cv::imread(img);
|
||
if (image.empty())
|
||
{
|
||
std::cout << "RSI: Unable to read image" << std::endl;
|
||
return;
|
||
}
|
||
|
||
//cv::blur(image, image, cv::Size(3, 3));
|
||
|
||
// 1. 灰度
|
||
cv::Mat gray;
|
||
cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
|
||
|
||
// 2. 去高光(inpaint)
|
||
cv::Mat mask = gray > 240;
|
||
cv::inpaint(gray, mask, gray, 5, cv::INPAINT_TELEA);
|
||
|
||
// 3. 伽马校正(弱化阴影)
|
||
cv::Mat lut(1, 256, CV_8U);
|
||
const float gamma = 0.8f;
|
||
for (int i = 0; i < 256; i++)
|
||
lut.at<uchar>(i) = cv::saturate_cast<uchar>(std::pow(i / 255.0, gamma) * 255);
|
||
cv::LUT(gray, lut, gray);
|
||
|
||
// 4. CLAHE 局部对比度增强
|
||
cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE(2.0, cv::Size(8, 8));
|
||
clahe->apply(gray, gray);
|
||
|
||
// 2. 形态学 Top-hat & Black-hat 提取高频细节
|
||
cv::Mat kernel = cv::getStructuringElement(
|
||
cv::MORPH_RECT, cv::Size(15, 15));
|
||
cv::Mat tophat, blackhat;
|
||
cv::morphologyEx(gray, tophat, cv::MORPH_TOPHAT, kernel);
|
||
cv::morphologyEx(gray, blackhat, cv::MORPH_BLACKHAT, kernel);
|
||
|
||
// 3. 高频增强:原图 + tophat - blackhat
|
||
cv::Mat enhanced;
|
||
enhanced = gray + tophat - blackhat;
|
||
|
||
// 4. 平滑降噪
|
||
cv::GaussianBlur(enhanced, enhanced, cv::Size(3, 3), 0);
|
||
|
||
// 5. 全局 Otsu 阈值
|
||
cv::Mat bw;
|
||
cv::threshold(
|
||
enhanced, bw,
|
||
0, 255,
|
||
cv::THRESH_BINARY | cv::THRESH_OTSU
|
||
);
|
||
// 保证文字白底黑字
|
||
double m = cv::mean(bw.row(bw.rows / 2))[0];
|
||
if (m < 128) cv::bitwise_not(bw, bw);
|
||
|
||
// 6. 小核闭运算补笔画断裂
|
||
cv::morphologyEx(
|
||
bw, bw,
|
||
cv::MORPH_CLOSE,
|
||
cv::getStructuringElement(
|
||
cv::MORPH_RECT, cv::Size(3, 3)
|
||
),
|
||
cv::Point(-1, -1), 1
|
||
);
|
||
|
||
// 7. 小核开运算去孤立噪点
|
||
cv::morphologyEx(
|
||
bw, bw,
|
||
cv::MORPH_OPEN,
|
||
cv::getStructuringElement(
|
||
cv::MORPH_RECT, cv::Size(3, 3)
|
||
),
|
||
cv::Point(-1, -1), 1
|
||
);
|
||
|
||
// 8. 转三通道输出
|
||
cv::Mat finalBGR;
|
||
cv::cvtColor(bw, finalBGR, cv::COLOR_GRAY2BGR);
|
||
|
||
cv::Mat output = finalBGR.clone();
|
||
|
||
cv::imshow("Processed Image " + std::to_string(image_no), output);
|
||
cv::imwrite("G:\\Users\\15819\\Desktop\\temp\\Processed Image " + std::to_string(image_no) + ".jpg", output);
|
||
|
||
auto results = uns::EasyOCR_CPP::FullAuto(image);
|
||
for (const auto& [index, info] : results)
|
||
{
|
||
const auto& [text, conf, rect] = info;
|
||
std::string a_text = uns::EOCR_SupportTools::WtoA(text);
|
||
std::string processed_vin = PreProcessVIN(a_text);
|
||
printf("RSI(%d): Result %d: Text = {%s(%d) -> |%s|(%d)}, Box = [%d, %d, %d, %d], Confidence = %.3f\n", image_no, (int)index, a_text.c_str(), (int)a_text.length(), processed_vin.c_str(), (int)processed_vin.length(), rect.tl().x, rect.tl().y, rect.br().x, rect.br().y, conf);
|
||
}
|
||
if (results.empty())
|
||
printf("RSI(%d): No Text Found\n", image_no);
|
||
}
|
||
|
||
int main()
|
||
{
|
||
std::wstring detect_model = L"..\\EasyOCR-CPP\\DetectionModel.onnx";
|
||
std::wstring recognize_model = L"..\\EasyOCR-CPP\\RecognitionModel-EN.onnx";
|
||
|
||
std::vector<std::string> images =
|
||
{
|
||
"G:\\Users\\15819\\Desktop\\error\\UPLOAD-20250709184942-99F24141-6C0A31D2.jpg",
|
||
"G:\\Users\\15819\\Desktop\\error\\UPLOAD-20250709185025-99F24141-A56037A2.jpg",
|
||
"G:\\Users\\15819\\Desktop\\error\\UPLOAD-20250709185205-99F24141-1D0C341A.jpg",
|
||
"G:\\Users\\15819\\Desktop\\error\\UPLOAD-20250709185450-99F24141-EC43D7FD.jpg",
|
||
"G:\\Users\\15819\\Desktop\\error\\UPLOAD-20250708185403-99F24141-1F2233FA.jpg",
|
||
"G:\\Users\\15819\\Desktop\\error\\UPLOAD-20250708184942-99F24141-C138BA94.jpg"
|
||
};
|
||
|
||
uns::EasyOCR_CPP::GlobalOCRConfig().SetDetectModelPath(detect_model);
|
||
uns::EasyOCR_CPP::GlobalOCRConfig().SetGPUUsage(uns::easyocr::GPUUsage::CPUOnly);
|
||
uns::EasyOCR_CPP::SetRecognitionModel(recognize_model, uns::easyocr::CharsetType::EN);
|
||
|
||
if (!uns::EasyOCR_CPP::InitDetectionModel())
|
||
{
|
||
std::cout << "Detector Init Failure!" << std::endl;
|
||
return -1;
|
||
}
|
||
if (!uns::EasyOCR_CPP::InitRecognitionModel())
|
||
{
|
||
std::cout << "Recognizer Init Failure!" << std::endl;
|
||
return -2;
|
||
}
|
||
|
||
for (auto&& [index, file] : images | ranges::views::enumerate)
|
||
RecognizeSingleImage(file, static_cast<int>(index));
|
||
|
||
cv::waitKey(0);
|
||
uns::EasyOCR_CPP::CleanupOCR();
|
||
return 0;
|
||
}
|
||
|
||
//VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
|