OPENCV学习笔记(2)

关于上一篇笔记,主要讲的是通过直方图识别,而直方图是色彩像素点的统计来,换句话说:我认为那种识别用的是色彩。而这里要写的是用轮廓识别,换句话说用的是形状。
看了这么多,我就回忆了一下所学到的结构体。
首先是图像本身的结构体:
typedef struct CvMat
{
int type; /* CvMat 标识 (CV_MAT_MAGIC_VAL), 元素类型和标记 */
int step; /* 以字节为单位的行数据长度*/
int* refcount; /* 数据引用计数 */
union
{
uchar* ptr;
short* s;
int* i;
float* fl;
double* db;
} data;
union
{
int rows;
int height;
};
union
{
int cols;
int width;
};
这个结构体是最基础的矩阵,而图像本身就是一个复杂的矩阵,所以图像是对这个结构体的继承:
typedef struct _IplImage
{
int nSize; /* IplImage大小 */
int ID; /* 版本 (=0)*/
int nChannels; /* 大多数OPENCV函数支持1,2,3 或 4 个通道 */
int alphaChannel; /* 被OpenCV忽略 */
int depth; /* 像素的位深度: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16U,
IPL_DEPTH_16S, IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F 可支持 */
char colorModel[4]; /* 被OpenCV忽略 */
char channelSeq[4]; /* 同上 */
int dataOrder; /* 0 - 交叉存取颜色通道, 1 - 分开的颜色通道.
cvCreateImage只能创建交叉存取图像 */
int origin; /* 0 - 顶—左结构,1 - 底—左结构 (Windows bitmaps 风格) */
int align; /* 图像行排列 (4 or 8). OpenCV 忽略它,使用 widthStep 代替 */
int width; /* 图像宽像素数 */
int height; /* 图像高像素数*/
struct _IplROI *roi;/* 图像感兴趣区域. 当该值非空只对该区域进行处理 */
struct _IplImage *maskROI; /* 在 OpenCV中必须置NULL */
void *imageId; /* 同上*/
struct _IplTileInfo *tileInfo; /*同上*/
int imageSize; /* 图像数据大小(在交叉存取格式下imageSize=image->height*image->widthStep),单位字节*/
char *imageData; /* 指向排列的图像数据 */
int widthStep; /* 排列的图像行大小,以字节为单位 */
int BorderMode[4]; /* 边际结束模式, 被OpenCV忽略 */
int BorderConst[4]; /* 同上 */
char *imageDataOrigin; /* 指针指向一个不同的图像数据结构(不是必须排列的),是为了纠正图像内存分配准备的 */
}IplImage;
值得注意的地方:首先是origin这个,当有些图像复制或者视频播放时候,由于原点坐标位置未定,很容造成图片倒置。这时就得用void cvFlip( const CvArr* src, CvArr* dst=NULL, int flip_mode=0)函数或者直接设定origin来改变坐标原点;widthstep就是CvMat的step;
构造方法:IplImage* cvCreateImage( CvSize size, int depth, int channels );
直方图结构:
typedef struct CvHistogram
{
int type;
CvArr* bins;
float thresh[CV_MAX_DIM][2]; /* 对于标准直方图,bins的值有左边界+右边界=2 */
float** thresh2; /* 对于非标准直方图 */
CvMatND mat; /* embedded matrix header for array histograms */
}CvHistogram;
由于直方图的复杂性,得到一个图片的直方图的步骤就不是一个函数完成的:
1,分割图片通道
2,求出bins数量及范围
3,CvHistogram* cvCreateHist( int dims, int* sizes, int type,float** ranges=NULL, int uniform=1 );
创建直方图
4,void cvCalcHist( IplImage** image, CvHistogram* hist,int accumulate=0, const CvArr* mask=NULL );
计算直方图

这回该回到轮廓识别的主题上了,出现了新的数据结构——序列CvSeq用于得到opencv函数中返回的一些数据。但看之前得学一下opencv的内存存储器。
CvMemStorage* cvCreateMemStorage( int block_size=0 );//创建默认值大小的内存空间
void cvReleaseMemStorage( CvMemStorage** storage );//释放内存空间
void cvClearMemStorage( CvMemStorage* storage );//清空内存块,可以用于重复使用,将内存返还给存储器,而不是返回给系统
void *cvMemStorageAlloc(CvMemStorage *storage,size_t size);//开辟内存空间
序列结构:
CvSeq
可动态增长元素序列(OpenCV_1.0已发生改变,详见cxtypes.h) Growable sequence of elements

#define CV_SEQUENCE_FIELDS() /
int flags; /* micsellaneous flags */ /
int header_size; /* size of sequence header */ /
struct CvSeq* h_prev; /* previous sequence */ /
struct CvSeq* h_next; /* next sequence */ /
struct CvSeq* v_prev; /* 2nd previous sequence */ /
struct CvSeq* v_next; /* 2nd next sequence */ /
int total; /* total number of elements */ /
int elem_size;/* size of sequence element in bytes */ /
char* block_max;/* maximal bound of the last block */ /
char* ptr; /* current write pointer */ /
int delta_elems; /* how many elements allocated when the sequence grows (sequence granularity) */ /
CvMemStorage* storage; /* where the seq is stored */ /
CvSeqBlock* free_blocks; /* free blocks list */ /
CvSeqBlock* first; /* pointer to the first sequence block */
typedef struct CvSeq
{
CV_SEQUENCE_FIELDS()
} CvSeq;
相关操作就不重复列出(排序,查找,逆序,拆分,复制,读取,写入切片的复制,移除,插入,),可以查找相关文档。
写出两种结构的序列访问:
1.for(int i=0;i<seq->total;++i)
 CvPoint* p = (CvPoint *)cvGetSeqElem(seq,i);//对于返回值是一个像素点
2.for(int i=0;i<seq->total;++i)
{
 float *p = (float *)cvGetSeqElem(results,i);
 CvPoint pt = cvPoint(cvRound(p[0]),cvRound(p[1]));
};//对于返回值是一一系列值

对于轮廓的查找用cvFindContours()函数,一般先把图像用cvCanny()进行处理;或者是从cvThreshold()以及cvAdaptiveThreshold()函数得到有边缘像素的图像。
int cvFindContours( CvArr* image, CvMemStorage* storage, CvSeq** first_contour,
int header_size=sizeof(CvContour), int mode=CV_RETR_LIST,
int method=CV_CHAIN_APPROX_SIMPLE, CvPoint offset=cvPoint(0,0) );
image
输入的 8-比特、单通道图像. 非零元素被当成 1, 0 象素值保留为 0 - 从而图像被看成二值的。为了从灰度图像中得到这样的二值图像,可以使用 cvThreshold, cvAdaptiveThreshold 或 cvCanny. 本函数改变输入图像内容。
storage
得到的轮廓的存储容器
first_contour
输出参数:包含第一个输出轮廓的指针
header_size
如果 method=CV_CHAIN_CODE,则序列头的大小 >=sizeof(CvChain),否则 >=sizeof(CvContour) .
mode
提取模式.
CV_RETR_EXTERNAL - 只提取最外层的轮廓
CV_RETR_LIST - 提取所有轮廓,并且放置在 list 中
CV_RETR_CCOMP - 提取所有轮廓,并且将其组织为两层的 hierarchy: 顶层为连通域的外围边界,次层为洞的内层边界。
CV_RETR_TREE - 提取所有轮廓,并且重构嵌套轮廓的全部 hierarchy
method
逼近方法 (对所有节点, 不包括使用内部逼近的 CV_RETR_RUNS).
CV_CHAIN_CODE - Freeman 链码的输出轮廓. 其它方法输出多边形(定点序列).
CV_CHAIN_APPROX_NONE - 将所有点由链码形式翻译(转化)为点序列形式
CV_CHAIN_APPROX_SIMPLE - 压缩水平、垂直和对角分割,即函数只保留末端的象素点;
CV_CHAIN_APPROX_TC89_L1,
CV_CHAIN_APPROX_TC89_KCOS - 应用 Teh-Chin 链逼近算法. CV_LINK_RUNS - 通过连接为 1 的水平碎片使用完全不同的轮廓提取算法。仅有 CV_RETR_LIST 提取模式可以在本方法中应用.
例子:
 

#include <cv.h>
#include <highgui.h>

IplImage *src = NULL;
IplImage *dst = NULL;
int f_thresh = 100;
CvMemStorage *f_storage = NULL;
void on_trackbar(int)
{
 if(f_storage==NULL)
 {
  dst = cvCreateImage(cvGetSize(src),8,1);
  f_storage = cvCreateMemStorage(0);
 }
 else
 {
  cvClearMemStorage(f_storage);
 }
 CvSeq *contours = 0;
 cvCvtColor(src,dst,CV_BGR2GRAY);
 cvThreshold(dst,dst,f_thresh,255,CV_THRESH_BINARY);//虽然第一个参数是const,但仍可以更改dst
 cvFindContours(dst,f_storage,&contours);
 cvZero(dst);
 if(contours)
 {
  cvDrawContours(
   dst,
   contours,
   CV_RGB(0,255,0),
   CV_RGB(255,0,200),
   100
   );
 }//把所有的边界画出
 cvShowImage("Contours",dst);
}
int main( int argc, char** argv )
{
 src = cvLoadImage("./HandOutdoorColor.jpg");
 cvNamedWindow("Contours",1);
 cvCreateTrackbar(
  "threshold",
  "Contours",
  &f_thresh,
  200,
  on_trackbar
  );
 on_trackbar(0);
 cvWaitKey();
 return 0;
}

再看一下一个一个画出的边界

#include "stdafx.h"
#include <cv.h>
#include <highgui.h>
#define CVX_RED  CV_RGB(0xff,0x00,0x00)
#define CVX_GREEN CV_RGB(0x00,0xff,0x00)
#define CVX_BLUE CV_RGB(0x00,0x00,0xff)

int main(int argc, char* argv[])
{
 cvNamedWindow( "contours", 1 );
 IplImage* img_8uc1 = NULL;
 img_8uc1 = cvLoadImage("./HandOutdoorColor.jpg",0);//载入图像的灰度图
 IplImage* img_edge = cvCreateImage( cvGetSize(img_8uc1), 8, 1 );
 IplImage* img_8uc3 = cvCreateImage( cvGetSize(img_8uc1), 8, 3 );
 cvThreshold( img_8uc1, img_edge, 128, 255, CV_THRESH_BINARY );
 CvMemStorage* storage = cvCreateMemStorage();
 CvSeq* first_contour = NULL;
 int Nc = cvFindContours(
  img_edge,
  storage,
  &first_contour,
  sizeof(CvContour),
  CV_RETR_CCOMP
 );
 int n=0,k;
 printf("/n/nHit any key to draw the next contour, ESC to quit/n/n");
 printf( "Total Contours Detected: %d/n", Nc );
 for( CvSeq* c=first_contour; c!=NULL; c=c->h_next )
 {
  cvCvtColor( img_8uc1, img_8uc3, CV_GRAY2BGR );
  cvDrawContours(
   img_8uc3,
   c,
   CVX_RED, 
   CVX_BLUE,
   10,
   2,
   8
   );
   printf("Contour #%d/n", n );
   cvShowImage("contours", img_8uc3 );
   printf(" %d elements:/n", c->total );
   for( int i=0; i<c->total; ++i )
   {
   CvPoint* p = CV_GET_SEQ_ELEM( CvPoint, c, i );
   printf("    (%d,%d)/n", p->x, p->y );
   }
   if((k = cvWaitKey()&0x7F) == 27)
   break;
  n++;
 }
 printf("Finished all contours. Hit key to finish/n");
 cvCvtColor( img_8uc1, img_8uc3, CV_GRAY2BGR );
 cvShowImage( "contours", img_8uc3 );
 cvWaitKey(0);
 cvDestroyWindow( "contours");
 cvReleaseImage( &img_8uc1 );
 cvReleaseImage( &img_8uc3 );
 cvReleaseImage( &img_edge );
 return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值