《学习opencv》第三章课后习题详解

时间:2023-02-10 14:07:19

第一题:

3_1:
// chapter_3.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "stdio.h"
#include "cxcore.h" //处理数据结构
#include "cv.h"
#include "types_c.h"


int _tmain(int argc, _TCHAR* argv[])
{
printf("a***选取一个负的浮点数,取他的绝对值,四舍五入后,取它的极值***\n");
double fudian = -123.4532;
printf("\t默认选取的负数为:%f\n", fudian);
double fudian1 = fabs(fudian);
printf("\t其绝对值为:%f\n", fudian1);
int fudian3 = cvRound(fudian1);
printf("\t四舍五入之后,极值为:%d\n", fudian3);
printf("b***产生一些随机数***\n");
CvRNG rng = cvRNG(cvGetTickCount());
printf("\t");
for (int i = 0; i < 10; i++)
{
printf("*%d", cvRandInt(&rng) % 256);
}
printf("\n");
printf("c***Cvpoint2D32f类型到CvPoint类型的转换***\n");
CvPoint2D32f cpf = cvPoint2D32f(89.2345, 78.6745);
printf("\t该cvpoint2d32f数为%f,%f\n", cpf.x, cpf.y);
CvPoint cpf1 = cvPointFrom32f(cpf);
printf("\t转换后的cvpoint数为%d,%d\n", cpf1.x, cpf1.y);
printf("c***Cvpoint类型到CvPoint2D32f类型的转换***\n");
CvPoint cv = cvPoint(678, 789);
CvPoint2D32f cv1 = cvPointTo32f(cv);
printf("\t原来的cvpoint数:%d,%d\n", cv.x, cv.y);
printf("\t现在的cvpoint2d32f数:%f,%f\n", cv1.x, cv1.y);
system("pause");
return 0;
}

函数解析:

fabs() //取绝对值
cvRound() //对double型数据四舍五入后返回int型数据
cvGetTickCount() //返回64位长整数的时间数据,为CvRNG设置的专用种子。
cvRNG() //函数 cvRNG 初始化随机数生成器并返回其状态。指向这个状态的指针可以传递给函数 cvRandInt, cvRandReal 和 cvRandArr
cvRandInt() //返回均匀分布的随机 32-bit 无符号整型值并更新 RNG 状态。
cvPointFrom32f() /*cvPoint转CvPoint2D32f
CV_INLINE CvPoint cvPointFrom32f( CvPoint2D32f point )
{
CvPoint ipt;
ipt.x = cvRound(point.x);
ipt.y = cvRound(point.y);

return ipt;
}
*/

cvPointTo32f() /*CvPoint2D32f转cvPoint
CV_INLINE CvPoint2D32f cvPointTo32f( CvPoint point )
{
return cvPoint2D32f( (float)point.x, (float)point.y );
}
*/

第二题:

#include"cv.h"
#include"stdafx.h"
#include"highgui.h"

int _tmain(int argc, _TCHAR* argv[])
{
CvMat* srcMat = cvCreateMat(100, 100, CV_8UC3);
cvSetZero(srcMat);
CvPoint cirPoint = cvPoint(50, 50);
int r = 40;
CvScalar circolor = cvScalar(25, 100,50);
cvCircle(srcMat, cirPoint, r,circolor);
cvNamedWindow("circle");
cvShowImage("circle", (CvArr*)srcMat);
cvWaitKey(0);
cvReleaseMat(&srcMat);
cvDestroyWindow("circle");
return 0;
}

函数解析:

cvCreateMat( int rows, int cols, int type ); //创建一个矩阵,长*高(像素),以及类型CV_8UC3为8位无符号三通道
cvSetZero(srcMat); //清零矩阵
cvCircle( CvArr* img, CvPoint center, int radius,CvScalar color, int thickness CV_DEFAULT(1),
int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0));
/*画圆
载体矩阵,圆心坐标(像素),半径(像素),圆周颜色,半径粗细,圆周线的类型(见),圆心坐标点和半径值的小数点位数
*/

第三、四题:

#include"stdafx.h"
#include"cv.h"
#include"highgui.h"

int _tmain(int argc, _TCHAR* argv[])
{
CvMat* mat = cvCreateMat(100, 100, CV_8UC3);
cvSetZero(mat);
//矩形平面
for (int row = 20; row < 40; row++)
for (int col = 5; col < 20; col++)
{
uchar* ptr = cvPtr2D((CvArr*)mat, row, col);
ptr[1] = 255;
}
/*3_3为矩形,代替for循环里的内容
uchar* ptr;
for (int row = 20; row <= 40; row++)
for (int col = 5; col <= 20; col++)
{
if (row == 20 || row == 40)
{
ptr = cvPtr2D((CvArr*)mat, row, col);
ptr[1] = 255;
}
else
{
ptr = cvPtr2D((CvArr*)mat, row, 5);
ptr[1] = 255;
ptr = cvPtr2D((CvArr*)mat, row, 20);
ptr[1] = 255;
}
}
*/
cvNamedWindow("green rectangle");
cvShowImage("green rectangle", (CvArr*)mat);
cvWaitKey(0);
cvReleaseMat(&mat);
cvDestroyWindow("green rectangle");
return 0;
}

函数解析:

cvPtr2D( const CvArr* arr, int idx0, int idx1, int* type CV_DEFAULT(NULL) );
/*
返回一个uchar型的指向矩阵中元素的指针,idx0,idx1为索引的值,最后一个为可选参数,表示输出值的类型
*/

第五题:

#include"stdafx.h"
#include"cv.h"
#include"highgui.h"

int _tmain(int argc, _TCHAR* argv[])
{
IplImage* img = cvCreateImage(cvSize(210, 210), 8, 1);
cvZero(img);
int hength = 210, width = 210, board = 10;
int value = 0;
for (int i=0,j=0; i<=200;)
{
cvSetImageROI(img, cvRect(i, j, width, hength));
cvSet(img, cvScalar(value, 0, 0));
cvResetImageROI(img);
i += 10;
j += 10;
if (value<+255)
value += 10;
}
cvNamedWindow("ROL");
cvShowImage("ROL", img);
cvWaitKey(0);
cvReleaseImage(&img);
cvDestroyWindow("ROL");
return 0;
}

函数解析:

cvSetImageROI( IplImage* image, CvRect rect );
/*
选择特定的区域,与cvResetImageROI连用,所选取的特定区域在这两个函数之间有效。
两个参数:所要选取的图像以及选取的范围
*/
cvSet(CvArr* arr,CvScalar value);//设置矩阵的值
cvResetImageROI( IplImage* image );//取消选中

第六题:

#include"stdafx.h"
#include"cv.h"
#include"highgui.h"

int _tmain(int argc, _TCHAR* argv[])
{
IplImage* img = NULL;//判断一下图像是否加载到
img = cvLoadImage("board.jpg");
IplImage* dst1, *dst2;
dst1 = cvCreateImage(cvSize(20, 30), img->depth, img->nChannels);
dst2 = cvCreateImage(cvSize(20, 30), img->depth, img->nChannels);
dst1->origin = img->origin;
dst1->widthStep = img->widthStep;
dst2->origin = img->origin;
dst2->widthStep = img->widthStep;
dst1->imageData = img->imageData + img->widthStep * 10 + img->nChannels * 5;
dst2->imageData = img->imageData + img->widthStep * 60 + img->nChannels * 50;
cvNot(dst1, dst1);
cvNot(dst2, dst2);

cvNamedWindow("img");

cvShowImage("img", img);

cvWaitKey(0);

cvReleaseImage(&img);
cvReleaseImage(&dst1);
cvReleaseImage(&dst2);

cvShowImage("img", img);
cvDestroyWindow("img");
return 0;
}

函数解析:

cvNot( const CvArr* src, CvArr* dst ) //将原矩阵里的像素值取反,送到目标矩阵
/*
这里需要注意的是结构IplImage里的几个参数:
origin:选择坐标原点是在左上角还是左下角
widthStep:图像的行大小,以字节为单位,永远是四的倍数,不足的话自动补齐。
如程序中的img图像width = 640,nchannels = 3,一个通道是八位代表一个字节,因此widthstep = 640*3 = 1920个字节
imageData:指向图像数据的指针,默认指向图像的第(1,1)个像素

这里创建的两个图像头img,img2并没有正真的图像数据,他们的imgeData指针指向img的图像数据,因此他们会显示在img图像上。
*/

第七题:

#include"stdafx.h"
#include"cv.h"
#include"highgui.h"

int _tmain(int argc, _TCHAR* argv[])
{
IplImage* src = cvLoadImage("board.jpg");
IplImage* Rimag = cvCreateImage(cvGetSize(src), src->depth, 1);
IplImage* Gimag = cvCreateImage(cvGetSize(src), src->depth, 1);
IplImage* Bimag = cvCreateImage(cvGetSize(src), src->depth, 1);
IplImage* clone1, *clone2;
double G_MAX_PIX = 0;
double G_MIN_PIX = 0;
cvSplit(src, Rimag, Gimag, Bimag,NULL);//分离时的顺序到底是RGB还是BGR还不确定?

clone1 = cvCloneImage(Gimag);
clone2 = cvCloneImage(Gimag);

cvMinMaxLoc(Gimag, &G_MIN_PIX, &G_MAX_PIX);

double thresh = (unsigned char)(G_MAX_PIX - G_MIN_PIX) / 2.0;


cvSet(clone1, cvScalar(thresh));
cvSetZero(clone2);
cvCmp(Gimag, clone1, clone2, CV_CMP_GE);
cvSubS(Gimag, cvScalar(thresh / 2.0), Gimag, clone2);

cvNamedWindow("src");
cvNamedWindow("clone2");

cvShowImage("src", src);
cvShowImage("clone2", clone2);

cvWaitKey(0);

cvReleaseImage(&src);
cvReleaseImage(&clone1);
cvReleaseImage(&Rimag);
cvReleaseImage(&Gimag);
cvReleaseImage(&Bimag);
cvReleaseImage(&clone2);


cvDestroyWindow("src");
cvDestroyWindow("clone2");
return 0;
}

函数解析:

cvSplit( const CvArr* src, CvArr* dst0, CvArr* dst1,CvArr* dst2, CvArr* dst3 );
/*分离图像,分割多通道数组成几个单通道数组或者从数组中提取一个通道,目标图像只能是单通道*/
cvCloneImage( const IplImage* image ); /*克隆图像,函数自己会单独开一块内存,将复制的图像数据放进去
而cvCopy()则必须先创建一个图像*/
cvMinMaxLoc( const CvArr* arr, double* min_val, double* max_val,CvPoint* min_loc CV_DEFAULT(NULL),
CvPoint* max_loc CV_DEFAULT(NULL),const CvArr* mask CV_DEFAULT(NULL) );
/*
获取图像中像素值最大、最小的值以及他们的CPoint位置
*/

cvCmp( const CvArr* src1, const CvArr* src2, CvArr* dst, int cmp_op );
/*
比较另个图像对应位置的像素值,如果条件为真,则结果矩阵对应位为0xff,否则为零。
cmp_op有以下几种:
CV_CMP_EQ (src1i == src2i)
CV_CMP_GT (src1i > src2i)
CV_CMP_GE (src1i >= src2i)
CV_CMP_LT (src1i < src2i)
CV_CMP_LE (src1i <= src2i)
CV_CMP_NE (src1i != src2i)
*/
cvSubS( const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask CV_DEFAULT(NULL))
/*
将原图像每一个像素值减去一个值得到目标图像。
最后一个参数mask为矩阵开关:如果该矩阵非空,则dst将会与mask比较,对应mask中0元素的位置,在dst中不会被改变
*/

第八题:

#include"stdafx.h"
#include"cv.h"
#include"highgui.h"
#include"stdio.h"

typedef struct my_struct
{
int i;
CvPoint point;
CvRect rect;
}Mystruct;

void write_my_struct(CvFileStorage* fs,const char* name, Mystruct* ms)
{
cvStartWriteStruct(fs, name, CV_NODE_MAP);
cvWriteInt(fs, "my_struct_value", ms->i);

cvStartWriteStruct(fs, "my_struct_point", CV_NODE_SEQ);



cvWriteInt(fs, 0, ms->point.x);
cvWriteInt(fs, 0, ms->point.y);
cvEndWriteStruct(fs);

cvStartWriteStruct(fs, "my_struct_rect", CV_NODE_SEQ);
cvWriteInt(fs, 0, ms->rect.x);
cvWriteInt(fs, 0, ms->rect.y);
cvWriteInt(fs, 0, ms->rect.width);
cvWriteInt(fs, 0, ms->rect.height);
cvEndWriteStruct(fs);

cvEndWriteStruct(fs);
}

void read_my_stuct(CvFileStorage* fs, CvFileNode* ms_node, my_struct* ms)
{
int i = cvGetFileNodeByName(fs, ms_node, "my_struct_value")->data.i;
ms->i = i;

CvSeq* s1 = cvGetFileNodeByName(fs, ms_node, "my_struct_point")->data.seq;
CvPoint point;
point.x = cvReadInt((CvFileNode*)cvGetSeqElem(s1, 0));
point.y = cvReadInt((CvFileNode*)cvGetSeqElem(s1, 1));
ms->point = point;

CvSeq* s2 = cvGetFileNodeByName(fs, ms_node, "my_struct_rect")->data.seq;
CvRect rect;
rect.x = cvReadInt((CvFileNode*)cvGetSeqElem(s2, 0));
rect.y = cvReadInt((CvFileNode*)cvGetSeqElem(s2, 1));
rect.width = cvReadInt((CvFileNode*)cvGetSeqElem(s2, 3));
rect.height = cvReadInt((CvFileNode*)cvGetSeqElem(s2, 2));
ms->rect = rect;
}

void ShowStructValue(Mystruct* pvalue)
{
printf("integer:%d\n", pvalue->i);
printf("CvPoint: (%d, %d)\n", pvalue->point.x, pvalue->point.y);
printf("CvRect: h-->%d\tw-->%d\t(%d, %d)\n", pvalue->rect.height,pvalue->rect.width, pvalue->rect.x, pvalue->rect.y);
}

int _tmain(int argc, _TCHAR* argv[])
{
const char* filename = "example.xml";
char name[12];
CvFileStorage* fs = cvOpenFileStorage(filename, 0, CV_STORAGE_WRITE);
for (int i = 0; i < 10; i++)
{
CvRNG rng = cvRNG(cvGetTickCount());
int total = cvRandInt(&rng);
my_struct ms = { total%256, cvPoint(total%512, total%233), cvRect(total%78, total%128, total%233, total%321)};
sprintf(name, "mystruct%d", i);
write_my_struct(fs, name, &ms);
}
cvReleaseFileStorage(&fs);

fs = cvOpenFileStorage(filename, 0, CV_STORAGE_READ);
my_struct ms1;
for (int i = 0; i < 10; i++)
{
sprintf(name, "mystruct%d", i);
CvFileNode* pnode = cvGetFileNodeByName(fs, NULL, name);
read_my_stuct(fs, pnode, &ms1);
ShowStructValue(&ms1);
}
cvReleaseFileStorage(&fs);
system("pause");
return 0;
}

函数解析:

cvStartWriteStruct()与cvEndWriteStruct()
/*在这两个函数之间写入结构体数据,当需要写入结构体的时候需要嵌套使用这两个函数,非结构体不需要
这两个函数的使用使写入到文件里的数据层次分明。
void cvStartWriteStruct(CvFileStorage* fs, const char* name, int struct_flags)
fs:文件存储器
name:为数据结构的名称
struct_flags:
CV_NODE_SEQ - 被写入的数据结构为序列结构。这样的数据没有名称。
CV_NODE_MAP - 被写入的数据结构为图表结构。这样的数据含有名称。
以上两个必须二选一。
CV_NODE_FLO 这个可选择标识符只能作用于YAML流。被写入的数据结构被看做一个数据流(不是数据块),它更加紧凑,当结构或数组里的数据是标量时,推荐用这个标志。
cvEndWriteStruct( CvFileStorage* fs );
结束写结构体
*/

void cvWriteInt( CvFileStorage* fs, const char* name, int value );//向指定文件存储器写入整型数据,name为数据结构的名称

CvFileNode* cvGetFileNodeByName( const CvFileStorage* fs,const CvFileNode* map,const char* name );
/*
根据数据结构的名称获取结点,只由获取结点才能读取该结点下的数据。返回该数据的结点
fs:文件存储器
map:文件结点这里指父结点,没有的话为null
name:为数据结构的名称
*/

int cvReadInt( const CvFileNode* node, int default_value CV_DEFAULT(0) )//从结点读取整型数据

CvFileStorage* cvOpenFileStorage( const char* filename, CvMemStorage* memstorage,int flags, const char* encoding CV_DEFAULT(NULL) );
/*打开文件,返回文件存储器
flags:
#define CV_STORAGE_READ 0
#define CV_STORAGE_WRITE 1
*/

typedef struct CvFileNode
{
int tag;
struct CvTypeInfo* info; /* type information
(only for user-defined object, for others it is 0) */
union
{
double f; /* scalar floating-point number */
int i; /* scalar integer number */
CvString str; /* text string */
CvSeq* seq; /* sequence (ordered collection of file nodes) */
CvFileNodeHash* map; /* map (collection of named file nodes) */
} data;
}

cvGetSeqElem( const CvSeq* seq, int index );
/*根据所得seq得到seq表示结构体中的每一项数据,返回
char类型的指针,可以利用强制类型转换,转换为序列中实际存储的数据类型
data.i:表示取出整型数据
data.sep:b表示取出结构数据
*/
给出一次存储后文件中结构,方便理解,结构类似于树
<?xml version="1.0"?><opencv_storage><mystruct0>//最外层的结构名,为CV_NODE_MAP结构型  <my_struct_value>32</my_struct_value>  <my_struct_point>32 94</my_struct_point>  <my_struct_rect>6 32 94 258</my_struct_rect></mystruct0></opencv_storage>

在画一幅图对应文件中的数据结构加强理解:

《学习opencv》第三章课后习题详解