我認爲在現有的識別結果上,應該能夠獲得進一步的加強。可是須要創建一個「識別和調整」的循環機制,而且對特別是tesseract的參數調節有進一步的認識。
// 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;
}