OpenCV

OpencvForUnity 插件的文檔  :https://enoxsoftware.github.io/OpenCVForUnity/3.0.0/doc/html/annotated.html

OpenCVSharp :    https://github.com/shimat/opencvsharp

OpenCVSharp 文檔: https://shimat.github.io/opencvsharp_docs/html/47b728ac-aff3-f998-4902-42d117710f4d.htm#!

如果沒使用Nuget則獲取DLL文件的方式,https://github.com/shimat/opencvsharp/releases

一、沒有模板的情況——根據一定規則進行識別

對於背景不是純色的圖片:

    /// <summary>
    /// 分水嶺算法
    /// MatchShapes Example
    /// https://baike.baidu.com/item/%E5%9B%BE%E5%83%8F%E4%BA%8C%E5%80%BC%E5%8C%96/1748870?fr=aladdin
    /// http://docs.opencv.org/3.1.0/d5/d45/tutorial_py_contours_more_functions.html
    /// https://docs.opencv.org/3.1.0/df/d4e/group__imgproc__c.html#ga5c79f8ca95dc7a93f7142b218bea61f3  //文檔
    /// https://docs.opencv.org/3.1.0/d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57  //文檔  threshold
    /// 
    ///  會把背景上的顏色分割出來,對圖像輪廓點進行多邊形擬合,對比頂點個數 【分水嶺算法】  優先使用二值法,不行了再考慮這種方法,這種方法誤差較大

 

using UnityEngine;
using System.Collections.Generic;
using UnityEngine.UI;
using System.Collections;

#if UNITY_5_3 || UNITY_5_3_OR_NEWER
using UnityEngine.SceneManagement;
#endif
using OpenCVForUnity;

namespace OpenCVForUnityExample
{
    /// <summary>
    /// 分水嶺算法
    /// MatchShapes Example
    /// https://baike.baidu.com/item/%E5%9B%BE%E5%83%8F%E4%BA%8C%E5%80%BC%E5%8C%96/1748870?fr=aladdin
    /// http://docs.opencv.org/3.1.0/d5/d45/tutorial_py_contours_more_functions.html
    /// https://docs.opencv.org/3.1.0/df/d4e/group__imgproc__c.html#ga5c79f8ca95dc7a93f7142b218bea61f3  //文檔
    /// https://docs.opencv.org/3.1.0/d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57  //文檔  threshold
    /// https://enoxsoftware.github.io/OpenCVForUnity/3.0.0/doc/html/annotated.html  //這個插件的文檔
    ///  會把背景上的顏色分割出來,對圖像輪廓點進行多邊形擬合,對比頂點個數 【分水嶺算法】  優先使用二值法,不行了再考慮這種方法,這種方法誤差較大
    /// 
    /// </summary>
    public class test_MatchShapesExample : MonoBehaviour
    {
        public float threshValue = 127;  //閥值
        public float periMin = 200;
        public float periMax = 1000;
        void Update()
        {
            if (Input.GetMouseButtonDown(1))
            {

                //分水嶺算法
                //srcMat
                Texture2D srcTexture = Resources.Load("matchshapes_Creat_Src") as Texture2D;
                Mat srcMat = new Mat(srcTexture.height, srcTexture.width, CvType.CV_8UC1);
                Utils.texture2DToMat(srcTexture, srcMat);
                Debug.Log("srcMat.ToString() " + srcMat.ToString());
                Imgproc.threshold(srcMat, srcMat, threshValue, 255, Imgproc.THRESH_OTSU);   

                //dstMat
                Texture2D dstTexture = Resources.Load("matchshapes_Creat_Src") as Texture2D;
                Mat dstMat = new Mat(dstTexture.height, dstTexture.width, CvType.CV_8UC3);
                Utils.texture2DToMat(dstTexture, dstMat);
                Debug.Log("dstMat.ToString() " + dstMat.ToString());


                List<MatOfPoint> srcContours = new List<MatOfPoint>();
                Mat srcHierarchy = new Mat();

                /// Find srcContours
                Imgproc.findContours(srcMat, srcContours, srcHierarchy, Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_NONE);

                Debug.Log("srcContours.Count " + srcContours.Count);

                for (int i = 0; i < srcContours.Count; i++)
                {
                    Imgproc.drawContours(dstMat, srcContours, i, new Scalar(255, 0, 0), 2, 8, srcHierarchy, 0, new Point());
                }

                for (int i = 0; i < srcContours.Count; i++)
                {
                    double returnVal = Imgproc.matchShapes(srcContours[1], srcContours[i], Imgproc.CV_CONTOURS_MATCH_I1, 0);
                    //Debug.Log("returnVal " + i + " " + returnVal);

                    MatOfPoint2f newMatOfPoint2f = new MatOfPoint2f(srcContours[i].toArray());
                    //主要是計算圖像輪廓的周長
                    double peri = Imgproc.arcLength(newMatOfPoint2f, true);
                    if (peri < periMin || peri > periMax)
                    {
                        continue;
                    }
                    else
                    {
                        //對圖像輪廓點進行多邊形擬合
                        MatOfPoint2f polyShape = new MatOfPoint2f();
                        Imgproc.approxPolyDP(newMatOfPoint2f, polyShape, 0.04f * peri, true);
                        int shapeLen = polyShape.toArray().Length;

                        if (shapeLen >= 3 && shapeLen <= 20)
                        {
                            Debug.LogWarning(shapeLen + "        " + returnVal);
                            OpenCVForUnity.Rect rect = Imgproc.boundingRect(srcContours[i]);
                            float width = rect.width;
                            float height = rect.height;
                            float ar = width / height;
                            //計算寬高比,判斷是矩形還是正方形
                            string name = null;
                            if (shapeLen == 3) { name = "triangle"; }
                            else if (shapeLen == 4)
                            {
                                //計算寬高比,判斷是矩形還是正方形
                                if (ar >= 0.95 && ar <= 1.05) { name = "square"; } else { name = "rectangle"; }
                            }
                            else { name = shapeLen.ToString(); }

                            Point point = new Point();
                            float[] radius = new float[1];
                            Imgproc.minEnclosingCircle(new MatOfPoint2f(srcContours[i].toArray()), point, radius);
                            //Debug.Log("point.ToString() " + point.ToString());
                            //Debug.Log("radius.ToString() " + radius[0]);

                            Imgproc.circle(dstMat, point, 5, new Scalar(0, 0, 255), -1);
                            Imgproc.putText(dstMat, " " + name, point, Core.FONT_HERSHEY_SIMPLEX, 0.4, new Scalar(0, 255, 0), 1, Imgproc.LINE_AA, false);
                        }
                    }
                }
                Texture2D texture = new Texture2D(dstMat.cols(), dstMat.rows(), TextureFormat.RGBA32, false);
                Utils.matToTexture2D(dstMat, texture);
                GameObject.Find("pic_RawImage").GetComponent<RawImage>().texture = texture;
            }
        }

    }
}

對於背景是純色的圖片識別圖片中的形狀:

            //此圖同背景的差別表現不在灰度值上(比如紋理不同),所以將這個差別特徵轉換爲灰度的差別,然後利用閾值選取技術來分割該圖像,動態調節閾值實現圖像的二值化可動態觀察其分割圖像的具體結果。
            //一般用在背景是同一種顏色時,如果背景含有多種顏色,則不行  
using OpenCVForUnity;
using OpenCVForUnityExample;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// https://blog.csdn.net/u013293580/article/details/84710933#commentsedit
/// </summary>
public class shapeDectedScript : MonoBehaviour {

    public RawImage color_Pic; //UI
    private Mat srcMat; //Mat格式存放處理的圖片

    public float size=5;
    public float threshValue = 80;  //閥值
    private Mat dstMat; //Mat格式存放處理的圖片
    private Texture2D flipTexture;
    public List<ShapeInfo> listShape=new List<ShapeInfo>(); 

    private void Update()
    {
            if (Input.GetMouseButtonDown(1))
        {


            System.Diagnostics.Stopwatch oTime = new System.Diagnostics.Stopwatch();   //定義一個計時對象  
            oTime.Start();


            listShape.Clear();
            Texture2D tex = (Texture2D)GameObject.Find("Quad").GetComponent<MeshRenderer>().materials[0].mainTexture;
            Texture2D flipTexture = new Texture2D(tex.width, tex.height);
            for (int i = 0; i < tex.height; i++)
            {
                flipTexture.SetPixels(0, i, tex.width, 1, tex.GetPixels(0, tex.height - i - 1, tex.width, 1));
            }
            flipTexture.Apply();



            //此圖同背景的差別表現不在灰度值上(比如紋理不同),所以將這個差別特徵轉換爲灰度的差別,然後利用閾值選取技術來分割該圖像,動態調節閾值實現圖像的二值化可動態觀察其分割圖像的具體結果。
            //一般用在背景是同一種顏色時,如果背景含有多種顏色,則不行  

            srcMat = new Mat(flipTexture.height, flipTexture.width, CvType.CV_8UC1);
            Utils.texture2DToMat(flipTexture, srcMat);

            //讀取圖片   參照圖   白色線
           // srcMat = Imgcodecs.imread(Application.dataPath + "/Resources/matchshapes_Creat_Test.png", 1);
            //圖片顏色模式轉換,轉爲灰度圖
            Imgproc.cvtColor(srcMat, srcMat, Imgproc.COLOR_BGR2GRAY);
            //圖片高斯模糊處理   
            Imgproc.GaussianBlur(srcMat, srcMat, new Size(size, size), 0);   // 使得內部有均勻一致的灰度值,而且此圖片的背景是在其他等級灰度值的均勻背景
            //圖片二值化處理   特徵轉換爲灰度的差別,再利用閥值進行分離背景
            Imgproc.threshold(srcMat, srcMat, threshValue, 255, Imgproc.THRESH_BINARY);  //圖像的集合性質只與像素值爲0或255的點的位置有關,不再涉及像素的多級值.






            dstMat = new Mat(flipTexture.height, flipTexture.width, CvType.CV_8UC1);
            Utils.texture2DToMat(flipTexture, dstMat);
            //讀取新圖片
            //dstMat =Imgcodecs.imread(Application.dataPath + "/Resources/matchshapes_Creat_Test.png", 1);
            //Imgproc.COLOR_BGR2RGB的顏色模式可以讓圖片保持原色
            Imgproc.cvtColor(dstMat, dstMat, Imgproc.COLOR_BGR2RGB);
            //對新圖片何原圖片進行對比
            List<MatOfPoint> srcContours = new List<MatOfPoint>();
            Mat srcHierarchy = new Mat();
            //尋找輪廓
            Imgproc.findContours(srcMat, srcContours, srcHierarchy, Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_NONE);
            for (int i = 0; i < srcContours.Count; i++)
            {
                //輪廓描邊
                //Imgproc.drawContours(dstMat, srcContours, i, new Scalar(255, 0, 0), 2, 8, srcHierarchy, 0, new Point());
                Point point = new Point();
                float[] radius = new float[1];
                //獲取點集最小外接圓點
                Imgproc.minEnclosingCircle(new MatOfPoint2f(srcContours[i].toArray()), point, radius);
                //在圓點位置繪製圓形
                Imgproc.circle(dstMat, point, 7, new Scalar(255, 255, 0), -1);
                MatOfPoint2f newMatOfPoint2f = new MatOfPoint2f(srcContours[i].toArray());
                enDetectShape enShape = detect(srcContours[i], newMatOfPoint2f);
                //在圖形圓心的(20,20)的右上方會繪製該輪廓的名字
                Imgproc.putText(dstMat, enShape.ToString(), new Point(point.x - 20, point.y - 20), Core.FONT_HERSHEY_SIMPLEX, 0.5, new Scalar(255, 0, 0), 2, Imgproc.LINE_AA, false);

                if (enShape != enDetectShape.Null)
                {
                    ShapeInfo shapeInfo = new ShapeInfo();
                    shapeInfo.point = new Vector2((float)point.x, tex.height - (float)point.y);
                    shapeInfo.enShape = enShape;
                    listShape.Add(shapeInfo);
                }
                
            }


            //定義Texture2D設置其寬高隨scrMat材質顏色模式爲RGBA32
             texture = new Texture2D(dstMat.cols(), dstMat.rows(), TextureFormat.RGBA32, false);
            //把Mat格式轉換成texture格式
            Utils.matToTexture2D(dstMat, texture);
            //把texture貼在UI RawImage上

            color_Pic.texture = texture;
            color_Pic.material.mainTexture= texture;

            oTime.Stop();  
            Debug.LogError("程序的運行時間:{0} 秒" + "   " + oTime.Elapsed.TotalSeconds);
            Debug.LogError("程序的運行時間:{0} 毫秒" + "    " + oTime.Elapsed.TotalMilliseconds);
        }


    }
    public Texture2D test;
    public Texture2D texture;
    public enDetectShape detect(MatOfPoint mp, MatOfPoint2f mp2f)
    {
        enDetectShape enShape = enDetectShape.Null;
        //主要是計算圖像輪廓的周長
        double peri = Imgproc.arcLength(mp2f, true);
      
        //對圖像輪廓點進行多邊形擬合
        MatOfPoint2f polyShape = new MatOfPoint2f();
        Imgproc.approxPolyDP(mp2f, polyShape, 0.04f * peri, true);
        int shapeLen = polyShape.toArray().Length;
        Debug.Log(shapeLen);
        //根據輪廓凸點擬合結果,判斷屬於那個形狀
        switch (shapeLen)
        {
            case 3:
                enShape = enDetectShape.Null;
                break;
            case 4:
                OpenCVForUnity.Rect rect = Imgproc.boundingRect(mp);
                float width = rect.width;
                float height = rect.height;
                float ar = width / height;
                //計算寬高比,判斷是矩形還是正方形
                if (ar >= 0.95 && ar <= 1.05)
                {
                    enShape = enDetectShape.Square;
                }
                else
                {
                    enShape = enDetectShape.Rectangle;  
                }
                break;
            case 5:
                enShape = enDetectShape.Pentagon;
                break;
            default:
                enShape = enDetectShape.Circle; 
                break;
        }
        return enShape;
    }
}

public class ShapeInfo
{
    public Vector3 point;
    public enDetectShape enShape;
}

public enum enDetectShape
{
    Null,
    Triangle,
    Square,
    Rectangle,
    Pentagon,
    Circle,
}

注意:該圖片翻轉是 由於direct是左上和opengl是左下座標的問題。

 

二、有模板圖片——比較圖片中形狀的相似度

https://blog.csdn.net/zhuason/article/details/78933250

1.輸入圖像

<1. Mat存在各種類型,maketype()  函數可以返回該Mat的類型。類型表示了材質中元素的類型以及材質的通道個數.

CvType:
    CvType.CV_8UC4是8位無符號四通道帶透明色RGB圖像,每一個像素在內存空間佔的空間大小是8位。
    CvType.CV_8UC1是 1通道(單通道) 

    CV_CN_SHIFT = 3;
    CV_DEPTH_MAX = (1 << CV_CN_SHIFT);   //= 1*2^3=8
    //前半句 :  把深度編號和(8-1)進行與運算  +    後半句:  (channels - 1)*2^3       
    //與運算目的:取某數中指定位   即:類型 = 數據類型的編號 + (通道數 - 1) * 8
    公式:  (depth & (CV_DEPTH_MAX - 1)) + ((channels - 1) << CV_CN_SHIFT);  

    Mat M(3,2,CV_8SC3,Scalar(0,0,4));
    cout<<"M="<<M<<endl;


    創造了一個行數(高度)是3,列數(寬度)是2的圖像,圖像的type是CV_8SC3,表示圖像的元素是8位無符        號整數,3個通道,圖像所有的元素都被初始化位(0,0,255),由於opencv顏色的順序是BGR,所以這張圖片是全紅色。
    常用的構造函數經常涉及到類型 type。type 可以是 CV_8UC1,CV_16SC1,…, CV_64FC4 等。裏面的 8U 表示 8 位無符號整數,16S 表示 16 位有符號整數,64F 表示 64 位浮點數(即 double 類型);C 後面的數表示通道數,例如 C1 表示一個 通道的圖像,C4 表示 4 個通道的圖像,以此類推。如果爲表明通道數,則爲如果你需要更多的通道數,需要用宏 CV_8UC(n),例如:Mat M(3,2, CV_8UC(5))
Mat中重定義了<<操作符,可以直接輸出所有像素值

Imgproc.cs: 圖片處理類

 Imgproc.GaussianBlur;

c#和c++之間的問題:

.net3.0   intptr和int類型不能相加;  再.net4.0纔可以相加。所以要把intptr類型轉換爲int32()類型。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章