乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      輪廓的查找、表達、繪制、特性及匹配(How to Use Contour? Find, Component, Construct, Features & Match)

       學海無涯GL 2013-04-10

      輪廓的查找、表達、繪制、特性及匹配(How to Use Contour? Find, Component, Construct, Features & Match)

      作者:王先榮

      前言
          輪廓是構成任何一個形狀的邊界或外形線。前面講了如何根據(jù)色彩及色彩的分布(直方圖對比和模板匹配)來進行匹配,現(xiàn)在我們來看看如何利用物體的輪廓。包括以下內容:輪廓的查找、表達方式、組織方式、繪制、特性、匹配。

       查找輪廓
          首先我們面對的問題是如何在圖像中找到輪廓,OpenCv(EmguCv)為我們做了很多工作,我們的任務只是調用現(xiàn)成的函數(shù)而已。Image<TColor,TDepth>類的FindContours方法可以很方便的查找輪廓,不過在查找之前,我們需要將彩色圖像轉換成灰度圖像,然后再將灰度圖像轉換成二值圖像。代碼如下所示:

      查找輪廓
      Image<Bgr, Byte> imageSource = new Image<Bgr, byte>(sourceImageFileName); //獲取源圖像
      Image<Gray, Byte> imageGray = imageSource.Convert<Gray, Byte>(); //將源圖像轉換成灰度圖像
      int thresholdValue = tbThreshold.Value; //用于二值化的閥值
      Image<Gray, Byte> imageThreshold = imageGray.ThresholdBinary(new Gray(thresholdValue), new Gray(255d)); //對灰度圖像二值化
      Contour<Point> contour=imageThreshold.FindContours();

       

      輪廓的表達方式
          使用上面的代碼可以得到圖像的默認輪廓,但是輪廓在電腦中是如何表達的呢?在OpenCv(EmguCv)中提供了兩類表達輪廓的方式:頂點的序列、Freeman鏈碼。

      1.頂點的序列
          用多個頂點(或各點間的線段)來表達輪廓。假設要表達一個從(0,0)到(2,2)的矩形,
      (1)如果用點來表示,那么依次存儲的可能是:(0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,1);
      (2)如果用點間的線段來表達輪廓,那么依次存儲的可能是:(0,0),(2,0),(2,2),(0,2)。
      以下代碼可以用來獲取輪廓上的點:

      for (int i = 0; i < contour.Total; i++)
      sbContour.AppendFormat("{0},", contour[i]);

       

       2.Freeman鏈碼
          Freeman鏈碼需要一個起點,以及從起點出發(fā)的一系列位移。每個位移有8個方向,從0~7分別指向從正北開始的8個方向。假設要用Freeman鏈碼表達從(0,0)到(2,2)的矩形,可能的表示方法是:起點(0,0),方向鏈2,2,4,4,6,6,0,0。
          EmguCv對Freeman鏈碼的支持很少,我們需要做一系列的工作才能在.net中使用Freeman鏈碼:
      (1)獲取Freeman鏈碼

      查找用Freeman鏈碼表示的輪廓
      //查找用Freeman鏈碼表示的輪廓
      Image<Gray,Byte> imageTemp=imageThreshold.Copy();
      IntPtr storage = CvInvoke.cvCreateMemStorage(0);
      IntPtr ptrFirstChain = IntPtr.Zero;
      int total = CvInvoke.cvFindContours(imageTemp.Ptr, storage, ref ptrFirstChain, sizeof(MCvChain), mode, CHAIN_APPROX_METHOD.CV_CHAIN_CODE, new Point(0, 0));


      (2)遍歷Freeman鏈碼上的點

      讀取Freeman鏈碼上的點
      //初始化Freeman鏈碼讀取
      [DllImport("cv200.dll")]
      public static extern void cvStartReadChainPoints(IntPtr ptrChain,IntPtr ptrReader);
      //讀取Freeman鏈碼的點
      [DllImport("cv200.dll")]
      public static extern Point cvReadChainPoint(IntPtr ptrReader);
      [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet = System.Runtime.InteropServices.CharSet.Ansi)]
      //定義鏈碼讀取結構
      public struct MCvChainPtReader
      {
      //seqReader
      public MCvSeqReader seqReader;
      /// char
      public byte code;
      /// POINT->tagPOINT
      public Point pt;
      /// char[16]
      [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 16)]
      public string deltas;
      }

      //將鏈碼指針轉換成結構
      MCvChain chain=(MCvChain)Marshal.PtrToStructure(ptrChain,typeof(MCvChain));
      //定義存放鏈碼上點的列表
      List<Point> pointList = new List<Point>(chain.total);
      //鏈碼讀取結構
      MCvChainPtReader chainReader = new MCvChainPtReader();
      IntPtr ptrReader = Marshal.AllocHGlobal(sizeof(MCvSeqReader) + sizeof(byte) + sizeof(Point) + 16 * sizeof(byte));
      Marshal.StructureToPtr(chainReader, ptrReader, false);
      //開始讀取鏈碼
      cvStartReadChainPoints(ptrChain, ptrReader);
      int i = 0;
      while (ptrReader != IntPtr.Zero && i < chain.total)
      {
      //依次讀取鏈碼上的每個點
      Point p = cvReadChainPoint(ptrReader);
      if (ptrReader == IntPtr.Zero)
      break;
      else
      {
      pointList.Add(p);
      sbChain.AppendFormat("{0},", p);
      i++;
      }
      }
      imageResult.DrawPolyline(pointList.ToArray(), true, new Bgr(lblExternalColor.BackColor), 2);

        

          需要注意的是:cvReadChainPoint函數(shù)似乎永遠不會滿足循環(huán)終止的條件,即ptrReader永遠不會被置為null,這跟《學習OpenCv》和參考上不一致;我們需要用chain.total來輔助終止循環(huán),讀取了所有的點之后就可以罷手了。

      輪廓之間的組織方式
          在查找到輪廓之后,不同輪廓是怎么組織的呢?根據(jù)不同的選擇,它們可能是:(1)列表;(2)雙層結構;(3)樹型結構。
          從縱向上來看,列表只有一層,雙層結構有一或者兩層,樹型結構可能有一層或者多層。
          如果要遍歷所有的輪廓,可以使用遞歸的方式,代碼如下:

      遍歷輪廓
      //遍歷輪廓,并生成遍歷結果
      private void TravelContour(Contour<Point> contour,ref int total,ref StringBuilder sbContour)
      {
      if (contour != null)
      {
      sbContour.Append("------------------------\r\n");
      sbContour.AppendFormat("輪廓{0},右節(jié)點:{1},下級節(jié)點:{2},外接矩形:({3})\r\n", total, contour.HNext != null, contour.VNext != null, contour.BoundingRectangle);
      sbContour.AppendFormat("包含{0}個點(面積:{1},周長:{2}):\r\n", contour.Total, contour.Area, contour.Perimeter);
      for (int i = 0; i < contour.Total; i++)
      sbContour.AppendFormat("{0},", contour[i]);
      sbContour.Append("\r\n");
      total++;
      if (contour.HNext != null)
      TravelContour(contour.HNext, ref total, ref sbContour);
      if (contour.VNext != null)
      TravelContour(contour.VNext, ref total, ref sbContour);
      }
      }

       

      輪廓的繪制
          輪廓的繪制比較簡單,用上面提到的方法取得輪廓的所有點,然后把這些點連接成一個多邊形即可。
          當然,對于用頂點序列表示的輪廓,用Image<TColor,TDepth>.Draw方法或者cvDrawContours函數(shù)可以很方便的繪制出輪廓。我發(fā)現(xiàn),如果將參數(shù)max_level設置成2,可以繪制出所有的輪廓。
          繪制輪廓的代碼如下:

      繪制輪廓
      Image<Bgr, Byte> imageResult = imageThreshold.Convert<Bgr, Byte>(); //結果圖像
      int maxLevel = 0; //繪制的輪廓深度
      int.TryParse(txtMaxLevel.Text, out maxLevel);
      imageResult.Draw(contour, new Bgr(lblExternalColor.BackColor), new Bgr(lblHoleColor.BackColor), maxLevel, 2);


      輪廓的特性
          輪廓的特性有很多,下面一一介紹。

      1.輪廓的多邊形逼近
          輪廓的多邊形逼近指的是:使用多邊形來近似表示一個輪廓。
          多邊形逼近的目的是為了減少輪廓的頂點數(shù)目。
          多邊形逼近的結果依然是一個輪廓,只是這個輪廓相對要粗曠一些。
          可以使用Contour<Point>.ApproxPoly方法或者cvApproxyPoly函數(shù)來對輪廓進行多邊形逼近,示例代碼如下:

      contour = firstContour.ApproxPoly(double.Parse(txtApproxParameter.Text), 2, new MemStorage());

        

      2.輪廓的關鍵點
          輪廓的關鍵點是:輪廓上包含曲線信息比較多的點。關鍵點是輪廓頂點的子集。
          可以使用cvFindDominantPoints函數(shù)來獲取輪廓上的關鍵點,該函數(shù)返回的結果一個包含 關鍵點在輪廓頂點中索引 的序列。再次強調:是索引,不是具體的點。如果要得到關鍵點的具體坐標,可以用索引到輪廓上去找。
          以下代碼演示了如何獲取輪廓上的關鍵點:

      輪廓的關鍵點
      //得到關鍵點信息
      private void GetDominantPointsInfo(Contour<Point> contour, ref StringBuilder sbContour, ref Image<Bgr, Byte> imageResult, double parameter1, double parameter2, double parameter3, double parameter4, Bgr dominantPointColor)
      {
      if (contour.Total > 2)
      {
      MemStorage storage = new MemStorage();
      try
      {
      IntPtr ptrSeq = cvFindDominantPoints(contour.Ptr, storage.Ptr, (int)CV_DOMINANT.CV_DOMINANT_IPAN, parameter1, parameter2, parameter3, parameter4);
      Seq<int> seq = new Seq<int>(ptrSeq, storage);
      sbContour.AppendFormat("{0}個關鍵點:\r\n", seq.Total);
      for (int i = 0; i < seq.Total; i++)
      {
      int idx = seq[i]; //關鍵點序列中存儲的數(shù)據(jù) 是 關鍵點在輪廓中所處位置的索引
      Point p = contour[idx]; //得到關鍵點的坐標
      sbContour.AppendFormat("{0}({1},{2}),", idx, p.X, p.Y);
      imageResult.Draw(new CircleF(new PointF(p.X, p.Y), 3), dominantPointColor, -1);
      }
      sbContour.Append("\r\n");
      }
      catch (CvException ex)
      {
      sbContour.AppendFormat("在獲取關鍵點時發(fā)生異常,錯誤描述:{0},錯誤源:{1},錯誤堆棧:{2}\r\n錯誤文件:{3},函數(shù)名:{4},行:{5},錯誤內部描述:{6}\r\n", ex.Message, ex.Source, ex.StackTrace, ex.FileName, ex.FunctionName, ex.Line, ex.ErrorStr);
      }
      catch (Exception e)
      {
      sbContour.AppendFormat("在獲取關鍵點時發(fā)生異常,錯誤描述:{0},錯誤源:{1},錯誤堆棧:{2}\r\n", e.Message, e.Source, e.StackTrace);
      }
      finally
      {
      storage.Dispose();
      }
      }
      }


      3.輪廓的周長和面積
          輪廓的周長可以用Contour<Point>.Perimeter屬性或者cvArcLength函數(shù)來獲取。
          輪廓的面積可以用Contour<Point>.Area屬性或者cvContourArea函數(shù)來獲取。

      4.輪廓的邊界框
          有三種常見的邊界框:矩形、圓形、橢圓。
          (1)矩形:在圖像處理系統(tǒng)中提供了一種叫Rectangle的矩形,不過它只能表達邊垂直或水平的特例;OpenCv中還有一種叫Box的矩形,它跟數(shù)學上的矩形一致,只要4個角是直角即可。
          如果要獲取輪廓的Rectangle,可以使用Contour<Point>.BoundingRectangle屬性或者cvBoundingRect函數(shù)。
          如果要獲取輪廓的Box,可以使用Contour<Point>.GetMinAreaRect方法或者cvMinAreaRect2函數(shù)。
          (2)圓形
          如果要獲取輪廓的圓形邊界框,可以使用cvMinEnclosingCircle函數(shù)。
          (3)橢圓
          如果要獲取輪廓的橢圓邊界框,可以使用cvFitEllipse2函數(shù)。
          下列代碼演示了如何獲取輪廓的各種邊界框:

      輪廓的邊界框
      //得到邊界框信息
      private void GetEdgeInfo(Contour<Point> contour, string edge, ref StringBuilder sbContour, ref Image<Bgr, Byte> imageResult, Bgr edgeColor)
      {
      if (edge == "Rect")
      //矩形
      imageResult.Draw(contour.BoundingRectangle, edgeColor, 2);
      else if (edge == "MinAreaRect")
      {
      //最小矩形
      MCvBox2D box = CvInvoke.cvMinAreaRect2(contour.Ptr, IntPtr.Zero);
      PointF[] points = box.GetVertices();
      Point[] ps = new Point[points.Length];
      for (int i = 0; i < points.Length; i++)
      ps[i] = new Point((int)points[i].X, (int)points[i].Y);
      imageResult.DrawPolyline(ps, true, edgeColor, 2);
      }
      else if (edge == "Circle")
      {
      //圓形
      PointF center;
      float radius;
      CvInvoke.cvMinEnclosingCircle(contour.Ptr, out center, out radius);
      imageResult.Draw(new CircleF(center, radius), edgeColor, 2);
      }
      else
      {
      //橢圓
      if (contour.Total >= 6)
      {
      MCvBox2D box = CvInvoke.cvFitEllipse2(contour.Ptr);
      imageResult.Draw(new Ellipse(box), edgeColor, 2);
      }
      else
      sbContour.Append("輪廓點數(shù)小于6,不能創(chuàng)建外圍橢圓。\r\n");
      }
      }

        

      5.輪廓的矩
          我們可以使用Contour<Point>.GetMoments方法或者cvMoments函數(shù)方便的得到輪廓的矩集,然后再相應的方法或函數(shù)獲取各種矩。
          特定的矩:MCvMoments.GetSpatialMoment方法、cvGetSpatialMoment函數(shù)
          中心矩:MCvMoments.GetCentralMoment方法、cvGetCentralMoment函數(shù)
          歸一化中心矩:MCvMoments.GetNormalizedCentralMoment方法、cvGetNormalizedCentralMoment函數(shù)
          Hu矩:MCvMoments.GetHuMoment方法、McvHuMoments.hu1~hu7字段、cvGetHuMoments函數(shù)
          以下代碼演示了如何獲取輪廓的矩:

      輪廓的矩
      //得到各種矩的信息
      private void GetMomentsInfo(Contour<Point> contour, ref StringBuilder sbContour)
      {
      //
      MCvMoments moments = contour.GetMoments();
      //遍歷各種情況下的矩、中心矩及歸一化矩,必須滿足條件:xOrder>=0; yOrder>=0; xOrder+yOrder<=3;
      for (int xOrder = 0; xOrder <= 3; xOrder++)
      {
      for (int yOrder = 0; yOrder <= 3; yOrder++)
      {
      if (xOrder + yOrder <= 3)
      {
      double spatialMoment = moments.GetSpatialMoment(xOrder, yOrder);
      double centralMoment = moments.GetCentralMoment(xOrder, yOrder);
      double normalizedCentralMoment = moments.GetNormalizedCentralMoment(xOrder, yOrder);
      sbContour.AppendFormat("矩(xOrder:{0},yOrder:{1}),矩:{2:F09},中心矩:{3:F09},歸一化矩:{4:F09}\r\n", xOrder, yOrder, spatialMoment, centralMoment, normalizedCentralMoment);
      }
      }
      }
      //Hu矩
      MCvHuMoments huMonents = moments.GetHuMoment();
      sbContour.AppendFormat("Hu矩 h1:{0:F09},h2:{1:F09},h3:{2:F09},h4:{3:F09},h5:{4:F09},h6:{5:F09},h7:{6:F09}\r\n", huMonents.hu1, huMonents.hu2, huMonents.hu3, huMonents.hu4, huMonents.hu5, huMonents.hu6, huMonents.hu7);
      }


      6.輪廓的輪廓樹
          輪廓樹用來描述某個特定輪廓的內部特征。注意:輪廓樹跟輪廓是一一對應的關系;輪廓樹不用于描述多個輪廓之間的層次關系。
          可以用函數(shù)cvCreateContourTree來構造輪廓樹。

      IntPtr ptrTree1 = CvInvoke.cvCreateContourTree(contour1.Ptr, new MemStorage().Ptr, thresholdOfCreate);

       

       7.輪廓的凸包和凸缺陷
          輪廓的凸包和凸缺陷用于描述物體的外形。凸包和凸缺陷很容易獲得,不過我目前不知道它們到底怎么使用。
          如果要判斷輪廓是否是凸的,可以用Contour<Point>.Convex屬性和cvCheckContourConvexity函數(shù)。
          如果要獲取輪廓的凸包,可以用Contour<Point>.GetConvexHull方法或者cvConvexHull2函數(shù),返回的是包含頂點的序列。
          如果要獲取輪廓的凸缺陷,可以用Contour<Point>.GetConvexityDefacts方法或者cvConvexityDefects函數(shù)。
          注意:EmguCv將缺陷的單詞拼寫錯了,defect才是缺陷。
          以下代碼演示了如何獲取輪廓的凸包及凸缺陷:

      輪廓的凸包和凸缺陷
      //得到凸包及缺陷信息
      private void GetConvexInfo(Contour<Point> contour,ref StringBuilder sbContour,ref Image<Bgr,Byte> imageResult)
      {
      if (!contour.Convex) //判斷輪廓是否為凸
      {
      //凸包
      Seq<Point> convexHull = contour.GetConvexHull(ORIENTATION.CV_CLOCKWISE);
      //缺陷
      Seq<MCvConvexityDefect> defects = contour.GetConvexityDefacts(new MemStorage(), ORIENTATION.CV_CLOCKWISE);
      //顯示信息
      sbContour.AppendFormat("輪廓的凸包有{0}個點,依次為:", convexHull.Total);
      Point[] points = new Point[convexHull.Total];
      for (int i = 0; i < convexHull.Total; i++)
      {
      Point p = convexHull[i];
      points[i] = p;
      sbContour.AppendFormat("{0},", p);
      }
      sbContour.Append("\r\n");
      imageResult.DrawPolyline(points, true, new Bgr(lblConvexColor.BackColor), 2);
      MCvConvexityDefect defect;
      sbContour.AppendFormat("輪廓有{0}個缺陷,依次為:\r\n", defects.Total);
      for (int i = 0; i < defects.Total; i++)
      {
      defect = defects[i];
      sbContour.AppendFormat("缺陷:{0},起點:{1},終點:{2},最深的點:{3},深度:{4}\r\n", i, defect.StartPoint, defect.EndPoint, defect.DepthPoint, defect.Depth);
      }
      }
      else
      sbContour.Append("輪廓是凸的,凸包和輪廓一樣。\r\n");
      }

       

       8.輪廓的成對幾何直方圖
          成對幾何直方圖的資料比較少,我是這么理解的。
          (1)輪廓保存的是一系列的頂點,輪廓是由一系列線段組成的多邊形。對于看起來光滑的輪廓(例如圓),只是線段條數(shù)比較多,線段長度比較短而已。實際上,電腦中顯示的任何曲線都由線段組成。
          (2)每兩條線段之間都有一定的關系,包括它們(或者它們的延長線)之間的夾角,兩條線段的夾角范圍是:(0,180)。
          (3)每兩條線段上的點之間還有距離關系,包括最短(?。┚嚯x、最遠(大)距離,以及平均距離。最大距離我用了一個偷懶的計算方法,我把輪廓外界矩形的對角線長度看作了最大距離。
          (4)成對幾何直方圖所用的統(tǒng)計數(shù)據(jù)包括了夾角和距離。
          可以用函數(shù)cvCalcPGH來計算輪廓的成對幾何直方圖,示例代碼如下:

      輪廓的成對幾何直方圖
      //生成成對幾何直方圖
      Rectangle rect1 = contour1.BoundingRectangle;
      float maxDist1 = (float)Math.Sqrt(rect1.Width * rect1.Width + rect1.Height * rect1.Height); //輪廓的最大距離:這里使用輪廓矩形邊界框的對角線長度
      int[] bins1 = new int[] { 60, 20 };
      RangeF[] ranges1 = new RangeF[] { new RangeF(0f, 180f), new RangeF(0f, maxDist1) }; //直方圖第0維為角度,范圍在(0,180),第2維為輪廓兩條邊緣線段的距離
      DenseHistogram hist1 = new DenseHistogram(bins1, ranges1);
      CvInvoke.cvCalcPGH(contour1.Ptr, hist1.Ptr);

       

       

      輪廓的匹配
          如果要比較兩個物體,可供選擇的特征很多。如果要判斷某個人的性別,可以根據(jù)他(她)頭發(fā)的長短來判斷,這很直觀,在長發(fā)男稀有的年代準確率也很高。也可以根據(jù)這個人尿尿的射程來判斷,如果射程大于0.50米,則是男性??傊?,方法很多,不一而足。
          我們在上文中得到了輪廓的這么多特征,它們也可以用于進行匹配。典型的輪廓匹配方法有:Hu矩匹配、輪廓樹匹配、成對幾何直方圖匹配。
      1.Hu矩匹配
          輪廓的Hu矩對包括縮放、旋轉和鏡像映射在內的變化具有不變性。Contour<Point>.MatchShapes方法和cvMatchShapes函數(shù)可以很方便的實現(xiàn)對2個輪廓間的匹配。
      2.輪廓樹匹配
          用樹的形式比較兩個輪廓。cvMatchContourTrees函數(shù)實現(xiàn)了輪廓樹的對比。
      3.成對幾何直方圖匹配
          在得到輪廓的成對幾何直方圖之后,可以使用直方圖對比的方法來進行匹配。如果您和我一樣忘記了直方圖的對比方式,可以看看我寫的另一篇文章《顏色直方圖的計算、顯示、處理、對比及反向投影(How to Use Histogram? Calculate, Show, Process, Compare and BackProject)》。

          各種輪廓匹配的示例代碼如下:

      輪廓的匹配
      復制代碼
      //開始匹配
      private void btnStartMatch_Click(object sender, EventArgs e)
      {
      //準備輪廓(這里只比較最外圍的輪廓)
      Image<Bgr, Byte> image1 = new Image<Bgr, byte>((Bitmap)pbImage1.Image);
      Image<Bgr, Byte> image2 = new Image<Bgr, byte>((Bitmap)pbImage2.Image);
      Image<Gray, Byte> imageGray1 = image1.Convert<Gray, Byte>();
      Image<Gray, Byte> imageGray2 = image2.Convert<Gray, Byte>();
      Image<Gray, Byte> imageThreshold1 = imageGray1.ThresholdBinaryInv(new Gray(128d), new Gray(255d));
      Image<Gray, Byte> imageThreshold2 = imageGray2.ThresholdBinaryInv(new Gray(128d), new Gray(255d));
      Contour<Point> contour1 = imageThreshold1.FindContours(CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, RETR_TYPE.CV_RETR_EXTERNAL);
      Contour<Point> contour2 = imageThreshold2.FindContours(CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, RETR_TYPE.CV_RETR_EXTERNAL);
      /*if (contour1.Perimeter / 50 > 2 && contour2.Perimeter / 50 > 2)
      {
      contour1 = contour1.ApproxPoly(contour1.Perimeter / 50, 2, new MemStorage()); //對輪廓進行多邊形逼近(參數(shù)設為輪廓周長的1/50)
      contour2 = contour2.ApproxPoly(contour2.Perimeter / 50, 2, new MemStorage());
      }
      */
      //進行匹配
      string result = "";
      if (rbHuMoments.Checked)
      result = MatchShapes(contour1, contour2); //Hu矩匹配
      else if (rbContourTree.Checked)
      result = MatchContourTrees(contour1, contour2); //輪廓樹匹配
      else if (rbPGH.Checked)
      result = MatchPghHist(contour1, contour2); //成對幾何直方圖匹配
      txtResult.Text += result;
      }

      //Hu矩匹配
      private string MatchShapes(Contour<Point> contour1, Contour<Point> contour2)
      {
      //匹配方法
      CONTOURS_MATCH_TYPE matchType = rbHuI1.Checked ? CONTOURS_MATCH_TYPE.CV_CONTOUR_MATCH_I1 : (rbHuI2.Checked ? CONTOURS_MATCH_TYPE.CV_CONTOURS_MATCH_I2 : CONTOURS_MATCH_TYPE.CV_CONTOURS_MATCH_I3);
      Stopwatch sw = new Stopwatch();
      sw.Start();
      //匹配
      double matchValue = contour1.MatchShapes(contour2, matchType);
      sw.Stop();
      double time = sw.Elapsed.TotalMilliseconds;
      return string.Format("Hu矩匹配({0:G}),結果:{1:F05},用時:{2:F05}毫秒\r\n", matchType, matchValue, time);
      }

      //輪廓樹匹配
      private string MatchContourTrees(Contour<Point> contour1, Contour<Point> contour2)
      {
      //生成輪廓樹
      double thresholdOfCreate = double.Parse(txtThresholdOfCreateContourTrees.Text); //生成輪廓樹的閥值
      IntPtr ptrTree1 = CvInvoke.cvCreateContourTree(contour1.Ptr, new MemStorage().Ptr, thresholdOfCreate);
      IntPtr ptrTree2 = CvInvoke.cvCreateContourTree(contour2.Ptr, new MemStorage().Ptr, thresholdOfCreate);
      //匹配
      double thresholdOfMatch = double.Parse(txtThresholdOfMatchContourTrees.Text); //比較輪廓樹的閥值
      Stopwatch sw = new Stopwatch();
      sw.Start();
      double matchValue = CvInvoke.cvMatchContourTrees(ptrTree1, ptrTree2, MATCH_CONTOUR_TREE_METHOD.CONTOUR_TREES_MATCH_I1, thresholdOfMatch);
      sw.Stop();
      double time = sw.Elapsed.TotalMilliseconds;
      return string.Format("輪廓樹匹配(生成輪廓樹的閥值:{0},比較輪廓樹的閥值:{1}),結果:{2:F05},用時:{3:F05}毫秒\r\n", thresholdOfCreate, thresholdOfMatch, matchValue, time);
      }

      //成對幾何直方圖匹配
      private string MatchPghHist(Contour<Point> contour1, Contour<Point> contour2)
      {
      //生成成對幾何直方圖
      Rectangle rect1 = contour1.BoundingRectangle;
      float maxDist1 = (float)Math.Sqrt(rect1.Width * rect1.Width + rect1.Height * rect1.Height); //輪廓的最大距離:這里使用輪廓矩形邊界框的對角線長度
      int[] bins1 = new int[] { 60, 20 };
      RangeF[] ranges1 = new RangeF[] { new RangeF(0f, 180f), new RangeF(0f, maxDist1) }; //直方圖第0維為角度,范圍在(0,180),第2維為輪廓兩條邊緣線段的距離
      DenseHistogram hist1 = new DenseHistogram(bins1, ranges1);
      CvInvoke.cvCalcPGH(contour1.Ptr, hist1.Ptr);
      Rectangle rect2 = contour2.BoundingRectangle;
      float maxDist2 = (float)Math.Sqrt(rect2.Width * rect2.Width + rect2.Height * rect2.Height);
      int[] bins2 = new int[] { 60, 20 };
      RangeF[] ranges2 = new RangeF[] { new RangeF(0f, 180f), new RangeF(0f, maxDist2) };
      DenseHistogram hist2 = new DenseHistogram(bins2, ranges2);
      CvInvoke.cvCalcPGH(contour2.Ptr, hist2.Ptr);
      //匹配
      Stopwatch sw = new Stopwatch();
      sw.Start();
      double compareResult;
      HISTOGRAM_COMP_METHOD compareMethod = rbHistCorrel.Checked ? HISTOGRAM_COMP_METHOD.CV_COMP_CORREL : (rbHistChisqr.Checked ? HISTOGRAM_COMP_METHOD.CV_COMP_CHISQR : (rbHistIntersect.Checked ? HISTOGRAM_COMP_METHOD.CV_COMP_INTERSECT : HISTOGRAM_COMP_METHOD.CV_COMP_BHATTACHARYYA));
      if (rbHistEmd.Checked)
      {
      //EMD
      //將直方圖轉換成矩陣
      Matrix<Single> matrix1 = FormProcessHist.ConvertDenseHistogramToMatrix(hist1);
      Matrix<Single> matrix2 = FormProcessHist.ConvertDenseHistogramToMatrix(hist2);
      compareResult = CvInvoke.cvCalcEMD2(matrix1.Ptr, matrix2.Ptr, DIST_TYPE.CV_DIST_L2, null, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
      matrix1.Dispose();
      matrix2.Dispose();
      }
      else
      {
      //直方圖對比方式
      hist1.Normalize(1d);
      hist2.Normalize(1d);
      compareResult = CvInvoke.cvCompareHist(hist1.Ptr, hist2.Ptr, compareMethod);
      }
      sw.Stop();
      double time = sw.Elapsed.TotalMilliseconds;
      return string.Format("成對幾何直方圖匹配(匹配方式:{0}),結果:{1:F05},用時:{2:F05}毫秒\r\n", rbHistEmd.Checked ? "EMD" : compareMethod.ToString("G"), compareResult, time);
      }

      復制代碼

       

        

          通過以上代碼,可以計算出兩個輪廓對比的值,但是這些值具體代表什么意義呢?實際上,我目前還不清楚,需要進行大量的試驗才行。

       

      感謝您耐心看完本文,希望對您有所幫助。

      分類: 圖像處理

        本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
        轉藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多