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, Funcbool> func) { for (int i = 0; i < src.Rows; i++) { for (int j = 0; j < src.Cols; j++) { if (func(src.At (i, j))) { src.At (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, Funcbool> 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 (i, j))) { bg.At (i, j) = src.At (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, Funcbool> 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 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; } } } }
素材:
效果展示:(这是视频的一张截图)
完成!!!
图片去绿幕的效果还是很粗糙的,后续会持续更新改进方法,希望大家点赞+关注
并欢迎大家留言...