C# 绿屏去除(色度键技术)并将背景图像/视频与网络摄像头流相结合

问题描述 投票:0回答:1

我正在开发一个应用程序,我需要将绿色/蓝色背景屏幕上的人物网络摄像头视频(绿色/蓝色的色调可能不同)与静态背景图像或视频叠加。

不幸的是,我找不到任何专业的解决方案(以C#框架/库的形式)来解决色键图像过滤问题。

我决定使用 OpenCV 库编写自己的解决方案。使用 @Burak 的 Python 解决方案,我通过将代码移植到 C# 部分解决了这个问题:

private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
    Mat mask, result = null;
    var worker = (BackgroundWorker)sender;
    while (!worker.CancellationPending)
    {
        using (var frame = videoCap.RetrieveMat())
        {
            // Get a chroma key mask
            mask = ChromaKeyMask(frame);

            if (videoBack != null)
            {
                if (videoBack.Width != frame.Width && videoBack.Height != frame.Height)
                {
                    videoBack = videoBack.Resize(frame.Size());
                }

                result = videoBack.Clone();

                frame.CvtColor(ColorConversionCodes.RGB2BGR).CopyTo(result, mask);
            }

            Dispatcher.Invoke(() =>
            {
                FrameImage.Source = frame.ToWriteableBitmap();
                MaskImage.Source = mask.ToWriteableBitmap();
                ResultImage.Source = result?.CvtColor(ColorConversionCodes.BGR2RGB).ToWriteableBitmap();

                mask?.Dispose();
                result?.Dispose();
            });
        }

        Thread.Sleep(20);
    }
}

private Mat ChromaKeyMask(Mat src)
{
    Mat mask;
    using (var hsvMat = src.CvtColor(ColorConversionCodes.BGR2HSV))
    {
        var min = new Scalar(minH, minS, minV);
        var max = new Scalar(maxH, maxS, maxV);

        // Threshold the HSV image to extract green color
        mask = hsvMat.InRange(min, max);
        Cv2.BitwiseNot(mask, mask);

        // Smooth edges of the mask
        var size = new OpenCvSharp.Size(3, 3);
        var kernel = Cv2.GetStructuringElement(MorphShapes.Rect, size);
        Cv2.MorphologyEx(mask, mask, MorphTypes.Open, kernel);
        Cv2.GaussianBlur(mask, mask, size, 0, 0);
    }
    return mask;
}

此代码可以工作,但对于生产级来说仍然不够好:蒙版“不稳定”和“浮动”,最终图像与背景组合后有一个绿色的“光环”。

所以,我的问题是:

  • 如何使蒙版更稳定(我已经尝试过基于蒙版的轮廓,但结果与我通过调用
    mask = hsvMat.InRange(min, max);
    得到的蒙版相差不远?
  • 如何正确组合视频帧、蒙版和背景图像/视频帧?也许,具有透明度的 Alpha 通道会有帮助?

我真的很抱歉(我是 OpenCV 和图像处理的新手),但我要求的是特定工作解决方案的答案,而不是关于色度键的一般想法(是的,我已经阅读了维基百科文章并观看了StackOverflow 上有很多问题/答案)。不需要使用C#; Python 和 C++ 都可以正常工作。

在这里您可以找到修改后的@Barak Python脚本,其中包含用于测试的图像。

我尝试了来自 https://www.visioforge.comhttps://www.graphicsmill.com 的不同 SDK:非常不稳定和/或简单无法工作。

我需要一个专业级色度键过滤解决方案,来实时处理网络摄像头流。

c# video-processing chromakey
1个回答
0
投票

绿屏/蓝屏色度键控的常用方法是依靠主体和背景之间的色相值差异。 然而,在您提供的示例图像中,主题图像本身存在一些绿色色调,这使得仅依靠 HSV 量是不够的。 我使用了另一种方法将图像转换为 YUV,因为在 YUV 的 V 分量中,主体图像的边缘和绿色背景之间存在明显的区别。 我对 OpenCV 不熟悉,所以我使用了 LEADTOOLS 成像 SDK(披露:我为其供应商工作)并使用您提供的图像进行了测试。这是我的 C# 代码:

  /* 
    This function assumes the following:
    - Both images have same dimensions
    - Both images have same bits per pixel, preferably 24
    - The location (10, 10) is part of the green background
   */
  private void GreenKeyCombine(RasterImage subject, RasterImage background)
  {
     ColorSeparateCommand sep = new ColorSeparateCommand(ColorSeparateCommandType.Yuv);
     sep.Run(subject);
     sep.DestinationImage.Page = 3; // Take the V plane of YUV and use it as a mask
     RasterImage mask = sep.DestinationImage.Clone();
     IntensityDetectCommand intDet = new IntensityDetectCommand(123, 255, RasterColor.Black, RasterColor.White, IntensityDetectCommandFlags.Master);
     intDet.Run(mask);
     // Create a region in the mask image from the area surrounding the subject
     mask.AddMagicWandToRegion(10, 10, RasterColor.Black, new RasterColor(5, 5, 5), RasterRegionCombineMode.Set);
     // Transfer the region to the subject. Use SetNot to invert the region and make it cover the subject person’s area
     subject.SetRegion(null, mask.GetRegion(null), RasterRegionCombineMode.SetNot);
     LeadRect rc = new LeadRect(0, 0, subject.Width, subject.Height);
     // Combine the subject with the background. The region causes the subject to be copied alone without surrounding area
     CombineFastCommand comb = new CombineFastCommand(background, rc, new LeadPoint(0, 0), CombineFastCommandFlags.SourceCopy);
     comb.Run(subject);
  }

这是我用你的图像得到的结果:

请注意,此处理是使用静态位图完成的,但通过使用成像 SDK 和多媒体 SDK,使用网络摄像头源或任何类型的视频应该很容易完成此处理。这是在 LEADTOOLS 附带的 2 个演示中完成的,它们是 DrawOnVideoDemo 和 CallbackDemo。如果您想尝试代码,您可以从此页面获得 SDK 的免费评估。

© www.soinside.com 2019 - 2024. All rights reserved.