【opencv】dnn示例-scene_text_recognition.cpp OpenCV库深度学习模型检测和识别图像中文本...

发布于:2024-04-20 ⋅ 阅读:(23) ⋅ 点赞:(0)

f8069d3b7bbe6af918488ea97075f824.png

1a2e61c97f3847272938be099baf4cea.png

1440 x 1080
0: 't'
1: 'i'
2: 'I'
3: '黄'

中文显示异常:文末有解决方案

3fa27f5ed397cb6a8e3feb4ab1d61693.png

934 x 1408
0: '蓝朝路'
1: 'BaoanHwy'
2: 'GucunPark'
3: '宝安公路'
4: '顾村公园'
5: 'HutaiRd.'
6: '沪太路'
7: '东'

中文显示异常:文末有解决方案

文件下载及参数设置参考:(文本识别、文本检测模型及参数)

TextRecognitionModel:
crnn.onnx:
url: https://drive.google.com/uc?export=dowload&id=1ooaLR-rkTl8jdpGy1DoQs0-X0lQsB6Fj
sha: 270d92c9ccb670ada2459a25977e8deeaf8380d3,
alphabet_36.txt: https://drive.google.com/uc?export=dowload&id=1oPOYx5rQRp8L6XQciUwmwhMCfX0KyO4b
parameter setting: -rgb=0;
description: The classification number of this model is 36 (0~9 + a~z).
             The training dataset is MJSynth.


crnn_cs.onnx:
url: https://drive.google.com/uc?export=dowload&id=12diBsVJrS9ZEl6BNUiRp9s0xPALBS7kt
sha: a641e9c57a5147546f7a2dbea4fd322b47197cd5
alphabet_94.txt: https://drive.google.com/uc?export=dowload&id=1oKXxXKusquimp7XY1mFvj9nwLzldVgBR
parameter setting: -rgb=1;
description: The classification number of this model is 94 (0~9 + a~z + A~Z + punctuations).
             The training datasets are MJsynth and SynthText.


crnn_cs_CN.onnx:
url: https://drive.google.com/uc?export=dowload&id=1is4eYEUKH7HR7Gl37Sw4WPXx6Ir8oQEG
sha: 3940942b85761c7f240494cf662dcbf05dc00d14
alphabet_3944.txt: https://drive.google.com/uc?export=dowload&id=18IZUUdNzJ44heWTndDO6NNfIpJMmN-ul
parameter setting: -rgb=1;
description: The classification number of this model is 3944 (0~9 + a~z + A~Z + Chinese characters + special characters).
             The training dataset is ReCTS (https://rrc.cvc.uab.es/?ch=12).             
 
             
TextDetectionModel:
- DB_IC15_resnet50.onnx:
url: https://drive.google.com/uc?export=dowload&id=17_ABp79PlFt9yPCxSaarVc_DKTmrSGGf
sha: bef233c28947ef6ec8c663d20a2b326302421fa3
recommended parameter setting: -inputHeight=736, -inputWidth=1280;
description: This model is trained on ICDAR2015, so it can only detect English text instances.


- DB_IC15_resnet18.onnx:
url: https://drive.google.com/uc?export=dowload&id=1vY_KsDZZZb_svd5RT6pjyI8BS1nPbBSX
sha: 19543ce09b2efd35f49705c235cc46d0e22df30b
recommended parameter setting: -inputHeight=736, -inputWidth=1280;
description: This model is trained on ICDAR2015, so it can only detect English text instances.


- DB_TD500_resnet50.onnx:
url: https://drive.google.com/uc?export=dowload&id=19YWhArrNccaoSza0CfkXlA8im4-lAGsR
sha: 1b4dd21a6baa5e3523156776970895bd3db6960a
recommended parameter setting: -inputHeight=736, -inputWidth=736;
description: This model is trained on MSRA-TD500, so it can detect both English and Chinese text instances.


- DB_TD500_resnet18.onnx:
url: https://drive.google.com/uc?export=dowload&id=1sZszH3pEt8hliyBlTmB-iulxHP1dCQWV
sha: 8a3700bdc13e00336a815fc7afff5dcc1ce08546
recommended parameter setting: -inputHeight=736, -inputWidth=736;
description: This model is trained on MSRA-TD500, so it can detect both English and Chinese text instances.

这段源代码是一个使用OpenCV库和深度学习模型检测和识别图像中文本的完整示例。下面简要介绍一下这个代码的主要部分。

包含头文件 & 命名空间: 引入了必要的头文件,包括OpenCV库和深度神经网络模块,以及使用标准和OpenCV命名空间。

参数解析: 代码开始部分定义了一些参数,用于存储模型路径、输入图像大小、二值化阈值、多边形阈值,以及其他相关配置。这些参数可以通过命令行提供。

主函数 (main): 解析命令行参数后,加载文本检测模型和文本识别模型,检测模型使用DBNet算法,识别模型使用CRNN算法。

加载词汇: 为识别模型加载词汇表,词汇表通常是字符集文件。

参数设置: 为检测和识别模型设置输入参数,包括缩放比例、输入尺寸以及RGB均值等。

处理输入图像: 读取输入图像,执行文本检测,然后对检测到的文本区域进行识别。

绘制结果: 在图像上绘制检测到的文本多边形,并在每个检测到的文本区域旁边显示识别的结果。

fourPointsTransform 函数: 对于检测到的每个文本区域,使用透视变换校正倾斜的文本,以便在识别模型中使用。

sortPts 函数: 辅助函数用于排序点,可用于未来的功能扩展。

代码的功能总结

从命令行接收输入参数,包括输入图像路径、检测和识别模型的路径,以及其他处理参数。

加载用于文本检测的DBNet模型和用于文本识别的CRNN模型。

对输入图像进行处理,使用检测模型识别文本区域。

对每个检测到的文本区域进行透视变换和裁剪。

使用识别模型对变换后的图块执行文本识别。

在原始图像上绘制检测到的文本区域,并显示识别的文本。

在屏幕上显示结果,并等待按键退出程序。

这段代码的核心在于集成OpenCV的DNN模块以及图像处理技术,实现从图像中自动检测和识别文本的流程。它可以作为一种端到端的文本检测和识别解决方案,在有文本的场景图片中自动识别出文字。

#include <iostream>  // 引用输入输出库
#include <fstream>   // 引用文件操作库
#include <opencv2/core.hpp>  // 引用OpenCV核心功能库
#include <opencv2/imgproc.hpp>  // 引用OpenCV图像处理库
#include <opencv2/highgui.hpp>  // 引用OpenCV高层GUI库
#include <opencv2/dnn/dnn.hpp>  // 引用OpenCV深度学习模块库


using namespace cv;  // 使用OpenCV命名空间
using namespace cv::dnn;  // 使用OpenCV深度学习命名空间


// 定义程序接收的命令行参数
std::string keys =
        "{ help  h                          | | Print help message. }"
        "{ inputImage i                     | td3.png| Path to an input image. Skip this argument to capture frames from a camera. }"
        "{ detModelPath dmp                 |DB_TD500_resnet18.onnx | Path to a binary .onnx model for detection. "
            "Download links are provided in doc/tutorials/dnn/dnn_text_spotting/dnn_text_spotting.markdown}"
        "{ recModelPath rmp                 |crnn_cs_CN.onnx | Path to a binary .onnx model for recognition. "
            "Download links are provided in doc/tutorials/dnn/dnn_text_spotting/dnn_text_spotting.markdown}"
        "{ inputHeight ih                   |736| image height of the model input. It should be multiple by 32.}"
        "{ inputWidth iw                    |736| image width of the model input. It should be multiple by 32.}"
        "{ RGBInput rgb                     |1| 0: imread with flags=IMREAD_GRAYSCALE; 1: imread with flags=IMREAD_COLOR. }"
        "{ binaryThreshold bt               |0.3| Confidence threshold of the binary map. }"
        "{ polygonThreshold pt              |0.5| Confidence threshold of polygons. }"
        "{ maxCandidate max                 |200| Max candidates of polygons. }"
        "{ unclipRatio ratio                |2.0| unclip ratio. }"
        "{ vocabularyPath vp                | alphabet_3944.txt | Path to benchmarks for evaluation. "
            "Download links are provided in doc/tutorials/dnn/dnn_text_spotting/dnn_text_spotting.markdown}";


// 定义用于文档透视变换的函数
void fourPointsTransform(const Mat& frame, const Point2f vertices[], Mat& result);


// 定义用于对点进行排序的函数
bool sortPts(const Point& p1, const Point& p2);


int main(int argc, char** argv)
{
    // 解析命令行参数
    CommandLineParser parser(argc, argv, keys);
    parser.about("Use this script to run an end-to-end inference sample of textDetectionModel and textRecognitionModel APIs\n"
                 "Use -h for more information");
    if (argc == 0 || parser.has("help"))
    {
        parser.printMessage();
        return 0;
    }


    // 提取命令行指定的参数
    float binThresh = parser.get<float>("binaryThreshold");
    float polyThresh = parser.get<float>("polygonThreshold");
    uint maxCandidates = parser.get<uint>("maxCandidate");
    String detModelPath = parser.get<String>("detModelPath");
    String recModelPath = parser.get<String>("recModelPath");
    String vocPath = parser.get<String>("vocabularyPath");
    double unclipRatio = parser.get<double>("unclipRatio");
    int height = parser.get<int>("inputHeight");
    int width = parser.get<int>("inputWidth");
    int imreadRGB = parser.get<int>("RGBInput");


    if (!parser.check())
    {
        parser.printErrors();
        return 1;
    }


    // 加载模型
    CV_Assert(!detModelPath.empty());
    TextDetectionModel_DB detector(detModelPath);  // 加载文本检测模型
    detector.setBinaryThreshold(binThresh)  // 设置二值化阈值
            .setPolygonThreshold(polyThresh)  // 设置多边形阈值
            .setUnclipRatio(unclipRatio)  // 设置非切割比例
            .setMaxCandidates(maxCandidates);  // 设置最大候选数量


    CV_Assert(!recModelPath.empty());
    TextRecognitionModel recognizer(recModelPath);  // 加载文本识别模型


    // 加载词汇表
    CV_Assert(!vocPath.empty());
    std::ifstream vocFile;
    vocFile.open(samples::findFile(vocPath));
    CV_Assert(vocFile.is_open());
    String vocLine;
    std::vector<String> vocabulary;  // 创建用于存储词汇的向量
    while (std::getline(vocFile, vocLine)) {
        vocabulary.push_back(vocLine);
    }
    recognizer.setVocabulary(vocabulary);
    recognizer.setDecodeType("CTC-greedy");


    // 设置检测模型和识别模型的参数
    double detScale = 1.0 / 255.0;
    Size detInputSize = Size(width, height);
    Scalar detMean = Scalar(122.67891434, 116.66876762, 104.00698793);
    detector.setInputParams(detScale, detInputSize, detMean);


    double recScale = 1.0 / 127.5;
    Scalar recMean = Scalar(127.5);
    Size recInputSize = Size(100, 32);
    recognizer.setInputParams(recScale, recInputSize, recMean);


    // 创建窗口用于展示结果
    static const std::string winName = "Text_Spotting";


    // 载入输入图像
    Mat frame = imread(samples::findFile(parser.get<String>("inputImage")));
    std::cout << frame.size << std::endl;


    // 运行文本检测和识别推理
    std::vector< std::vector<Point> > detResults;  // 存储检测结果
    detector.detect(frame, detResults);  // 运行文本检测
    Mat frame2 = frame.clone();  // 克隆原图用于绘制结果


    if (detResults.size() > 0) {
        // 文本识别
        Mat recInput;
        if (!imreadRGB) {
            cvtColor(frame, recInput, cv::COLOR_BGR2GRAY);  // 转换图像到灰度格式
        } else {
            recInput = frame;
        }
        std::vector< std::vector<Point> > contours;
        for (uint i = 0; i < detResults.size(); i++)
        {
            const auto& quadrangle = detResults[i];
            CV_CheckEQ(quadrangle.size(), (size_t)4, "");  // 确保每个检测到的形状是四边形


            contours.emplace_back(quadrangle);


            std::vector<Point2f> quadrangle_2f;
            for (int j = 0; j < 4; j++)
                quadrangle_2f.emplace_back(quadrangle[j]);


            // 执行透视变换和裁剪
            Mat cropped;
            fourPointsTransform(recInput, &quadrangle_2f[0], cropped);


            std::string recognitionResult = recognizer.recognize(cropped);  // 识别文本
            std::cout << i << ": '" << recognitionResult << "'" << std::endl;


            putText(frame2, recognitionResult, quadrangle[3], FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2);  // 在图像上绘制识别结果
        }
        polylines(frame2, contours, true, Scalar(0, 255, 0), 2);  // 绘制检测到的文本轮廓
    } else {
        std::cout << "No Text Detected." << std::endl;
    }
    imshow(winName, frame2);  // 显示结果
    waitKey();  // 等待用户按键


    return 0;  // 程序结束
}


// 文档透视变换函数定义
void fourPointsTransform(const Mat& frame, const Point2f vertices[], Mat& result)
{
    const Size outputSize = Size(100, 32);  // 设置输出图像尺寸


    // 定义透视变换的目标顶点
    Point2f targetVertices[4] = {
        Point(0, outputSize.height - 1),
        Point(0, 0),
        Point(outputSize.width - 1, 0),
        Point(outputSize.width - 1, outputSize.height - 1)
    };
    // 获取透视变换矩阵
    Mat rotationMatrix = getPerspectiveTransform(vertices, targetVertices);


    // 应用透视变换
    warpPerspective(frame, result, rotationMatrix, outputSize);


#if 0
    imshow("roi", result);  // 可以选择展示变换后的区域
    waitKey();
#endif
}


// 点排序函数定义
bool sortPts(const Point& p1, const Point& p2)
{
    return p1.x < p2.x;  // 按照X坐标排序
}

57af8302d1351b43d75ee32d14c7ff1e.png

06fc9efc9923470ca116eea2cec9ead3.png