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