我似乎有一种几乎正常工作的棕褐色调。由于某种原因,图像的一部分原来是柠檬绿!有谁知道我可能做错了什么?方法发布在下面。
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 < rgbValues.Length; i += 4)
{
rgbValues[i + 2] = (byte)((.393 * rgbValues[i + 2]) + (.769 * rgbValues[i + 1]) + (.189 * (rgbValues[i + 0]))); //red
rgbValues[i + 1] = (byte)((.349 * rgbValues[i + 2]) + (.686 * rgbValues[i + 1]) + (.168 * (rgbValues[i + 0]))); //green
rgbValues[i + 0] = (byte)((.272 * rgbValues[i + 2]) + (.534 * rgbValues[i + 1]) + (.131 * (rgbValues[i + 0]))); //blue
if ((rgbValues[i + 2]) > 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);
}
答案 0 :(得分:6)
你的算法中有2个问题(至少,如果你遵循here中的算法描述)。
首先,正如其他人指出的那样,你有字节类型溢出。 其次,所有输出颜色值必须基于输入颜色值,而不是按顺序计算。
这是固定的主循环代码:
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函数内部,我将颜色值从double
转换为int
,否则调用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);
}
}
}
答案 1 :(得分:5)
要解决此问题,请按以下方式更改循环:
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
}
计算中出现算术溢出,这就是错误颜色的原因。 {<1}}类型的表达式在之前显式转换为,然后将其与255进行比较,因此它永远不会超过255。
答案 2 :(得分:2)
你的价值观已经四处蔓延。
您使用(rgbValues[i + 0]) > 255
尝试防范此操作无效,因为byte[]
无法存储超过255的值,因此值会在您放入时立即溢出并包装他们在rgbValues
。在将它们存储在数组中之前,您需要将它们钳位。 C#的函数Math.Min()
非常适用于此目的。
另一方面,鉴于你正在溢出,你可能想要首先解决这个问题 - 夹紧将产生“过度曝光”效应(因为过度曝光是夹紧),这可能是不可取的。调整你的系数,以便你改变颜色但不改变(感知的)亮度(我没有参考;对不起)。
作为一个完全独立的问题,如@Yacoder所述,您的第一行修改了第二行使用的输入,依此类推,因此您的计算将会关闭。您需要临时变量中的三个输入或三个输出。
您可能还想查看System.Drawing.Imaging
是否有颜色矩阵图像转换操作,因为这是您在这里手动执行的操作,系统提供的版本可能会要快点(我不知道C#所以我不能对此发表评论。)