ImageLockMode.UserInputBuffer, PixelFormat.Format24bppRgb, BmpData);
Stopwatch Sw = new Stopwatch(); // 只获取计算用时 Sw.Start(); for (Y = 0; Y < Height; Y++)
{
System.Buffer.BlockCopy(ImageData, Stride * Y, ImageDataC, StrideC * (Y + 1), 3); // 填充扩展图的左侧第一列像素(不包括第一个和最后一个点)
System.Buffer.BlockCopy(ImageData, Stride * Y + (Width - 1) * 3, ImageDataC, StrideC * (Y + 1) + (Width + 1) * 3, 3); // 填充最右侧那一列的数据
System.Buffer.BlockCopy(ImageData, Stride * Y, ImageDataC, StrideC * (Y + 1) + 3, Width * 3);
}
System.Buffer.BlockCopy(ImageDataC, StrideC, ImageDataC, 0, StrideC); // 第一行
System.Buffer.BlockCopy(ImageDataC, (HeightC - 2) * StrideC, ImageDataC, (HeightC - 1) * StrideC, StrideC); // 最后一行
for (Y = 0; Y < Height; Y++)
{
Speed = Y * Stride;
SpeedOne = StrideC * Y; for (X = 0; X < Width; X++)
{
SpeedTwo = SpeedOne + StrideC; // 尽量减少计算
SpeedThree = SpeedTwo + StrideC; // 下面的就是严格的按照Sobel算字进行计算,代码中的*2一般会优化为移位或者两个Add指令的,如果你不放心,当然可以直接改成移位
BlueOne = DataCP[SpeedOne] + 2 * DataCP[SpeedTwo] + DataCP[SpeedThree] - DataCP[SpeedOne + 6] - 2 * DataCP[SpeedTwo + 6] - DataCP[SpeedThree + 6];
GreenOne = DataCP[SpeedOne + 1] + 2 * DataCP[SpeedTwo + 1] + DataCP[SpeedThree + 1] - DataCP[SpeedOne + 7] - 2 * DataCP[SpeedTwo + 7] - DataCP[SpeedThree + 7];
RedOne = DataCP[SpeedOne + 2] + 2 * DataCP[SpeedTwo + 2] + DataCP[SpeedThree + 2] - DataCP[SpeedOne + 8] - 2 * DataCP[SpeedTwo + 8] - DataCP[SpeedThree + 8];
BlueTwo = DataCP[SpeedOne] + 2 * DataCP[SpeedOne + 3] + DataCP[SpeedOne + 6] - DataCP[SpeedThree] - 2 * DataCP[SpeedThree + 3] - DataCP[SpeedThree + 6];
GreenTwo = DataCP[SpeedOne + 1] + 2 * DataCP[SpeedOne + 4] + DataCP[SpeedOne + 7] - DataCP[SpeedThree + 1] - 2 * DataCP[SpeedThree + 4] - DataCP[SpeedThree + 7];
RedTwo = DataCP[SpeedOne + 2] + 2 * DataCP[SpeedOne + 5] + DataCP[SpeedOne + 8] - DataCP[SpeedThree + 2] - 2 * DataCP[SpeedThree + 5] - DataCP[SpeedThree + 8];
PowerBlue = BlueOne * BlueOne + BlueTwo * BlueTwo;
PowerGreen = GreenOne * GreenOne + GreenTwo * GreenTwo;
PowerRed = RedOne * RedOne + RedTwo * RedTwo; if (PowerBlue > 65025) PowerBlue = 65025; // 处理掉溢出值
if (PowerGreen > 65025) PowerGreen = 65025; if (PowerRed > 65025) PowerRed = 65025;
DataP[Speed] = LutP[PowerBlue]; // 查表
DataP[Speed + 1] = LutP[PowerGreen];
DataP[Speed + 2] = LutP[PowerRed];
Speed += 3; // 跳往下一个像素
SpeedOne += 3;
}
}
Sw.Stop(); this.Text = "计算用时: " + Sw.ElapsedMilliseconds.ToString() + " ms";
Bmp.UnlockBits(BmpData); // 必须先解锁,否则Invalidate失败 }
Pic.Invalidate();
}
同样的效果,同样的图像,计算用时330ms。
我们在来看看相同代码的汇编码:
BlueOne = DataCP[SpeedOne] + 2 * DataCP[SpeedTwo] + DataCP[SpeedThree] - DataCP[SpeedOne + 6] - 2 * DataCP[SpeedTwo + 6] - DataCP[SpeedThree + 6];
00000318 movzx eax,byte ptr [esi+edi]
0000031c mov dword ptr [ebp-74h],eax
0000031f movzx edx,byte ptr [esi+ebx]
00000323 add edx,edx
00000325 add eax,edx
00000327 movzx edx,byte ptr [esi+ecx]
0000032b mov dword ptr [ebp-7Ch],edx
0000032e add eax,edx
00000330 movzx edx,byte ptr [esi+edi+6]
00000335 mov dword ptr [ebp-78h],edx
00000338 sub eax,edx
0000033a movzx edx,byte ptr [esi+ebx+6]
0000033f add edx,edx
00000341 sub eax,edx
00000343 movzx edx,byte ptr [esi+ecx+6]
00000348 mov dword ptr [ebp-80h],edx
0000034b sub eax,edx
0000034d mov dword ptr [ebp-30h],eax 生产的汇编码简洁,意义明确,对比下少了很多指令。当然速度会快很多。
注意这一段代码:
fixed (byte* P = &ImageData[0], CP = &ImageDataC[0], LP = &SqrValue[0])
{ byte* DataP = P, DataCP = CP, LutP = LP; 如果你把更换为:
fixed (byte* DataP = &ImageData[0], DataCP = &ImageDataC[0], LutP = &SqrValue[0])
{ 代码的速度反而比纯数组版的还慢,至于为什么,实践为王吧,我也没有去分析,反正我知道有这个结果。你可以参考铁哥的一篇文章:
闲谈.Net类型之public的不public,fixed的不能fixed
当然这个还可以进一步做小动作的的优化,比如movzx eax,byte ptr [esi+edi] 这句中,esi其实就是数组的基地址,向这样写DataCP[SpeedOne] ,每次都会有这个基址+偏移的计算的,如果能实时直接动态控制一个指针变量,使他直接指向索要的位置,则少了一次加法,虽然优化不是很明显,基本可以达到问中之前所提到的300ms的时间了。具体的代码可见附件。
很多人可能对我这些东西不感冒,说这些东西丢给GPU比你现在的.......希望这些朋友也不要过分的打击吧,每个人都有自己的爱好,我只爱好CPU。
更多PhotoShop算法原理解析系列 - 风格化-查找边缘。相关文章请关注PHP中文网!
Photoshop默认保存的文件格式,可以保留所有有图层、色版、通道、蒙版、路径、未栅格化文字以及图层样式等。
关键词:PhotoShop算法原理解析系列 - 风格化-搜索边缘。