基础算法篇漏掉了几个算法,其中也有我曾认为过于简单无需说明的,干脆再来个续集补全吧。
统计灰度图像的直方图。很简单,无需赘言。有整体和局部统计两个函数:
// 统计灰度图像的直方图
//
// src: 图像数据首地址
// width: 图像宽度
// height: 图像高度
// count: 直方图数据指针
//
void CPlate::StatsHistogram(void *src, int width, int height, int *count)
{
int i, j;
int n = width * height;
uint8 *p;
int *ct;
memset(count, 0, 256 * sizeof(int));
for (i = 0, p = (uint8 *)src, ct = count; i < n; i++, p++)
ct[*p]++;
}
// 统计灰度图像指定范围的图像块的直方图
//
// src: 图像数据首地址
// x: 起始坐标x
// y: 起始坐标y
// w: 图像块宽度
// h: 图像块高度
// count: 直方图数据指针
//
void CPlate::StatsHistogram(void *src, int x, int y, int w, int h, int *count)
{
int i, j;
uint8 *p0, *p;
int *ct;
memset(count, 0, 256 * sizeof(int));
for (i = 0, p0 = (uint8 *)src + y * W, ct = count; i < h; i++, p0 += W)
for (j = 0, p = p0 + x; j < w; j++, p++)
ct[*p]++;
}
然后是大津法(OTSU)自适应阈值的搜索算法。它是一种基于最大类间方差的经典算法,网络上介绍其原理的资料很多,这里就不多解释了。与众不同的是,下列函数代码中加了个初始阈值,从这个阈值开始向前、向后搜索最佳阈值,而不是每次都从0开始搜索。这是因为车牌区域统计出的类间方差曲线类似于开口向下的抛物线,在最佳阈值处有极大值。利用此性质可加速最佳阈值的搜索。
// 用大津法计算灰度图像二值化分割的阈值
//
// count: 直方图数据指针
// threshold: 指定初始阈值,将从这个阈值开始搜索
// back: 背景平均灰度
// fore: 前景平均灰度
//
// 返回值: 灰度阈值(等于参数threshold返回值)
//
int CPlate::OTSU_Threshold(int *count, int &threshold, int &back, int &fore)
{
int i, j;
int *ct;
// w0为前景像素点数
// s0为前景像素点的灰度值总和
// u0为前景像素点平均灰度
// w1为背景像素点数
// s1为背景像素点的灰度值总和
// u1为背景像素点平均灰度
// u为整幅图像的平均灰度
// u0, u1, u都带有4位小数
int w0, w1, s0, s1;
int ww0, ww1, ss0, ss1;
int ctt;
int found = 0;
int u0, u1, u, du0, du1;
int64 delta, max_delta;
// 统计前景相关数据
for (i = 0, ww1 = 0, ss1 = 0, ct = count; i < threshold; i++, ct++)
{
if (*ct)
{
ww1 += *ct;
ss1 += *ct * i;
}
}
// 统计背景相关数据
for (i = threshold, ww0 = 0, ss0 = 0, ct = count + i; i < 256; i++, ct++)
{
if (*ct)
{
ww0 += *ct;
ss0 += *ct * i;
}
}
if (ww0 + ww1 == 0) return threshold;
// 整幅图像的平均灰度
u = ((ss0 + ss1) << 4) / (ww0 + ww1);
u0 = ww0 ? (ss0 << 4) / ww0 : u;
u1 = ww1 ? (ss1 << 4) / ww1 : u;
du0 = u0 - u;
du1 = u1 - u;
max_delta = (int64)ww0 * du0 * du0 + (int64)ww1 * du1 * du1;
// 向前搜索,找出前景与背景图像的类间方差最大者
if (1)
{
w0 = ww0; w1 = ww1; s0 = ss0; s1 = ss1;
for (i = threshold - 1, ct = count + i, ctt = 0; i > 0 && ctt < 2; i--, ct--)
{
if (*ct)
{
w0 += *ct;
w1 -= *ct;
s0 += *ct * i;
s1 -= *ct * i;
u0 = w0 ? (s0 << 4) / w0 : u;
u1 = w1 ? (s1 << 4) / w1 : u;
du0 = u0 - u;
du1 = u1 - u;
delta = (int64)w0 * du0 * du0 + (int64)w1 * du1 * du1;
// 在找到最佳阈值之前,delta >= max_delta 的条件会持续或间隔一两个灰度值出现
// 找到之后,就再也不会出现了
// delta值的变化类似于开口向下的抛物线,而max_delta是极大值
if (delta >= max_delta)
{
max_delta = delta;
threshold = i;
found = 1;
ctt = 0;
}
else ctt++;
}
}
}
if (!found)
{
//