车牌识别及验证码识别的一般思路[zz]
2010-01-19 23:18
211 查看
http://blog.csdn.net/housisong/archive/2008/04/12/2286776.aspx
图形图像处理-之-一个复杂度为常数的快速局部自适应算法 上篇
HouSisong@GMail.com
2008.04.12
(2009.03.10 可以到这里下载完整的可以编译的项目源代码: http://cid-10fa89dec380323f.skydrive.live.com/browse.aspx/.Public?uc=4
tag: 图像二值化,局部自适应,二维模板
摘要: 图像处理中,某些算法在对一个像素的处理都需要根据周围很多像素的综合信息
来做处理,这类算法一般叫做局部自适应算法,用以得到更好的处理效果;但很多时候
这都可能成为一个性能瓶颈,因为对一个像素点都需要做大量的处理;本文将提供我使
用的一个复杂度为常数的快速局部自适应算法。
(当然,某些二维模板不一定能够拆解成常数算法,但很多还是可以拆解成线性算法的)
正文:
代码使用C++,编译器:VC2005
测试平台:(CPU:AMD64x2 4200+(2.37G); 内存:DDR2 677(双通道); 编译器:VC2005)
A:像素使用ARGB32颜色类型,颜色和图片的数据定义:
typedef unsigned
char
TUInt8;
//
[0..255]
typedef unsigned
long
TUInt32;
struct
TARGB32
//
32 bit color
{
TUInt8 b,g,r,a;
//
a is alpha
};
struct
TPicRegion
//
一块颜色数据区的描述,便于参数传递
{
TARGB32
*
pdata;
//
颜色数据首地址
long
byte_width;
//
一行数据的物理宽度(字节宽度);
//
注意: abs(byte_width)有可能大于等于width*sizeof(TARGB32);
unsigned
long
width;
//
像素宽度
unsigned
long
height;
//
像素高度
};
//
那么访问一个点的函数可以写为:
inline TARGB32
&
Pixels(
const
TPicRegion
&
pic,
const
long
x,
const
long
y)
{
return
( (TARGB32
*
)((TUInt8
*
)pic.pdata
+
pic.byte_width
*
y) )[x];
}
B:图像二值化的一个简单实现
函数的作用是将一幅彩色图像转化成黑白两色的图像;
算法很简单,像素的亮度值大于127(也可以预先统计出源图片的平均亮度值作
为阈值)的转换为白色,否则设置为黑色,实现如下:
const
double
cs_gray_red
=
0.299
;
const
double
cs_gray_green
=
0.587
;
const
double
cs_gray_blue
=
0.114
;
inline
long
getGray0(
const
TARGB32
&
color)
//
获得颜色的亮度
{
return
(
long
)(color.r
*
cs_gray_red
+
color.g
*
cs_gray_green
+
color.b
*
cs_gray_blue);
}
void
threshold0(
const
TPicRegion
&
dst,
const
TPicRegion
&
src)
{
long
width
=
dst.width;
if
(src.width
<
width) width
=
src.width;
long
height
=
dst.height;
if
(src.height
<
height) height
=
src.height;
TARGB32
*
srcLine
=
src.pdata;
TARGB32
*
dstLine
=
dst.pdata;
for
(
long
y
=
0
;y
<
height;
++
y)
{
for
(
long
x
=
0
;x
<
width;
++
x)
{
long
light
=
getGray0(srcLine[x]);
if
(light
>=
127
)
//
设置为白色
{
dstLine[x].b
=
255
;
dstLine[x].g
=
255
;
dstLine[x].r
=
255
;
dstLine[x].a
=
255
;
}
else
//
设置为黑色
{
dstLine[x].b
=
0
;
dstLine[x].g
=
0
;
dstLine[x].r
=
0
;
dstLine[x].a
=
0
;
}
}
(TUInt8
*&
)srcLine
+=
src.byte_width;
//
下一行颜色
(TUInt8
*&
)dstLine
+=
dst.byte_width;
//
下一行颜色
}
}
原图像(图像大小: 640x480):
函数效果:
速度测试:
//////////////////////////////////////////////////////////////
//threshold0
177.1 FPS
//////////////////////////////////////////////////////////////
C:我们来简单优化一下threshold0的速度
getGray0涉及到浮点计算和浮点数取整,可以改写为一个整数定点数算法(见代
码中的getGrayInt函数);
在取黑白值的时候涉及到一个逻辑判断,从而生成了一个分支,可以优化掉;
在写颜色值的时候可以一次写入4个颜色分量;
详细的代码如下:
const
long
cs_gray_red_16
=
(
long
)(cs_gray_red
*
(
1
<<
16
));
const
long
cs_gray_green_16
=
(
long
)(cs_gray_green
*
(
1
<<
16
));
const
long
cs_gray_blue_16
=
(
long
)(cs_gray_blue
*
(
1
<<
16
));
inline
long
getGrayInt(
const
TARGB32
&
color)
{
return
(color.r
*
cs_gray_red_16
+
color.g
*
cs_gray_green_16
+
color.b
*
cs_gray_blue_16)
>>
16
;
}
void
threshold1(
const
TPicRegion
&
dst,
const
TPicRegion
&
src)
{
long
width
=
dst.width;
if
(src.width
<
width) width
=
src.width;
long
height
=
dst.height;
if
(src.height
<
height) height
=
src.height;
TARGB32
*
srcLine
=
src.pdata;
TARGB32
*
dstLine
=
dst.pdata;
for
(
long
y
=
0
;y
<
height;
++
y)
{
for
(
long
x
=
0
;x
<
width;
++
x)
{
long
light
=
getGrayInt(srcLine[x]);
TUInt32 color
=
((
127
-
light)
>>
31
);
//
利用了整数的编码方式来消除了分支
((TUInt32
*
)dstLine)[x]
=
color;
//
一次写4个字节
}
(TUInt8
*&
)srcLine
+=
src.byte_width;
(TUInt8
*&
)dstLine
+=
dst.byte_width;
}
}
threshold1实现的功能和threshold0完全相同;threshold1的速度为:
//////////////////////////////////////////////////////////////
//threshold1
747.6 FPS
//////////////////////////////////////////////////////////////
(当然,该函数还可以继续优化的,比如使用MMX、SSE等指令,可以得到更快的速度;)
D:一个局部自适应图像二值化算法的实现
局部自适应二值化:对于某个像素p,求其周围MxM范围内的像素的平均亮
度I, 若像素p的亮度大于I,则该像素设置为白色,否则设置为黑色;
在边界处,统计周围亮度的时候可能会访问到图像以外,为了在边界处也得到好的效果,
可以返回一个图像内的对应映射像素,完成该功能的函数为getMapBorderColor;
实现如下:
//
执行边界检查和映射的颜色访问函数
inline
const
TARGB32
&
getMapBorderColor(
const
TPicRegion
&
src,
long
x,
long
y)
{
if
(x
<
0
) x
=-
x
-
1
;
long
width2
=
src.width
*
2
;
while
(x
>=
width2) x
-=
width2;
if
(x
>=
src.width) x
=
width2
-
x
-
1
;
if
(y
<
0
) y
=-
y
-
1
;
long
height2
=
src.height
*
2
;
while
(y
>=
height2) y
-=
height2;
if
(y
>=
src.height) y
=
height2
-
y
-
1
;
return
Pixels(src,x,y);
}
//
返回图片src中以(x0,y0)为中心距离localHalfWidth以内的所有像素的亮度和
long
getLocalLight_quadratic(
const
TPicRegion
&
src,
long
x0,
long
y0,
long
localHalfWidth)
{
long
sumLight
=
0
;
for
(
long
y
=
y0
-
localHalfWidth;y
<=
y0
+
localHalfWidth;
++
y)
{
for
(
long
x
=
x0
-
localHalfWidth;x
<=
x0
+
localHalfWidth;
++
x)
{
const
TARGB32
&
mapBorderColor
=
getMapBorderColor(src,x,y);
sumLight
+=
getGrayInt(mapBorderColor);
}
}
return
sumLight;
}
void
localAdaptiveThreshold_quadratic(
const
TPicRegion
&
dst,
const
TPicRegion
&
src,
long
localWidth)
{
long
width
=
dst.width;
if
(src.width
<
width) width
=
src.width;
long
height
=
dst.height;
if
(src.height
<
height) height
=
src.height;
TARGB32
*
srcLine
=
src.pdata;
TARGB32
*
dstLine
=
dst.pdata;
long
localHalfWidth
=
localWidth
/
2
;
long
tLocalWidth
=
localHalfWidth
*
2
+
1
;
long
tLocalWidthSqr
=
tLocalWidth
*
tLocalWidth;
for
(
long
y
=
0
;y
<
height;
++
y)
{
for
(
long
x
=
0
;x
<
width;
++
x)
{
long
sumLight
=
getLocalLight_quadratic(src,x,y,localHalfWidth);
long
light
=
getGrayInt(srcLine[x]);
//
localWidth^2*255<=(2^31-1) => localWidth<=2901
TUInt32 color
=
((sumLight
-
light
*
tLocalWidthSqr)
>>
31
);
((TUInt32
*
)dstLine)[x]
=
color;
}
(TUInt8
*&
)srcLine
+=
src.byte_width;
(TUInt8
*&
)dstLine
+=
dst.byte_width;
}
}
函数效果:
localWidth=151
localWidth=51
localWidth=17
localWidth=5
恩,效果不错:)
图形图像处理-之-一个复杂度为常数的快速局部自适应算法 上篇
HouSisong@GMail.com
2008.04.12
(2009.03.10 可以到这里下载完整的可以编译的项目源代码: http://cid-10fa89dec380323f.skydrive.live.com/browse.aspx/.Public?uc=4
tag: 图像二值化,局部自适应,二维模板
摘要: 图像处理中,某些算法在对一个像素的处理都需要根据周围很多像素的综合信息
来做处理,这类算法一般叫做局部自适应算法,用以得到更好的处理效果;但很多时候
这都可能成为一个性能瓶颈,因为对一个像素点都需要做大量的处理;本文将提供我使
用的一个复杂度为常数的快速局部自适应算法。
(当然,某些二维模板不一定能够拆解成常数算法,但很多还是可以拆解成线性算法的)
正文:
代码使用C++,编译器:VC2005
测试平台:(CPU:AMD64x2 4200+(2.37G); 内存:DDR2 677(双通道); 编译器:VC2005)
A:像素使用ARGB32颜色类型,颜色和图片的数据定义:
typedef unsigned
char
TUInt8;
//
[0..255]
typedef unsigned
long
TUInt32;
struct
TARGB32
//
32 bit color
{
TUInt8 b,g,r,a;
//
a is alpha
};
struct
TPicRegion
//
一块颜色数据区的描述,便于参数传递
{
TARGB32
*
pdata;
//
颜色数据首地址
long
byte_width;
//
一行数据的物理宽度(字节宽度);
//
注意: abs(byte_width)有可能大于等于width*sizeof(TARGB32);
unsigned
long
width;
//
像素宽度
unsigned
long
height;
//
像素高度
};
//
那么访问一个点的函数可以写为:
inline TARGB32
&
Pixels(
const
TPicRegion
&
pic,
const
long
x,
const
long
y)
{
return
( (TARGB32
*
)((TUInt8
*
)pic.pdata
+
pic.byte_width
*
y) )[x];
}
B:图像二值化的一个简单实现
函数的作用是将一幅彩色图像转化成黑白两色的图像;
算法很简单,像素的亮度值大于127(也可以预先统计出源图片的平均亮度值作
为阈值)的转换为白色,否则设置为黑色,实现如下:
const
double
cs_gray_red
=
0.299
;
const
double
cs_gray_green
=
0.587
;
const
double
cs_gray_blue
=
0.114
;
inline
long
getGray0(
const
TARGB32
&
color)
//
获得颜色的亮度
{
return
(
long
)(color.r
*
cs_gray_red
+
color.g
*
cs_gray_green
+
color.b
*
cs_gray_blue);
}
void
threshold0(
const
TPicRegion
&
dst,
const
TPicRegion
&
src)
{
long
width
=
dst.width;
if
(src.width
<
width) width
=
src.width;
long
height
=
dst.height;
if
(src.height
<
height) height
=
src.height;
TARGB32
*
srcLine
=
src.pdata;
TARGB32
*
dstLine
=
dst.pdata;
for
(
long
y
=
0
;y
<
height;
++
y)
{
for
(
long
x
=
0
;x
<
width;
++
x)
{
long
light
=
getGray0(srcLine[x]);
if
(light
>=
127
)
//
设置为白色
{
dstLine[x].b
=
255
;
dstLine[x].g
=
255
;
dstLine[x].r
=
255
;
dstLine[x].a
=
255
;
}
else
//
设置为黑色
{
dstLine[x].b
=
0
;
dstLine[x].g
=
0
;
dstLine[x].r
=
0
;
dstLine[x].a
=
0
;
}
}
(TUInt8
*&
)srcLine
+=
src.byte_width;
//
下一行颜色
(TUInt8
*&
)dstLine
+=
dst.byte_width;
//
下一行颜色
}
}
原图像(图像大小: 640x480):
函数效果:
速度测试:
//////////////////////////////////////////////////////////////
//threshold0
177.1 FPS
//////////////////////////////////////////////////////////////
C:我们来简单优化一下threshold0的速度
getGray0涉及到浮点计算和浮点数取整,可以改写为一个整数定点数算法(见代
码中的getGrayInt函数);
在取黑白值的时候涉及到一个逻辑判断,从而生成了一个分支,可以优化掉;
在写颜色值的时候可以一次写入4个颜色分量;
详细的代码如下:
const
long
cs_gray_red_16
=
(
long
)(cs_gray_red
*
(
1
<<
16
));
const
long
cs_gray_green_16
=
(
long
)(cs_gray_green
*
(
1
<<
16
));
const
long
cs_gray_blue_16
=
(
long
)(cs_gray_blue
*
(
1
<<
16
));
inline
long
getGrayInt(
const
TARGB32
&
color)
{
return
(color.r
*
cs_gray_red_16
+
color.g
*
cs_gray_green_16
+
color.b
*
cs_gray_blue_16)
>>
16
;
}
void
threshold1(
const
TPicRegion
&
dst,
const
TPicRegion
&
src)
{
long
width
=
dst.width;
if
(src.width
<
width) width
=
src.width;
long
height
=
dst.height;
if
(src.height
<
height) height
=
src.height;
TARGB32
*
srcLine
=
src.pdata;
TARGB32
*
dstLine
=
dst.pdata;
for
(
long
y
=
0
;y
<
height;
++
y)
{
for
(
long
x
=
0
;x
<
width;
++
x)
{
long
light
=
getGrayInt(srcLine[x]);
TUInt32 color
=
((
127
-
light)
>>
31
);
//
利用了整数的编码方式来消除了分支
((TUInt32
*
)dstLine)[x]
=
color;
//
一次写4个字节
}
(TUInt8
*&
)srcLine
+=
src.byte_width;
(TUInt8
*&
)dstLine
+=
dst.byte_width;
}
}
threshold1实现的功能和threshold0完全相同;threshold1的速度为:
//////////////////////////////////////////////////////////////
//threshold1
747.6 FPS
//////////////////////////////////////////////////////////////
(当然,该函数还可以继续优化的,比如使用MMX、SSE等指令,可以得到更快的速度;)
D:一个局部自适应图像二值化算法的实现
局部自适应二值化:对于某个像素p,求其周围MxM范围内的像素的平均亮
度I, 若像素p的亮度大于I,则该像素设置为白色,否则设置为黑色;
在边界处,统计周围亮度的时候可能会访问到图像以外,为了在边界处也得到好的效果,
可以返回一个图像内的对应映射像素,完成该功能的函数为getMapBorderColor;
实现如下:
//
执行边界检查和映射的颜色访问函数
inline
const
TARGB32
&
getMapBorderColor(
const
TPicRegion
&
src,
long
x,
long
y)
{
if
(x
<
0
) x
=-
x
-
1
;
long
width2
=
src.width
*
2
;
while
(x
>=
width2) x
-=
width2;
if
(x
>=
src.width) x
=
width2
-
x
-
1
;
if
(y
<
0
) y
=-
y
-
1
;
long
height2
=
src.height
*
2
;
while
(y
>=
height2) y
-=
height2;
if
(y
>=
src.height) y
=
height2
-
y
-
1
;
return
Pixels(src,x,y);
}
//
返回图片src中以(x0,y0)为中心距离localHalfWidth以内的所有像素的亮度和
long
getLocalLight_quadratic(
const
TPicRegion
&
src,
long
x0,
long
y0,
long
localHalfWidth)
{
long
sumLight
=
0
;
for
(
long
y
=
y0
-
localHalfWidth;y
<=
y0
+
localHalfWidth;
++
y)
{
for
(
long
x
=
x0
-
localHalfWidth;x
<=
x0
+
localHalfWidth;
++
x)
{
const
TARGB32
&
mapBorderColor
=
getMapBorderColor(src,x,y);
sumLight
+=
getGrayInt(mapBorderColor);
}
}
return
sumLight;
}
void
localAdaptiveThreshold_quadratic(
const
TPicRegion
&
dst,
const
TPicRegion
&
src,
long
localWidth)
{
long
width
=
dst.width;
if
(src.width
<
width) width
=
src.width;
long
height
=
dst.height;
if
(src.height
<
height) height
=
src.height;
TARGB32
*
srcLine
=
src.pdata;
TARGB32
*
dstLine
=
dst.pdata;
long
localHalfWidth
=
localWidth
/
2
;
long
tLocalWidth
=
localHalfWidth
*
2
+
1
;
long
tLocalWidthSqr
=
tLocalWidth
*
tLocalWidth;
for
(
long
y
=
0
;y
<
height;
++
y)
{
for
(
long
x
=
0
;x
<
width;
++
x)
{
long
sumLight
=
getLocalLight_quadratic(src,x,y,localHalfWidth);
long
light
=
getGrayInt(srcLine[x]);
//
localWidth^2*255<=(2^31-1) => localWidth<=2901
TUInt32 color
=
((sumLight
-
light
*
tLocalWidthSqr)
>>
31
);
((TUInt32
*
)dstLine)[x]
=
color;
}
(TUInt8
*&
)srcLine
+=
src.byte_width;
(TUInt8
*&
)dstLine
+=
dst.byte_width;
}
}
函数效果:
localWidth=151
localWidth=51
localWidth=17
localWidth=5
恩,效果不错:)
相关文章推荐
- 车牌识别及验证码识别的一般思路
- 车牌识别及验证码识别的一般思路
- 车牌识别及验证码识别的一般思路
- 车牌识别及验证码识别的一般思路
- 车牌识别及验证码识别的一般思路
- 车牌识别及验证码识别的一般思路
- 车牌识别及验证码识别的一般思路
- 车牌识别及验证码识别的一般思路(花了我好久才看完。。)
- 图形数字的识别算法: 车牌识别及验证码识别的一般思路
- 车牌识别及验证码识别的一般思路
- 车牌识别及验证码识别的一般思路
- 车牌识别及验证码识别的一般思路
- 车牌识别及验证码识别的一般思路
- 车牌识别及验证码识别的一般思路 ———看到好文章果断转载。。。。
- 车牌识别及验证码识别的一般思路
- 车牌识别及验证码识别的一般思路
- 车牌识别及验证码识别的一般思路
- 车牌识别及验证码识别的一般思路
- 车牌识别及验证码识别的一般思路
- 车牌识别及验证码识别的一般思路