GDI+中的Lomography效果

3

我想实现与这个教程中类似的方式对图像进行色彩滤镜处理。

目前我不太理解颜色矩阵,我的数学基础也很差,所以有点难理解。

请问有没有示例代码可以帮助我实现滤镜处理的部分?我已经编写了添加暗角效果的代码。

非常感谢提前。

编辑:代码需要安全,因为我想能够在图像处理程序中使用它。如果之前表述不清,请见谅。

1个回答

7
请看这里,我认为那里有你需要的所有内容。 编辑
        public static bool Invert(Bitmap b)
        {
            BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

            int stride = bmData.Stride;
            System.IntPtr Scan0 = bmData.Scan0;

            // Create the Byte-Array
            int bytes = Math.Abs(bmData.Stride) * b.Height;
            byte[] p = new byte[bytes];

            // Copy RGB values into Byte-Array
            System.Runtime.InteropServices.Marshal.Copy(Scan0, p, 0, bytes);


            int nOffset = stride - b.Width * 3;
            int nWidth = b.Width * 3;

            int i = 0;

            for (int y = 0; y < b.Height; ++y)
            {
                for (int x = 0; x < nWidth; ++x)
                {
                        p[i] = (byte)(255 - p[i]);
                        ++i;
                }
                i += nOffset;
            }

            // Copy RGB back to image
            System.Runtime.InteropServices.Marshal.Copy(p, 0, Scan0, bytes);

            b.UnlockBits(bmData);

            return true;
        }

编辑

这是您需要的翻译:

public class Lomography
{

    public static Bitmap getImage(Bitmap bmp)
    {
        using (Graphics g = Graphics.FromImage(bmp))
        {
            using (Bitmap CurveLayer = (Bitmap)bmp.Clone())
            {
                Lomography.SCurve(CurveLayer);
                g.DrawImage(CurveLayer, new Rectangle(0, 0, bmp.Width, bmp.Height));
            }
        }

        using (Graphics g = Graphics.FromImage(bmp))
        {
            using (Bitmap ColorLayer = (Bitmap)bmp.Clone())
            {

                Lomography.Colorize(ColorLayer, -12, 25, -12, Lomography.ColorArea.Shadows);
                g.DrawImage(ColorLayer, new Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel);
            }
        }

        using (Graphics g = Graphics.FromImage(bmp))
        {
            using (Bitmap ColorLayer = (Bitmap)bmp.Clone())
            {

                Lomography.Colorize(ColorLayer, 12, 12, -25, Lomography.ColorArea.Hightlights);
                g.DrawImage(ColorLayer, new Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel);
            }
        }

        using (Graphics g = Graphics.FromImage(bmp))
        {
            RectangleF gradient = new RectangleF(-bmp.Width * 0.3f, -bmp.Height * 0.3f, bmp.Width * 1.6f, bmp.Height * 1.6f);

            GraphicsPath gp = new GraphicsPath();
            gp.AddEllipse(gradient);
            using (PathGradientBrush pgb = new PathGradientBrush(gp))
            {
                pgb.CenterColor = Color.Yellow;
                pgb.CenterPoint = new Point(bmp.Width / 2, bmp.Height / 2);
                ColorBlend cb = new ColorBlend(3);

                cb.Colors[0] = Color.Black;
                cb.Colors[1] = Color.Transparent;
                cb.Colors[2] = Color.Transparent;

                cb.Positions[0] = 0f;
                cb.Positions[1] = 0.55f;
                cb.Positions[2] = 1f;

                pgb.InterpolationColors = cb;
                g.FillEllipse(pgb, gradient);
            }
        }
        return bmp;
    }

    public enum ColorArea
    {
        Midtones,
        Shadows,
        Hightlights
    }

    public static bool Colorize(Bitmap b, int red, int green, int blue, ColorArea ca)
    {
        if (red < -255 || red > 255) return false;
        if (green < -255 || green > 255) return false;
        if (blue < -255 || blue > 255) return false;

        BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

        int bytes = Math.Abs(bmData.Stride) * b.Height;
        int stride = bmData.Stride;
        System.IntPtr Scan0 = bmData.Scan0;

        byte[] p = new byte[bytes];
        System.Runtime.InteropServices.Marshal.Copy(Scan0, p, 0, bytes);

        int i = 0;

        int nOffset = stride - b.Width * 3;
        int nPixel;

        for (int y = 0; y < b.Height; ++y)
        {
            for (int x = 0; x < b.Width; ++x)
            {
                int pdif = (p[i + 2] + p[i + 1] + p[i]) / 3;

                int newred = p[i + 2];
                int newgreen = p[i + 1];
                int newblue = p[i];


                if (ca == ColorArea.Shadows)
                {
                    float multi = (1 - newred / 255);
                    newred += (int)(red * multi);

                    multi = (1 - newgreen / 255);
                    newgreen += (int)(green * multi);

                    multi = (1 - newblue / 255);
                    newblue += (int)(blue * multi);
                }

                if (ca == ColorArea.Hightlights)
                {
                    float multi = (newred / 255);
                    newred += (int)(red * multi);

                    multi = (newgreen / 255);
                    newgreen += (int)(green * multi);

                    multi = (newblue / 255);
                    newblue += (int)(blue * multi);
                }

                if (ca == ColorArea.Midtones)
                {

                    float multi = 0;

                    if (newred > 127)
                        multi = 127f / newred;
                    else
                        multi = newred / 127f;
                    newred += (int)(red * multi);


                    if (newgreen > 127)
                        multi = 127f / newgreen;
                    else
                        multi = newgreen / 127f;
                    newgreen += (int)(green * multi);

                    if (newblue > 127)
                        multi = 127f / newblue;
                    else
                        multi = newblue / 127f;

                    newblue += (int)(blue * multi);
                }


                nPixel = newred;
                nPixel = Math.Max(nPixel, 0);
                p[i + 2] = (byte)Math.Min(255, nPixel);

                nPixel = newgreen;
                nPixel = Math.Max(nPixel, 0);
                p[i + 1] = (byte)Math.Min(255, nPixel);

                nPixel = newblue;
                nPixel = Math.Max(nPixel, 0);
                p[i + 0] = (byte)Math.Min(255, nPixel);

                i += 3;
            }
            i += nOffset;
        }



        System.Runtime.InteropServices.Marshal.Copy(p, 0, Scan0, bytes);
        b.UnlockBits(bmData);

        return true;
    }

    public static bool SCurve(Bitmap b)
    {

        BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

        int bytes = Math.Abs(bmData.Stride) * b.Height;
        int stride = bmData.Stride;
        System.IntPtr Scan0 = bmData.Scan0;

        byte[] p = new byte[bytes];
        System.Runtime.InteropServices.Marshal.Copy(Scan0, p, 0, bytes);

        int i = 0;

        int nOffset = stride - b.Width * 3;
        int nPixel;

        Point[] points = GetCoordinates();

        for (int y = 0; y < b.Height; ++y)
        {
            for (int x = 0; x < b.Width; ++x)
            {
                int hue = (p[i] == 255) ? 255 : -1;
                int hue1 = (p[i + 1] == 255) ? 255 : -1;
                int hue2 = (p[i + 2] == 255) ? 255 : -1;

                int p2 = p[i + 2];

                foreach (var point in points)
                {
                    if (hue2 == -1 && point.X >= p[i + 2])
                        hue2 = point.Y;

                    if (hue1 == -1 && point.X >= p[i + 1])
                        hue1 = point.Y;

                    if (hue == -1 && point.X >= p[i])
                        hue = point.Y;

                    if (hue != -1 && hue1 != -1 && hue2 != -1)
                        break;
                }

                nPixel = hue2;
                nPixel = Math.Max(nPixel, 0);
                p[i + 2] = (byte)Math.Min(255, nPixel);

                nPixel = hue1;
                nPixel = Math.Max(nPixel, 0);
                p[i + 1] = (byte)Math.Min(255, nPixel);

                nPixel = hue;
                nPixel = Math.Max(nPixel, 0);
                p[i + 0] = (byte)Math.Min(255, nPixel);

                i += 3;
            }
            i += nOffset;
        }



        System.Runtime.InteropServices.Marshal.Copy(p, 0, Scan0, bytes);
        b.UnlockBits(bmData);

        return true;
    }

    private static Point[] GetCoordinates()
    {
        List<Point> points = new List<Point>();
        int height = 255;
        int width = 255;

        double y0 = height;
        double y1 = height;
        double y2 = height * 0.75d;
        double y3 = height * 0.5d;

        double x0 = 0;
        double x1 = width * 0.25d;
        double x2 = width * 0.35d;
        double x3 = width * 0.5d;

        for (int i = 0; i < 1000; i++)
        {
            double t = i / 1000d;
            double xt = (-x0 + 3 * x1 - 3 * x2 + x3) * (t * t * t) + 3 * (x0 - 2 * x1 + x2) * (t * t) + 3 * (-x0 + x1) * t + x0;
            double yt = (-y0 + 3 * y1 - 3 * y2 + y3) * (t * t * t) + 3 * (y0 - 2 * y1 + y2) * (t * t) + 3 * (-y0 + y1) * t + y0;


            points.Add(new Point((int)xt, 255 - (int)yt));

        }

        y0 = height * 0.5d;
        y1 = height * 0.25d;
        y2 = 0;
        y3 = 0;

        x0 = width * 0.5d;
        x1 = width * 0.65d;
        x2 = width * 0.75d;
        x3 = width;

        for (int i = 0; i < 1000; i++)
        {
            double t = i / 1000d;

            double xt = (-x0 + 3 * x1 - 3 * x2 + x3) * (t * t * t) + 3 * (x0 - 2 * x1 + x2) * (t * t) + 3 * (-x0 + x1) * t + x0;
            double yt = (-y0 + 3 * y1 - 3 * y2 + y3) * (t * t * t) + 3 * (y0 - 2 * y1 + y2) * (t * t) + 3 * (-y0 + y1) * t + y0;

            points.Add(new Point((int)xt, 255 - (int)yt));
        }
        return points.ToArray();
    }

}

通过调整颜色和曲线,可以得到最佳结果。

曲线中的点是通过2个三次贝塞尔曲线在GetCoordinates函数中计算出来的。第一次运行获取左下曲线的点,第二次运行获取右上点。

enter image description here

编辑

您也可以使用此示例项目来获得最佳颜色和曲线。(您可以拖动曲线上的点)

enter image description here

编辑 为了加快速度,当曲线改变时,请将CurveCoordinates加载一次,而不是在SCurve函数中。将曲线坐标保存在int数组(int[256])中。现在,您不必循环搜索点,只需从数组中获取即可:hue = y = points[x]

    public bool SCurve(Bitmap b)
    {
        BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

        int bytes = Math.Abs(bmData.Stride) * b.Height;
        int stride = bmData.Stride;
        System.IntPtr Scan0 = bmData.Scan0;

        byte[] p = new byte[bytes];
        System.Runtime.InteropServices.Marshal.Copy(Scan0, p, 0, bytes);

        int i = 0;

        int nOffset = stride - b.Width * 3;
        int nPixel;

        int[] points = GetCoordinates();
        if (points == null)
            return false;

        for (int y = 0; y < b.Height; ++y)
        {
            for (int x = 0; x < b.Width; ++x)
            {
                if (state == State.Abort)
                {
                    b.UnlockBits(bmData);
                    return false;
                }

                int hue = points[p[i]];
                int hue1 = points[p[i + 1]];
                int hue2 = points[p[i + 2]];

                int p2 = p[i + 2];



                nPixel = hue2;
                nPixel = Math.Max(nPixel, 0);
                p[i + 2] = (byte)Math.Min(255, nPixel);

                nPixel = hue1;
                nPixel = Math.Max(nPixel, 0);
                p[i + 1] = (byte)Math.Min(255, nPixel);

                nPixel = hue;
                nPixel = Math.Max(nPixel, 0);
                p[i + 0] = (byte)Math.Min(255, nPixel);

                i += 3;

            }
            i += nOffset;
        }



        System.Runtime.InteropServices.Marshal.Copy(p, 0, Scan0, bytes);
        b.UnlockBits(bmData);
        return true;
    }

    private int[] GetCoordinates()
    {
        int[] points = new int[256];

        int height = 255;
        int width = 255;

        double y0 = height;
        double y1 = height * (curve1.p1.Y / (double)curve1.Height);
        double y2 = height * (curve1.p1.Y / (double)curve1.Height);
        double y3 = height * 0.5d;

        double x0 = 0;
        double x1 = width * (curve1.p1.X / (double)curve1.Width);
        double x2 = width * (curve1.p2.X / (double)curve1.Width);
        double x3 = width * 0.5d;

        for (int i = 0; i < 1000; i++)
        {
            double t = i / 1000d;
            double xt = (-x0 + 3 * x1 - 3 * x2 + x3) * (t * t * t) + 3 * (x0 - 2 * x1 + x2) * (t * t) + 3 * (-x0 + x1) * t + x0;
            double yt = (-y0 + 3 * y1 - 3 * y2 + y3) * (t * t * t) + 3 * (y0 - 2 * y1 + y2) * (t * t) + 3 * (-y0 + y1) * t + y0;


            points[(int)xt] = 255 - (int)yt;
        }

        y0 = height * 0.5d;
        y1 = height * (curve1.p3.Y / (double)curve1.Height); ;
        y2 = height * (curve1.p4.Y / curve1.Height);
        y3 = 0;

        x0 = width * 0.5d;
        x1 = width * (curve1.p3.X / (double)curve1.Width);
        x2 = width * (curve1.p4.X / (double)curve1.Width);
        x3 = width;

        for (int i = 0; i < 1000; i++)
        {
            double t = i / 1000d;

            double xt = (-x0 + 3 * x1 - 3 * x2 + x3) * (t * t * t) + 3 * (x0 - 2 * x1 + x2) * (t * t) + 3 * (-x0 + x1) * t + x0;
            double yt = (-y0 + 3 * y1 - 3 * y2 + y3) * (t * t * t) + 3 * (y0 - 2 * y1 + y2) * (t * t) + 3 * (-y0 + y1) * t + y0;

            points[(int)xt] = 255 - (int)yt;
        }

        points[255] = (int)(255 - y3);

        return points;
    }

谢谢。我之前没有表达清楚。那个例子使用了不安全代码的部分。我需要能够在网站上使用它,所以不能使用它。 - James South
是的,但很多服务器会忽略该语句并在机器配置中覆盖它。尽管较慢,但最好使用安全的代码。 - James South
该死!我下班后会开始调试这个,然后再回复你。 - James South
非常感谢你的帮助。如果没有你,我自己肯定需要花费很长时间才能解决这个问题。非常感激! - James South
这段代码可以运行,但SCurve方法似乎运行得非常缓慢 - 正在尝试找出解决方案;需要将针对Point[]数组的for循环替换为其他东西... - Henry C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接