如果你做图像处理有一定的经验,并且实战过N次,那么你一定知道代码优化对这个行业是多么的重要。今天,我们首先简单谈谈访问图像像素技术的优化。
首先,我们后面的优化都要基于这个前提:我们是以一维数组的方式来访问图像的数据的,且:
1、这个一维数组的数组名字为:ImageData
2、数组的大小为Stride*Height。其中Stride表示图像的一个扫描行占用的字节数,这个数字必须是4的倍数。Height为图像的高度。
3、数组的类型为byte(unsigned Char?)。
4、图像的宽度为Width,每个像素占用的字节数用BytePerPixel变量表示,24位图像该变量的值为3,32位图像该变量的值为4.
首先我们看看如何访问24或32位图像的像素值。比如要获取第X行第Y列(以0为起点)像素的绿色分量,则应该用ImageData(Stride*Y+X*BytePerPixel+1)表示,红色分量则为ImageData(Stride*Y+X*BytePerPixel+2)。好,这样我们就可以写个简单的反色的代码了。
1 For Y = 0 To Height - 1
2 For X = 0 To Width - 1
3 ImageData(Y * Stride + X * BytePerPixel) = 255 - ImageData(Y * Stride + X * BytePerPixel) 'Blue分量
4 ImageData(Y * Stride + X * BytePerPixel + 1) = 255 - ImageData(Y * Stride + X * BytePerPixel + 1) 'Green分量
5 ImageData(Y * Stride + X * BytePerPixel + 2) = 255 - ImageData(Y * Stride + X * BytePerPixel + 2) 'Red分量
6 Next
7 Next
注意到反色一般是不处理Alpha通道的。
上述代码思路清晰、描述准确,每行的意义明显,是作为新手最好的熟悉图像内存摆布的表达方式,作为考试题的话肯定是可以打100分的。但是如果是作为项目赚钱的话,顶多是个60分吧。
首先,我们观察,在每行中出来了大量的重复计算:Y * Stride + X * BytePerPixel,我们应该只要计算一次他就可以,好的,接着改进:
1 For Y = 0 To Height - 1
2 For X = 0 To Width - 1
3 Speed = Y * Stride + X * BytePerPixel
4 ImageData(Speed) = 255 - ImageData(Speed) 'Blue分量
5 ImageData(Speed + 1) = 255 - ImageData(Speed + 1) 'Green分量
6 ImageData(Speed + 2) = 255 - ImageData(Speed + 2) 'Red分量
7 Next
8 Next
计算速度会有大幅度的提升,好可以拿个80分了。
还有没有改良的空间呢,注意观察在X层的循环中, Y * Stride始终是一个定量,而我们每次都重复计算了他,有必要把他提到外层的循环中,同时我们还想对X * BytePerPixel做点手脚,尽量减少乘法,毕竟乘法的CPU周期比加法要多一些。好,看我们最后的改进版本:
1 For Y = 0 To Height - 1
2 Speed = Y * Stride
3 For X = 0 To Width - 1
4 ImageData(Speed) = 255 - ImageData(Speed) 'Blue分量
5 ImageData(Speed + 1) = 255 - ImageData(Speed + 1) 'Green分量
6 ImageData(Speed + 2) = 255 - ImageData(Speed + 2) 'Red分量
7 Speed = Speed + BytePerPixel '跳到下一个像素
8 Next
9 Next
也有人喜欢用下面的方式:
1 LineAddBytes = Stride - Width * BytePerPixel
2 For Y = 0 To Height - 1
3 For X = 0 To Width - 1
4 ImageData(Speed) = 255 - ImageData(Speed) 'Blue分量
5 ImageData(Speed + 1) = 255 - ImageData(Speed + 1) 'Green分量
6 ImageData(Speed + 2) = 255 - ImageData(Speed + 2) 'Red分量
7 Speed = Speed + BytePerPixel '跳到下一个像素
8 Next
9 Speed = Speed + LineAddBytes '补齐扫描行最后的数据
10 Next
第二种表达方式更加突出了扫描行的大小并不一定等于图像宽度*每像素的占用的字节数,所以在每次扫描一行之后要注意补齐未处理的那部分。这也是很多图像处理初学者在处理图像时可能会遇到处理后的图像效果沿对角线错位的原因。包括我们很多的专业的数字图像处理书,比如我常看的朗锐的那本VC图像处理教程,都没有很注意这个问题。而那些教材一般所测试用的图像是传说中的lena图像,这个图像大小似乎是256*256,由于宽度正好是4的倍数,LineAddBytes这个变量为0,因此这个问题就被隐藏起来了。
我个人更习惯于使用第一种表达方式。
对于使用C或C++编程的朋友,上述代码还有可以优化的地方,++运算符能替代某些算式的。
有两个问题提醒大家注意:
1、图像处理算法中在正常情况下都是先按行处理,在进行列方向递增,这样做对于代码的优化有很大的好处,因为图像在内存的数据摆布也是一行接着一行的。
2、两个方向的循环注意一般都是从下标0开始的,一般不建议从1开始。