更快的位图对比度算法

时间:2012-01-24 17:04:07

标签: c# performance bitmap contrast

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

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

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

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

代码:

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 < 0)
        newValue = 0;
    if (newValue > 255)
        newValue = 255;

    destPixels[i] = (byte)newValue;
}

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

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

2 个答案:

答案 0 :(得分:10)

根据你运行它的机器,你的技术可能会很慢。如果您使用的是没有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]];
}

答案 1 :(得分:3)

@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设置图像像素数据,您会发现效果更佳。

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