5

I have a tool with trackbar slider controls used to adjust an image's brightness, contrast, gamma, etc.

I am trying to get real-time updates to my image while the user drags the slider. The brightness and gamma algorithms are an acceptable speed (around 170ms). But the contrast algorithm is about 380ms.

Basically my form is a tool window with sliders. Each time the image is updated, it sends an event to the parent which redraws the new image. The tool window keeps the original unmodified image locked in memory so I always have access to the bytes of it. So basically I do this each time the ValueChanged event for a slider (such as the Contrast slider) is changed.

  • LockBits of the working (destination) bitmap as Format24bppRgb (original bitmap is in Format32bppPArgb)
  • Marshal.Copy the bits to a byte[] array
  • Check which operation I'm doing (which slider was chosen)
  • Use the following code for Contrast:

Code:

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;
}

I read once about using integer instead of floating point values to increase the speed of contrast, but I couldn't find that article again.

I tried using unsafe code (pointers) but actually noticed a speed decrease. I assume it was because the code was using nested for loops to iterate x and y instead of a single loop.

4

2 回答 2

11

根据您运行它的机器,您的技术可能会很慢。如果您使用的是没有 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]];
}
于 2012-01-24T17:20:20.433 回答
3

@BitBank 按要求回答您的问题,我想补充一点,如果您追求性能,您应该考虑获取像素数据并在之后设置它的代码。

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

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您会发现它的性能更好。

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

于 2012-01-25T11:36:46.167 回答