2012-02-25 150 views
2

我似乎有一个几乎工作正常的棕褐色调。出于某种原因,图像的一部分变成柠檬绿!有谁知道我可能会做错什么?方法如下。这个棕褐色调转换算法有什么问题?

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); 

} 

Original Sepia

+1

请不要用“C#”和这样的前缀您的图书。这就是标签的用途。 – 2012-02-25 22:07:32

+0

@KevinReid对不起,发布内容有点慢! – BigBug 2012-02-25 22:14:28

回答

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 
} 

有在计算中出现溢出,这就是错误颜色的原因。 double类型的表达式被显式转换为byte它是相对于255之前,因此比255

+1

这是不完整的,不会产生棕褐色色调图像。 – 2012-02-25 22:33:10

+0

这个工程!谢谢 – BigBug 2012-02-25 22:36:16

+1

每个颜色组件都需要单独转换,请参阅我的答案。 – 2012-02-25 22:37:06

2

你的价值观是围绕溢出和包装。

您试图防止这种情况的(rgbValues[i + 0]) > 255没有效果因为byte[]不能存储值超过255反正,这样的价值观泛滥,只要你把它们放在rgbValues包裹。你需要在之前将它们夹在之前,将它们存储在数组中。 C#有一个功能Math.Min(),这对于这个目的非常好。另一方面,考虑到你已经溢出了,你可能首先要解决这个问题 - 夹持会产生一个“过度曝光”的效果(因为曝光过度是夹持的),这可能是不希望的。调整你的系数,使你改变颜色,但不改变(感知)的亮度(我没有这个参考;对不起)。

作为一个完全独立的问题,正如@Yacoder所指出的那样,您的第一行修改了第二行使用的输入等等,所以您的计算将关闭。您需要临时变量中的三个输入或三个输出。

你可能也想看看是否System.Drawing.Imaging彩色矩阵图像变换操作,因为这是你的手在这里做什么,以及系统提供的版本可能会更快。 (我不知道C#,所以我不能对此作出评论。)

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函数里面我投的颜色值从doubleint否则Min(double, double)超载被称为255最先被转换为double,然后可能回字节,涉及额外的四舍五入。

在情况下,如果有人需要样本控制台应用程序怀旧转换器,这里是最后的代码,我有:

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); 
     } 
    } 
} 
+0

你可以刚刚使用'255.0'明确指出255是一个双。 – 2012-02-25 22:32:16

+0

@YuriyGuts:我不想让它变成双倍的,我想把255从整数类型转换为字节,所以我可以依靠这个转换来处理字节范围。 – 2012-02-25 22:35:28