Unity2018.3.10f1,我之前一直是5.6.3开发,5.6.3好像不支持这个opencv 建议升级到2018以上的版本,不然升级可能会出问题
这个抠图Demo为我去年3月29做的测试项目,后面被应用为某KTV里面抠像唱歌的项目 这里只放我的测试项目
之前在简书发的文章:利用OpenCVsharp 抠出摄像头下的人(1)
由于这是我直接在OpenCVDemo里面直接做的,所以有一个Demo是我改动的,别的应该都是他的案例Demo,我做的人物抠图Demo的路径为Assets\OpenCV+Unity\Demo\Face_Detector
每一个Demo的核心代码只有一个脚本,都在demo文件夹中和场景放在一起了。
demo的脚本名为FaceDetectorScene 下载了工程的可以直接去看这个脚本 其中他的核心为OpenCvSharp 这个库,很多函数都在这个库里面 可以直接调用OpenCV写好的算法,非常方便。
以下为我的抠像核心代码,只有片段,全篇请下载我的工程。(由于工程是去年3月份做的,所以我快忘得差不多了。所以请无视掉没有文字解释的注释,当初测试效果时候用的一些测试图片如果你想看效果的话可以开启这些图片)
#region 利用摄像头把人物抠出来
// detect everything we're interested in
processor.ProcessTexture(input, TextureParameters);
// mark detected objects
// processor.MarkDetected();
// processor.Image now holds data we'd like to visualize
output = Unity.MatToTexture(processor.Image, output); // if output is valid texture it's buffer will be re-used, otherwise it will be re-created
//没用的
//Mat answer = Unity.TextureToMat(output);
//摄像头拍到的原始图像赋值给finalMat
finalMat = Unity.TextureToMat(output);
//把原始图像转成灰度图
finalMat = finalMat.CvtColor(ColorConversionCodes.BGR2GRAY);
//摄像头拍到的原始图像赋值给colorMat
colorMat = Unity.TextureToMat(output);
//bool变量控制保存灰度图
if (!save)
{
save = true;
saveMat = finalMat;
//利用一个变量的内存空间重复储存mat转换成Tex2D的图片
saveTex2D = Unity.MatToTexture(finalMat, saveTex2D);
saveImg.texture = saveTex2D;
//saveImg.texture = Unity.MatToTexture(savemat,(Texture2D)saveImg.texture);
}
//原始图的灰度图(1参)和保存图的灰度图(2参) 相减后的图(差值图)差值结果图为一个新的空图
Cv2.Absdiff(finalMat, saveMat, diffmat);
//显示差值图
diffTex2D = Unity.MatToTexture(diffmat, diffTex2D);
diff.texture = diffTex2D;
//差值图高斯模糊,第一个参为需要模糊的图,第二个参为模糊后的图,
//第三个参为高斯模糊的范围(可变参数),第四第五个参数为高斯模糊范围的偏差(可变参数||0)
Cv2.GaussianBlur(diffmat, diffmat, size, sigmaX, sigmaY);
//将高斯模糊后的差值图二值化 第一个参数为阀值(可变参数),第二个参数为二值化的最大值,
//第三个参数为二值化的类型
diffmat = diffmat.Threshold(thresh, maxval, ThresholdTypes.Binary);
//显示经过灰度,差值,高斯模糊后的二值图
//把二值图膨胀 第一个参为需要膨胀的图,第二个参为膨胀后的图,第三个参是元素类型
//第四个参数填null ,第五个是膨胀次数(可变参数)
Cv2.Dilate(diffmat, diffmat, null, null, time, BorderTypes.Constant);
finalTex2D = Unity.MatToTexture(diffmat, finalTex2D);
finalImg.texture = finalTex2D;
//利用经过灰度,差值,高斯模糊后的二值图再膨胀后的图 找轮廓
//第一个参数为需要找的图,第二个参数为空数组,函数完成后会out出值,第三个参数和第二个一样
//第四个参数为需要的轮廓类型(百度) 第五个参数为轮廓近似方法(百度)
Cv2.FindContours(diffmat, out points, out hierarchyIndices,
RetrievalModes.External, ContourApproximationModes.ApproxNone);
//创建一个遮罩 第一个参数是找轮廓的图的size,第二个参数是图片的类型,第三个参数是图片的颜色
Mat hole = new Mat(diffmat.Size(), MatType.CV_8UC3, new Scalar(0, 0, 0, 0));
//Debug.Log(diffmat.Size());
//遍历所有轮廓进行填充里面的内容
for (int i = 0; i < hierarchyIndices.Length; i++)
{
//填充轮廓里面的内容 第一个参数为刚创建的遮罩图,第二个参数为out出来的点的数组
//第三个参数是循环数,第四个参数为要填充的颜色,第五个参数为画线的宽度(-1就是填充)
//后面3个参数不用修改
Cv2.DrawContours(
hole, points, i, Scalar.All(255), -1);
}
//创建一个原图的副本(百度抄的 不懂这一步)
Mat crop = new Mat(colorMat.Rows, colorMat.Cols, MatType.CV_8UC3,new Scalar(0,255,0));
//把膨胀后的图腐蚀掉膨胀效果(去白边)
Cv2.Erode(hole, erodeMat, null, null, erodeTime);
//把膨胀后的图绘制到RawImage上
erodeTex2D = Unity.MatToTexture(erodeMat, erodeTex2D);
erodeImg.texture = erodeTex2D;
//显示遮罩图
maskTex2D = Unity.MatToTexture(erodeMat, maskTex2D);
//Debug.Log(crop.Size());
mask.texture = maskTex2D;
//将原图拷贝到遮罩图层,用原图.出copyto方法,第一个参为刚才创建的原图副本,第二个参数为遮罩
colorMat.CopyTo(crop, erodeMat);
//将最终抠出来的人物图显示在屏幕上
//colorTex2D = Unity.MatToTexture(crop, colorTex2D);
//colorImg.texture = colorTex2D;
#endregion
1.准备一个摄像头
2.打开FaceDetectorScene这个场景
3.确认抠图对象active为true
4.运行demo
5.弹出提示框点击右侧按钮
6.由于我开了抠图的shader所以会自动扣掉黑色的背景(黑色衣服和头发也会被扣)
7.摄像头对准静态背景
8.点击一下koutu对象上FaceDetectorScene脚本中的save按钮
这个按钮会将你当前的背景图片保存和新拍摄的图片进行比对之后抠图
9.进行以上步骤之后即可看见效果(推荐使用外置摄像头)
以下为我使用了笔记本摄像头后抠图的效果
由于我穿的是黑衣服加上用的是笔记本摄像头 所以效果不是特别好,如果用外置摄像头加上纯色静态背景则可以达到不错的效果,根据现场条件你也可以调节脚本上的一些属性来达到比较完美的效果。
本想用蓝奏云传工程的。。没想到最大文件大小为100M
只能用百度云了,如果大家有更好的上传资源的网盘可以私信我
Unity+OpenCV实现人物抠图功能
提取码:alsp
QQ群号:139077032
这个群为csdn爱思考的猴子大佬建的,我当初刚入行时经常看这位大佬的博客。大家同行有兴趣的话可以进群交流分享
后面有空的话会更新之前做过的一些功能的(离我上次更新博客时间过了整整一年)
shader就挂载在koutu对象的材质球上,有兴趣的可以自己看一下
把黑色给改为透明而已(不是我写的 网上找的现成的)