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理解為自動化圖像處理的 “效率工具”


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM