2012-02-25 23 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#」などを使用してタイトルを接頭辞ないでください。それがタグのためのものです。 –

+0

@KevinReid申し訳ありませんが、投稿が少し遅かったです! – BigBug

答えて

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 
} 

それはだ、あなたの計算で発生した算術オーバーフローがあります。間違った色の理由。それは255と比較され前にタイプdoubleの発現は、明示的にbyteにキャストされている、したがって、あなたがからアルゴ説明に従っている場合は、少なくとも、(あなたのアルゴ中2つの問題点を持っている255

+1

これは不完全でセピア調の画像を生成しません。 –

+0

これが機能します!ありがとう – BigBug

+1

すべての色のコンポーネントを別々に変換する必要があります、私の答えを参照してください。 –

2

あなたの値は、ラッピングを周りにあふれています。 byte[]はとにかく255上を値を格納することはできませんので(rgbValues[i + 0]) > 255でこれを防御するために

あなたの試みは何の効果を持っていないので、値がオーバーフローし、できるだけ早くあなたがrgbValuesに入れてラップ。 をクランプしてからをアレイに格納する必要があります。 C#には、この目的に優れた関数Math.Min()があります。

一方、オーバーフローを起こしているとすれば、クランプではオーバー露光がクランプされているため、クランプによって「オーバー露光」効果が生成されます(おそらくこれは望ましくありません)。色を変更するが、(知覚される)明るさを変更しないように係数を調整してください(私はこれについての参考文献はありません;申し訳ありません)。

@Yacoderが指摘しているように、完全に別の問題として、最初の行は2番目の入力などの入力を変更するため、計算がオフになります。一時変数の3つの入力または3つの出力のいずれかが必要です。

また、それはあなたがここに手でやっていることだし、システムが提供するバージョンは、おそらく速くなりますのでSystem.Drawing.Imagingは、カラーマトリックス画像変換操作を持っているかどうかを検討することをお勧めします。 (私はそれにコメントすることはできませんので、私はC#を知りません。)

6

より大きくなることはありませんhere)。

まず、他の人が指摘しているように、バイトタイプのオーバーフローが発生しています。 第2に、すべての出力カラー値は入力カラー値に基づいていなければなりません。 MIN関数内に、おそらく当時、私はそうMin(double, double)過負荷が呼び出されintdoubleからカラー値をキャストし、255が最初にdoubleに変換されるとことを

 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 
     } 

注:ここでは

は、固定されたメインループのコードですバイトで、余分な丸めが必要です。場合

誰かがサンプルコンソールアプリケーションのセピアコンバータを必要とする場合、ここで私が持っている最終的なコードがあります:

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が二重であることを明示的に述べるのに '255.0'を使うことができました。 –

+0

@YuriyGuts:私はそれを2倍にしたくありません。255を整数型から1バイトに変換したいので、その変換をバイト範囲内に頼ることができます。 –

関連する問題