opencv学习篇(8)轮廓的查找、表达、绘制、特性及匹配

本文介绍了如何在OpenCV中查找和处理图像轮廓,包括转换图像、查找轮廓、轮廓表达(顶点序列和Freeman链码)、组织方式、绘制、特性(如周长、面积、边界框、矩、凸包等)以及轮廓的匹配方法(Hu矩匹配、轮廓树匹配、成对几何直方图匹配)。
摘要由CSDN通过智能技术生成

前言
    轮廓是构成任何一个形状的边界或外形线。前面讲了如何根据色彩及色彩的分布(直方图对比和模板匹配)来进行匹配,现在我们来看看如何利用物体的轮廓。包括以下内容:轮廓的查找、表达方式、组织方式、绘制、特性、匹配。

 查找轮廓
    首先我们面对的问题是如何在图像中找到轮廓,OpenCv(EmguCv)为我们做了很多工作,我们的任务只是调用现成的函数而已。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链码需要一个起点,以及从起点出发的一系列位移。每个位移有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链码表示的轮廓
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链码读取
[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 );
复制代码
  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值