本例仅以本人浅薄理解,妄想自制文字识别程序,实际在识别部分未有完善。

<?php
class readChar{
private $imgSize; //图片尺寸
private $imgGd2; //图像转GD2
private $Index=array(); //颜色索引(key即为颜色索引)
private $bigColor; //二维图像颜色值(存储索引)
function __construct($imgPath){
$this->imgSize=getimagesize($imgPath);
$this->imgSize['size']=$this->imgSize[0]*$this->imgSize[1];
$this->imgGd2=imagecreatefromstring(file_get_contents($imgPath));
if (imageistruecolor($this->imgGd2)) {
imagetruecolortopalette($this->imgGd2, false, 256);//真彩图片转换为调色板
}
$this->setGray();
}
function __destruct(){
imagedestroy($this->imgGd2);
}
private function showImg(){
foreach($this->Index as $k=>$v){
imagecolorset($this->imgGd2,$k,$v,$v,$v);
}
header('Content-type: image/jpg');
imagejpeg($this->imgGd2);
exit;
}
private function setGray(){
/*
灰度化
RGB均值/RGB单值/最大/最小/人性化:0.3R+0.59G+0.11B
bug:若灰度值相等的两个颜色,刚好是主要颜色 则会识别不出来
*/
for($i=ImageColorstotal($this->imgGd2)-1;$i>=0;$i--){
$rgb=ImageColorsForIndex($this->imgGd2,$i);
$this->Index[$i]=(int)(($rgb['red']+$rgb['green']+$rgb['blue'])/3); //imagecolorset改变索引颜色
}
$this->bigColor=array();
$pro=array(); //各灰度值占比
for($x=0;$x<$this->imgSize[0];$x++){
$this->bigColor[$x]=array();
for($y=0;$y<$this->imgSize[1];$y++){
$Index=ImageColorAt($this->imgGd2, $x, $y);
$this->bigColor[$x][$y]=$Index;
$pro[$this->Index[$Index]]=@$pro[$this->Index[$Index]]+1;
}
}
array_walk($pro,function(&$v){$v=$v/$this->imgSize['size'];});
$this->setTwo($pro); }
private function setTwo($pro){
/*
二值化 T很重要
以T为阈值,低于T的为白否则为黑
双峰法
迭代法:
OSTU(大津法):不懂
前景和背景的分割阈值记作T,前景像素点数占比为ω0,平均灰度μ0;背景像素点数占比例ω1,平均灰度为μ1。
总平均灰度记为μ
类间方差记假设图像的背景较暗,并且图像的大小为M×N,灰度值小于阈值T的像素数为N0,大于阈值T的像素数为N1
则有:
       ω0=N0/ M×N (1)
       ω1=N1/ M×N (2)
       N0+N1=M×N (3)
       ω0+ω1=1    (4)
       μ=ω0*μ0+ω1*μ1 (5)
g=ω0(μ0-μ)^2+ω1(μ1-μ)^2 (6)
将式(5)代入式(6),得到等价公式: g=ω0ω1(μ0-μ1)^2
类间方差g最大时的阈值T,即为所求
P分位法:需已知目标占图像的比例,以不同灰度值进行分割若比例≈P 则T为该灰度值
*/
$T=127;
$g_max=0;
for ($i=0;$i<256;$i++){
$w0 = $w1 = $u0_temp = $u1_temp = $u0 = $u1 = $g_tmp = 0;
for ($j=0;$j<256;$j++){
if ($j <= $i){ //背景部分
$w0 += @$pro[$j];
$u0_temp += $j * @$pro[$j];
}else{ //前景部分
$w1 += @$pro[$j];
$u1_temp += $j * @$pro[$j];
}
}
$u0 = $w0==0?0:$u0_temp / $w0;
$u1 = $w1==0?0:$u1_temp / $w1;
$g_tmp =$w0 *$w1* pow(($u0 - $u1), 2);//类间方差 g=w0*w1*(u0-u1)^2
if ($g_tmp > $g_max){
$g_max = $g_tmp;
$T = $i;
}
}
for($x=0;$x<$this->imgSize[0];$x++){
for($y=0;$y<$this->imgSize[1];$y++){
$index = $this->bigColor[$x][$y];
if($this->Index[$index]<=$T){
$this->Index[$index]=0;
}else{
$this->Index[$index]=255;
}
}
}
$this->avgFilter();
}
private function avgFilter(){
/*
代码不实现
均值滤波器、自适应维纳滤波器、中值滤波器、形态学噪声滤除器、小波去噪
滤波前对于图片边界:不处理/填充0 or 255/填充临近灰度值
*/
return $this->getChar(); }
private function getChar(){
/*
拆字
*/
$pointTotal=array(); //Y轴统计
for($x=0;$x<$this->imgSize[0];$x++){
for($y=0;$y<$this->imgSize[1];$y++){
@$pointTotal[$y]+=$this->Index[$this->bigColor[$x][$y]]>0?0:1;
}
}
$chars=array(); //Y轴划线
$prev = $pointTotal[0];
$tmpLine=array();
foreach($pointTotal as $k=>$v){
if($v==0 && $prev!=0){
//imageline ($this->imgGd2,0,$k,$this->imgSize[0]-1,$k,0);//划线 对程序无用
$tmpLine[]=$k;
}elseif($v!=0 && $prev==0){
//imageline ($this->imgGd2,0,$k-1,$this->imgSize[0]-1,$k,0);//划线 对程序无用
$tmpLine[]=$k-1;
}
$prev=$v;
if(count($tmpLine)==2){
$chars[]=$tmpLine;
$tmpLine=array();
}
}
if(!$chars){
//imageline ($this->imgGd2,0,0,$this->imgSize[0]-1,0,0);//划线 对程序无用
//imageline ($this->imgGd2,0,$this->imgSize[1]-1,$this->imgSize[0]-1,$this->imgSize[1]-1,0);//划线 对程序无用
$chars []=array(0,$this->imgSize[1]-1);
}
foreach($chars as $line=>$ypoint){
$pointTotal=array();//每行的X轴统计
for($x=0;$x<$this->imgSize[0];$x++){
$pointTotal[$x]=0;
for($y=$ypoint[0];$y<=$ypoint[1];$y++){
$pointTotal[$x]+=$this->Index[$this->bigColor[$x][$y]]>0?0:1;
}
}
$xLine=array();
$tmpLine=array();//每行X轴划线
$prev = $pointTotal[0];
foreach($pointTotal as $k=>$v){
if($v==0 && $prev!=0){
//imageline ($this->imgGd2,$k,$ypoint[0],$k,$ypoint[1],0);//划线 对程序无用
$tmpLine[]=$k-1;
}
if($v!=0 && $prev==0){
//imageline ($this->imgGd2,$k-1,$ypoint[0],$k-1,$ypoint[1],0);//划线 对程序无用
$tmpLine[]=$k;
}
if(count($tmpLine)==2){
$xLine[]=$tmpLine;
$tmpLine=array();
}
$prev=$v;
}
foreach($xLine as $k=>$v){
$v['xcode']=$v['ycode']=array();
for($x=$v[0];$x<=$v[1];$x++){
for($y=$ypoint[0];$y<=$ypoint[1];$y++){
$gry = $this->Index[$this->bigColor[$x][$y]]>0?0:1;
@$v['xcode'][$x-$v[0]] +=$gry;
@$v['ycode'][$y-$ypoint[0]] +=$gry;
}
}
$xLine[$k]=$v;
}
$chars[$line]['xline']=$xLine;
}
$this->bigColor=null;
foreach($chars as $v){
foreach($v['xline'] as $vv){
$this->tranChar($vv['xcode'],$vv['ycode']);
}
}
}
private function tranChar($myX,$myY){
/*
识别文字
本例用到的php自带函数 similar_text
通过把每个字x和y轴做映射,然后和模板做相似度匹配(模板图为50x50所以需将映射做压缩处理)
*/
$tplx='0,0,0,0,0,0,0,0,12,22,30,34,23,16,13,11,10,8,8,8,8,8,8,6,6,6,6,8,8,7,8,9,10,10,12,14,20,34,30,26,16,0,0,0,0,0,0,0,0,0';
$tply='9,14,17,15,11,10,10,8,8,8,9,8,8,7,8,8,7,8,7,8,8,8,8,8,8,8,8,8,8,8,8,7,8,7,8,8,7,8,8,8,8,9,8,9,10,12,15,17,13,9';
$diff=count($myX)-count($myY);
$middle = (int)(abs($diff)/2);
if($diff<0){
$minMy=&$myX;
}else{
$minMy=&$myY;
}
for($i=0;$i<abs($diff);$i++){
if($i<$middle){
array_unshift($minMy,0);
continue;
}
array_push($minMy,0);
}
$ratio = 50/count($myX);
$newX=array();
$newY=array();
foreach($myX as $k=>$v){
$key = min(ceil($k*$ratio),49);
is_array(@$newX[$key]) || $newX[$key]=array();
is_array(@$newY[$key]) || $newY[$key]=array();
$newX[$key][]=$myX[$k];
$newY[$key][]=$myY[$k];
}
array_walk($newY,function(&$v){$v=round(array_sum($v)/count($v));});
array_walk($newX,function(&$v){$v=round(array_sum($v)/count($v));}); $sx=similar_text(implode(',',$newX),$tplx);
$sy=similar_text(implode(',',$newY),$tply);
echo 'X:'.$sx.'/'.strlen($tplx).'='.($sx/strlen($tplx));
echo "<br>";
echo 'Y:'.$sy.'/'.strlen($tply).'='.($sy/strlen($tply));
exit;
}
}
new readChar("imgurl.jpg");

附上模板图片:

OCR技术浅析-自写篇(2)的更多相关文章

  1. OCR技术浅析-无代码篇(1)

    图像识别中最贴近我们生活的可能就是 OCR 技术了. OCR 的定义:OCR (Optical Character Recognition,光学字符识别)是指电子设备(例如扫描仪或数码相机)检查纸上打 ...

  2. OCR技术浅析-tesserOCR(3)

    tesserOCR使用 tesserOCR是文字识别软件(惠普公司开源) Optical Character Recognition (OCR)即光学字符辨识是把打印文本转换成一个数字表示的过程.它有 ...

  3. AI时代的OCR识别技术浅析

    人工智能这个词可谓是耳熟能详,近几年人工智能热潮再次席卷而来,引起轰动的要数google的AlphaGo,相继打败了围棋界的韩国选手李世石以及世界冠军柯洁,见证了人工智能发展的里程碑式的变革,人工智能 ...

  4. 文本识别OCR浅析:特征篇

    OCR技术浅探:特征提取(1) 研究背景 关于光学字符识别(Optical Character Recognition, 下面都简称OCR),是指将图像上的文字转化为计算机可编辑的文字内容,众多的研究 ...

  5. Python爬虫入门教程 56-100 python爬虫高级技术之验证码篇2-开放平台OCR技术

    今日的验证码之旅 今天你要学习的验证码采用通过第三方AI平台开放的OCR接口实现,OCR文字识别技术目前已经比较成熟了,而且第三方比较多,今天采用的是百度的. 注册百度AI平台 官方网址:http:/ ...

  6. OCR技术

    "起初我写这篇教程是在情人节,OCR可以带给你一整年的爱". 你之前肯定已经见过,OCR技术被应用于在平板电脑上将扫描文件处理成手写字迹,还被应用于谷歌最近添加到他们的Transl ...

  7. 【OCR技术系列之一】字符识别技术总览

    最近入坑研究OCR,看了比较多关于OCR的资料,对OCR的前世今生也有了一个比较清晰的了解.所以想写一篇关于OCR技术的综述,对OCR相关的知识点都好好总结一遍,以加深个人理解. 什么是OCR? OC ...

  8. OCR技术(光学字符识别)

    什么是OCR? OCR英文全称是optical character recognition,中文叫光学字符识别.它是利用光学技术和计算机技术把印在或者写在纸上的 文字读取出来,并转换成一种计算机能够接 ...

  9. 手机微硬盘读取速度>50MB/s eMMC技术浅析

    转载:http://mobile.zol.com.cn/296/2968659_all.html#p2968659 手机微硬盘读取速度>50MB/s 在开始今天的话题之前,请大家随笔者一起时光倒 ...

随机推荐

  1. C指针和数组的关系详解

    1.C中数组和指针的关系 对于任意类型的数组arr,对于同类型的指针类型parr(确切一点,可以假设类型为int,即int arr[], *parr).它们之间有如下"内幕": 1 ...

  2. Perl进程间数据共享

    本文介绍的Perl进程间数据共享内容主体来自于<Pro Perl>的第21章. IPC简介 通过fork创建多个子进程时,进程间的数据共享是个大问题,要么建立一个进程间通信的通道,要么找到 ...

  3. 权限管理系统之项目框架搭建并集成日志、mybatis和分页

    前一篇博客中使用LayUI实现了列表页面和编辑页面的显示交互,但列表页面table渲染的数据是固定数据,本篇博客主要是将固定数据变成数据库数据. 一.项目框架 首先要解决的是项目框架问题,搭建什么样的 ...

  4. 一统江湖的大前端(2)—— Mock.js + Node.js 如何与后端潇洒分手

    <一统江湖的大前端>系列是自己的前端学习笔记,旨在介绍javascript在非网页开发领域的应用案例和发现各类好玩的js库,不定期更新.如果你对前端的理解还是写写页面绑绑事件,那你真的是有 ...

  5. .Net Core 实践 - 使用log4net记录日志(2)

    实现目标:将log4net的相关操作封装成一个 .Net Standard类库 demo地址:https://github.com/PuzzledAlien/log4net_demo/tree/mas ...

  6. 你需要一点点CIL

    1.当我们程序集中有大量反射的时候,性能往往会下降很快.我们目的很明确 如何解决反射造成的这些影响,其中之一个正确且高逼格的做法是 使用 CIL指令去实现.如何实现需要我们拥有若干基础知识.知道 CI ...

  7. MyCat做MySQL负载均衡(享学课堂,咕泡学院听课笔记)

    不要用战术上的勤奋,掩盖战略上的懒惰. 一.数据库集群演示 演示的数据库的表分了三种, 1.配置表,存储一些配置文件,其他业务表需要关联读取,每个数据库都存储配置表的全部内容,即操作Mycat,所有集 ...

  8. .Net Core 编码规范

    .Net Core 编码规范 标签: 未分类 概述 规范制定原则 方便代码的交流和维护. 不影响编码的效率,不与大众习惯冲突. 使代码更美观.阅读更方便. 使代码的逻辑更清晰.更易于理解. 术语定义 ...

  9. 你真的了解PeopleSoft中的function和method方法嘛

    谈下function和method在内嵌与外部传参的区别 1.内嵌函数(Internal Functions) 看下现在输出&x的话会返回什么值? 2.内嵌函数(Internal Functi ...

  10. C++ 11 创建和使用共享 weak_ptr

    1.为什么需要weak_ptr? 在正式介绍weak_ptr之前,我们先来回忆一下shared_ptr的一些知识.我们知道shared_ptr是采用引用计数的智能指针,多个shared_ptr实例可以 ...