调用cvSetImageROI()函数来构造RIO,实现对图像指定范围的蓝色通道增加150级操作。笔者将其写成了通过cmd调用和直接调用的两个版本,具体代码如下:
#include
#include
using namespace std;
int main(int argc, char* argv[])
{
IplImage* src;
/*******************************cmd*********************************/
//if (argc == 7 && ((src = cvLoadImage(argv[1], 1)) != 0)) //判断传递给主函数的参数个数,加载图像至内存
//{
// int x = atoi(argv[2]); //字符串类型转化为整型 char2int
// int y = atoi(argv[3]);
// int width = atoi(argv[4]);
// int height = atoi(argv[5]);
// int add = atoi(argv[6]);
/*******************************Address*********************************/
if (src = cvLoadImage("D:\\Template\\OpenCV\\Template12_SetROI\\Debug\\3.jpg")) //加载图像至内存,返回指向的指针
{
int x = 100;
int y = 100;
int width = 100;
int height = 100;
int add = 150;
cvSetImageROI(src, cvRect(x, y, width, height)); //设置ROI
cvAddS(src, cvScalar(add), src); //数组与标量的元素级相加
cvResetImageROI(src); //释放ROI
cvNamedWindow("Roi_Add", CV_WINDOW_AUTOSIZE); //创建窗口
cvShowImage("Roi_Add", src); //显示图片
cvWaitKey(0);
cvReleaseImage(&src); //释放图像内存
cvDestroyWindow("Roi_Add"); //释放窗口
}
return 0;
}
利用widthStep方法RIO所有像素值增加1,实现cvSetImageROI()函数相同的功能,代码如下:
#include
#include
using namespace std;
int main(int argc, char* argv[])
{
IplImage* interest_img = cvLoadImage("D:\\Template\\OpenCV\\Template12_SetROI\\Debug\\3.jpg");
CvRect interest_rect = cvRect(100,100,100,100);
IplImage* sub_img = cvCreateImageHeader( //创建一个图像头
cvSize(interest_rect.width, interest_rect.height), //设置图像头的宽度、高度(与ROI相同)
interest_img->depth, //设置图像头的深度(与ROI相同)
interest_img->nChannels); //设置图像头的通道数(与ROI相同)
sub_img->origin = interest_img->origin; //设置图像头原点(与ROI相同)
sub_img->widthStep = interest_img->widthStep; //设置行数据长度(可逐行步进到下一行开头)
sub_img->imageData = interest_img->imageData+ //指向第一行数据的指针+
interest_rect.y*interest_img->widthStep + //行数*行数据长度+
interest_rect.x*interest_img->nChannels; //列数*通道数
cvAddS(sub_img, cvScalar(150), sub_img); //数组与标量的元素级相加
cvNamedWindow("Rio_Add", CV_WINDOW_AUTOSIZE); //创建窗口
cvShowImage("Rio_Add", interest_img); //显示图片
cvWaitKey(0);
cvReleaseImage(&interest_img); //释放图像内存
cvReleaseImage(&sub_img); //释放图像内存
cvDestroyWindow("Rio_Add"); //释放窗口
return 0;
}
运行结果如下图:
widthStep操作可以保持一幅图像的多个子区域处于活动状态,而不必像cvSetImageROI()函数一样反复的设置、重置区域。
调用cvAddWeighted()函数来实现Alpha融合,实现对两张图像的融合。笔者将其写成了通过cmd调用和直接调用的两个版本,具体代码如下:
#include
#include
using namespace std;
int main(int argc, char* argv[])
{
IplImage* src1,*src2;
/*******************************cmd*********************************/
//if (argc == 9 && ((src1 = cvLoadImage(argv[1], 1)) != 0) && ((src2 = cvLoadImage(argv[2], 1)) != 0)) //判断传递给主函数的参数个数,加载图像至内存
//{
// int x = atoi(argv[3]); //字符串类型转化为整型 char2int
// int y = atoi(argv[4]);
// int width = atoi(argv[5]);
// int height = atoi(argv[6]);
// double alpha = (double)atof(argv[7]);
// double beta = (double)atof(argv[8]);
/*******************************Address*********************************/
src1 = cvLoadImage("D:\\Template\\OpenCV\\Template13_AlphaBlend\\Debug\\2.jpg");
src2 = cvLoadImage("D:\\Template\\OpenCV\\Template13_AlphaBlend\\Debug\\3.jpg"); //加载图像至内存,返回指向的指针
int x = 50;
int y = 0;
int width = 320;
int height = 320;
double alpha = 0.8;
double beta = 0.2;
cvSetImageROI(src1, cvRect(x, y, width, height)); //设置ROI
cvSetImageROI(src2, cvRect(150, 70, width, height)); //设置ROI
cvAddWeighted(src1, alpha, src2, beta,0.0,src1); //AlphaBlend
cvResetImageROI(src1);
cvResetImageROI(src2); //释放ROI
cvNamedWindow("Roi_Add", CV_WINDOW_AUTOSIZE); //创建窗口
cvShowImage("Roi_Add", src1); //显示图片
cvWaitKey(0);
cvReleaseImage(&src1); //释放图像内存
cvReleaseImage(&src2); //释放图像内存
cvDestroyWindow("Roi_Add"); //释放窗口
//}
return 0;
}
笔者未构造相应的色彩转换矩阵,因此省略了编写色彩变换矩阵那个环节,为求方便读入文件时仅打印了部分参数,程序代码如下:
#include
#include
using namespace std;
int main()
{
CvFileStorage* fs = cvOpenFileStorage("cfg.xml",0,CV_STORAGE_WRITE); //创建并打开CvFileStorage写数据
//0:临时内存区域创建
cvWriteInt(fs, "frame_count", 10); //写整型数据
cvStartWriteStruct(fs,"frame_size",CV_NODE_SEQ); //创建结构
cvWriteInt(fs, 0, 320); //编写结构
cvWriteInt(fs, 0, 200);
cvEndWriteStruct(fs); //结束结构编写
// cvWrite(fs, "color_cvt_matrix", cmatrix); //编写色彩变换矩阵
cvReleaseFileStorage(&fs); //释放CvFileStorage句柄
CvFileStorage* fs1 = cvOpenFileStorage("cfg.xml", 0, CV_STORAGE_READ); //创建并打开CvFileStorage写数据
//0:临时内存区域创建
int frame_cout = cvReadIntByName(fs1, 0, "frame_count", 5); //读一个有名称整数 0:文件节点
CvSeq* s = cvGetFileNodeByName(fs1, 0, "frame_size")->data.seq; //获取文件节点
int frame_width = cvReadInt((CvFileNode*)cvGetSeqElem(s,0)); //读一个无名称整数。
int frame_height = cvReadInt((CvFileNode*)cvGetSeqElem(s,1)); //cvGetSeqElem:指向指定序列元素指针
CvMat * color_cvt_matrix = (CvMat*)cvReadByName(fs1, 0, "color_cvt_matrix"); //找到对象并解码
cvReleaseFileStorage(&fs1); //释放CvFileStorage句柄
cout << "frame_width = " << frame_width << endl << endl; //输出cfg.xml中存储的宽度
system("pause"); //暂停
}
此处应当注意,生成的cfg.xml文件的位置,与程序运行的根目录有关。
当在VS环境下使用本地Windows调试器调试运行时,生成的cfg.xml文件位置与项目文件的根目录在一起,如下图:
而直接运行已经编译生成的.exe文件进行写入,此时生成的cfg.xml文件位置,则是.exe文件存放的根目录,如下图:
本例完成的工作如下:
1. 选取一个负的浮点数,取绝对值,四舍五入后取极值;
2. 以系统时间作为种子产生一些随机数;
3. 创建CvPoint2D32f和CvPoint,并进行互相转换;
具体代码如下:
#include
#include
#include
#include
using namespace std;
int main()
{
float in, absolute;
int extremum;
in = -1.55;
absolute = abs(in); //取绝对值
extremum = cvRound(absolute); //四舍五入取整
cout << "输入= " << in << endl;
cout << "绝对值= " << absolute << endl;
cout << "四舍五入极值= " << extremum << endl;
system("pause");
CvRNG rng;
rng = cvRNG(cvGetTickCount()); //64位长整数的时间数据作为种子
for (int i = 0; i<5; i++)
{
printf("%d\n", cvRandInt(&rng) % 6); //返回均匀分布32位的随机数,%6将会是0~255的正整数
printf("%.2f\n", cvRandReal(&rng)); //返回均匀分布,0~1之间的随机小数
}
printf("Tick Frequency= %f\n", cvGetTickFrequency()); //系统时钟频率
system("pause");
CvPoint2D32f pointFloat;
CvPoint pointInt;
pointFloat = cvPoint2D32f(3.33, 2.22);
pointInt = cvPointFrom32f(pointFloat); //CvPoint2D32f-->CvPoint
pointFloat = cvPointTo32f(pointInt); //CvPoint2D32f<--CvPoint
cout << "整型点= " << pointInt.x << pointInt.y << endl;
cout << "浮点型= " << pointFloat.x << pointFloat.y << endl;
printf("浮点型= %f %f\n", pointFloat.x, pointFloat.y);
system("pause");
}
运行结果如下图:
如果使用cout输出发现数值为整数,千万不要认为CvPointf到CvPoint2D32f的转换失败了,而是因为ANSI C++里一个浮点型若是小数部分为0,直接输出必然是不带小数点,结果正如上图。
本例完成的工作如下:
1. 创建一个三通道二维矩阵,字节类型,大小500*500,赋值为0,使用CVCircle()函数画一个圆,并显示,结果如图 1;
2. 通过CVPtrD()函数将指针指向中间的通道并设置为“绿色”,以(20,5)和(40,20)为顶点画一个绿色的长方形,结果如图1;
3. 创建一个100*100的RGB图像,赋值为0,以(20,5)和(40,20)为顶点画一个绿色的平面,结果如图2;
4. 创建一个210*210的单通道图像,赋值为0,使用RIO和cvSet()建立一个增长如金字塔状的数组,最外层为0,第二层为20,第三层40,以此类推,每层为10像素宽度,结果如图3;
5. 读取一个图像,创建两个原点位置设置、深度、通道、行长度都与读取图像相同的图像头。在新的图像头中设置宽度为20,高度为30,将imageData指针指向像素(5,10)和(50,60)像素位置,传递这两个新的图像头给cvNot(),显示读取的图像,大图像中有两个矩形,矩形内的值是原始值的反值,结果如图4。
具体代码如下(代码中附有打印矩阵函数,方便调试时查看矩阵的值):
#include
#include
#include
#include
using namespace std;
int main()
{
CvMat* firstMat = cvCreateMat(500,500, CV_8UC3);
CvPoint center = cvPoint(250, 250);
CvPoint rec1 = cvPoint(20, 5);
CvPoint rec2 = cvPoint(40, 20);
int radius = 100;
CvScalar color = CV_RGB(55, 55, 255);
CvSize image1size = cvSize(100, 100);
IplImage* img1 = cvCreateImage(image1size, IPL_DEPTH_8U, 3);
CvSize image2size = cvSize(210, 210);
int x = 0;
int y = 0;
int width = 210;
int height = 210;
int add = 20;
IplImage* img2 = cvCreateImage(image2size, IPL_DEPTH_8U, 1);
IplImage* img3 = cvLoadImage("D:\\Template\\OpenCV\\Template16_Create_Mat_Image_Imageheader\\Debug\\3.jpg");
IplImage* img3header1 = cvCreateImageHeader(cvSize(20,30), img3->depth, img3->nChannels); //创建两个新的图像头,通道、深度与原图相同
IplImage* img3header2 = cvCreateImageHeader(cvSize(20, 30), img3->depth, img3->nChannels); //大小20*30
img3header1->widthStep = img3->widthStep; //设置widthStep与原图像相同
img3header2->widthStep = img3->widthStep;
img3header1->origin = img3->origin; //设置原点与原图像相同
img3header2->origin = img3->origin;
img3header1->imageData = img3->imageData + 5 * img3->widthStep + 10 * img3->nChannels; //指向(5,10)像素位置
img3header2->imageData = img3->imageData + 50 * img3->widthStep + 60 * img3->nChannels; //指向(50,60)像素位置
cvZero(firstMat); //矩阵元素清0
cvZero(img1); //图像元素设置为0
cvZero(img2); //图像元素设置为0
cvNot(img3header1, img3header1);
cvNot(img3header2, img3header2);
/******************打印矩阵********************/
//for (int i = 0; icols; i++) //矩阵指针行寻址
//{
// for (int j = 0; jrows; j++) //矩阵指针列寻址
// {
// int text = CV_MAT_ELEM(*firstMat,char, i, j); //获取i行j列元素值
// cout << text << " "; //空格
// }
// cout << endl; //换行
//}
cvCircle(firstMat, center, radius, color); //画一个圆
/**************矩阵上画一个绿色矩形,使用cvPtr2D算法***************/
for (int i = 20; i<40; i++) //矩阵指针行寻址
{
for (int j = 5; j<20; j++) //矩阵指针列寻址
{
uchar *ptr = cvPtr2D(firstMat, i, j); //index1 行 index2 列
ptr[1] = 255; //*****ptr[0]=255为蓝色 ptr[1]=255为绿色 ptr[2]=255为红色***/
}
}
/***********图像上画一个绿色矩形,指针算法**************/
for (int i = 20; i<40; i++) //矩阵指针行寻址
{
uchar *ptr = (uchar*)img1->imageData + i*img1->widthStep; //index1 行 index2 列
for (int j = 5; j<20; j++) //矩阵指针列寻址
{
ptr[3*j+1] = 255; //*****ptr[0]=255为蓝色 ptr[1]=255为绿色 ptr[2]=255为红色***/
}
}
/******************图像上做一个金字塔状数组,用RIO和cvSet()建立********************/
for (int i = 0; i < 10; i++)
{
cvSetImageROI(img2, cvRect(x, y, width, height)); //设置ROI
// cvAddS(img2, cvScalar(add), img2); //数组与标量的元素级相加
cvSet(img2, cvScalar(20*i)); //设置所选通道所有值
cvResetImageROI(img2); //释放ROI
x += 10;
y += 10;
width -= 20;
height -= 20;
}
cvNamedWindow("Mat", CV_WINDOW_AUTOSIZE); //在窗口中显示
cvShowImage("Mat", firstMat);
cvNamedWindow("Image1", CV_WINDOW_AUTOSIZE); //在窗口中显示
cvShowImage("Image1", img1);
cvNamedWindow("Image2", CV_WINDOW_AUTOSIZE); //在窗口中显示
cvShowImage("Image2", img2);
cvNamedWindow("Image3", CV_WINDOW_AUTOSIZE); //在窗口中显示
cvShowImage("Image3", img3);
cvWaitKey(0);
cvReleaseMat(&firstMat);
cvReleaseImage(&img1);
cvReleaseImage(&img2);
cvReleaseImage(&img3);
cvDestroyWindow("Mat");
cvDestroyWindow("Image1");
cvDestroyWindow("Image2");
cvDestroyWindow("Image3");
}
本例完成的工作如下:
1. 加载一幅真实的图像,使用cvSplit()将图像分割为红、绿、蓝三个通道的图像,找到并显示绿图;
2. 克隆绿图两次,clone1和clone2;
3. 找出绿色平面的最大、最小值;
4. 将clone1的元素赋值为thresh=(max-min)/2.0;
5. 将clone2的元素赋值为0,调用cvCmp()函数,将clone2穿建伟一个标识绿图中值超过thresh的掩码图像;
6. 使用cvSubS()函数进行处理,并显示结果。
具体代码如下(为了显示对比效果明显最后的cvSubS()叠加的值设置为100):
#include
#include
#include
#include
using namespace std;
int main()
{
IplImage* img = cvLoadImage("D:\\Template\\OpenCV\\Template17_Image_Split_Clone_CmpMask\\Debug\\3.jpg");
IplImage* imgR = cvCreateImage(cvGetSize(img), img->depth, 1);
IplImage* imgG = cvCreateImage(cvGetSize(img), img->depth, 1);
IplImage* imgB = cvCreateImage(cvGetSize(img), img->depth, 1);
IplImage* clone1 = cvCreateImage(cvGetSize(img), img->depth, 1);
IplImage* clone2 = cvCreateImage(cvGetSize(img), img->depth, 1);
double* minG = NULL;
double* maxG = NULL;
uchar thresh = (uchar)((maxG - minG) / 2.0);
cvSplit(img, imgR, imgG, imgB, NULL);
cvNamedWindow("Green", CV_WINDOW_AUTOSIZE); //在窗口中显示绿图
cvShowImage("Green", imgG);
cvCopy(imgG, clone1); //克隆绿图
cvCopy(imgG, clone2);
cvMinMaxLoc(imgG, minG, maxG); //找出绿图平面中最大值最小值
cvSet(clone1, cvScalar(thresh)); //clone1所有元素赋值为thresh
cvZero(clone2); //clone2赋值为0
cvCmp(imgG, clone1, clone2, CV_CMP_GE); //设置clone2为标识绿图中超过thresh的掩码
// cvSubS(imgG, cvScalar(thresh / 2), imgG, clone2);
cvSubS(imgG, cvScalar(100), imgG, clone2);
cvNamedWindow("Change", CV_WINDOW_AUTOSIZE); //在窗口中显示绿图
cvShowImage("Change", imgG);
cvWaitKey(0);
cvReleaseImage(&img);
cvReleaseImage(&imgR);
cvReleaseImage(&imgG);
cvReleaseImage(&imgB);
cvReleaseImage(&clone1);
cvReleaseImage(&clone1);
cvDestroyWindow("Green");
cvDestroyWindow("Change");
}
本例完成的工作如下:
1. 创建一个结构体包含int、CvPoint和CvRect;
2. 构造该结构体的读写函数;
3. 创建一个长度为10的该结构体数组,写入磁盘、读入内存。
具体代码如下(本例中文件名处更改了路径,因此创建的文件出现在D盘):
#include
#include
#include
#include
using namespace std;
typedef struct mystruct
{
int m_x;
CvPoint m_point;
CvRect m_rect;
}mystruct;
void write_mystruct(CvFileStorage *fs, char *name, mystruct *ms)
{
fs = cvOpenFileStorage(name, 0, CV_STORAGE_WRITE); //创建并打开CvFileStorage写数据
//0:临时内存区域创建
cvWriteInt(fs, "my_int", ms->m_x); //写整型数据
cvStartWriteStruct(fs, "my_point", CV_NODE_SEQ); //创建结构
cvWriteInt(fs, 0, ms->m_point.x); //编写结构
cvWriteInt(fs, 0, ms->m_point.y);
cvEndWriteStruct(fs); //结束结构编写
cvStartWriteStruct(fs, "my_rect", CV_NODE_SEQ);
cvWriteInt(fs, 0, ms->m_rect.height);
cvWriteInt(fs, 0, ms->m_rect.width);
cvWriteInt(fs, 0, ms->m_rect.x);
cvWriteInt(fs, 0, ms->m_rect.y);
cvEndWriteStruct(fs);
cvReleaseFileStorage(&fs); //释放CvFileStorage句柄
}
void read_mystruct(CvFileStorage *fs, CvFileNode* ms_node, mystruct *ms)
{
fs = cvOpenFileStorage("D:\\mystruct.xml", 0, CV_STORAGE_READ); //创建并打开CvFileStorage读数据
int frame_cout = cvReadIntByName(fs, 0, "my_int", 5); //读一个有名称整数 0:文件节点
CvSeq* s = cvGetFileNodeByName(fs, 0, "my_point")->data.seq; //获取文件节点
int point_x = cvReadInt((CvFileNode*)cvGetSeqElem(s, 0)); //读一个无名称整数
int point_y = cvReadInt((CvFileNode*)cvGetSeqElem(s, 1)); //cvGetSeqElem:指向指定序列元素指针
CvSeq* s1 = cvGetFileNodeByName(fs, 0, "my_rect")->data.seq;
int rect_height = cvReadInt((CvFileNode*)cvGetSeqElem(s1, 0));
int rect_width = cvReadInt((CvFileNode*)cvGetSeqElem(s1, 1));
int rect_x = cvReadInt((CvFileNode*)cvGetSeqElem(s1, 2));
int rect_y = cvReadInt((CvFileNode*)cvGetSeqElem(s1, 3));
cvReleaseFileStorage(&fs); //释放CvFileStorage句柄
cout << "rect_height = " << rect_height << endl; //输出cfg.xml中存储的宽度
system("pause"); //暂停
}
int main()
{
mystruct m_array[10];
CvFileStorage *m_fs = NULL;
CvFileNode* m_node = NULL;
m_array[0] = { 1, cvPoint(1, 1), cvRect(1, 1, 1, 1) };
m_array[1] = { 2, cvPoint(2, 2), cvRect(2, 2, 2, 2) };
write_mystruct(m_fs, "D:\\mystruct.xml", m_array);
read_mystruct(m_fs, m_node, m_array);
}