更快的位图对比度算法

我有一个带有轨迹栏滑块控件的工具,用于调整图像的亮度,对比度,伽玛等。

当用户拖动滑块时,我试图获得我的图像的实时更新。 亮度和伽玛算法是可接受的速度(大约170ms)。 但对比度算法大约是380ms。

基本上我的表单是一个带滑块的工具窗口。 每次更新图像时,它都会向父项发送一个事件,重新绘制新图像。 工具窗口将原始未修改的图像锁定在内存中,因此我始终可以访问它的字节。 因此,每次更改滑块的ValueChanged事件(例如“对比度”滑块)时,我都会这样做。

  • 工作(目标)位图的LockBits为Format24bppRgb(原始位图在Format32bppPArgb中)
  • Marshal。将位复制到byte []数组
  • 检查我正在做的操作(选择了哪个滑块)
  • 对比度使用以下代码:

码:

double newValue = 0; double c = (100.0 + contrast) / 100.0; c *= c; for (int i = 0; i < sourcePixels.Length; i++) { newValue = sourcePixels[i]; newValue /= 255.0; newValue -= 0.5; newValue *= c; newValue += 0.5; newValue *= 255; if (newValue  255) newValue = 255; destPixels[i] = (byte)newValue; } 

我读了一次关于使用整数而不是浮点值来提高对比度的速度,但我再也找不到那篇文章了。

我尝试使用不安全的代码(指针),但实际上注意到速度降低。 我假设这是因为代码使用嵌套for循环来迭代x和y而不是单个循环。

根据你运行它的机器,你的技术可能会很慢。 如果您使用的是没有FPU的ARM系统,那么每个操作都需要一段时间。 由于您对每个字节应用相同的操作,因此更快的技术是为对比度级别创建256条目查找表,然后通过表格转换每个图像字节。 你的循环看起来像:

 byte contrast_lookup[256]; double newValue = 0; double c = (100.0 + contrast) / 100.0; c *= c; for (int i = 0; i < 256; i++) { newValue = (double)i; newValue /= 255.0; newValue -= 0.5; newValue *= c; newValue += 0.5; newValue *= 255; if (newValue < 0) newValue = 0; if (newValue > 255) newValue = 255; contrast_lookup[i] = (byte)newValue; } for (int i = 0; i < sourcePixels.Length; i++) { destPixels[i] = contrast_lookup[sourcePixels[i]]; } 

@BitBank按照要求回答你的问题,我想补充一点,如果你在演出后你应该考虑你的代码获取像素数据并在之后进行设置。

使用指针的完整工作代码( for循环代码上的@BitBank道具):

 private unsafe void ApplyContrast(double contrast, Bitmap bmp) { byte[] contrast_lookup = new byte[256]; double newValue = 0; double c = (100.0 + contrast) / 100.0; c *= c; for (int i = 0; i < 256; i++) { newValue = (double)i; newValue /= 255.0; newValue -= 0.5; newValue *= c; newValue += 0.5; newValue *= 255; if (newValue < 0) newValue = 0; if (newValue > 255) newValue = 255; contrast_lookup[i] = (byte)newValue; } var bitmapdata = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb); int PixelSize = 4; for (int y = 0; y < bitmapdata.Height; y++) { byte* destPixels = (byte*)bitmapdata.Scan0 + (y * bitmapdata.Stride); for (int x = 0; x < bitmapdata.Width; x++) { destPixels[x * PixelSize] = contrast_lookup[destPixels[x * PixelSize]]; // B destPixels[x * PixelSize + 1] = contrast_lookup[destPixels[x * PixelSize + 1]]; // G destPixels[x * PixelSize + 2] = contrast_lookup[destPixels[x * PixelSize + 2]]; // R //destPixels[x * PixelSize + 3] = contrast_lookup[destPixels[x * PixelSize + 3]]; //A } } bmp.UnlockBits(bitmapdata); } 

如果您使用Marshal.Copy设置图像像素数据,您会发现这样做效果更好。

这应该比当前代码执行得更快,并且还可以减少内存占用,这在处理非常大的图像时很有用。