C# OpenCVSharp图像入门_给绿幕图片视频加背景
阅读原文时间:2023年08月11日阅读:1

  OpenCV的全称是Open Source Computer Vision Library,是一个跨平台的计算机视觉库。OpenCV是由英特尔公司发起并参与开发,以BSD许可证授权发行,可以在商业和研究领域中免费使用。OpenCV可用于开发实时的图像处理计算机视觉以及模式识别程序。该程序库也可以使用英特尔公司的IPP进行加速处理。

  OpenCVSharp是一个.Net平台使用的OpenCV封装库。现在网上关于openCV的教程基本都是c++和python,如果是C#方向,可以跟着这两个语言的步骤自己写demo。

  开始我们今天的课程。

一、分析

  这个功能只要是给绿幕人物加上背景,比如:在直播的时候,因为私密或者其他原因,不想透露出背景

  设想只需要购买一块绿幕,然后上网挑选一张自己喜欢的背景照片,就可以拥有一个好看的背景。

  如何替换视频背景呢?我们一步一步入门!!

  1)绿幕相片扣出人物

  2)人物放进背景图片

  3)操作视频帧图片,实现替换绿幕。

  需要安装的nuget包,注意查看依赖项选版本

OpenCvSharp4
OpenCvSharp4.runtime.win

二、绿幕相片扣出人物

  只展示核心代码

  1)识别绿幕函数(一般操作图片是使用指针的,为了更好理解,我们这里先At执行,后面会讲使用指针改进)

    //删除绿幕  
    private unsafe void RemoveImageScreen(Mat src, Func<Vec3b, bool> func)  
    {  
        for (int i = 0; i < src.Rows; i++)  
        {  
            for (int j = 0; j < src.Cols; j++)  
            {  
                if (func(src.At<Vec3b>(i, j)))  
                {  
                    src.At<Vec3b>(i, j) = new Vec3b(0, 0, 0);  
                }  
            }  
        }  
    }

  2)选择图片并清除绿幕

        using (ResourcesTracker t = new ResourcesTracker())  
        {  
            Bitmap bitmap = new Bitmap(pictBox\_origin.Image);  
            var mat = BitmapConverter.ToMat(bitmap);  
            RemoveImageScreen(mat,  
                p =>  
                {  
                    int max = Math.Max(p.Item0, Math.Max(p.Item1, p.Item2));  
                    if (max == p.Item1 && p.Item1 > 30) //BGR,当G最大时且大于30时,可以根据实际调节这个阈值  
                        return true;  
                    return false;  
                });  
            pictBox\_result.Image = BitmapConverter.ToBitmap(mat\_bg);  

        }

  效果展示:(我是跟着杨神的思路写的这个程序,素材就直接用杨神了,这篇是入门级别,可以看完我这篇再去观摩杨神的)

  杨中科(就是下图这个帅哥):https://www.bilibili.com/read/cv8850462?spm_id_from=333.999.0.0

  

三、人物放进背景图片

  合并图片函数

    private unsafe void MergeImageAt(Mat bg, Mat src, Func<Vec3b, bool> func)  
    {  
        Cv2.Resize(bg, bg, src.Size());//以背景人物大小为准  
        for (int i = 0; i < bg.Rows; i++)  
        {  
            for (int j = 0; j < bg.Cols; j++)  
            {  
                if (func(src.At<Vec3b>(i, j)))  
                {  
                    bg.At<Vec3b>(i, j) = src.At<Vec3b>(i, j);  
                }  
            }  
        }  
    }

  续上上一步,加上合并图片的步骤

        using (ResourcesTracker t = new ResourcesTracker())  
        {  
            Bitmap bitmap = new Bitmap(pictBox\_origin.Image);  
            var mat = BitmapConverter.ToMat(bitmap);  
            var mat\_bg = t.T(Cv2.ImRead("images/bg2.jpg"));  
            RemoveImageScreen(mat,  
                p =>  
                {  
                    int max = Math.Max(p.Item0, Math.Max(p.Item1, p.Item2));  
                    if (max == p.Item1 && p.Item1 > 30)  
                        return true;  
                    return false;  
                });  
            MergeImageAt( mat\_bg, mat,  
                p =>  
                {  
                    if (p == new Vec3b(0, 0, 0))  
                    {  
                        return false;  
                    }  
                    return true;  
                }  
                );  
            pictBox\_result.Image = BitmapConverter.ToBitmap(mat\_bg);  
        }

  效果如图:

  

四、操作视频帧图片,实现替换绿幕

  和图片的区别,就是需要先逐帧获取 视频/摄像机 的图片,然后按上述操作进行

  直接附上完整Demo(已经将两个函数改为指针操作,大家可以先试试原来的At操作,可以明显看到视频是慢速播放)

using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyOpenCV
{
public static class RemoveGreenScreen
{
public static unsafe void Start()
{
//VideoCapture videoCapture = new VideoCapture(1, VideoCaptureAPIs.DSHOW);//摄像头
VideoCapture videoCapture = new VideoCapture(@"images/绿幕视频.mp4");
using (videoCapture)
using (Mat frameMat = new Mat())
using (Mat mat_bg = Cv2.ImRead("images/bg.png"))
{
if (videoCapture.CaptureType == CaptureType.Camera)//如果是摄像头
{
videoCapture.FrameWidth = 800;
videoCapture.FrameHeight = 600;
videoCapture.FourCC = "MJPG";
}
while (true)
{
if (!videoCapture.Read(frameMat))
{
//如果是视频文件,从头部开始播放
if (videoCapture.CaptureType == CaptureType.File)
{
videoCapture.PosFrames = 0;
}
continue;
}
RemoveImageScreen(frameMat,
p =>
{
int max = Math.Max(p.Item0, Math.Max(p.Item1, p.Item2));
if (max == p.Item1 && p.Item1 > 30)
return true;
return false;
});
var bg_clone = mat_bg.Clone();
MergeImage(bg_clone, frameMat,
p =>
{
if (p == new Vec3b(0, 0, 0))
{
return false;
}
return true;
});
Cv2.ImShow("press any key to quit", bg_clone);
if (Cv2.WaitKey(1) > 0)
{
break;
}
}
}
Cv2.DestroyAllWindows();
}

    private static unsafe void RemoveImageScreen(Mat src, Func<Vec3b, bool> func)  
    {  
        Vec3b\* start = (Vec3b\*)src.DataStart;  
        Vec3b\* end = (Vec3b\*)src.DataEnd;  
        for (Vec3b\* p = start; p <= end; p++)  
        {  
            if (func(\*p))  
            {  
                \*p = new Vec3b(0, 0, 0);  
            }  
        }  
    }  
    private static unsafe void MergeImage(Mat bg, Mat src, Func<Vec3b, bool> func)  
    {  
        Cv2.Resize(bg, bg, src.Size());  
        Vec3b\* bg\_pointer = (Vec3b\*)bg.DataStart;  
        Vec3b\* start = (Vec3b\*)src.DataStart;  
        Vec3b\* end = (Vec3b\*)src.DataEnd;  
        for (Vec3b\* p = start; p <= end; p++, bg\_pointer++)  
        {  
            \*bg\_pointer = func(\*p) ? \*p : \*bg\_pointer;  
        }  
    }  
}  

}

  素材:

      

  效果展示:(这是视频的一张截图)

  

  完成!!!

  图片去绿幕的效果还是很粗糙的,后续会持续更新改进方法,希望大家点赞+关注

  并欢迎大家留言…

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章