wpf c# .NET 绿屏背景去除

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20901093/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 10:23:31  来源:igfitidea点击:

c# .NET green screen background remove

c#wpfimage-manipulationchromakey

提问by user2964381

I am working on a photo software for desktop PC that works on Windows 8. I would like to be able to remove the green background from the photo by means of chroma keying.

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

I'm a beginner in image manipulation, i found some cool links ( like http://www.quasimondo.com/archives/000615.php), but I can't transale it in c# code.

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

I'm using a webcam (with aforge.net) to see a preview and take a picture. I tried color filters but the green background isn't really uniform, so this doesn't work.

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

How to do that properly in C#?

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

回答by Mario

It will work, even if the background isn't uniform, you'll just need the proper strategy that is generous enough to grab all of your greenscreen without replacing anything else.

它会起作用,即使背景不统一,您只需要适当的策略,该策略足够慷慨以获取所有绿屏而无需替换任何其他内容。

Since at least some links on your linked page are dead, I tried my own approach:

由于您的链接页面上至少有一些链接已失效,因此我尝试了自己的方法:

  • The basics are simple: Compare the image pixel's color with some reference value or apply some other formula to determine whether it should be transparent/replaced.

  • The most basic formula would involve something as simple as "determine whether green is the biggest value". While this would work with very basic scenes, it can screw you up (e.g. white or gray will be filtered as well).

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

  • 最基本的公式将涉及诸如“确定绿色是否为最大值”之类的简单内容。虽然这适用于非常基本的场景,但它可能会把你搞砸(例如,白色或灰色也会被过滤掉)。

I've toyed around a bit using some simple sample code. While I used Windows Forms, it should be portable without problems and I'm pretty sure you'll be able to interpret the code. Just note that this isn't necessarily the most performant way to do this.

我使用一些简单的示例代码玩弄了一下。虽然我使用了 Windows 窗体,但它应该是可移植的,没有问题,而且我很确定您将能够解释代码。请注意,这不一定是执行此操作的最高效方法。

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

I've used an example image from Wikipediaand got the following result:

我使用了Wikipedia 中示例图像并得到以下结果:

Masked result (magenta would be replaced by your background)

Masked result (magenta would be replaced by your background)

Just note that you might need different thresholds (8and 96in my code above), you might even want to use a different term to determine whether some pixel should be replaced. You can also add smoothening between frames, blending (where there's less green difference), etc. to reduce the hard edges as well.

不过请注意,你可能需要不同的阈值(896在我上面的代码),你甚至可能要使用不同的术语,以确定一些像素是否要进行更换。您还可以在帧之间添加平滑、混合(绿色差异较小的地方)等,以减少硬边。

回答by Francesc MP

I've tried Mario solution and it worked perfectly but it's a bit slow for me. I looked for a different solution and I found a project that uses a more efficient method here. Github postworthy GreenScreen

我已经尝试过马里奥解决方案,它工作得很好,但对我来说有点慢。我寻找了不同的解决方案,并在这里找到了一个使用更有效方法的项目。 Github 发布后的 GreenScreen

That project takes a folder and process all files, I just need an image so I did this:

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

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

Do not forget to dispose of your Input Bitmap and the return of this method. If you need to save the image just use the Save instruction of Bitmap.

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

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

Here you can find methods to process an image even faster.Fast Image Processing in C#

在这里,您可以找到更快地处理图像的方法。C#中的快速图像处理

回答by Gusdor

Chromakey on a photo should assume an analog input. In the real world, exact values are very rare.

照片上的色度应假定为模拟输入。在现实世界中,精确值非常罕见。

How do you compensate for this? Provide a threshold around the green of your choice in both hue and tone. Any colour within this threshold (inclusive) should be replaced by your chosen background; transparent may be best. In the first link, the Mask In and Mask Out parameters achieve this. The pre and post blur parameters attempt to make the background more uniform to reduce encoding noise side effects so that you can use a narrower (preferred) threshold.

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

For performance, you may want to write a pixel shader to zap the 'green' to transparent but that is a consideration for after you get it working.

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