opencv实践-OCR识别c++版

1. OCR 简介

OCR (Optical Character Recognition,光学字符识别)是指电子设备(例如扫描仪或数码相机)检查纸上打印的字符,通过检测暗、亮的模式确定其形状,然后用字符识别方法将形状翻译成计算机文字的过程;即,针对印刷体字符,采用光学的方式将纸质文档中的文字转换成为黑白点阵的图像文件,并通过识别软件将图像中的文字转换成文本格式,供文字处理软件进一步编辑加工的技术。如何除错或利用辅助信息提高识别正确率,是OCR最重要的课题,ICR(Intelligent Character Recognition)的名词也因此而产生。衡量一个OCR系统性能好坏的主要指标有:拒识率、误识率、识别速度、用户界面的友好性,产品的稳定性,易用性及可行性等。

2. 实现步骤

  • 灰度化处理:
  • 采用高斯滤波去噪
  • 边缘检测
  • 查找轮廓并排序:
  • 绘制全部轮廓
  • 多边形逼近
  • 仿射变换
  • 写入图片文件
  • 扫描读取
  • 写入本地

3. 代码实现

识别图片:
在这里插入图片描述
源码:

#include <opencv2/opencv.hpp>
#include <vector>
#include <map>
#include <string>
#include "tesseract/baseapi.h"
#include "leptonica\allheaders.h"

using namespace cv;
using namespace std;

void order_point(const std::vector<cv::Point>& inPots, std::vector<cv::Point>& outPots)
{
    
    
	// 左上的坐标一定是x, y加起来最小的坐标.
	// 右下的坐标一定是x, y加起来最大的坐标.
	// 右上角的x, y相减的差值一定是最小的.
	// 左下角的x, y相减的差值, 一定是最大.
	// diff的作用是后一列减前一列得到的差值 y - x
	int index = 0;
	map<int, int> sum_dict;
	map<int, int> diff_dict;
	for (auto& p : inPots)
	{
    
    
		int sum = p.x + p.y;
		int diff = p.y - p.x;
		sum_dict.insert(std::make_pair(sum, index));
		diff_dict.insert(std::make_pair(diff, index));
		index++;
	}
	outPots[0] = inPots.at(sum_dict.begin()->second);
	outPots[1] = inPots.at(diff_dict.begin()->second);
	outPots[2] = inPots.at(sum_dict.rbegin()->second);
	outPots[3] = inPots.at(diff_dict.rbegin()->second);
}

cv::Mat four_point_transform(const cv::Mat &image, const std::vector<cv::Point> &pts)
{
    
    
	std::vector<cv::Point> rect(4);
	order_point(pts, rect);
	cv::Point lt, rt, rb, lb;
	lt = rect.at(0);
	rt = rect.at(1);
	rb = rect.at(2);
	lb = rect.at(3);

	//空间中两点的距离,并且要取最大的距离确保全部文字都看得到
	int widthA = sqrt(pow((rb.x - lb.x), 2) + pow((rb.y - lb.y), 2));
	int widthB = sqrt(pow((rt.x - lt.x), 2) + pow((rt.y - lt.y), 2));
	int max_width = max(widthA, widthB);

	int heightA = sqrt(pow((rt.x - rb.x), 2) + pow((rt.y - rb.y), 2));
	int heightB = sqrt(pow((lt.x - lb.x), 2) + pow((lt.y - lb.y), 2));
	int max_height = max(heightA, heightB);

	//计算变换矩阵
	Point2f AffinePoints0[4] = {
    
     Point2f(lt.x, lt.y), Point2f(rt.x, rt.y), Point2f(rb.x, rb.y), Point2f(lb.x, lb.y) };
	Point2f AffinePoints1[4] = {
    
     Point2f(0, 0), Point2f(max_width, 0), Point2f(max_width, max_height), Point2f(0, max_height) };

	Mat Trans = cv::getPerspectiveTransform(AffinePoints0, AffinePoints1);
	cv::Mat dst;
	cv::warpPerspective(image, dst, Trans, Size(max_width, max_height), INTER_CUBIC);

	return dst;
}

cv::Mat Image_Pretreatment(cv::Mat image)
{
    
    
	// 计算比例.限定高度500
	// 此时像素点都缩小了一定的比例,进行放射变换时要还原
	float ratio = image.rows / 500.0;
	// 拷贝一份
	cv::Mat image_copy = image.clone();
	// 修改尺寸
	cv::resize(image_copy, image, cv::Size(image.cols / ratio, 500));
	//cv::imshow("image", image);
	// 图片预处理
	
	// 灰度化处理
	cv::Mat gray, Gaussian, edged;
	cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
	//cv::imshow("gray", gray);
	// 高斯平滑
	cv::GaussianBlur(gray, Gaussian, Size(5, 5), 0);
	// cv_show('Gaussian', Gaussian)
	// 边缘检测,寻找边界(为后续查找轮廓做准备)
	cv::Canny(Gaussian, edged, 70, 200);
	//cv::imshow("edged", edged);
	// 查找轮廓
	std::vector<std::vector<cv::Point>> cnts;
	cv::findContours(edged, cnts, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
	// 将轮廓按照面积降序排序
	sort(cnts.begin(), cnts.end(), [&](std::vector<cv::Point> &v1, std::vector<cv::Point> &v2) {
    
    
		return cv::contourArea(v1) > cv::contourArea(v2);
		});
	
	// 绘制所有轮廓
	cv::Mat image_contours = image.clone();
	cv::drawContours(image_contours, cnts, -1, (0, 0, 255), 1);
	//cv::imshow("image_contours", image_contours);
	image_contours.release();

	vector<cv::Point> screen_cnt;
	// 遍历轮廓找出最大的轮廓.
	for (auto& c : cnts) {
    
    
		// 计算轮廓周长
		int perimeter = cv::arcLength(c, true);
		// 多边形逼近,得到近似的轮廓
		// 近似完后,只剩下四个顶点的角的坐标
		vector<cv::Point> approx(c.size());
		cv::approxPolyDP(c, approx, 0.02 * perimeter, true);
		// 最大的轮廓
		if (approx.size() == 4) {
    
    
			// 接收approx
			screen_cnt = approx;
			break;
		}
	}
	// 画出多边形逼近
	cv::Mat image_screen_cnt = image.clone();
	cv::drawContours(image_screen_cnt, std::vector< vector<cv::Point>>{
    
    screen_cnt}, -1, (0, 0, 255), 1);
	//cv::imshow("image_screen_cnt", image_screen_cnt);
	image_screen_cnt.release();
	// 进行仿射变换,使图片变正
	for (auto& scr_t : screen_cnt)
	{
    
    
		scr_t.x *= ratio;
		scr_t.y *= ratio;
	}
	cv::Mat warped = four_point_transform(image_copy, screen_cnt);
	//cv::imshow("warped", warped);
	// 二值处理,先转成灰度图
	cv::Mat warped_gray;
	cv::cvtColor(warped, warped_gray, cv::COLOR_BGR2GRAY);
	// 再二值化处理
	cv::Mat ref;
	cv::threshold(warped_gray, ref, 150, 255, cv::THRESH_BINARY);
	//cv::namedWindow("ref", WINDOW_NORMAL);
	//cv::imshow("ref", ref);
	//cv::waitKey(0);

	return ref;
}

int main()//ocr_demo()
{
    
    
	// 读取图片
	cv::Mat image = cv::imread("C:\\Users\\Administrator\\Desktop\\1.jpg");
	
	// 返回透视变换的结果
	cv::Mat ref = Image_Pretreatment(image);
	// 把处理好的图片写入图片文件.
	cv::imwrite("ref.jpg", ref);
	
	char* outText;
	// Open input image with leptonica library
	Pix* img_pix = pixRead("ref.jpg");
	api->SetImage(img_pix);
	// Get OCR result
	outText = api->GetUTF8Text();
	printf("OCR output:\n%s", outText);

	// Destroy used object and release memory
	api->End();
	delete api;
	delete[] outText;
	pixDestroy(&img_pix);

	return 0;
}

4. 待完成

上述代码中的ocr识别部分,由于tesseract库一直没调通,因此没做验证,后期会补上。

本文参考:
https://blog.csdn.net/weixin_56197703/article/details/124309367

猜你喜欢

转载自blog.csdn.net/wyw0000/article/details/129871169