OpenCV在字符提取中进行的预处理(转)
OCR简介
熟悉OCR的人都了解,OCR大致分为两个部分:
-文字提取text extractor
-文字识别text recognition
其中,第一部分是属于图像处理部分,涉及到图像分割的知识,而第二部分则大多数利用谷歌的Tesseract来进行字符的识别,设计到的东西不多,当然也不难,难的是要能够做到非常准确的识别率,以及它的识别速率。
文字提取
这一部分工作是很关键的,因为文字提取的好坏,直接影响到最后的识别结果,相当于预处理部分,是非常重要的,其主要目的是为了分割出文字字符。
主要涉及工作有:
- : -灰度化
- -锐化
- -Otsu
- -处理0和1边界值
- -如果有必要,还需要进行噪声去除,这里要涉及到找连通分量的相关计算;
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在字符提取中进行的预处理(转)的更多相关文章
- sklearn中的数据预处理和特征工程
小伙伴们大家好~o( ̄▽ ̄)ブ,沉寂了这么久我又出来啦,这次先不翻译优质的文章了,这次我们回到Python中的机器学习,看一下Sklearn中的数据预处理和特征工程,老规矩还是先强调一下我的开发环境是 ...
- OpenCV函数:提取轮廓相关函数使用方法
opencv中提供findContours()函数来寻找图像中物体的轮廓,并结合drawContours()函数将找到的轮廓绘制出.首先看一下findContours(),opencv中提供了两种定义 ...
- 机器学习实战基础(八):sklearn中的数据预处理和特征工程(一)简介
1 简介 数据挖掘的五大流程: 1. 获取数据 2. 数据预处理 数据预处理是从数据中检测,纠正或删除损坏,不准确或不适用于模型的记录的过程 可能面对的问题有:数据类型不同,比如有的是文字,有的是数字 ...
- 《Python CookBook2》 第一章 文本 - 检查字符串中是否包含某字符集合中的字符 && 简化字符串的translate方法的使用
检查字符串中是否包含某字符集合中的字符 任务: 检查字符串中是否出现了某个字符集合中的字符 解决方案: 方案一: import itertools def containAny(seq,aset): ...
- 【剑指Offer学习】【面试题55:字符流中第一个不反复的字符】
题目:请实现一个函数用来找出字符流中第一个仅仅出现一次的字符. 举例说明 比如,当从字符流中仅仅读出前两个字符"go"时.第一个仅仅出现一次的字符是'g'.当从该字符流中读出前六个 ...
- SOM聚类与Voroni图在验证码字符分割中的应用
http://www.docin.com/p-1300981517.html SOM聚类与Voroni图在验证码字符分割中的应用
- C:函数:功能:实现字符数组中所有字母的倒序存放并输出
前两天小测碰到一道题,建立一个函数,功能:实现字符数组中所有字母的倒序存放并输出,一开始觉得简单跟数字数组差不多,运行一下发现很多格式错误,这些是不必要的错误,现在就来说下,先说一下代码思路:定义一个 ...
- 剑指offer——python【第54题】字符流中第一个不重复的字符
题目描述 请实现一个函数用来找出字符流中第一个只出现一次的字符.例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g".当从该字符流中读出 ...
- 剑指offer(54)字符流中第一个不重复的数字
题目描述 请实现一个函数用来找出字符流中第一个只出现一次的字符.例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g".当从该字符流中读出 ...
随机推荐
- IntersectionObserver简介
写在前面 在移动端,有个很重要的概念,叫做懒加载,适用于一些图片资源特别多,ajax数据特别多的页面中,经常会有动态加载数据的场景中,这个时候,我们通常是使用监听scroll或者使用setInterv ...
- html 框架 內聯框架
框架的作用:可以在瀏覽器同時顯示不止一個html頁面.一個html文檔也叫做一個框架. 垂直框架:設置窗口垂直排列顯示成一行 <frameset cols="20%,80%" ...
- Java之"instanceof"和"isInstance"代码举例
源码: /** * @Date:2018-04-20 * @Description:判断Instance * - instanceof方法返回一个boolean类型的值,意在告诉我们对象是不是某个特定 ...
- python之tkinter使用-滚动条
# GUI:tkinter使用 # 通过调节滚动条改变标签中字体大小 import tkinter as tk def resize(ev=None): '''改变label字体大小''' label ...
- C从源码到运行发生了哪些事
一个C/C++程序从源代码到可执行程序主要经历了四个阶段: ①预处理.包括展开宏.处理#include,#if,#ifdef等指令.删除注释.还有一些其他操作.相关命令:gcc -E或cpp ②编译. ...
- python成长之路八 -- 内置函数
1,python内置函数 内置函数 abs() dict() help() min() setattr() all() dir() hex() next() slice() a ...
- day14 装饰器
装饰器 本质上就是函数,功能是为其他函数添加附加功能 原则:不修改被修饰函数的源代码,以及调用方式,即完全不能有任何改变 装饰器 = 高阶函数+ 函数嵌套+ 闭包 高阶函数:函数作为参数或者返回一个函 ...
- 【POJ 3176】Cow Bowling(DP)
题 Description The cows don't use actual bowling balls when they go bowling. They each take a number ...
- 自学Zabbix9.2 zabbix网络发现规则配置详解+实战
点击返回:自学Zabbix之路 点击返回:自学Zabbix4.0之路 点击返回:自学zabbix集锦 自学Zabbix9.2 zabbix网络发现规则配置详解+实战 1. 创建网络发现规则 Conf ...
- luogu3305/bzoj3130 费用流 (二分答案+dinic)
Bob肯定想挑一个流量最大的边,然后把所有的费用都加给它呗 那Alice就让流量最大的边尽量小呗 那就二分一下答案再dinic呗 #include<bits/stdc++.h> #defi ...