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()類型。