2013-01-16 95 views
1

我想要過濾位圖圖像以增加或減少色調,飽和度和亮度值。更快的算法來改變位圖中的色相/飽和度/亮度

我的代碼工作正常,但是很慢

我在內存中鎖定了兩個位圖,原始源和當前目標。用戶可以移動各種軌跡欄控件來修改每個值,然後將其轉換爲HSL值。例如,軌跡欄上的值對應於-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

我會做的第一件事是緩存結果,並使用不安全的代碼,用於訪問數組更快。 – SimpleVar

+0

+1。像這樣的微碼,陣列訪問完全殺死你。每次訪問都會檢查最小/最大值。歡迎不安全的指針代碼。並且從字節移動到同時具有所有值的結構。 – TomTom

+0

你們有沒有這方面的文件記錄?當數組是指針時,Afaik數組語法等於指針語法,否?反正沒有最小/最大限制。 – Rotem

回答

4

我結束了研究ImageAttributes和嘉洛斯,發現性能優良。

這裏是我是如何實現它的飽和度和亮度過濾器:

// 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(); 
} 
+1

有趣的部分是你可以使用單個矩陣計算許多不同的效果,然後乘以矩陣合併效果。 – Rotem

1

你需要分析你的應用程序,看看問題在哪裏。

隨機

建議:

  • 使用32位每像素格式,以避免未對齊的讀取。 (和讀取整個32作爲單一操作)
  • 避免多個RGB < - > HSL轉換
+0

那麼我使用相同的代碼來進行對比度,RGB亮度,伽瑪,反轉等等,而且速度非常快。所以RGB/HSL轉換是主要的瓶頸。也許你可以詳細說明我如何避免多次轉換。我想過使用查找表,但我怎樣才能使用查找表的雙值?使用字節值很容易,因爲只有256種可能性。 –

+0

@Moozhe,你的代碼最多可以做2個無用的轉換(如果你需要改變H的全部3個L)。否則 - 看看你的FromRGB代碼並進行優化。在最壞的情況下,16M的預計算表的整數值不是世界的盡頭... –

+0

@AlexeiLevenkov它實際上是16.7m顏色* 4字節= 67MB,不太可行。更不用說這些可能比圖像中實際存在的更多的值,使得LUT生成比計算慢。 – Rotem

相關問題