1. 前言
继续优化技术的探索,今天以一个
的Sobel算子进行边缘检测的算法为例来看看如何使用SSE指令集对其进行优化。
2. 原理介绍
众所周知,在传统的图像边缘检测算法中,最常用的一种算法是利用Sobel算子完成的。Sobel算子一共有
个,一个是检测水平边缘的算子,另一个是检测垂直边缘的算子。
Sobel算子的优点是可以利用快速卷积函数,简单有效,且对领域像素位置的影响做了加权,可以降低边缘模糊程度,有较好效果。然而Sobel算子并没有基于图像的灰度信息进行处理,所以在提取图像边缘信息的时候可能不会让人视觉满意。
我们来看一下怎么构造Sobel算子?
Sobel算子是在一个坐标轴的方向进行非归一化的高斯平滑,在另外一个坐标轴方向做一个差分,
大小的Sobel算子是由
平滑算子和
差分算子全卷积得到,其中
代表Sobel算子的半径,必须为奇数。
对于窗口大小为
的
非归一化Sobel平滑算子等于
阶的二项式展开式的系数,而
Sobel平滑算子等于
阶的二项式展开式的系数两侧补
,然后向前差分。
在这个例子中:我们要构造一个
阶的Sobel非归一化的
Sobel平滑算子和Sobel差分算子:
Sobel平滑算子: 取二项式的阶数为
,然后计算展开式系数为,
也即是
,这就是
阶的非归一化的Sobel平滑算子。
Sobel差分算子:取二项式的阶数为
,然后计算二项展开式的系数,即为:
,两侧补
并且前向差分得到
,第
项差分后可以直接删除。
Sobel算子:将
阶的Sobel平滑算子和Sobel差分算子进行全卷积
,即可得到
的Sobel算子。
其中
方向的Sobel算子为:
而
方向的Sobel算子为:
3. 原始实现
我们先放出针对
的Sobel算子的原始实现代码,如下所示:
inline unsigned char IM_ClampToByte(int Value)
{
if (Value < 0)
return 0;
else if (Value > 255)
return 255;
else
return (unsigned char)Value;
//return ((Value | ((signed int)(255 - Value) >> 31)) & ~((signed int)Value >> 31));
}
void Sobel_FLOAT(unsigned char *Src, unsigned char *Dest, int Width, int Height, int Stride) {
int Channel = Stride / Width;
unsigned char *RowCopy = (unsigned char*)malloc((Width + 2) * 3 * Channel);
unsigned char *First = RowCopy;
unsigned char *Second = RowCopy + (Width + 2) * Channel;
unsigned char *Third = RowCopy + (Width + 2) * 2 * Channel;
//拷贝第二行数据,边界值填充
memcpy(Second, Src, Channel);
memcpy(Second + Channel, Src, Width*Channel);
memcpy(Second + (Width + 1)*Channel, Src + (Width - 1)*Channel, Channel);
//第一行和第二行一样
memcpy(First, Second, (Width + 2) * Channel);
//拷贝第三行数据,边界值填充
memcpy(Third, Src + Stride, Channel);
memcpy(Third + Channel, Src + Stride, Width * Channel);
memcpy(Third + (Width + 1) * Channel, Src + Stride + (Width - 1) * Channel, Channel);
for (int Y = 0; Y < Height; Y++) {
unsigned char *LinePS = Src + Y * Stride;
unsigned char *LinePD = Dest + Y * Stride;
if (Y != 0) {
unsigned char *Temp = First;
First = Second;
Second = Third;
Third = Temp;
}
if (Y == Height - 1) {
memcpy(Third, Second, (Width +