天天看點

OpenCV自帶dnn的Example研究(6)— text_detection

這個部落格系列,簡單來說,今天我們就是要研究

https://docs.opencv.org/master/examples.html下的

OpenCV自帶dnn的Example研究(6)— text_detection

6個檔案,看看在最新的OpenCV中,它們是如何發揮作用的。

在配置使用的過程中,需要注意使用較高版本的VS避免編譯器相容問題;由于DNN程式的運作依賴于訓練成功的模型,是以需要預先下載下傳準備;此外如果出現各種報錯,需要對症下藥。

此外,由于需要使用common.hpp檔案,是以需要引入dnn目錄到include中

OpenCV自帶dnn的Example研究(6)— text_detection

用到的資料集都放在:

連結:https://pan.baidu.com/s/1WPoXU3VodErPHZo6Yc21xA 

提取碼:01no 

如果你沒找到,那一定是我忘了。

=====================================================================================友善的分割線============================

對于這個例子,之前我結合tesseract做過一個更好的,這裡就不重複了。将其轉過來:

EAST+Tesseract識别自然場景下發票序号

目前的代碼基本可用,但是需要進行進一步的重構。

獲得所有的rect

OpenCV自帶dnn的Example研究(6)— text_detection

經過篩檢後去掉很多

OpenCV自帶dnn的Example研究(6)— text_detection

裡面就有我需要的。

OpenCV自帶dnn的Example研究(6)— text_detection

那麼這裡的識别還是有一定問題的,主要是east有漏的情況出現。适當進行修正。

OpenCV自帶dnn的Example研究(6)— text_detection

那麼識别的結果主要是兩個entire,一個是前後有多餘字元;二個是可能存在錯誤。

OpenCV自帶dnn的Example研究(6)— text_detection
OpenCV自帶dnn的Example研究(6)— text_detection
OpenCV自帶dnn的Example研究(6)— text_detection
OpenCV自帶dnn的Example研究(6)— text_detection

我認為在現有的識别結果上,應該可以得到進一步的增強。但是需要建立一個“識别和調整”的循環機制,并且對特别是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;

}

繼續閱讀