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. 线性代数的本质与几何意义 02. 线性组合、张成的空间、基(3blue1brown 咪博士 图文注解版)

    1. 线性组合 接下来我们要换一个角度来看向量.以二维平面直角坐标系为例,i, j 分别是沿 2 个坐标轴方向的单位向量.那么坐标平面上的其他向量,例如 [ 3  -2 ] [3−与 i, j 是什么 ...

  2. golang自定义struct字段标签

    原文链接: https://sosedoff.com/2016/07/16/golang-struct-tags.html struct是golang中最常使用的变量类型之一,几乎每个地方都有使用,从 ...

  3. Leetcode 371.两整数之和 By Python

    不使用运算符 + 和 - ,计算两整数 a .b 之和. 示例 1: 输入: a = 1, b = 2 输出: 3 示例 2: 输入: a = -2, b = 3 输出: 1 思路 比如\(5+6=1 ...

  4. luogu2634 聪聪可可 (树形dp)

    要求出两点间距离==0(mod3) 的数量,然后除以(n*n) 设f[i][j]为i的子树到i的距离==j(mod3)的数量,然后做树形dp即可 因为要最简,所以要求一下gcd,然后除下去 #incl ...

  5. NOIP2018普及组模拟赛

    向老师给的模拟赛,还没普及组难... 题目在洛谷团队里. 第一试三道水题,我46分钟就打完了,然后就AK了. 第二试一看,除了第二题要思考一段时间之外,还是比较水的,但是我得了Rank倒1,115分. ...

  6. window.open打开页面居中显示

    <script type="text/javascript"> function openwindow(url,name,iWidth,iHeight) { var u ...

  7. (转)Spring文件上传,包括一次选中多个文件

    背景: http://www.cnblogs.com/lixuwu/p/8495275.html已经实现了单文件的上传和下载,多文件的上传是另一种情景,这里记录下来 实现过程 先说前台. 运行以后就是 ...

  8. Kafka+Log4j2日志

    默认你已经安装配置了Zookeeper和Kafka. 为了目录清晰,我的Kafka配置文件的Zookeeper部分是:加上了节点用来存放Kafka信息 启动Zookeeper,然后启动Kafka. Z ...

  9. 警告: No data sources are configured to run this SQL and provide advanced code assistance. Disable this inspection via problem menu (Alt+Enter). more... (Ctrl+F1) SQL dialect is not configured. Postgr

    python3出现问题: 警告: No data sources are configured to run this SQL and provide advanced code assistance ...

  10. C语言实现KMP模式匹配算法

    next: /*! * Description: * author scictor <scictor@gmail.com> * date 2018/7/4 */ #include < ...