What is wrong with this sepia tone conversion algorithm?

前端 未结 3 886
刺人心
刺人心 2021-02-11 01:41

I seem to have a sepia tone that is almost working properly. For some reason a portion of the image turns out to be lime green! Does anyone know what i might be doing wrong? Met

3条回答
  •  轮回少年
    2021-02-11 01:56

    You have 2 problems in your algo (at least, if you follow algo description from here).

    First, as others pointed out, you have byte type overflow. Second, all your output color values must be based on the input color values, not calculated sequentially.

    Here's the fixed main loop code:

            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
            }
    

    Note that inside the Min function I cast the color value from double to int otherwise Min(double, double) overload is called and 255 gets first converted to double and then possibly back to byte, involving an extra rounding.

    In case if someone needs a sample console app sepia converter, here's the final code I have:

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

提交回复
热议问题