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之列表操作(list)

    # 列表操作功能汇总 print("列表操作功能汇总") list_demo = ['first', 'second', 'thrid', 'fourth'] # 复制list_d ...

  2. selenium之测试卫星资料页面操作(元素遍历)

    # 测试气象卫星资料页面功能 # author:gongxr # date:2017-07-24 import random, time from selenium import webdriver ...

  3. ZJOI2019 Day1 题解

    想要继续向前,就从克服内心的恐惧开始. 麻将 题意 在麻将中,我们称点数连续的三张牌或三张点数一样的成为面子,称两张点数一样的牌为对子.一副十四张麻将牌的胡牌条件是可以分成四个面子和一个对子或者分成七 ...

  4. SESSION和cookie的使用和区别

    PHP中SESSION和cookie的使用和区别 cookie 是一种在远程浏览器端储存数据并以此来跟踪和识别用户的机制. PHP在http协议的头信息里发送cookie, 因此 setcookie( ...

  5. MT【234】正方形染色(二)

    有$n$个正方形排成一行,今用红,白,黑三种颜色给这$n$个正方形染色,每个正方形只能染一种颜色.如果要求染这三种颜色的正方形都是偶数个,问有多少种不同的染色方法. 解答: 设有$a_n$种不同的染法 ...

  6. 【BZOJ1818】[CQOI2010]内部白点(树状数组,扫描线)

    [BZOJ1818][CQOI2010]内部白点(树状数组,扫描线) 题面 BZOJ 题解 不难发现\(-1\)就是在搞笑的. 那么对于每一行,我们显然可以处理出来最左和最右的点,那么等价于我们在横着 ...

  7. emwin之LISTWHEEL控件之LISTWHEEL_SetPos函数的使用

    @2018-09-05 [小记] LISTWHEEL控件的LISTWHEEL_SetPos函数的使用注意事项: 该函数不能在创建窗口的WM_INIT_DIALOG分支下作为修改LISTWHEEL控件属 ...

  8. luogu3195/bzoj1010 玩具装箱(斜率优化dp)

    推出来式子然后斜率优化水过去就完事了 #include<cstdio> #include<cstring> #include<algorithm> #include ...

  9. 【数学】NOIP数论内容整理

    NOIP数论内容整理 注:特别感谢sdsy的zxy神仙以及lcez的tsr筮安帮助审稿 一.整除: 对于\(a,b~\in~Z\),若\(\exists~k~\in~Z\),\(s.t.~b~=~k~ ...

  10. csp20160904解题报告

    dijkstra+贪心 每次加一个到起始点(首都)距离最小的点. 然后加边,这个最小点必然通过一条边和已加入的某个点相连,在这个最小点与已加入的点相连的边中,选取最短的一条边加入. 证明如下: 前提: ...