【问题标题】:Faster algorithm to change Hue/Saturation/Lightness in a bitmap更快的算法来改变位图中的色相/饱和度/亮度
【发布时间】:2012-12-31 03:27:39
【问题描述】:

我正在尝试过滤位图图像以增加或减少色相、饱和度和亮度值。

我的代码运行良好,但是速度很慢

我将两个位图锁定在内存中,即原始源和当前目标。用户可以移动各种 trackbar 控件来修改每个值,然后将其转换为 HSL 值。例如,trackbar 上的值对应的范围是 -1.0 到 1.0。

每次引发跟踪栏值更改的事件时,我都会运行一个函数来锁定目标位图并将 HSL 值应用于源位图,然后将结果存储在目标位图中。完成后,我解锁目标位图并在屏幕上绘制图像。

之前我使用查找表作为我的其他过滤器,因为我正在执行按字节操作。但是我不知道如何使用 HSL 来应用它。这是我正在使用的代码:

byte red, green, blue;

for (int i = 0; i < sourceBytes.Length; i += 3)
{
    blue = sourceBytes[i];
    green = sourceBytes[i + 1];
    red = sourceBytes[i + 2];

    Color newColor = Color.FromArgb(red, green, blue);

    if (ModifyHue)
        newColor = HSL.ModifyHue(newColor, Hue);

    if (ModifySaturation)
        newColor = HSL.ModifySaturation(newColor, Saturation);

    if (ModifyLightness)
        newColor = HSL.ModifyBrightness(newColor, Lightness);

    destBytes[i] = newColor.B;
    destBytes[i + 1] = newColor.G;
    destBytes[i + 2] = newColor.R;
}

这是我的 ModifyBrightness 函数:

public static Color ModifyBrightness(Color color, double brightness)
{
    HSL hsl = FromRGB(color);
    hsl.L *= brightness;
    return hsl.ToRGB();
}

所以基本上如果他们的亮度滑块在最中间,它的值将是 0,当我将它传递给函数时,我会将其转换为“1.0”,因此它将亮度乘以 1.0,这意味着它不会改变。如果他们将滑块一直拖到右侧,它的值将是 100,这将导致修改器为 2.0,所以我将亮度值乘以 2.0 使其翻倍。

【问题讨论】:

  • 我要做的第一件事是缓存结果,并使用不安全的代码来更快地访问数组。
  • +1。使用这样的微码,阵列访问完全杀死你。每次访问都是检查最小值/最大值。请快乐不安全的指针代码。并从字节移动到同时具有所有值的结构。
  • 你们有这方面的文档参考吗?当数组是指针时,Afaik 数组语法等于指针语法,不是吗?反正没有最小/最大限制。
  • @OP 如果您只需要修改 HSL,您也可以将 ImageAttributes 与 ColorMatrix 一起使用,它的执行速度将比您当前的代码快得多。
  • 我对亮度、对比度、伽玛、反转和灰度滤镜使用相同的代码结构,并且使用查找表非常快。我尝试过使用指针,但实际上我注意到性能下降。数组索引不是这里的瓶颈,它是 RGB 到 HSL 的转换。正如我在问题中所说,我想使用类似于查找表的东西,但我不知道如何将其应用于 HSL,因为您无法为每个可能的双精度值创建查找表,它与 256 不同不同的字节值。

标签: c# .net bitmap hsl


【解决方案1】:

我最终研究了 ImageAttributes 和 ColorMatrix,发现性能非常好。

这是我为饱和度和亮度滤镜实现它的方法:

// Luminance vector for linear RGB
const float rwgt = 0.3086f;
const float gwgt = 0.6094f;
const float bwgt = 0.0820f;

private ImageAttributes imageAttributes = new ImageAttributes();
private ColorMatrix colorMatrix = new ColorMatrix();
private float saturation = 1.0f;
private float brightness = 1.0f;

protected override void OnPaint(object sender, PaintEventArgs e)
{
    base.OnPaint(sender, e);

    e.Graphics.DrawImage(_bitmap, BitmapRect, BitmapRect.X, BitmapRect.Y, BitmapRect.Width, BitmapRect.Height, GraphicsUnit.Pixel, imageAttributes);
}

private void saturationTrackBar_ValueChanged(object sender, EventArgs e)
{
    saturation = 1f - (saturationTrackBar.Value / 100f);

    float baseSat = 1.0f - saturation;

    colorMatrix[0, 0] = baseSat * rwgt + saturation;
    colorMatrix[0, 1] = baseSat * rwgt;
    colorMatrix[0, 2] = baseSat * rwgt;
    colorMatrix[1, 0] = baseSat * gwgt;
    colorMatrix[1, 1] = baseSat * gwgt + saturation;
    colorMatrix[1, 2] = baseSat * gwgt;
    colorMatrix[2, 0] = baseSat * bwgt;
    colorMatrix[2, 1] = baseSat * bwgt;
    colorMatrix[2, 2] = baseSat * bwgt + saturation;

    imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

    Invalidate();
}

private void brightnessTrackBar_ValueChanged(object sender, EventArgs e)
{
    brightness = 1f + (brightnessTrackBar.Value / 100f);

    float adjustedBrightness = brightness - 1f;

    colorMatrix[4, 0] = adjustedBrightness;
    colorMatrix[4, 1] = adjustedBrightness;
    colorMatrix[4, 2] = adjustedBrightness;

    imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

    Invalidate();
}

【讨论】:

  • 有趣的是,您可以使用单个矩阵计算许多不同的效果,然后将矩阵相乘以组合效果。
【解决方案2】:

您需要分析您的应用并查看问题所在。

随机建议:

  • 使用每像素 32 位格式来避免未对齐的读取。 (并将整个 32 读取为单个操作)
  • 避免多次 RGB HSL 转换

【讨论】:

  • 我对对比度、RGB 亮度、伽玛、反转等使用相同的代码。而且速度非常快。所以 RGB/HSL 转换是主要的瓶颈。也许您可以详细说明我如何避免多次转换。我曾想过使用查找表,但如何将 LUT 用于双精度值?使用字节值很容易,因为只有 256 种可能性。
  • @Moozhe,您的代码开始时最多执行 2 个无用的转换(如果您需要更改所有 3 个 H S L)。否则 - 查看您的 FromRGB 代码并进行优化。在最坏的情况下,预计算表的 16M 整数值并不是世界末日...
  • @AlexeiLevenkov 实际上是 1670 万色 * 4 字节 = 67MB,不太可行。更不用说这些可能比图像中实际存在的值更多,使得 LUT 生成比计算慢。
  • @Rotem 67Mb 大约是现代相机中一张图像的大小,因此每个项目的内存量并非闻所未闻。 OP 应该能够测量并查看在这种情况下 CPU 与内存是否适用于该特定情况。
  • 你说得对,我应该只将其转换为 HSL 一次而不是 3 次。实际上,在我的测试中,我只使用了 Lightness 滤镜,而且速度非常慢。其他过滤器被关闭。所以我可以解决这个问题,但我仍然需要通过其他方式提高性能。
猜你喜欢
  • 1970-01-01
  • 2020-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多