最近用AzureKinect测试无背景抠像,发现头发总是缺失一块。
解决办法:用Opencv图像处理提取头发部分,然后与原图像叠加。
环境:Opencv for unity 、 AzureKinect for unity 、unity2019.2.9
详细说明:
1、首先提取Kinect彩色图像colorTexture和人体alphaTexture(这两个texture在Azure for unity里面的BackgroundRemovalManager可以得到)并转为Mat。(下图位置1)
2、获取头部位置,在该位置画一个圆形遮罩。然后再将彩色图像根据遮罩提取头部,将头部图像转为HSV并通过阈值提取出头发部分的遮罩(需要用opencvForUnity,opencvForUnity 其实是移植的opencv for java,如果找不到unity的实现,建议初学者(包括我哈哈)去寻找java的实现。)(下图位置2)
3、将人像alphaMat与第二部提取的遮罩合并。得到处理过的遮罩。(下图位置3)
4、将彩色图像根据第三步的遮罩提取出来人像,这样就修复了头发破损。
主要代码块:
void body_getHair()
{
Vector3 pos = Camera.main.WorldToViewportPoint(GameObject.Find("GreenBall").transform.position);
GameObject.Find("01").GetComponent<RawImage>().texture = GetAlphaTex();
HeadPos = new Point((1-pos.x) * 1920, (pos.y) * 1080+100);
print("headPos:"+HeadPos);
if (colorTexture&&alphaTexture)
{
RenderTexture2Texture2D_ColorTex(colorTexture); //将texture转成texture2D方便转成Mat,
RenderTexture2Texture2D_AlphaTex(alphaTexture);//将texture转成texture2D方便转成Mat
src = new Mat(tsr.height, tsr.width, CvType.CV_8UC4);
Utils.texture2DToMat(tsr, src);
src_alpha = new Mat(tsr_alpha.height, tsr_alpha.width, CvType.CV_8UC1);
Utils.texture2DToMat(tsr_alpha, src_alpha);
Mat imgOrigin = src.clone();
Mat maskCopyTo = Mat.zeros(imgOrigin.size(), CvType.CV_8UC1); // 创建copyTo方法的mask,大小与原图保持一致
Imgproc.circle(maskCopyTo, HeadPos, 160, Scalar.all(255), -2, 8, 0); // 画出圆的轮廓
Mat copy = new Mat();
imgOrigin.copyTo(copy, maskCopyTo);
Utils.matToTexture2D(copy, t2);
GameObject.Find("02").GetComponent<RawImage>().texture = t2;
Mat hsv = new Mat();
Imgproc.cvtColor(copy, hsv, Imgproc.COLOR_BGR2HSV);
Mat maskk = hsv.clone();
Core.inRange(hsv, new Scalar(0, 0, 0), new Scalar(180, 255, 46), maskk);
Mat newMask = new Mat();
maskk.copyTo(newMask, maskCopyTo);
Mat dist = new Mat();
Core.add(src_alpha, newMask, dist);
Utils.matToTexture2D(dist, t3);
GameObject.Find("03").GetComponent<RawImage>().texture = t3;
Mat last = new Mat();
src.copyTo(last, dist);
print(dist.width() + ":" + dist.height());
Utils.matToTexture2D(last, t4);
GameObject.Find("04").GetComponent<RawImage>().texture = t4;
}
}
public void RenderTexture2Texture2D_ColorTex(RenderTexture rt)
{
RenderTexture preRT = RenderTexture.active;
RenderTexture.active = rt;
tsr.ReadPixels(new UnityEngine.Rect(0, 0, rt.width, rt.height), 0, 0);
tsr.Apply();
RenderTexture.active = preRT;
}
public void RenderTexture2Texture2D_AlphaTex(RenderTexture rt)
{
RenderTexture preRT = RenderTexture.active;
RenderTexture.active = rt;
tsr_alpha.ReadPixels(new UnityEngine.Rect(0, 0, rt.width, rt.height), 0, 0);
tsr_alpha.Apply();
RenderTexture.active = preRT;
}