现在的位置: 首页 > 综合 > 正文

OpenCV库基础知识

2013年10月08日 ⁄ 综合 ⁄ 共 9322字 ⁄ 字号 评论关闭

使用库的技术问题


怎么访问图像像素

(坐标是从0开始的,并且是相对图像原点的位置。图像原点或者是左上角 (img->origin=IPL_ORIGIN_TL) 或者是左下角 (img->origin=IPL_ORIGIN_BL) )

  • 假设有 8-bit 1-通道的图像 I (IplImage* img):
I(x,y) ~ ((uchar*)(img->imageData + img->widthStep*y))[x]
  • 假设有 8-bit 3-通道的图像 I (IplImage* img):
I(x,y)blue ~ ((uchar*)(img->imageData + img->widthStep*y))[x*3]
I(x,y)green ~ ((uchar*)(img->imageData + img->widthStep*y))[x*3+1]
I(x,y)red ~ ((uchar*)(img->imageData + img->widthStep*y))[x*3+2]
例如,给点 (100,100) 的亮度增加 30 ,那么可以这样做:
CvPoint pt = {100,100};
((uchar*)(img->imageData + img->widthStep*pt.y))[pt.x*3] += 30;
((uchar*)(img->imageData + img->widthStep*pt.y))[pt.x*3+1] += 30;
((uchar*)(img->imageData + img->widthStep*pt.y))[pt.x*3+2] += 30;
或者更高效地:
CvPoint pt = {100,100};
uchar* temp_ptr = &((uchar*)(img->imageData + img->widthStep*pt.y))[pt.x*3];
temp_ptr[0] += 30;
temp_ptr[1] += 30;
temp_ptr[2] += 30;
  • 假设有 32-bit 浮点数, 1-通道 图像 I (IplImage* img):
I(x,y) ~ ((float*)(img->imageData + img->widthStep*y))[x]
  • 现在,一般的情况下,假设有 N-通道,类型为 T 的图像:
I(x,y)c ~ ((T*)(img->imageData + img->widthStep*y))[x*N + c]
你可以使用宏 CV_IMAGE_ELEM( image_header, elemtype, y, x_Nc )
I(x,y)c ~ CV_IMAGE_ELEM( img, T, y, x*N + c )
也有针对各种图像(包括 4 通道图像)和矩阵的函数(cvGet2D, cvSet2D), 但是它们非常慢。


如何访问矩阵元素?

方法是类似的(下面的例子都是针对 0 起点的列和行)

  • 设有 32-bit 浮点数的实数矩阵 M (CvMat* mat):
M(i,j) ~ ((float*)(mat->data.ptr + mat->step*i))[j]
  • 设有 64-bit 浮点数的复数矩阵 M (CvMat* mat):
Re M(i,j) ~ ((double*)(mat->data.ptr + mat->step*i))[j*2]
Im M(i,j) ~ ((double*)(mat->data.ptr + mat->step*i))[j*2+1]
  • 对单通道矩阵,有宏 CV_MAT_ELEM( matrix, elemtype, row, col ), 例如对 32-bit 浮点数的实数矩阵:
M(i,j) ~ CV_MAT_ELEM( mat, float, i, j ),
例如,这儿是一个 3x3 单位矩阵的初始化:
CV_MAT_ELEM( mat, float, 0, 0 ) = 1.f;
CV_MAT_ELEM( mat, float, 0, 1 ) = 0.f;
CV_MAT_ELEM( mat, float, 0, 2 ) = 0.f;
CV_MAT_ELEM( mat, float, 1, 0 ) = 0.f;
CV_MAT_ELEM( mat, float, 1, 1 ) = 1.f;
CV_MAT_ELEM( mat, float, 1, 2 ) = 0.f;
CV_MAT_ELEM( mat, float, 2, 0 ) = 0.f;
CV_MAT_ELEM( mat, float, 2, 1 ) = 0.f;
CV_MAT_ELEM( mat, float, 2, 2 ) = 1.f;


如何在 OpenCV 中处理我自己的数据

设你有 300x200 32-bit 浮点数 image/array, 也就是对一个有 60000 个元素的数组。

int cols = 300, rows = 200;
float* myarr = new float[rows*cols];
// 第一步,初始化 CvMat 头
CvMat mat = cvMat( rows, cols,
                  CV_32FC1, // 32 位浮点单通道类型
                  myarr // 用户数据指针(数据没有被复制)
                  );
// 第二步,使用 cv 函数, 例如计算 l2 (Frobenius) 模
double norm = cvNorm( &mat, 0, CV_L2 );
...
delete myarr;

其它情况在参考手册中有描述。 见 cvCreateMatHeadercvInitMatHeadercvCreateImageHeadercvSetData


如何读入和显示图像

/* usage: prog <image_name> */
#include <cv.h>
#include <highgui.h>

#pragma comment(lib, "cv.lib")
#pragma comment(lib, "cxcore.lib")
#pragma comment(lib, "highgui.lib")

int main( int argc, char** argv )
{
    IplImage* img;
    if( argc == 2 && (img = cvLoadImage( argv[1], 1)) != 0 )
    {
        cvNamedWindow( "Image view", 1 );
        cvShowImage( "Image view", img );
        cvWaitKey(0); // 非常重要,内部包含事件处理循环
        cvDestroyWindow( "Image view" );
        cvReleaseImage( &img );
        return 0;
    }
    return -1;
}


如何检测和处理轮廓线

参考 squares demo

/*
在程序里找寻矩形
*/
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#pragma comment(lib, "cv.lib")
#pragma comment(lib, "cxcore.lib")
#pragma comment(lib, "highgui.lib")

int thresh = 50;
IplImage* img = 0;
IplImage* img0 = 0;
CvMemStorage* storage = 0;
CvPoint pt[4];
const char* wndname = "Square Detection Demo";

// helper function:
// finds a cosine of angle between vectors
// from pt0->pt1 and from pt0->pt2 
double angle( CvPoint* pt1, CvPoint* pt2, CvPoint* pt0 )
{
	double dx1 = pt1->x - pt0->x;
	double dy1 = pt1->y - pt0->y;
	double dx2 = pt2->x - pt0->x;
	double dy2 = pt2->y - pt0->y;
	return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
}

// returns sequence of squares detected on the image.
// the sequence is stored in the specified memory storage
CvSeq* findSquares4( IplImage* img, CvMemStorage* storage )
{
	CvSeq* contours;
	int i, c, l, N = 11;
	CvSize sz = cvSize( img->width & -2, img->height & -2 );
	IplImage* timg = cvCloneImage( img ); // make a copy of input image
	IplImage* gray = cvCreateImage( sz, 8, 1 ); 
	IplImage* pyr = cvCreateImage( cvSize(sz.width/2, sz.height/2), 8, 3 );
	IplImage* tgray;
	CvSeq* result;
	double s, t;
	// create empty sequence that will contain points -
	// 4 points per square (the square's vertices)
	CvSeq* squares = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvPoint), storage );

	// select the maximum ROI in the image
	// with the width and height divisible by 2
	cvSetImageROI( timg, cvRect( 0, 0, sz.width, sz.height ));

	// down-scale and upscale the image to filter out the noise
	cvPyrDown( timg, pyr, 7 );
	cvPyrUp( pyr, timg, 7 );
	tgray = cvCreateImage( sz, 8, 1 );

	// find squares in every color plane of the image
	for( c = 0; c < 3; c++ )
	{
		// extract the c-th color plane
		cvSetImageCOI( timg, c+1 );
		cvCopy( timg, tgray, 0 );

		// try several threshold levels
		for( l = 0; l < N; l++ )
		{
			// hack: use Canny instead of zero threshold level.
			// Canny helps to catch squares with gradient shading   
			if( l == 0 )
			{
				// apply Canny. Take the upper threshold from slider
				// and set the lower to 0 (which forces edges merging) 
				cvCanny( tgray, gray, 0, thresh, 5 );
				// dilate canny output to remove potential
				// holes between edge segments 
				cvDilate( gray, gray, 0, 1 );
			}
			else
			{
				// apply threshold if l!=0:
				//     tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
				cvThreshold( tgray, gray, (l+1)*255/N, 255, CV_THRESH_BINARY );
			}

			// find contours and store them all as a list
			cvFindContours( gray, storage, &contours, sizeof(CvContour),
				CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );

			// test each contour
			while( contours )
			{
				// approximate contour with accuracy proportional
				// to the contour perimeter
				result = cvApproxPoly( contours, sizeof(CvContour), storage,
					CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0 );
				// square contours should have 4 vertices after approximation
				// relatively large area (to filter out noisy contours)
				// and be convex.
				// Note: absolute value of an area is used because
				// area may be positive or negative - in accordance with the
				// contour orientation
				if( result->total == 4 &&
					fabs(cvContourArea(result,CV_WHOLE_SEQ)) > 1000 &&
					cvCheckContourConvexity(result) )
				{
					s = 0;

					for( i = 0; i < 5; i++ )
					{
						// find minimum angle between joint
						// edges (maximum of cosine)
						if( i >= 2 )
						{
							t = fabs(angle(
								(CvPoint*)cvGetSeqElem( result, i ),
								(CvPoint*)cvGetSeqElem( result, i-2 ),
								(CvPoint*)cvGetSeqElem( result, i-1 )));
							s = s > t ? s : t;
						}
					}

					// if cosines of all angles are small
					// (all angles are ~90 degree) then write quandrange
					// vertices to resultant sequence 
					if( s < 0.3 )
					{
						for( i = 0; i < 4; i++ )
						{
							cvSeqPush( squares,	(CvPoint*)cvGetSeqElem( result, i ));
						}
					}
				}

				// take the next contour
				contours = contours->h_next;
			}
		}
	}

	// release all the temporary images
	cvReleaseImage( &gray );
	cvReleaseImage( &pyr );
	cvReleaseImage( &tgray );
	cvReleaseImage( &timg );

	return squares;
}

// the function draws all the squares in the image
void drawSquares( IplImage* img, CvSeq* squares )
{
	CvSeqReader reader;
	IplImage* cpy = cvCloneImage( img );
	int i;

	// initialize reader of the sequence
	cvStartReadSeq( squares, &reader, 0 );

	// read 4 sequence elements at a time (all vertices of a square)
	for( i = 0; i < squares->total; i += 4 )
	{
		CvPoint* rect = pt;
		int count = 4;

		// read 4 vertices
		memcpy( pt, reader.ptr, squares->elem_size );
		CV_NEXT_SEQ_ELEM( squares->elem_size, reader );
		memcpy( pt + 1, reader.ptr, squares->elem_size );
		CV_NEXT_SEQ_ELEM( squares->elem_size, reader );
		memcpy( pt + 2, reader.ptr, squares->elem_size );
		CV_NEXT_SEQ_ELEM( squares->elem_size, reader );
		memcpy( pt + 3, reader.ptr, squares->elem_size );
		CV_NEXT_SEQ_ELEM( squares->elem_size, reader );

		// draw the square as a closed polyline 
		cvPolyLine( cpy, &rect, &count, 1, 1, CV_RGB(0,255,0), 3, CV_AA, 0 );
	}

	// show the resultant image
	cvShowImage( wndname, cpy );
	cvReleaseImage( &cpy );
}

void on_trackbar( int a )
{
	if( img )
	{
		drawSquares( img, findSquares4( img, storage ) );
	}
}

char* names[] = { "pic1.png", "pic2.png", "pic3.png",
"pic4.png", "pic5.png", "pic6.png", 0 };

int main(int argc, char** argv)
{
	int i, c;
	// create memory storage that will contain all the dynamic data
	storage = cvCreateMemStorage(0);

	for( i = 0; names[i] != 0; i++ )
	{
		// load i-th image
		img0 = cvLoadImage( names[i], 1 );
		if( !img0 )
		{
			printf("Couldn't load %s/n", names[i] );
			continue;
		}
		img = cvCloneImage( img0 );

		// create window and a trackbar (slider) with parent "image" and set callback
		// (the slider regulates upper threshold, passed to Canny edge detector) 
		cvNamedWindow( wndname, 1 );
		cvCreateTrackbar( "canny thresh", wndname, &thresh, 1000, on_trackbar );

		// force the image processing
		on_trackbar(0);
		// wait for key.
		// Also the function cvWaitKey takes care of event processing
		c = cvWaitKey(0);
		// release both images
		cvReleaseImage( &img );
		cvReleaseImage( &img0 );
		// clear memory storage - reset free space position
		cvClearMemStorage( storage );
		if( c == 27 )
		{
			break;
		}
	}

	cvDestroyWindow( wndname );

	return 0;
}


如何用 OpenCV 来定标摄像机


可以使用/OpenCV/samples/c目录下的calibration.cpp这个程序,程序的输入支持USB摄像机,avi文件或者图片
1。使用说明

a.输入为图片时:
// example command line (for copy-n-paste):
// calibration -w 6 -h 8 -s 2 -n 10 -o camera.yml -op -oe [<list_of_views.txt>]

/* The list of views may look as following (discard the starting and ending ------ separators):
-------------------
view000.png
view001.png
#view002.png
view003.png
view010.png
one_extra_view.jpg
-------------------

that is, the file will contain 6 lines, view002.png will not be used for calibration,
other ones will be (those, in which the chessboard pattern will be found)

b.输入为摄像机或者avi文件时

"When the live video from camera is used as input, the following hot-keys may be used:/n"

           "  <ESC>, 'q' - quit the program/n"

           "  'g' - start capturing images/n"

           "  'u' - switch undistortion on/off/n";


c。输入参数说明

"Usage: calibration/n"

           "     -w <board_width>         # the number of inner corners per one of board dimension/n"

           "     -h <board_height>        # the number of inner corners per another board dimension/n"

           "     [-n <number_of_frames>]  # the number of frames to use for calibration/n"

           "                              # (if not specified, it will be set to the number/n"

           "                              #  of board views actually available)/n"

           "     [-d <delay>]             # a minimum delay in ms between subsequent attempts to capture a next view/n"

           "                              # (used only for video capturing)/n"

           "     [-s <square_size>]       # square size in some user-defined units (1 by default)/n"

           "     [-o <out_camera_params>] # the output filename for intrinsic [and extrinsic] parameters/n"

           "     [-op]                    # write detected feature points/n"

           "     [-oe]                    # write extrinsic parameters/n"

           "     [-zt]                    # assume zero tangential distortion/n"

           "     [-a <aspect_ratio>]      # fix aspect ratio (fx/fy)/n"

           "     [-p]                     # fix the principal point at the center/n"

           "     [-v]                     # flip the captured images around the horizontal axis/n"

           "     [input_data]             # input data, one of the following:/n"

           "                              #  - text file with a list of the images of the board/n"

           "                              #  - name of video file with a video of the board/n"

           "                              # if input_data not specified, a live view from the camera is used/n"

2.经多次使用发现,不指定 -p参数时计算的结果误差较大,主要表现在对u0,v0的估计误差较大,因此建议使用时加上-p参数

抱歉!评论已关闭.