这种棕褐色调转换算法有什么问题?

我似乎有一种几乎正常工作的棕褐色调。 由于某种原因,图像的一部分原来是柠檬绿! 有谁知道我可能做错了什么? 方法发布在下面。

private void SepiaBitmap(Bitmap bmp) { Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppRgb); IntPtr ptr = bmpData.Scan0; int numPixels = bmpData.Width * bmp.Height; int numBytes = numPixels * 4; byte[] rgbValues = new byte[numBytes]; System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, numBytes); for (int i = 0; i  255) { rgbValues[i + 2] = 255; } if ((rgbValues[i + 1]) > 255) { rgbValues[i + 1] = 255; } if ((rgbValues[i + 0]) > 255) { rgbValues[i + 0] = 255; } } System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, numBytes); this.Invalidate(); bmp.UnlockBits(bmpData); } 

原版的乌贼

要解决此问题,请更改此循环:

 for (int i = 0; i < rgbValues.Length; i += 4) { int red = rgbValues[i + 2]; int green = rgbValues[i + 1]; int blue = rgbValues[i + 0]; rgbValues[i + 2] = (byte)Math.Min((.393 * red) + (.769 * green) + (.189 * blue), 255.0); // red rgbValues[i + 1] = (byte)Math.Min((.349 * red) + (.686 * green) + (.168 * blue), 255.0); // green rgbValues[i + 0] = (byte)Math.Min((.272 * red) + (.534 * green) + (.131 * blue), 255.0); // blue } 

计算中出现算术溢出,这就是错误颜色的原因。 类型double的表达式与255进行比较之前显式地转换为byte ,因此它永远不会大于255。

您的算法中有2个问题(至少,如果您从此处遵循算法描述)。

首先,正如其他人指出的那样,你有字节类型溢出。 其次,所有输出颜色值必须基于输入颜色值,而不是按顺序计算。

这是固定的主循环代码:

  for (int i = 0; i < rgbValues.Length; i += 4) { int inputRed = rgbValues[i + 2]; int inputGreen = rgbValues[i + 1]; int inputBlue = rgbValues[i + 0]; rgbValues[i + 2] = (byte) Math.Min(255, (int)((.393 * inputRed) + (.769 * inputGreen) + (.189 * inputBlue))); //red rgbValues[i + 1] = (byte) Math.Min(255, (int)((.349 * inputRed) + (.686 * inputGreen) + (.168 * inputBlue))); //green rgbValues[i + 0] = (byte) Math.Min(255, (int)((.272 * inputRed) + (.534 * inputGreen) + (.131 * inputBlue))); //blue } 

请注意,在Min函数内部,我将颜色值从doubleint否则调用Min(double, double)重载,255首先转换为double,然后可能返回到byte,包括额外的舍入。

如果有人需要一个示例控制台应用程序棕褐色转换器,这里是我的最终代码:

 namespace ConsoleApplication8_Sepia { using System; using System.Drawing; using System.Drawing.Imaging; class Program { static void Main(string[] args) { Bitmap b = (Bitmap)Bitmap.FromFile("c:\\temp\\source.jpg"); SepiaBitmap(b); b.Save("c:\\temp\\destination.jpg", ImageFormat.Jpeg); } private static void SepiaBitmap(Bitmap bmp) { Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb); IntPtr ptr = bmpData.Scan0; int numPixels = bmpData.Width * bmp.Height; int numBytes = numPixels * 4; byte[] rgbValues = new byte[numBytes]; System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, numBytes); for (int i = 0; i < rgbValues.Length; i += 4) { int inputRed = rgbValues[i + 2]; int inputGreen = rgbValues[i + 1]; int inputBlue = rgbValues[i + 0]; rgbValues[i + 2] = (byte)Math.Min(255, (int)((.393 * inputRed) + (.769 * inputGreen) + (.189 * inputBlue))); //red rgbValues[i + 1] = (byte)Math.Min(255, (int)((.349 * inputRed) + (.686 * inputGreen) + (.168 * inputBlue))); //green rgbValues[i + 0] = (byte)Math.Min(255, (int)((.272 * inputRed) + (.534 * inputGreen) + (.131 * inputBlue))); //blue } System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, numBytes); bmp.UnlockBits(bmpData); } } } 

你的价值观四溢,四处乱窜。

您尝试使用(rgbValues[i + 0]) > 255来防止这种情况无效, 因为无论如何byte[]无法存储超过255的值,因此只要将它们放入rgbValues ,这些值就会溢出并包装。 将它们存储在arrays中之前,您需要将它们钳制起来。 C#有一个Math.Min()函数,非常适合这个目的。

另一方面,鉴于你正在溢出,你可能想要首先解决这个问题 – 夹紧会产生“过度曝光”效应(因为过度曝光是夹紧),这可能是不可取的。 调整你的系数,以便你改变颜色,但不改变(感知)亮度(我没有参考;对不起)。

作为一个完全独立的问题,如@Yacoder所述,您的第一行修改了第二行使用的输入,依此类推,因此您的计算将会关闭。 您需要临时变量中的三个输入或三个输出。

您可能还想查看System.Drawing.Imaging是否具有颜色矩阵图像转换操作,因为这是您在这里手动执行的操作,系统提供的版本可能会更快。 (我不知道C#所以我不能对此发表评论。)