65.9K
CodeProject 正在变化。 阅读更多。
Home

颜色比例过滤器

starIconstarIconstarIconstarIconstarIcon

5.00/5 (6投票s)

2007年6月20日

CPOL

4分钟阅读

viewsIcon

63157

downloadIcon

2234

灰度和颜色刻度滤镜。

Screenshot - Faster_Grayscale_Filter.jpg

引言

这里有一些 Visual Basic .NET 类,用于实现将 RGB 图像更改为颜色和灰度图像的图像滤镜。添加了一个小型演示项目来展示滤镜的使用。

背景

我一直在使用文章 “如何将彩色图像转换为灰度图像” 中描述的灰度滤镜,并产生了制作一个滤镜的想法,该滤镜可以使图像看起来像一些旧的棕色和白色照片。 在解决问题的过程中,我发现将图像转换为任何颜色刻度都非常容易。

在提到的文章中,我们可以找到两种将彩色图像转换为灰度图像的方法,倾向于使用颜色矩阵的方法,因为它是“执行单色处理的有效且快速的方法”。 如果我们采用书面方法,这是正确的,但是经过一些努力,另一种方法证明要快得多。

改进灰度滤镜

以上文章中提到的方法如下

Public Function ConvertToGrayscale(ByVal source As Bitmap) as Bitmap
  Dim bm as new Bitmap(source.Width,source.Height)
  Dim x
  Dim y
  For y=0 To bm.Height
    For x=0 To bm.Width
      Dim c as Color = source.GetPixel(x,y)
      Dim luma as Integer = CInt(c.R*0.3 + c.G*0.59 + c.B*0.11)
      bm.SetPixel(x,y,Color.FromArgb(luma,luma,luma))
    Next
  Next
  Return bm
End Function

在主循环中,我们有三个函数调用,这使执行时间比必要的要长。 如果我们将位图数据复制到数组中,然后直接操作像素字节,则可以摆脱获取和设置像素的操作。 调用函数 Color.FromArgb(luma,luma,luma) 没有必要,因为它始终返回 R=G=B=luma。 最终的转换方法如下

Public Function ExecuteRgb8( _
         ByVal img As System.Drawing.Image) As System.Drawing.Image
   Dim result As Bitmap = New Bitmap(img)
   Dim bmpData As BitmapData = result.LockBits( _
                  New Rectangle(0, 0, result.Width, result.Height), _
                  ImageLockMode.ReadWrite, img.PixelFormat)
   Dim pixelBytes As Integer = _
            System.Drawing.Image.GetPixelFormatSize(img.PixelFormat) \ 8
   'Get the address of the first line.
   Dim ptr As IntPtr = bmpData.Scan0
   Dim size As Integer = bmpData.Stride * result.Height
   Dim pixels(size - 1) As Byte
   Dim index As Integer
   Dim Y As Integer
   Dim mulR As Double = _factorRed / 100
   Dim mulG As Double = _factorGreen / 100
   Dim mulB As Double = _factorBlue / 100
   'Copy the RGB values into the array.
   System.Runtime.InteropServices.Marshal.Copy(ptr, pixels, 0, size)
   'Main loop.
   For row As Integer = 0 To result.Height - 1
      For col As Integer = 0 To result.Width - 1
         index = (row * bmpData.Stride) + (col * pixelBytes)
         Y = CInt(System.Math.Round( _
                  mulR * pixels(index + 2) + _
                  mulG * pixels(index + 1) + _
                  mulB * pixels(index + 0)))
         If (Y > 255) Then Y = 255
         'Save new values.
         pixels(index + 2) = CByte(Y)
         pixels(index + 1) = CByte(Y)
         pixels(index + 0) = CByte(Y)
      Next
   Next
   'Copy the RGB values back to the bitmap
   System.Runtime.InteropServices.Marshal.Copy(pixels, 0, ptr, size)
   'Unlock the bits.
   result.UnlockBits(bmpData)
   Return result
End Function

该方法使用单独的颜色分量因子来定义颜色分量对灰色阴影的影响。

该函数具有更多的程序步骤,并且看起来更复杂,但是执行时间比使用颜色矩阵的方法短 15%到 20%。

转换函数包装在一个合适的类中,允许更改颜色因子以进行转换。 我的上一篇文章中介绍了滤镜的基础知识:色相饱和度亮度滤镜。 可以使用 Brightness 属性设置两组标准颜色因子。

该类也实现了一种带有颜色矩阵的方法; 您可以使用 UseColorMatrix 属性来控制将哪种方法用于转换。

将滤镜扩展到任何颜色刻度

您可以找到一些不是黑白而是棕白的老照片。 我想更改灰度滤镜以进行这种转换。 完成后,我发现该转换可以完成到任何颜色刻度,因此滤镜不称为棕色刻度,而称为颜色刻度。

对灰度滤镜进行了两个小的更改以执行颜色刻度转换。 首先,我们为主循环外部的每个颜色分量准备颜色表

'Prepare color scale table
Dim paletteR(255) As Byte
Dim paletteG(255) As Byte
Dim paletteB(255) As Byte
Dim c As Integer
For i As Integer = 0 To 255
   c = CInt(Math.Round((CDbl(_endColor.R) - CDbl(_startColor.R)) * _
            CDbl(i) / 255.0 + _startColor.R, 0))
   If c < 0 Then c = 0
   If c > 255 Then c = 255
   paletteR(i) = CByte(c)
   c = CInt(Math.Round((CDbl(_endColor.G) - CDbl(_startColor.G)) * _
            CDbl(i) / 255.0 + _startColor.G, 0))
   If c < 0 Then c = 0
   If c > 255 Then c = 255
   paletteG(i) = CByte(c)
   c = CInt(Math.Round((CDbl(_endColor.B) - CDbl(_startColor.B)) * _
            CDbl(i) / 255.0 + _startColor.B, 0))
   If c < 0 Then c = 0
   If c > 255 Then c = 255
   paletteB(i) = CByte(c)
Next

颜色表计算起初很简单,但是当使用不同的起始和结束颜色时,我不得不切换到双精度和整数以避免溢出。

在主循环中,我们在表中搜索值。

'Save new values.
pixels(index + 2) = paletteR(Y)
pixels(index + 1) = paletteG(Y)
pixels(index + 0) = paletteB(Y)

使用代码

使用滤镜很简单。 设置属性并调用执行函数。

我提供了一个小型演示项目,您可以在其中使用两个滤镜,更改颜色因子,颜色,最后保存过滤后的图像。

使用了两个选项卡,一个用于 RGB 到灰度,另一个用于 RGB 到颜色刻度转换。 在两个选项卡上,如果我们选择自定义阈值,则使用滑块来更改颜色分量的阈值。 在“颜色”选项卡上,我们可以使用颜色选择对话框来选择开始和结束颜色。

RGB 到颜色刻度转换的方法与 RGB 到灰度转换的方法一样快,因为颜色表仅在主循环外部准备一次。 无论如何,使用了一个小技巧来使程序更快。 图像在显示之前会被缩放,并且所有转换都在缩放后的图像上完成。 当用户对过滤后的图像感到满意并保存时,将在保存之前对原始图像进行转换。

关注点

作为程序示例发现的并不总是最佳解决方案。 总是值得尝试,检查和改进。

当您学习到某些东西时,如何使用这些知识取决于您的想象力。

将代码转换为 C 的程序员如果使用“脏”编程而不将位图数据复制到数组中,则可能会获得更快的执行速度。

历史

  • 2007-06-19:版本 1.0。
© . All rights reserved.