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. WebService概述

    一.WebService介绍 什么是WebService? 一言以蔽之:WebService是一种跨编程语言和跨操作系统平台的远程调用技术. 所谓跨编程语言和跨操作平台,就是说服务端程序采用java编 ...

  2. SpringBoot(十七)_springboot跨域处理

    本文转自:Vi的技术博客 什么是跨域 首先,我们需要了解一下一个URL是怎么组成的: // 协议 + 域名(子域名 + 主域名) + 端口号 + 资源地址 http: + // + www.baidu ...

  3. springMVC 使用WebApplicationContext获取ApplicationContext对象

    主要用于从application中获取bean 1.applicationContext 在web.xml中使用listener配置 <context-param> <param-n ...

  4. VS2017+WIN10自动生成类、接口的说明(修改类模板的方法)

    微软发布VS2017的时候,我第一时间离线一份专业版,安装到了自己的电脑上,开始体验,但是问题来了,在开发中建立类和接口的时候,说 明注释总要自己写一次,烦!~~于是还是像以前一样改IDE默认的类和接 ...

  5. 画caffe训练loss曲线

    Linux下操作 1. 将loss值存储到lossInf.txt中 fName1='loss.txt' cat loss.log | grep "solver.cpp:218] Iterat ...

  6. BZOJ3425[POI2013]Polarization——DP+bitset+分块

    题目描述 Everyone knew it would only be a matter of time. So what? Faced for years on, a peril becomes t ...

  7. BZOJ4378[POI2015]Logistyka——树状数组

    题目描述 维护一个长度为n的序列,一开始都是0,支持以下两种操作:1.U k a 将序列中第k个数修改为a.2.Z c s 在这个序列上,每次选出c个正数,并将它们都减去1,询问能否进行s次操作.每次 ...

  8. UVALive5870-Smooth Visualization-模拟水题

    很水的模拟题,拿数组搞就好了. 注意边界的地方不要算重. #include <cstdio> #include <cstring> #include <algorithm ...

  9. ecplise debug 无法命中断点 一直在加载中

    发生原因:可能是特殊关闭了Ecplise 导致 1.这个是没问题的,网上大部分都说这个问题 2.删除所有断点再来(试了无效) 3.删除  X:\workspace\.metadata\.plugins ...

  10. 【洛谷P1073】最优贸易

    题目大意:给定一个 N 个点,M 条边(存在反向边)的有向图,点有点权,求一条从 1 到 N 的路径上,任意选出两个点 p,q (p 在前,q在后),两点点权的差值最大. 根据最短路的 dp 思想,可 ...