这个博客系列,简单来说,今天我们就是要研究

6个文件,看看在最新的OpenCV中,它们是如何发挥作用的。
在配置使用的过程中,需要注意使用较高版本的VS避免编译器兼容问题;由于DNN程序的运行依赖于训练成功的模型,因此需要预先下载准备;此外如果出现各种报错,需要对症下药。
此外,由于需要使用common.hpp文件,所以需要引入dnn目录到include中
用到的数据集都放在:
链接:https://pan.baidu.com/s/1WPoXU3VodErPHZo6Yc21xA 
提取码:01no 
如果你没找到,那一定是我忘了。
=====================================================================================友善的分割线============================
对于这个例子,之前我结合tesseract做过一个更好的,这里就不重复了。将其转过来:

EAST+Tesseract识别自然场景下发票序号
目前的代码基本可用,但是需要进行进一步的重构。

获得所有的rect
经过筛检后去掉很多
里面就有我需要的。

那么这里的识别还是有一定问题的,主要是east有漏的情况出现。适当进行修正。

那么识别的结果主要是两个entire,一个是前后有多余字符;二个是可能存在错误。




我认为在现有的识别结果上,应该可以得到进一步的增强。但是需要建立一个“识别和调整”的循环机制,并且对特别是tesseract的参数调节有进一步的认识。


重新编译了OpenCV4,并且对代码重构,看上去效果非常不错:
// EAST+Tesseract实现自然场景下发票编码识别
// by jsxyhelu.cnblogs.com
#include "pch.h"
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgproc/imgproc_c.h>
#include <opencv2/dnn.hpp>
#include <allheaders.h> // leptonica main header for image io
#include <baseapi.h> // tesseract main header
using namespace std;
using namespace cv;
using namespace cv::dnn;
using namespace std;
//对east的结果进行解码
void decode(const Mat& scores, const Mat& geometry, float scoreThresh,
    std::vector<RotatedRect>& detections, std::vector<float>& confidences)
{
    detections.clear();
    CV_Assert(scores.dims == 4); CV_Assert(geometry.dims == 4); CV_Assert(scores.size[0] == 1);
    CV_Assert(geometry.size[0] == 1); CV_Assert(scores.size[1] == 1); CV_Assert(geometry.size[1] == 5);
    CV_Assert(scores.size[2] == geometry.size[2]); CV_Assert(scores.size[3] == geometry.size[3]);
    const int height = scores.size[2];
    const int width = scores.size[3];
    for (int y = 0; y < height; ++y)
    {
        const float* scoresData = scores.ptr<float>(0, 0, y);
        const float* x0_data = geometry.ptr<float>(0, 0, y);
        const float* x1_data = geometry.ptr<float>(0, 1, y);
        const float* x2_data = geometry.ptr<float>(0, 2, y);
        const float* x3_data = geometry.ptr<float>(0, 3, y);
        const float* anglesData = geometry.ptr<float>(0, 4, y);
        for (int x = 0; x < width; ++x)
        {
            float score = scoresData[x];
            if (score < scoreThresh)
                continue;
            // Decode a prediction.
            // Multiple by 4 because feature maps are 4 time less than input image.
            float offsetX = x * 4.0f, offsetY = y * 4.0f;
            float angle = anglesData[x];
            float cosA = std::cos(angle);
            float sinA = std::sin(angle);
            float h = x0_data[x] + x2_data[x];
            float w = x1_data[x] + x3_data[x];
            Point2f offset(offsetX + cosA * x1_data[x] + sinA * x2_data[x],
                offsetY - sinA * x1_data[x] + cosA * x2_data[x]);
            Point2f p1 = Point2f(-sinA * h, -cosA * h) + offset;
            Point2f p3 = Point2f(-cosA * w, sinA * w) + offset;
            RotatedRect r(0.5f * (p1 + p3), Size2f(w, h), -angle * 180.0f / (float)CV_PI);
            detections.push_back(r);
            confidences.push_back(score);
        }
    }
}
int main()
{
    //参数和常量准备
    String model = "./frozen_east_text_detection.pb";
    std::vector<Mat> outs;
    std::vector<String> outNames(2);
    outNames[0] = "feature_fusion/Conv_7/Sigmoid";
    outNames[1] = "feature_fusion/concat_3";
    Mat  blob;
    std::vector<RotatedRect> boxes;
    std::vector<float> confidences;
    std::vector<int> indices;
    char cbuf[255];
    // 引入EAST model
    Net net = readNet(model);
    //对tesseract进行初始化操作
    tesseract::TessBaseAPI tess;
    if (tess.Init("E:\\sandbox\\新建文件夹\\tessdata", "eng"))
    {
        std::cout << "OCRTesseract: Could not initialize tesseract." << std::endl;
        return 1;
    }
    Mat src = imread("E:\\未来项目\\(15)微模式ocr\\发票图片\\2.png");
    Mat board = src.clone();//用于显示图片
    blobFromImage(src, blob, 1.0, Size(320, 320), Scalar(), true, false);//Scalar采用默认是设置
    net.setInput(blob);
    net.forward(outs, outNames);
    Mat scores = outs[0];
    Mat geometry = outs[1];
    decode(scores, geometry, 0.5, boxes, confidences);//注意0.5是超参数
    NMSBoxes(boxes, confidences, 0.5, 0.4, indices);
    Point2f ratio((float)src.cols / 320, (float)src.rows / 320);//缩放比例
    //获得最终框选结果
    for (size_t i = 0; i < indices.size(); ++i)
    {
        RotatedRect& box = boxes[indices[i]];    
        Point2f vertices[4];
        box.points(vertices);
        for (int j = 0; j < 4; ++j)
        {
            vertices[j].x *= ratio.x;
            vertices[j].y *= ratio.y;
        }
        Point2f* lastItemPointer = (vertices + sizeof vertices / sizeof vertices[0]);
        vector<Point2f> contour(vertices, lastItemPointer);
        //筛选出所有矩形中中心点y值小于整个图像1/6的举行,绘制最小外接矩形
        Rect boundRect = boundingRect(Mat(contour));
        //对rect适当进行扩充
        boundRect = cv::Rect(boundRect.tl().x - 5, boundRect.tl().y, boundRect.width + 10, boundRect.height);
        if (boundRect.y < src.rows / 6)
        {
            Mat roi = src(boundRect);
            //绘制外接边线
            for (int j = 0; j < 4; ++j)
                line(board, vertices[j], vertices[(j + 1) % 4], Scalar(0, 255, 0), 1);
            rectangle(board, boundRect, Scalar(0, 0, 255));//绘制外接最小矩形
            //打印数据
            sprintf_s(cbuf, "E:\\未来项目\\(15)微模式ocr\\发票图片\\roi\\%d.jpg", i);//打印出来
            imwrite(cbuf, roi);
            //将切割出来的图片输入tesseract中
            auto pixs = pixRead(cbuf);
            if (!pixs)
            {
                std::cout << "Cannot open input file: " << std::endl;
                return 1;
            }
            // recognize
            tess.SetImage(pixs);
            tess.Recognize(0);
            // get result and delete[] returned char* string
            std::cout << std::unique_ptr<char[]>(tess.GetUTF8Text()).get() << std::endl;
            putText(board, std::unique_ptr<char[]>(tess.GetUTF8Text()).get(), boundRect.tl(), 1, 1.0f, Scalar(0, 255, 0));
            // cleanup
            tess.Clear();
            pixDestroy(&pixs);
        }
    }
    imshow("board", board);
    cv::waitKey();
    getchar();
    return 0;
}

OpenCV自带dnn的Example研究(6)— text_detection的更多相关文章

  1. OpenCV自带dnn的Example研究(1)— classification

    这个博客系列,简单来说,今天我们就是要研究 https://docs.opencv.org/master/examples.html下的 6个文件,看看在最新的OpenCV中,它们是如何发挥作用的. ...

  2. OpenCV自带dnn的Example研究(2)— colorization

    这个博客系列,简单来说,今天我们就是要研究 6个文件,看看在最新的OpenCV中,它们是如何发挥作用的. 在配置使用的过程中,需要注意使用较高版本的VS避免编译器兼容问题:由于DNN程序的运行依赖于训 ...

  3. OpenCV自带dnn的Example研究(3)— object_detection

    这个博客系列,简单来说,今天我们就是要研究 https://docs.opencv.org/master/examples.html下的 6个文件,看看在最新的OpenCV中,它们是如何发挥作用的. ...

  4. OpenCV自带dnn的Example研究(4)— openpose

    这个博客系列,简单来说,今天我们就是要研究 https://docs.opencv.org/master/examples.html下的 6个文件,看看在最新的OpenCV中,它们是如何发挥作用的. ...

  5. OpenCV自带dnn的Example研究(5)— segmentation

    这个博客系列,简单来说,今天我们就是要研究 https://docs.opencv.org/master/examples.html下的 6个文件,看看在最新的OpenCV中,它们是如何发挥作用的. ...

  6. OpenCv dnn模块扩展研究(1)--style transfer

    一.opencv的示例模型文件   使用Torch模型[OpenCV对各种模型兼容并包,起到胶水作用], 下载地址: fast_neural_style_eccv16_starry_night.t7 ...

  7. 使用opencv自带的融合函数

    [wiki,blog]使用opencv自带的融合函数 [wiki,blog]使用opencv自带的融合函数 /*M/////////////////////////////////////////// ...

  8. 如何利用OpenCV自带的级联分类器训练程序训练分类器

    介绍 使用级联分类器工作包括两个阶段:训练和检测. 检测部分在OpenCVobjdetect 模块的文档中有介绍,在那个文档中给出了一些级联分类器的基本介绍.当前的指南描述了如何训练分类器:准备训练数 ...

  9. 【计算机视觉】如何使用opencv自带工具训练人脸检测分类器

    前言 使用opencv自带的分类器效果并不是很好,由此想要训练自己的分类器,正好opencv有自带的工具进行训练.本文就对此进行展开. 步骤 1.查找工具文件: 2.准备样本数据: 3.训练分类器: ...

随机推荐

  1. FPGA的年龄

    FPGA的年龄 1984年,Xilinx公司发布了第一个FPGA(但直到1985年这些器件才真正发货).尽管这些器件比当时那些简单的可编程逻辑器件(PLD)复杂的多,但大多数数字设计工程师却仅仅用这些 ...

  2. java流类基础练习。

    总结:BufferedReader.InputStreamReader.字节与字符的区别. package com.ds; import java.io.*; //从控制台输入字符串,输到sdd时停止 ...

  3. kafka集群安装和kafka-manager

    1.软件环境 (3台服务器-测试)10.11.12.31 mykafka110.11.12.32 mykafka210.11.12.33 mykafka3 [root@localhost ~]# ca ...

  4. 转:InnoDB多版本(MVCC)实现简要分析

    InnoDB多版本(MVCC)实现简要分析 基本知识 假设对于多版本(MVCC)的基础知识,有所了解.InnoDB为了实现多版本的一致读,采用的是基于回滚段的协议. 行结构 InnoDB表数据的组织方 ...

  5. 基于OpenCV的火焰检测(三)——HSI颜色判据

    上文向大家介绍了如何用最简单的RGB判据来初步提取火焰区域,现在我要给大家分享的是一种更加直观的判据--HSI判据. 为什么说HSI判据是更加直观的判据呢?老规矩,先介绍一下HSI色彩模型: HSI颜 ...

  6. 2015.3.5 VS2005调用VC6 dll 时结构参数的传递

    结构只能以地址方式进行传递,dll只能传递结构不能传递类 VS端: [DllImport(@"D:\程序\VC程序\MfcDllspace\Debug\space.dll")] p ...

  7. 如何成功且顺序的进入centos系统的安全模式?(图文详解)

    说白了,这个很简单! 见 -bash : ** : command not found的问题解决(图文详解)

  8. 【277】◀▶ Python 列表/元组/字典说明

    目录: 前言 一.访问列表中的值 二.更新列表 三.删除列表元素 四.Python 列表脚本操作符 五.Python 列表函数 & 方法 参考:Python 列表(List)使用说明 列表截取 ...

  9. import requests

  10. python笔记--3--函数、生成器、装饰器、函数嵌套定义、函数柯里化

    函数 函数定义语法: def 函数名([参数列表]): '''注释''' 函数体 函数形参不需要声明其类型,也不需要指定函数返回值类型 即使该函数不需要接收任何参数,也必须保留一对空的圆括号 括号后面 ...