Opencv for Unity去除离散区域

老规矩,先看效果图:


如图所示,图上有一些离散的区域,需要将这些区域找到并去除,还要将透明通道图的毛刺边缘做平滑处理。实现思路是这样的:

  1. 拿到图的Alpha通道,利用Core.split()方法将原图的通道分离而得到。
  2. 利用方法findContours()方法查找Alpha通道的连续区域
  3. 通过对比区域的面积,拿到面积小于10000的区域集合和大于10000的区域集合
  4.  利用Imgproc.drawContours()方法将大面积区域画成白色,小面积区域画成黑色
  5. 将Imgproc.blur()方法和二值化(写的算法,不是Imgproc.threshold)
  6. 最后利用Mat.copyTo()方法,将原图和Alpha通道图生成最后的图

上代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using OpenCVForUnity;
using System.IO;
using UnityEngine.UI;
public class FindDongBoke : MonoBehaviour {

	// Use this for initialization
	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 () {
		
	}
}

对于不太了解ps等相关技能的开发者,比较难理解其中的逻辑。


猜你喜欢

转载自blog.csdn.net/thinkcg/article/details/76064772