c#.NET绿屏背景删除

时间:2014-01-03 10:06:34

标签: c# wpf image-manipulation chromakey

我正在开发适用于Windows 8的台式电脑照片软件。我希望能够通过色度键控从照片中删除绿色背景。

我是图像处理的初学者,我发现了一些很酷的链接(比如http://www.quasimondo.com/archives/000615.php),但我不能用c#代码转换它。

我正在使用网络摄像头(aforge.net)查看预览并拍照。 我尝试了彩色滤镜,但绿色背景不是很均匀,所以这不起作用。

如何在C#中正确地做到这一点?

3 个答案:

答案 0 :(得分:8)

它会起作用,即使背景不统一,你也只需要适当的策略来攫取你所有的绿幕而不更换任何其他东西。

由于链接页面上至少有一些链接已经死亡,我尝试了自己的方法:

  • 基础知识很简单:将图像像素的颜色与某个参考值进行比较,或应用其他一些公式来确定它是否应透明/替换。

  • 最基本的公式将涉及一些简单的事情,并确定绿色是否是最大值"。虽然这适用于非常基本的场景,但它可以搞砸你(例如白色或灰色也会被过滤)。

我使用一些简单的示例代码玩弄了一下。虽然我使用的是Windows Forms,但它应该没有问题,我很确定你能够解释代码。请注意,这不一定是执行此操作的最佳方式。

Bitmap input = new Bitmap(@"G:\Greenbox.jpg");

Bitmap output = new Bitmap(input.Width, input.Height);

// Iterate over all piels from top to bottom...
for (int y = 0; y < output.Height; y++)
{
    // ...and from left to right
    for (int x = 0; x < output.Width; x++)
    {
        // Determine the pixel color
        Color camColor = input.GetPixel(x, y);

        // Every component (red, green, and blue) can have a value from 0 to 255, so determine the extremes
        byte max = Math.Max(Math.Max(camColor.R, camColor.G), camColor.B);
        byte min = Math.Min(Math.Min(camColor.R, camColor.G), camColor.B);

        // Should the pixel be masked/replaced?
        bool replace =
            camColor.G != min // green is not the smallest value
            && (camColor.G == max // green is the biggest value
            || max - camColor.G < 8) // or at least almost the biggest value
            && (max - min) > 96; // minimum difference between smallest/biggest value (avoid grays)

        if (replace)
            camColor = Color.Magenta;

        // Set the output pixel
        output.SetPixel(x, y, camColor);
    }
}

我使用了example image from Wikipedia并获得了以下结果:

Masked result (magenta would be replaced by your background)

请注意,您上面的代码中可能需要不同的阈值(896),您甚至可能希望使用不同的术语来确定是否应替换某些像素。您还可以在帧之间添加平滑,混合(绿色差异较小)等,以减少硬边缘。

答案 1 :(得分:2)

我已经尝试过Mario解决方案,并且效果很好,但是对我来说有点慢。 我寻找了不同的解决方案,然后在这里找到了一个使用更有效方法的项目。 Github postworthy GreenScreen

该项目需要一个文件夹并处理所有文件,我只需要一个图像,所以我这样做了:

private Bitmap RemoveBackground(Bitmap input)
    {
        Bitmap clone = new Bitmap(input.Width, input.Height, PixelFormat.Format32bppArgb);
        {
            using (input)
            using (Graphics gr = Graphics.FromImage(clone))
            {
                gr.DrawImage(input, new Rectangle(0, 0, clone.Width, clone.Height));
            }

            var data = clone.LockBits(new Rectangle(0, 0, clone.Width, clone.Height), ImageLockMode.ReadWrite, clone.PixelFormat);

            var bytes = Math.Abs(data.Stride) * clone.Height;
            byte[] rgba = new byte[bytes];
            System.Runtime.InteropServices.Marshal.Copy(data.Scan0, rgba, 0, bytes);

            var pixels = Enumerable.Range(0, rgba.Length / 4).Select(x => new {
                B = rgba[x * 4],
                G = rgba[(x * 4) + 1],
                R = rgba[(x * 4) + 2],
                A = rgba[(x * 4) + 3],
                MakeTransparent = new Action(() => rgba[(x * 4) + 3] = 0)
            });

            pixels
                .AsParallel()
                .ForAll(p =>
                {
                    byte max = Math.Max(Math.Max(p.R, p.G), p.B);
                    byte min = Math.Min(Math.Min(p.R, p.G), p.B);

                    if (p.G != min && (p.G == max || max - p.G < 7) && (max - min) > 20)
                        p.MakeTransparent();
                });

            System.Runtime.InteropServices.Marshal.Copy(rgba, 0, data.Scan0, bytes);
            clone.UnlockBits(data);

            return clone;
        }
    }

不要忘记处理输入位图和此方法的返回。 如果需要保存图像,请使用位图的保存指令。

clone.Save(@"C:\your\folder\path", ImageFormat.Png);

在这里您可以找到更快处理图像的方法。Fast Image Processing in C#

答案 2 :(得分:0)

照片上的色度键应采用模拟输入。在现实世界中,确切的值非常罕见。

你如何弥补这一点?在色调和色调方面围绕您选择的绿色提供阈值。此阈值(包括)内的任何颜色都应替换为您选择的背景;透明可能是最好的。在第一个链接中,Mask In和Mask Out参数实现了这一点。前后模糊参数尝试使背景更均匀,以减少编码噪声副作用,以便您可以使用更窄(首选)的阈值。

为了提高性能,您可能需要编写一个像素着色器来将“绿色”切换为透明,但这是在您使其工作后的一个考虑因素。