5

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

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

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

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

4

3 回答 3

8

它会起作用,即使背景不统一,您只需要足够大方的适当策略即可抓住所有绿屏而不更换其他任何东西。

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

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

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

我使用一些简单的示例代码玩弄了一些。虽然我使用的是 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);
    }
}

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

蒙面结果(洋红色将被您的背景取代)

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

于 2014-01-03T11:22:20.737 回答
4

我已经尝试过马里奥解决方案,它运行良好,但对我来说有点慢。我寻找了一个不同的解决方案,并在这里找到了一个使用更有效方法的项目。 Github 值得关注的 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;
        }
    }

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

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

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

于 2018-11-12T10:47:12.093 回答
0

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

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

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

于 2014-01-03T10:23:55.367 回答