OCR简介
熟悉OCR的人都了解,OCR大致分为两个部分:

-文字提取text extractor
-文字识别text recognition

其中,第一部分是属于图像处理部分,涉及到图像分割的知识,而第二部分则大多数利用谷歌的Tesseract来进行字符的识别,设计到的东西不多,当然也不难,难的是要能够做到非常准确的识别率,以及它的识别速率。

文字提取
这一部分工作是很关键的,因为文字提取的好坏,直接影响到最后的识别结果,相当于预处理部分,是非常重要的,其主要目的是为了分割出文字字符。
主要涉及工作有:

  1. : -灰度化
  2. -锐化
  3. -Otsu
  4. -处理0和1边界值
  5. -如果有必要,还需要进行噪声去除,这里要涉及到找连通分量的相关计算;
 void TextDetector::segmentText(cv::Mat &spineImage, cv::Mat &segSpine, bool removeNoise){

     cv::Mat spineGray;
cvtColor(spineImage, spineGray, CV_BGR2GRAY);
imshow("gray source" , spineGray);
spineGray = spineGray - 0.5;
// WriteData("/Users/eternity/Desktop/未命名文件夹/gray1.txt", spineGray);
// waitKey();
cv::Mat spineAhe;
adaptiveHistEqual(spineGray, spineAhe, 0.01);
imshow("ahe", spineAhe);
// WriteData("/Users/eternity/Desktop/未命名文件夹/gray2.txt", spineAhe); int window_num = ; double window_h = (spineImage.rows / (double)window_num + 1e-); int window_w = spineImage.cols; cv::Mat spine_th = cv::Mat::zeros(spineGray.size(), CV_8U); for (int i = ; i < window_num; i ++) {
double cut_from_r = window_h * i;
double cut_to_r = window_h * (i+);
cv::Mat window_img = cv::Mat::zeros(Size(cut_to_r-cut_from_r + , window_w), CV_8U);
cv::Rect rect = cv::Rect(, cut_from_r, window_w-, cut_to_r - cut_from_r + );
window_img = cv::Mat(spineGray, rect);
imshow("window section", window_img); sharpenImage(window_img, window_img);
imshow("sharpen", window_img);
// waitKey();
// WriteData("/Users/eternity/Desktop/未命名文件夹/gray4.txt", window_img);
double max_local,min_local;
minMaxLoc(window_img, &min_local, &max_local);
double color_diff = max_local - min_local;
double thresh;
cv::Mat window_tmp;
if (color_diff > )
thresh = threshold(window_img, window_tmp, , , THRESH_OTSU);
else
thresh = ;
// cout<<thresh<<endl;
cv::Mat seg_window(window_img.size(), CV_64F);
imgQuantize(window_img, seg_window, thresh);
// WriteData("/Users/eternity/Desktop/未命名文件夹/quantize2.txt", seg_window);
seg_window = seg_window == ;
// seg_window = seg_window / 255;
//处理0边界值
vector<int> cols1,cols2,rows1,rows2;
findKEdgeFirst(seg_window, , , rows1, cols1);
findKEdgeLast (seg_window, , , rows2, cols2);
float max_zero_dist, max_one_dist;
if(cols1.empty() || cols2.empty())
max_zero_dist = 0.0;
else{
float avg_right = (rows2[]+rows2[]+rows2[]+rows2[]+rows2[]) / (float)sizeof(rows2);
float avg_left = (rows1[]+rows1[]+rows1[]+rows1[]+rows1[]) / (float)sizeof(rows1);
max_zero_dist = avg_right - avg_left;
}
cols1.clear();
cols2.clear();
rows1.clear();
rows2.clear(); //处理1边界值
findKEdgeFirst(seg_window, , , rows1, cols1);
findKEdgeLast (seg_window, , , rows2, cols2);
if(cols1.empty() || cols2.empty())
max_one_dist = ;
else{
float avg_right = (rows2[]+rows2[]+rows2[]+rows2[]+rows2[]) / (float)sizeof(rows2);
float avg_left = (rows1[]+rows1[]+rows1[]+rows1[]+rows1[]) / (float)sizeof(rows1);
max_one_dist = avg_right - avg_left;
}
cols1.clear();
cols2.clear();
rows1.clear();
rows2.clear(); cv::Mat idx;
findNonZero(seg_window, idx);
int one_count = (int)idx.total();
int zero_count = (int)seg_window.total() - one_count; float one_zero_diff = max_one_dist - max_zero_dist;
float dist_limit = ; if(one_zero_diff > dist_limit)
seg_window = ~ seg_window;
else{
if(one_zero_diff > -dist_limit && one_count > zero_count)
seg_window = ~ seg_window;
} seg_window.copyTo(cv::Mat( spine_th, rect));
// imshow("spine_th", spine_th);
// waitKey(); }
//去除噪声
if (removeNoise) {
vector<vector<cv::Point>> contours;
imshow("spine_th", spine_th);
// WriteData("/Users/eternity/Desktop/未命名文件夹/quantize1.txt", spine_th);
// waitKey();
findContours(spine_th, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE); for (int i = ; i < contours.size(); i ++) {
//compute bounding rect
cv::Rect rect = boundingRect(contours[i]);
double bbox_aspect = rect.width / (double)rect.height;
int bbox_area = rect.width * rect.height;
//compute solidity
vector<vector<Point>> hull();
convexHull( contours[i], hull[] );
double convex_area = contourArea(hull[]);
double solidity = bbox_area / convex_area; for (int j = ; j < contours[i].size(); j ++) {
if ( (rect.width > spineImage.cols / 1.001)
|| (rect.width > spineImage.cols / 1.4 && bbox_aspect > 5.0)
|| (rect.height > spineImage.cols / 1.1)
|| (bbox_area < pow(spineImage.cols/, ))
|| (bbox_aspect > 0.5 && bbox_aspect < 1.7 && solidity > 0.9) ) spine_th.at<int>(contours[i][j].x, contours[i][j].y) = ;
// WriteData("/Users/eternity/Desktop/未命名文件夹/quantize2.txt", spine_th);
} } }
segSpine = spine_th;
// transpose(segSpine, segSpine);
// flip(segSpine, segSpine, 0);
imshow("segspine", segSpine);
// waitKey();
spine_th.release(); }
//对图片进行level量化
void TextDetector::imgQuantize(cv::Mat &src, cv::Mat &dst, double level){
dst = cv::Mat::zeros(src.rows, src.cols, CV_8U);
for (int i = ; i < src.rows; i ++) {
uchar *data = src.ptr<uchar>(i);
uchar *data2 = dst.ptr<uchar>(i);
for (int j = ; j < src.cols; j ++) {
if(data[j] <= level)
data2[j] = ;
else
data2[j] = ; }
} }
//找出最左边界处,前edgeValue个值为k的边界值
void TextDetector::findKEdgeFirst(cv::Mat &data, int edgeValue,int k,vector<int> &rows,vector<int> &cols){
int count = ;
for (int i = ; i < data.cols; i ++) {
uchar *u = data.ptr<uchar>(i);
for (int j = ; j < data.rows; j ++) {
if(edgeValue == (int)u[j]){
if(count < k){
count ++;
cols.push_back(i);
rows.push_back(j);
} } }
} }
//找出最右边界处,倒数edgeValue个值为k的边界值
void TextDetector::findKEdgeLast(cv::Mat &data, int edgeValue,int k,vector<int> &rows, vector<int> &cols){
int count = ;
for (int i = data.cols - ; i >= ; i --) {
uchar *u = data.ptr<uchar>(i);
for (int j = data.rows - ; j >= ; j --) {
if(edgeValue == (int)u[j]){
if(count < k){
count ++;
cols.push_back(i);
rows.push_back(j);
} }
} } }
//直方图均衡
void TextDetector::adaptiveHistEqual(cv::Mat &src,cv::Mat &dst,double clipLimit)
{
Ptr<cv::CLAHE> clahe = createCLAHE();
clahe->setClipLimit(clipLimit);
clahe->apply(src, dst);
}

---------------------
作者:eternity1118_
来源:CSDN
原文:https://blog.csdn.net/eternity1118_/article/details/52575374
版权声明:本文为博主原创文章,转载请附上博文链接!

OpenCV在字符提取中进行的预处理(转)的更多相关文章

  1. sklearn中的数据预处理和特征工程

    小伙伴们大家好~o( ̄▽ ̄)ブ,沉寂了这么久我又出来啦,这次先不翻译优质的文章了,这次我们回到Python中的机器学习,看一下Sklearn中的数据预处理和特征工程,老规矩还是先强调一下我的开发环境是 ...

  2. OpenCV函数:提取轮廓相关函数使用方法

    opencv中提供findContours()函数来寻找图像中物体的轮廓,并结合drawContours()函数将找到的轮廓绘制出.首先看一下findContours(),opencv中提供了两种定义 ...

  3. 机器学习实战基础(八):sklearn中的数据预处理和特征工程(一)简介

    1 简介 数据挖掘的五大流程: 1. 获取数据 2. 数据预处理 数据预处理是从数据中检测,纠正或删除损坏,不准确或不适用于模型的记录的过程 可能面对的问题有:数据类型不同,比如有的是文字,有的是数字 ...

  4. 《Python CookBook2》 第一章 文本 - 检查字符串中是否包含某字符集合中的字符 && 简化字符串的translate方法的使用

    检查字符串中是否包含某字符集合中的字符  任务: 检查字符串中是否出现了某个字符集合中的字符 解决方案: 方案一: import itertools def containAny(seq,aset): ...

  5. 【剑指Offer学习】【面试题55:字符流中第一个不反复的字符】

    题目:请实现一个函数用来找出字符流中第一个仅仅出现一次的字符. 举例说明 比如,当从字符流中仅仅读出前两个字符"go"时.第一个仅仅出现一次的字符是'g'.当从该字符流中读出前六个 ...

  6. SOM聚类与Voroni图在验证码字符分割中的应用

    http://www.docin.com/p-1300981517.html SOM聚类与Voroni图在验证码字符分割中的应用  

  7. C:函数:功能:实现字符数组中所有字母的倒序存放并输出

    前两天小测碰到一道题,建立一个函数,功能:实现字符数组中所有字母的倒序存放并输出,一开始觉得简单跟数字数组差不多,运行一下发现很多格式错误,这些是不必要的错误,现在就来说下,先说一下代码思路:定义一个 ...

  8. 剑指offer——python【第54题】字符流中第一个不重复的字符

    题目描述 请实现一个函数用来找出字符流中第一个只出现一次的字符.例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g".当从该字符流中读出 ...

  9. 剑指offer(54)字符流中第一个不重复的数字

    题目描述 请实现一个函数用来找出字符流中第一个只出现一次的字符.例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g".当从该字符流中读出 ...

随机推荐

  1. python 从filelist.txt中拷贝文件到另一文件夹中

    #! python #coding:utf-8 ##!/usr/bin/python # Filename : fileCp.py import sys import os import shutil ...

  2. sakila数据库及其他数据库实例文件

    下载地址: https://dev.mysql.com/doc/index-other.html Other MySQL Documentation This page provides additi ...

  3. String的实例化与static final修饰符

    String两种实例化方式 一种是通过双引号直接赋值的方式,另外一种是使用标准的new调用构造方法完成实例化.如下: String str = "abcd"; String str ...

  4. MT【29】介绍向量的外积及应用举例

    我们在学校教材里学到的数量积(内积)其实还有一个孪生兄弟向量积(外积),这个对参加自主招生以及竞赛的学生来讲是需要掌握的,这里稍作介绍: 原理: 例题: 应用:

  5. 03 Zabbix4.0添加cisco交换机基本监控步骤

    点击返回:自学Zabbix之路 点击返回:自学Zabbix4.0之路 点击返回:自学zabbix集锦 03 Zabbix4.0添加cisco交换机基本监控步骤 主题监控一台cisco网络设备的6项内容 ...

  6. 学习Spring Boot:(二十二)使用 AOP

    前言 AOP 1,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.基于AOP实现的功能不会破坏原来程序逻辑,因此它可以很好的对业务逻辑的各个部分进行隔离,从而使得业 ...

  7. 【BZOJ1064】【NOI2008】假面舞会(图论,搜索)

    题面 Description 一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会.今年的面具都是主办方特别定制的.每个参加舞会的人都可以在入场时选择一个自己喜欢的面 具.每个面具都有一个编号 ...

  8. Sharepoint 性能之SQL Server内存设置

    In this article, let's understand the Minimum and Maximum server memory settings of SQL Server. The ...

  9. Azure KeyVault设置策略和自动化添加secrets键值对

    一. 关于Azure Key Vault Azure 密钥保管库可帮助保护云应用程序和服务使用的加密密钥和机密. 借助 Key Vault,可使用密钥来加密密钥和机密(例如身份验证密钥.存储帐户密钥. ...

  10. Java关键字synchronized详解

    Java多线程thread互联网制造  synchronized 关键字,代表这个方法加锁,相当于不管哪一个线程A每次运行到这个方法时,都要检查有没有其它正在用这个方法的线程B(或者C D等),有的话 ...