Opencv for Unity去除离散区域
老规矩,先看效果图:
如图所示,图上有一些离散的区域,需要将这些区域找到并去除,还要将透明通道图的毛刺边缘做平滑处理。实现思路是这样的:
- 拿到图的Alpha通道,利用Core.split()方法将原图的通道分离而得到。
- 利用方法findContours()方法查找Alpha通道的连续区域
- 通过对比区域的面积,拿到面积小于10000的区域集合和大于10000的区域集合
- 利用Imgproc.drawContours()方法将大面积区域画成白色,小面积区域画成黑色
- 将Imgproc.blur()方法和二值化(写的算法,不是Imgproc.threshold)
- 最后利用Mat.copyTo()方法,将原图和Alpha通道图生成最后的图
上代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using OpenCVForUnity;
using System.IO;
using UnityEngine.UI;
public class FindDongBoke : MonoBehaviour {
void Start () {
ChuliQuyu(Resources.Load("bq") as Texture2D);
}
void ChuliQuyu(Texture2D t2dSrc)
{
Mat matSrc;
/// <summary>
/// 结果Mat
/// </summary>
Mat matO;
/// <summary>
/// alpha通道
/// </summary>
Texture2D t2dAlpha;
/// <summary>
/// 处理后的遮罩
/// </summary>
Texture2D t2dAlphaChuli;
Texture2D t2dJieguo;
/// <summary>
/// 原图分离出的通道
/// </summary>
List<Mat> mats = new List<Mat>();
//区域集合
List<MatOfPoint> contours = new List<MatOfPoint>();
MatOfPoint hullPointMat = new MatOfPoint();
//小区域集合
List<MatOfPoint> contours_xiao = new List<MatOfPoint>();
//大区域集合,人头
List<MatOfPoint> contours_da = new List<MatOfPoint>();
t2dAlpha = new Texture2D(t2dSrc.width, t2dSrc.height, TextureFormat.ARGB32, false);
t2dAlphaChuli = new Texture2D(t2dSrc.width, t2dSrc.height, TextureFormat.ARGB32, false);
t2dJieguo = new Texture2D(t2dSrc.width, t2dSrc.height, TextureFormat.ARGB32, false);
matSrc = new Mat(t2dSrc.height, t2dSrc.width, CvType.CV_8UC4);
//非常重要,一定要定义为1通道,不知道为什么----------------否则效果不恒定,一会正确一会错误---------------------------------------------------------------------------------------
matO = new Mat(matSrc.size(), CvType.CV_8UC1, new Scalar(0, 0, 0, 0));
Utils.texture2DToMat(t2dSrc, matSrc);
//111.切割4通道matSrc,需要其alpha通道
Core.split(matSrc, mats);
Debug.Log("原图的通道数::" + mats.Count);
//222.二值化通道,否则可能会有不干净的背景区域与头部联通,二值化可以打断这些可能存在的联通
Imgproc.threshold(mats[3], mats[3], 128, 255, Imgproc.THRESH_TOZERO);
//333.二值化通道转成Texture2D
Utils.matToTexture2D(mats[3], t2dAlpha);
File.WriteAllBytes(Application.streamingAssetsPath + "/" + t2dSrc.name + "_Alpha.png", t2dAlpha.EncodeToPNG());
//444.查找区域集合
Imgproc.findContours(mats[3], contours, hullPointMat, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
Debug.Log("alpha通道图上的区域数量::" + contours.Count);
//555.计算大小区域集合
for (int i = 0; i < contours.Count; i++)
{
//print(Imgproc.contourArea(contours[i]));
if (Imgproc.contourArea(contours[i]) < 10000)
{
contours_xiao.Add(contours[i]);
}
else
{
Debug.Log("面积不小于10000" + Imgproc.contourArea(contours[i]));
contours_da.Add(contours[i]);
}
}
//666.大区域画白,小区域画黑
Imgproc.drawContours(mats[3], contours_xiao, -1, new Scalar(0, 0, 0), -1);
Imgproc.drawContours(mats[3], contours_da, -1, new Scalar(255, 255, 255), -1);
Utils.matToTexture2D(mats[3], t2dAlphaChuli);
File.WriteAllBytes(Application.streamingAssetsPath + "/" + t2dSrc.name + "_AlphaChuli.png", t2dAlphaChuli.EncodeToPNG());
#region 通道图边缘平滑处理,去毛刺
//777.模糊处理
Imgproc.blur(mats[3], mats[3], new Size(7, 7));
Utils.matToTexture2D(mats[3], t2dAlphaChuli);
//888.再次二值化,模糊和二值化产生平滑边缘的效果
for (int i = 0; i < t2dAlphaChuli.width; i++)
{
for (int j = 0; j < t2dAlphaChuli.height; j++)
{
if (t2dAlphaChuli.GetPixel(i, j).r < 0.5f)
t2dAlphaChuli.SetPixel(i, j, Color.black);
else
t2dAlphaChuli.SetPixel(i, j, Color.white);
}
}
t2dAlphaChuli.Apply();
File.WriteAllBytes(Application.streamingAssetsPath + "/" + t2dSrc.name + "_AlphaChuliSmooth.png", t2dAlphaChuli.EncodeToPNG());
Utils.texture2DToMat(t2dAlphaChuli, mats[3]);
#endregion
//999.利用遮罩从原图取图,遮罩中非0的区域保持,0的区域清除
matSrc.copyTo(matO, mats[3]);
Utils.matToTexture2D(matO, t2dJieguo);
GameObject.Find("Canvas/RawImage1").GetComponent<RawImage>().texture = t2dAlpha;
GameObject.Find("Canvas/RawImage2").GetComponent<RawImage>().texture = t2dAlphaChuli;
GameObject.Find("Canvas/RawImage3").GetComponent<RawImage>().texture = t2dJieguo;
File.WriteAllBytes(Application.streamingAssetsPath + "/" + t2dSrc.name + "_Jieguo.png", t2dJieguo.EncodeToPNG());
matSrc.Dispose();
matO.Dispose();
mats.Clear();
contours.Clear();
contours_da.Clear();
contours_xiao.Clear();
t2dSrc = null;
t2dAlpha = null;
t2dAlphaChuli = null;
}
// Update is called once per frame
void Update () {
}
}