openCV从入门到放弃

与图像处理之间的关系,opencv的简介和使用定位

如题…因为偶然的机会需要用到图像处理,像我这么爱学习 并且动手能力又强的人怎么能没有心得笔记呢,哇哈哈哈。非要说的low逼点这玩意儿这玩意儿就是像素处理。找出像素中的特征规律 然后根据这些规律去处理数据,就是图像特征处理 不是什么鸟人工智能 不是什么鸟人工智能 不是什么鸟人工只能 重要的话说3遍,但是普遍的商业公司对外宣称却都是吹嘘的啥人工智能高大上的 ,因为像素数据可以理解为信号量,所以是有一些数学理论或者公式能让他更好的工作的理论基础的,基于这里面的理论深度要说的话某些算法也可以达到很高深,所以称之为智能也不为过。数学不好也不要太害怕。实在很半吊子  那也没法 将就着搞吧 ,比如我 哇哈哈哈哈。  

一些基本的知识

图像的像素数据可以理解为信号量,图像像素之间的坐标关系称之为空域 ,主要手段和理念是图形学 形态学。常见进行对象分割和特征查找的手段有 轮廓查找 霍夫圆 联通区域标定 腐蚀 膨胀 等 都是空域算法,其作用本意都是让空域有意义 进行对象分割 形成有意义的数据。

关于滤波:

去噪点 ,其实就是模糊化 让毛刺高波跟周围像素平均 也叫均值滤波 低通滤波。锐化,其实就是让毛刺像素加上跟周围的方差 这样大的被放大 也叫高通滤波 ,可以让边缘更明显。颜色空间的处理 ,简易理解可理解为直方图处理 ,可理解为上面一样,理论化的说 跟书上说的一样也就是波处理。让某一部分波不可见 而把另一部分拉伸到0~255的范围 让特征部分对比更明显 不至于浪费输出带宽,典型的对比度调整 对比度均匀化 本质都是波处理 ,阈值化 典型的削波处理 ,波处理都是会损失信息的 尤其是阈值化 ,但是我们这样做的目的是 让计算机更容易判别 比如轮廓识别。   一些简易而有效的思路也可以使用并不是多高深的数学公式算法 ,比如有干扰线的验证码识别 怎么进行分割 其实可以用 直方图投影。最低值那一部分 也就是最细的那一部分 肯定就是干扰线 ,则可进行分割。   
很久以前偶尔看到一直不明白搞那些花里胡哨的的不同色调的玩意儿图像效果到底有神马作用 好看吗,多年后的我好像稍微明白那么一丁点儿了,那些不是给你看的而是给计算机看的。  
好吧我也不知道我唧唧歪歪说的些啥 ,就当我 就当你们都懂了吧 哇哈哈哈哈。好了咸淡扯完了。接下来opencv使用,其实就直接看那些opencv入门类的书其实就成 但是这玩意儿是C++写的 默认也是c++绑定 。网上代码见得最多就是 python绑定的代码。但是我是懂C++的  ,这不就派上用场了 找了C++的opencv入门教程  结合c++教程里opencv的概念理论 然后找了点python绑定的参考代码 改吧改吧 一周多时间 就搞会了 ,我感觉我膨胀了  
哇哈哈哈哈。  

openCV基本使用

 载入一幅图像,基本理念是像素数据都存储在Mat里面 ,对矩阵进行颜色变换 相当于就是整体颜色更换了一种处理方式。

 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理解为自动化图像处理的 “效率工具”

© 版权声明
THE END
喜欢就支持一下吧!
点赞0
分享