与图像处理之间的关系,opencv的简介和使用定位
一些基本的知识
关于滤波:
去噪点 ,其实就是模糊化 让毛刺高波跟周围像素平均 也叫均值滤波 低通滤波。锐化,其实就是让毛刺像素加上跟周围的方差 这样大的被放大 也叫高通滤波 ,可以让边缘更明显。颜色空间的处理 ,简易理解可理解为直方图处理 ,可理解为上面一样,理论化的说 跟书上说的一样也就是波处理。让某一部分波不可见 而把另一部分拉伸到0~255的范围 让特征部分对比更明显 不至于浪费输出带宽,典型的对比度调整 对比度均匀化 本质都是波处理 ,阈值化 典型的削波处理 ,波处理都是会损失信息的 尤其是阈值化 ,但是我们这样做的目的是 让计算机更容易判别 比如轮廓识别。
openCV基本使用
1 Mat src = Cv2.ImRead("lena.jpg");//new Mat("lena.jpg", ImreadModes.Color); 2 //对颜色矩阵进行变换 3 //Mat src2= src.CvtColor(ColorConversionCodes.RGB2GRAY); 4 //输出变换后的矩阵数据 发现就变成灰度的了 5 //Cv2.ImShow("src", src2); 6 7 //画一条线段 8 //这里要求的参数是inputArray ,其实可以简单的当做Mat类型即可 P67 9 Cv2.Line(src, 0, 0, 20, 20, new Scalar(0, 255, 0)); 10 Cv2.ImShow("src", src);
opencv里面有几种基本的数据类型比如Point Path这些 ,细想一下就能够想得到 ,图像处理 倒腾的是什么 特征 特征 特征 ,这些东西就是来进行数据交互用的,有可能是用作输入 比如你要标注一个ROI区域 ,也有可能是用作opencv输出用的 比如opencv从图中检测出来一个几何图形 给你的反馈。好接下来我们演示一下简单的标注 图像叠加混合 转存。
1 Mat image = new Mat("lena.jpg"); 2 Mat logo = new Mat("car.jpg"); 3 //定义一个mat类型 用于存放图像的ROI 4 //方法1 5 //好像在某篇帖子上是说过 ROI感兴趣区域是用mat定义的,然后缩放还是裁剪的时候是会以此为参照 6 7 //把lena图的感兴趣区域设置为小图大小? 8 //基本理念 9 //实质上是通过image 结合Rect建立了一个选区 ,其实操作还是操作的原来的像素, 10 //对原图的影响始终超不出此区域(注意imgROI并不是一块独立的数据区域) 11 Mat imageROI = new Mat(image, new Rect(100, 100, logo.Cols, logo.Height)); 12 13 //把小图叠加到原图的感兴趣区域? 14 //注意 操作后 imageROI的图像是小图大小 通过透明混合了两幅图(原图和 imageROI都操作了) 15 Cv2.AddWeighted(imageROI, 1, logo, 0.3, 0, imageROI);//此方法必须两个图宽高一致 16 17 //这是网上的代码 c++的构造器代码套路方式 ,通过一个对象作为参数创建一个对象 18 // Mat src = imread("test.jpg"); 19 // Mat logo = imread("logo.jpg"); 20 ////设定ROI区域 21 //Mat ROI = src(Rect(20, 25, logo.cols, logo.rows));//注意这边Rect函数,先列后行(长*高(宽)) 22 23 //注意 24 imageROI.Line(0, 0, 460, 425, new Scalar(0, 0, 255)); 25 26 //int[] compression_params = new int[2]; 27 //compression_params[0] = (int)OpenCvSharp.ImwriteFlags.PngCompression; 28 //compression_params[1] = 9; 29 //imageROI.ImWrite("ROI混合区域.png", compression_params); 30 //image.ImWrite("叠加.png", compression_params); 31 32 Cv2.ImShow("叠加", image);
1 Mat srcImage1 = new Mat("car.jpg"); 2 Mat srcImage2 = new Mat("ship.jpg"); 3 float g_dalphaValue = (float)trackBar1.Value/(float)trackBar1.Maximum; 4 float g_dBetaValue = 1.0f - g_dalphaValue;//1.0f;// 5 //根据alpha 和beta进行线性混合 6 Mat dstImage = new Mat(); 7 Cv2.AddWeighted(srcImage1, g_dalphaValue, srcImage2, g_dBetaValue, 0.0, dstImage); 8 9 Bitmap dstBmp = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(dstImage); 10 pictureBox1.Image = dstBmp;
效果:
ROI也就是感兴趣区域,只要标定过后然后整个操作都只影响这个区域范围(注意图中画的红色线段是从小图ROI区域开始的)。整个过程中进行对象分割然后分块处理降低整体图像对算法的效果影响 ,这也是图像处理里面的常规套路。
太无聊了,来点稍微有丁点意思的
来点稍微有丁点意思的,是的让你体会到人类智慧以及计算机的神奇之处哇哈哈哈哈,注意这里的有意思之处是来源于opencv底层的几何检测函数功能 说白了我们只是个搬砖的 如果你更有脾气一点 你应该把检测的原理搞懂。好 我们要做的是检测图像中的所有几何图形 有哪些是一类的 我们把它标注出来,用到了上面讲的opencv基本套路和概念。
1 public void ShapeMatchTest() 2 { 3 Mat src = Cv2.ImRead("shape4.png"); 4 Mat src2 = src.CvtColor(ColorConversionCodes.RGB2GRAY); 5 Mat src3 = src2.Threshold(250, 255, ThresholdTypes.BinaryInv);//THRESH_BINARY_INV 6 7 Mat[] findeds; 8 9 //用于存储轮廓的? 10 OpenCvSharp.Point[][] contours; 11 HierarchyIndex[] hierarchy;// Vec4i[]错误 9 参数 2: 无法从“out OpenCvSharp.Vec4i[]”转换为“out OpenCvSharp.Hier 12 13 // Mat[] contours2; 14 //Mat hierarchy2 = new Mat(); 15 16 //找出并显示了所有轮廓 ,注意并不是一个完整的路径 标识一个轮廓 而是二维数组 17 src3.FindContours(out contours, out hierarchy, RetrievalModes.CComp, ContourApproximationModes.ApproxSimple); 18 //src3.FindContours(out contours2, hierarchy2, RetrievalModes.CComp, ContourApproximationModes.ApproxSimple); 19 20 //for (int i = 0; i < contours2.Length; i++) 21 //{ 22 // src.DrawContours(contours2[i], contours2,i, new Scalar(0, 255, 0), 3); 23 //} 24 Bitmap resultImg = MatCopyToBmp(src); 25 26 27 //显示轮廓点 事实证明 每个contours[i] 是一个路径 ,并且每个路径里面的点是顺序排列的 28 System.Drawing.Graphics g = Graphics.FromImage(resultImg); 29 //DrawBmpContours(g, contours); 30 // ret = cv2.matchShapes(k0,c0,1,0.0) 31 // cv2.drawContours(img,[c0],-1,(0,255,0),2) 32 33 //对所有相近轮廓进行归类 双重list 34 List<List< OpenCvSharp.Point[]>> matched = new List<List<OpenCvSharp.Point[]>>(); 35 OpenCvSharp.Point[][] contours2 = (OpenCvSharp.Point[][])contours.Clone(); 36 37 //首先把第一个轮廓归为一组 38 matched.Add(new List<OpenCvSharp.Point[]>() { contours[0]}); 39 40 41 for (int i = 1; i < contours.Length; i++)//直接从第二个开始 42 { 43 //假定没匹配到 44 bool matchedok=false; 45 //在已经归好类的组里面找 46 for (int j = 0; j < matched.Count; j++) 47 { 48 //当前跟归好类的匹配 因为一类的都是一样的所以只匹配第一个 49 var ret = Cv2.MatchShapes(contours[i], matched[j][0], ShapeMatchModes.I1, 0.0); 50 Console.WriteLine(ret); 51 //<0.05基本上可以准确区分 52 //shape.png <1可区分正方形三角形 53 //数值越大区分能力越弱 54 //0.2基本可区分锐角三角形 钝角三角形 (正方形 包括旋转了的) ,区分能力已经较弱 55 if (ret <0.1) 56 { 57 //如果匹配上则往那一类里面归 58 matchedok=true; 59 matched[j].Add(contours[i]); 60 break; 61 } 62 } 63 //如果当前的跟以前的所有的都没匹配上则另启一组 64 if(matchedok==false){ 65 matched.Add(new List<OpenCvSharp.Point[]>() { contours[i]}); 66 } 67 } 68 //只绘制相似的图形 69 //OpenCvSharp.Point[][] contoursMatched = new OpenCvSharp.Point[matched.Count][]; 70 //for (int i = 0; i < contoursMatched.Length; i++) 71 //{ 72 // src.DrawContours(contoursMatched, i, new Scalar(0, 255, 0), 3); 73 //} 74 75 Scalar[] scars = new Scalar[]{ 76 new Scalar(255,0,0), 77 new Scalar(0,255,0), 78 new Scalar(0,0,255), 79 new Scalar(255,255,0), 80 new Scalar(255,0,255),new Scalar(0,255,255), 81 new Scalar(0,0,0)}; 82 83 Console.WriteLine(matched.Count + "组"); 84 int scrindx=0; 85 for (int i = 0; i < matched.Count; i++) 86 { 87 Random rdm = new Random(); 88 Scalar cor = scars[(++scrindx)%7];//new Scalar(rdm.Next(1, 255), rdm.Next(1, 255), rdm.Next(1, 255)); 89 for (int j = 0; j < matched[i].Count; j++) 90 { 91 src.DrawContours(matched[i], j, cor, 3); 92 } 93 } 94 95 //绘制结果到窗体 96 Graphics gForm = Graphics.FromHwnd(this.Handle); 97 gForm.DrawImage(resultImg, new PointF(0, 0)); 98 Cv2.ImShow("hahaha", src); 99 }
效果图:
经过一段时间的学习呢,最后我的工作也完成了,由于特征比较明显算法本身难度不是想象的那么大 opencv呢太重了,所以最终没有用到任何第三方的库,提前祝大家端午节愉快。。。
总结
以前我用GDI+的SetPixel()处理像素不是被人骂吗,实际上opencv这玩意儿要说的话照我的就是可以理解为一个 进行像素处理 的 “效率工具” 并且我试过了写的也确实很牛逼 算法的效率很高。并且自带有一些图像处理的惯用套路 和算法在里面 算法的参数调节 数据展现都是蛮方便的 特别是mat矩阵数据跟API无缝结合的设计。然后能搞懂一些数学知识是最好,最不济也必须学习一些图像处理的基本概念。有些算法可以不用完全搞懂原理 但是得搞懂大概的过程 否则调参数你不知道怎么调对最终结果会产生什么影响,然后自己模拟一个过程 通过opencv的多个函数相结合就能达到想要的效果比如低通滤波然后腐蚀在某些图就能够达到对象分割。总之opencv理解为自动化图像处理的 “效率工具”