这篇文章是我在学习OpenCV基础功能过程中的一个记录,方便自己加深记忆,也帮大家避坑
PS:获取插件环境配置好直接把所有场景打包,很好玩(建议把 OpenCVForUnityExample (主菜单场景)设置为第一个场景就好)
先贴三篇文章:
[A] 环境配置:https://www.jianshu.com/p/09d93a7cc3ed
[B] 启蒙我的文章:OpenCVforUnity中识别图片中的基础图形 https://blog.csdn.net/u013293580/article/details/84710933
[C] 反面教材:看了上一篇文章最后没学完的文章 https://blog.csdn.net/qq_40544338/article/details/104613413
B文章中贴的代码其实是他自己慢慢调整的一个过程,顺序上和逻辑上对C文章作者造成了误解,也促使我进行了更深入的学习...
目标:获取一张图上存在的所有图形并对他们进行标记
流程:
1.读取图片变成Mat(OpenCV用的一种什么数据形式,隐约记得本科线性代数用过,矩阵?马上要吃饭,懒得查了)
2.对图片做处理(处理为便于解析的版本)
3.再次读图片存为另一个Mat(原图已经处理成黑白的了,换一个新的)
4.遍历所有图形信息,并把信息贴到新Mat上
5.把图片显示到unity场景中的一个RawImage上
脚本:
using System.Collections; using System.Collections.Generic; using System; using OpenCVForUnity.CoreModule; using OpenCVForUnity.ImgcodecsModule; using UnityEngine; using UnityEngine.UI; using OpenCVForUnity.UnityUtils; using OpenCVForUnity; using OpenCVForUnity.ImgprocModule; using OpenCVForUnity.UnityUtils.Helper; public class TestFindShape : MonoBehaviour { public RawImage Pic; //UI private Mat scrMat; //Mat格式存放处理的图片 private Mat dstMat; //Mat格式存放处理的图片 string shape; void Start() { FindShape(); } public void FindShape() { //1.读取图片变成Mat //读取图片 scrMat = Imgcodecs.imread(Application.dataPath + "/Resources/FindingContours.png", 1); //2.对图片做处理(处理为便于解析的版本) //图片颜色模式转换 Imgproc.cvtColor(scrMat, scrMat, Imgproc.COLOR_BGR2GRAY); //图片高斯模糊处理 Imgproc.GaussianBlur(scrMat, scrMat, new Size(5, 5), 0); //图片二值化处理 Imgproc.threshold(scrMat, scrMat, 64, 255, Imgproc.THRESH_BINARY); //3.再次读图片存为另一个Mat(原图已经处理成黑白的了,换一个新的) //读取图片 dstMat = Imgcodecs.imread(Application.dataPath + "/Resources/FindingContours.png", 1); //Imgproc.COLOR_BGR2RGB的颜色模式可以让图片保持原色 Imgproc.cvtColor(dstMat, dstMat, Imgproc.COLOR_BGR2RGB); //4.遍历所有图形信息,并把信息贴到dstMat上 //获取图形列表 ListsrcContours = new List (); Mat srcHierarchy = new Mat(); //寻找轮廓 Imgproc.findContours(scrMat, 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, 255, 255), 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(0, 0, 255), -1); MatOfPoint2f newMatOfPoint2f = new MatOfPoint2f(srcContours[i].toArray()); shape = GetComponent ().detect(srcContours[i], newMatOfPoint2f); //在图形圆心的(20,20)的右上方会绘制该轮廓的名字 Imgproc.putText(dstMat, shape, new Point(point.x - 20, point.y - 20), Core.Formatter_FMT_C, 0.5, new Scalar(255, 0, 0), 2, Imgproc.LINE_AA, false); } //5.把图片显示到unity场景中的一个RawImage上 //定义Texture2D设置其宽高随scrMat材质颜色模式为RGBA32 Texture2D texture = new Texture2D(scrMat.cols(), scrMat.rows(), TextureFormat.RGBA32, false); //把texture贴在UI RawImage上 Pic.texture = texture; //把Mat格式转换成texture格式(修改后) Utils.matToTexture2D(dstMat, texture); } public string detect(MatOfPoint mp, MatOfPoint2f mp2f) { string shape = "unidentified"; double peri; //主要是计算图像轮廓的周长 peri = Imgproc.arcLength(mp2f, true); //对图像轮廓点进行多边形拟合 MatOfPoint2f polyShape = new MatOfPoint2f(); Imgproc.approxPolyDP(mp2f, polyShape, 0.04 * peri, true); int shapeLen = polyShape.toArray().Length; //根据轮廓凸点拟合结果,判断属于那个形状 switch (shapeLen) { case 3: shape = "triangle"; 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) //{ // shape = "square"; //} //else //{ // shape = "rectangle"; //} break; case 5: shape = "pentagon"; break; default: shape = "circle"; break; } return shape; } }
GetComponent().detect(srcContours[i], newMatOfPoint2f);这句报错,说GetComponent()需要格式,
@lgd666666:有这句吗,是不是没挂组件,Demo场景里的脚本上有挂东西的。