需要模拟精灵v7.15 下载:http://www.yhhe.net/bbs/dispbbs.asp?boardID=4&ID=2851&page=1
模拟精灵识别验证码的能用是强大的,一个函数即可以去除杂色杂点,但是有时候验证码中有大量的干扰线,并且位置随机变动的太历害,这时候我们在处理验证码以前首先去除这些干扰线并准确的去除背景提取字符.
下面是一个模拟精灵初步处理后的验证码图片.已经去除了杂色、杂点.但是上面还是有干扰线.
一个可选的办法是用中值滤波再处理一下。img:median(2); 一个函数调用就可以,但是这样虽然去掉了干扰线,原来的字符也被少量的破坏了。
但是在算法的设计上存在问题,效果不理想。
下面是改进算法以后重新写的代码,不但能去除杂点,而且可以去除周围的空白(提取位置随机变化的验证码),
稍加修改还能有更多的用途.
下面是自动处理以后的效果
(干扰线没有了,周围的空白没有了,速度也很快,接近零延时)
下面是全部的源代码:
--[[
用一个table结构{x=0; y=0}表示图像上的「坐标点」
用一组点构成table结构表示图像上的一条「线」。所有相连的黑色的点被认为是一条「连通线」。
找出最长的一条「连通线」,被认为是字符,其他的认为是杂点。
算法原理与种子填充算法相似。
首先让用img:bpp函数处理为黑白图片,并初步去除杂色。
先找到一个黑点,创建一个表示「坐标点」对象,并添加到「连通线」中。
然后在黑点周围8个点中,再找黑色的点,找到就添加到「连通线」,这样一直递归下去
直到遍历图像所有点,可能有几块。
清除杂点使用方法
image.scan(img);
清除杂点并切去掉周围的空白
image.scan(img,true);
--]]
function image.scan(img,crop)
--用一个table数组记录所有的「连通线」
assert(img:ok(),"image.scan 的参数必须是一个有效的图片");
local tlines ={};
--首先计算出图片的高度宽度,避免重复的调用
local w = img:width();
local h = img:height();
--[[以table形式定义一个数组,对应图象中的每个点。
作用相当一个开关,首先值为false,但黑点首次被遍历到时。把这个值变为true。
下次,再找到这个点时忽略。避免重复加入连通线。
--]]
local tchked ={};
for i=0,w,1 do
tchked[i]={};
for j=0,h,1 do
tchked[i][j]=false;
end;
end;
-----去噪
img:bpp(1);
img:bpp(24);
--首先计算出各点的颜色值,避免在循环递归中重复的取
local tcl={};
for i=0,w,1 do
tcl[i]={};
for j=0,h,1 do
tcl[i][j]=img:getPos(i,j);
end;
end;
--[[
算点数函数
参数x,y 坐标
参数tab 所属连通线;
--]]
local function seed(x,y,tab)
---出界了则返回
if(x<0 or y<0 or x>w or y>h) then
return;
end;
---点的颜色为白色时,返回,不处理。
if(tcl[x][y]==16777215) then
return;
end;
---值为1,则计数加1,返回
if ( tchked[x][y]) then
return ;
else
table.insert(tab,{x=x,y=y} );--添加到连通线里
tchked[x][y]=true;---当值为0时,把值置为1。
seed(x+1,y-1,tab);
seed(x,y-1,tab);
seed(x-1,y-1,tab);
seed(x-1,y,tab);
seed(x+1,y,tab);
seed(x-1,y+1,tab);
seed(x,y+1,tab);
return seed(x+1,y+1,tab); --这里可以用一个尾调用(参考教程中的函数部份),加快递归的速度。
end;
end;
---------------------------
----遍历图像中的所有点
for i=0,w,1 do
for j=0,h,1 do
---如果是黑色的点,而且没有被计过数,则调用seed函数。
if(tcl[i][j]==0 and (not tchked[i][j])) then
local tab = {}
seed(i,j,tab);
table.insert(tlines,tab); --添加一条连通线
end;
end;
end;
--现在tlines 里记录了的有的连通线,我们现在需要根据连通线的长度排序
sproc = function(l,l2)
return table.maxn(l) > table.maxn(l2);--长的连通线排到前面
end;
table.sort(tlines,sproc)
--把图像全部画成白色的点
for i=0,w,1 do
for j=0,h,1 do
img:setPos( i , j, 16777215);
end;
end;
--然后把最长的一条连通线画上去
for i,point in ipairs(tlines[1]) do
img:setPos( point.x, point.y , 0);
end;
--如果需要去掉周围的空白
if(crop)then
local n = table.maxn(tlines[1])
--排序最长连通线中的所有坐标点
sproc = function(pt,pt2)
return (pt.x <pt2.x );--*左的排前面
end;
table.sort(tlines[1],sproc);
local x,x2 = tlines[1][1].x, tlines[1][n].x;
--排序最长连通线中的所有坐标点
sproc = function(pt,pt2)
return (pt.y <pt2.y );--*上的排前面
end;
table.sort(tlines[1],sproc);
local y,y2 = tlines[1][1].y, tlines[1][n].y;
img:Crop( x,y,x2+1,y2)
end;
end;