调用cvSetMouseCallback()函数来注册鼠标回调函数,以响应鼠标事件,并根据具体的event来确定给出的响应。具体代码如下:
#include
#include
#include
#include
using namespace std;
void my_mouse_callback(int event, int x, int y,
int flags, void* param); //声明回调函数
CvRect box; //所画矩形的角点、宽高
bool drawing_box = false; //判断左键是否按下,按下为true
void draw_box(IplImage* img, CvRect rect) //绘制矩形
{
cvRectangle(img,
cvPoint(box.x,box.y), //两个对顶点
cvPoint(box.x+box.width,box.y+box.height),
cvScalar(0x00, 0x00, 0xff)); //BGR 设置为红色
}
int main(int argc, char* argv[])
{
box = cvRect(-1, -1, 0, 0); //(x,y,width,height)
IplImage* image = cvCreateImage(cvSize(200, 200), IPL_DEPTH_8U, 3); //200*200 8位 3通道
cvZero(image); //清零
IplImage* temp = cvCloneImage(image); //克隆图像,复制图像随原图像变化
cvNamedWindow("Box Example");
cvSetMouseCallback("Box Example", my_mouse_callback, (void*)image); //注册回调函数
//事件产生窗口、回调函数名、事件产生影响的类
while (1)
{
cvCopyImage(image, temp); //复制图像,不随原图像变化(清除temp图像上实时显示的那部分矩形)
if (drawing_box) //左键按下,开始绘制(左键按下时动态显示矩形)
draw_box(temp, box); //在temp图像上画矩形
cvShowImage("Box Example", temp); //显示temp
if (cvWaitKey(15) == 27) //15ms刷新一次
break;
}
cvReleaseImage(&image);
cvReleaseImage(&temp);
cvDestroyWindow("Box Example");
}
void my_mouse_callback(int event, int x, int y, int flags, void* param)
{
IplImage* image = (IplImage*)param;
switch (event)
{
case CV_EVENT_MOUSEMOVE:
{
if (drawing_box) //左键已按下标志,开始绘制
{
box.width = x - box.x; //x为当前鼠标的位置
box.height = y - box.y; //box.x为左键按下时的位置
}
}
break;
case CV_EVENT_LBUTTONDOWN:
{
drawing_box = true;
box = cvRect(x, y, 0, 0); //记录矩形起始位置
}
break;
case CV_EVENT_LBUTTONUP:
{
drawing_box = false; //左键已松开标志,结束绘制
if (box.width<0) //将width转化为正数,保证绘图从左向右进行
{
box.x += box.width; //box.x=box.x+box.width 回移起点位置
box.width *= -1; //box.x为左键按下时的位置
}
if (box.height<0) //将height转化为正数,保证绘图从上向下进行
{
box.y += box.height;
box.height *= -1;
}
draw_box(image, box);
}
break;
}
}
调用cvCreateTrackbar()函数来创建一个2值滚动条,以实现按钮开关功能。具体代码如下:
#include
#include
#include
#include
using namespace std;
int g_switch_value = 0;
void switch_off_function()
{
cout << "switch off" << endl;
cout << g_switch_value << endl;
}
void switch_on_function()
{
cout << "switch on" << endl;
cout << g_switch_value << endl;
}
void switch_callback(int position)
{
if (position == 0)
switch_off_function();
else
switch_on_function();
}
int main(int argc, char* argv[])
{
cvNamedWindow("Demo Window",1);
cvCreateTrackbar("Switch", "Demo Window", &g_switch_value, 1, switch_callback);
//创建滚动条(滚动条名称,所属窗口,当前位置指针【拖动时,替换指针指向的整数】,最大值,回调函数)
while (1)
{
if (cvWaitKey(15) == 27) //15ms刷新一次
break;
}
cvDestroyWindow("Demo Window");
}
本例完成的工作如下:
1. 从视频文件中读入数据;
2. 将读入的图像转换为灰度图;
3. 对图像做Canny边缘检测;
4. 将三个过程的处理结果显示在不同的窗口中;
5. 将三个步骤显示在一个图像中;
6. 在图像的三个不同的部分上分别写上合适的文字标签;
具体代码如下:
#include <cv.h>
#include <highgui.h>
using namespace std;
IplImage* doCanny(IplImage *in, double lowThresh, double highThresh, double aperture)
{
if (in->nChannels != 1)
{
cout << in->nChannels << endl;
cout << "不能输出" << endl;
return(0); //Canny只能处理灰度图像
}
IplImage* out = cvCreateImage(cvGetSize(in), IPL_DEPTH_8U, 1); //cvSize(cvGetSize(image)),报错
cvCanny(in, out, lowThresh, highThresh, aperture);
//(输入,输出,控制边缘连接,强边缘初始分割,Sobel算子大内核大小)
return(out);
}
int main(int argc, char** argv)
{
cvNamedWindow("Natural", CV_WINDOW_AUTOSIZE);
cvNamedWindow("Gray", CV_WINDOW_AUTOSIZE);
cvNamedWindow("Canny", CV_WINDOW_AUTOSIZE);
cvNamedWindow("All", CV_WINDOW_AUTOSIZE);
CvCapture* capture = cvCreateFileCapture("D:\\Template\\OpenCV\\Template21_AVI2Gray_Canny_Text\\ConsoleApplication1\\好想告诉你.avi");
IplImage* frame1=cvQueryFrame(capture);
CvFont font; //字体变量
int width = (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH);
int height = (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT);
CvSize size = cvSize(width,height);
CvSize size1 = cvSize(width * 3, height);
IplImage* frame2 = cvCreateImage(size, IPL_DEPTH_8U, 1); //单通道,8位,相同大小,灰度
IplImage* frame3 = cvCreateImage(size, IPL_DEPTH_8U, 1); //Canny
IplImage* frame_gray = cvCreateImage(size, IPL_DEPTH_8U, 3); //三通道
IplImage* frame_canny = cvCreateImage(size, IPL_DEPTH_8U, 3);
IplImage* frame4 = cvCreateImage(size1, IPL_DEPTH_8U, frame1->nChannels); //All
cvZero(frame4);
//创建三个新的图像头,通道、深度与原图相同,分别指向开始处、1/3处、2/3处
IplImage* imgheader1 = cvCreateImageHeader(size, frame1->depth, frame1->nChannels);
//IplImage* imgheader2 = cvCreateImageHeader(size, frame2->depth, frame2->nChannels);//错 压缩为1/3
//IplImage* imgheader3 = cvCreateImageHeader(size, frame3->depth, frame3->nChannels);
IplImage* imgheader2 = cvCreateImageHeader(size, frame2->depth, frame1->nChannels); //正确
IplImage* imgheader3 = cvCreateImageHeader(size, frame3->depth, frame1->nChannels);
imgheader1->widthStep = frame4->widthStep; //设置widthStep与原图像相同
imgheader2->widthStep = frame4->widthStep;
imgheader3->widthStep = frame4->widthStep;
imgheader1->origin = frame4->origin; //设置原点与原图像相同 左上
imgheader2->origin = frame4->origin;
imgheader3->origin = frame4->origin;
imgheader1->imageData = frame4->imageData; //指向开始像素位置
imgheader2->imageData = frame4->imageData + 1 * 3 * width * frame2->nChannels; //指向1/3像素位置
imgheader3->imageData = frame4->imageData + 2 * 3 * width * frame3->nChannels; //指向2/3像素位置
cvInitFont(&font, CV_FONT_HERSHEY_COMPLEX, 1.0, 1.0, 1.0);
while (1)
{
frame1 = cvQueryFrame(capture);
if (!frame1) break;
cvConvertImage(frame1, frame2, CV_BGR2GRAY); //图像强制转化为gray ****
frame3 = doCanny(frame2, 50, 150, 3); //灰度图做Canny边缘检测
//cvCopy(frame1, imgheader1); //错误,此时显示后两幅图像会被压缩为1/3
//cvCopy(frame2, imgheader2);
//cvCopy(frame3, imgheader3);
cvCopy(frame1, imgheader1); //正确
cvConvertImage(frame2, frame_gray, CV_GRAY2BGR);
cvCopy(frame_gray, imgheader2);
cvConvertImage(frame3, frame_canny, CV_GRAY2BGR);
cvCopy(frame_canny, imgheader3);
cvPutText(frame4, "Origin", cvPoint(100, 25), &font, cvScalar(255, 255, 255)); //文字标签
cvPutText(frame4, "Gray", cvPoint(450, 25), &font, cvScalar(255, 255, 255));
cvPutText(frame4, "Canny", cvPoint(750, 25), &font, cvScalar(255, 255, 255));
cvShowImage("Natural", frame1);
cvShowImage("Gray", frame2);
cvShowImage("Canny", frame3);
cvShowImage("All", frame4);
char c = cvWaitKey(32);
if (c == 27) break;
}
cvReleaseCapture(&capture);
cvDestroyWindow("Natural");
cvDestroyWindow("Gray");
cvDestroyWindow("Canny");
cvDestroyWindow("All");
}
运行结果如下图:
本例的难点在b中,即实现将所有三个步骤显示在一个图像中,书中提示创建三个图像头,使图像头分别指向图像数据的开始处,1/3处,2/3处,然后使用cvCopy()复制。此时,在创建总的显示图像和三个图像头时,图像的通道数设置很关键,因为三张图像同时显示在一张图片中,但三个图像的通道是不同的,图1位三通道,其后两个为单通道。如果总的显示图像创建为三通道,而三个图像头为一个三通道,两个单通道,上述代码中两部分代码做如下修改:
Part1:
IplImage* imgheader2 = cvCreateImageHeader(size, frame2->depth, frame2->nChannels);
IplImage* imgheader3 = cvCreateImageHeader(size, frame3->depth, frame3->nChannels);
Part2:
cvCopy(frame1, imgheader1);
cvCopy(frame2, imgheader2);
cvCopy(frame3, imgheader3);
此时的显示的整个图像,后两个图像头对应的图像区域会被压缩为1/3,究其原因就是未将后两个图像头转化为三通道图像,其结果如下图:
本例完成的工作如下:
1. 读入并显示一张图片;
2. 鼠标点击图像时获取对应像素的颜色值(BGR)和对应像素的坐标;
3. 用文本将颜色值和对应像素的坐标显示在点击处;
本题a中要求将坐标显示到上例图像中,方法类似,具体代码如下:
#include
#include
using namespace std;
void my_mouse_callback(int event, int x, int y, int flags, void* param); //声明回调函数
void Get_Show_BGR(IplImage* img, CvPoint point) //获取、显示像素点处的RGB
{
CvFont font; //字体变量
CvScalar s; //BGR值变量
char str[40] = { 0 };
cvInitFont(&font, CV_FONT_HERSHEY_COMPLEX, 1.0, 1.0, 1.0); //初始化字体相关参数
s = cvGet2D(img, point.x, point.y);
cout << " B: " << s.val[0] << " G: " << s.val[1] << " R: " << s.val[2];
//字符串格式化命令,格式化的数据写入某个字符串中
sprintf(str, "(%d,%d)(%.0f,%.0f,%.0f)", point.x, point.y, s.val[2], s.val[1], s.val[0]);
cvPutText(img, str, point, &font, cvScalar(255, 255, 255)); //输出相应的文字标签
}
int main(int argc, char** argv) //带参数的mian函数
{
IplImage *img = cvLoadImage("D:\\Template\\OpenCV\\Template22_Trigger_Show_BGR_Coordinate\\Debug\\3.jpg");
cvNamedWindow("Example1", CV_WINDOW_AUTOSIZE);
cvSetMouseCallback("Example1", my_mouse_callback, (void*)img); //注册回调函数
//事件产生窗口、回调函数名、事件产生影响的类
while (1)
{
cvShowImage("Example1", img);
char c = cvWaitKey(32);
if (c == 27) break;
}
cvReleaseImage(&img);
cvDestroyWindow("Example1");
}
void my_mouse_callback(int event, int x, int y, int flags, void* param)
{
IplImage* image = (IplImage*)param;
CvPoint point; //鼠标按下的坐标点
switch (event)
{
case CV_EVENT_LBUTTONDOWN:
{
point = cvPoint(x, y); //记录左键按下的位置
Get_Show_BGR(image, point);
}
break;
}
}
本例完成的工作如下:
1. 读入显示图像,鼠标左键按住画矩形框;
2. 下一次按键时,恢复为原始图像,并重新开始绘制矩形;
3. 在独立窗口中画颜色直方图,显示选定区域内各像素范围对应的每个通道像素点的数量;
具体代码如下:
#include
#include
#include
#include
using namespace std;
void my_mouse_callback(int event, int x, int y, int flags, void* param); //声明回调函数
CvRect box; //所画矩形的角点、宽高
bool drawing_box = false; //判断左键是否按下,按下为true
bool drawing_col = false; //判断是否重绘图像
int R=0; //R像素点个数
int G=0; //G像素点个数
void draw_box(IplImage* img, CvRect rect) //绘制矩形
{
cvRectangle(img,
cvPoint(box.x,box.y), //两个对顶点
cvPoint(box.x+box.width,box.y+box.height),
cvScalar(0x00, 0x00, 0xff)); //BGR 设置为红色
}
IplImage* createCoordinate(IplImage* img)
{
int x_grid=0;
int y_grid = 0;
int x_interval=90;
int y_interval = 560;
CvFont font; //字体变量
char str[40] = { 0 };
cvInitFont(&font, CV_FONT_HERSHEY_PLAIN, 1.0, 1.0, 1.0); //初始化字体相关参数
cvSet(img, CV_RGB(255, 255, 255)); //白色背景
cvLine(img, cvPoint(40, 560), cvPoint(980, 560), CV_RGB(100, 100, 100), 1); //X轴
cvLine(img, cvPoint(965, 550), cvPoint(980, 560), CV_RGB(100, 100, 100), 1); //X轴箭头
cvLine(img, cvPoint(965, 570), cvPoint(980, 560), CV_RGB(100, 100, 100), 1); //X轴箭头
cvLine(img, cvPoint(40, 560), cvPoint(40, 40), CV_RGB(100, 100, 100), 1); //Y轴
cvLine(img, cvPoint(30, 55), cvPoint(40, 40), CV_RGB(100, 100, 100), 1); //Y轴箭头
cvLine(img, cvPoint(50, 55), cvPoint(40, 40), CV_RGB(100, 100, 100), 1); //Y轴箭头
for (int i = 0; i < 8; i++) //X坐标
{
sprintf(str, "(%d,%d) ", x_grid, x_grid + 31); //字符串格式化命令,格式化的数据写入某个字符串中
cvPutText(img, str, cvPoint(x_interval, 580), &font, cvScalar(0, 0, 0)); //输出相应的文字标签
x_grid += 32;
x_interval += 110;
}
for (int j = 0; j < 10; j++) //Y坐标
{
sprintf(str, "%d", y_grid); //字符串格式化命令,格式化的数据写入某个字符串中
cvPutText(img, str, cvPoint(10, y_interval), &font, cvScalar(0, 0, 0)); //输出相应的文字标签
y_grid += 50;
y_interval -= 50;
}
return img;
}
void draw_column(IplImage* img1, CvRect rect, IplImage* img2) //绘制矩形
{
CvScalar s; //BGR值变量
double blue;
double green;
double red;
int Grid=0;
int Interval=90;
int number=0;
CvFont font; //字体变量
char str1[10] = { 0 };
char str2[10] = { 0 };
char str3[10] = { 0 };
int B[8] = { 0 }; //B像素点个数
int G[8] = { 0 }; //B像素点个数
int R[8] = { 0 }; //B像素点个数
cvInitFont(&font, CV_FONT_HERSHEY_PLAIN, 1.0, 1.0, 1.0); //初始化字体相关参数
/******************图像上制定区域内RGB像素值,使用cvPtr2D算法********************/
for (int i = box.x; i < (box.x + box.width); i++) //矩阵指针行寻址
{
for (int j = box.y; j < (box.y + box.height); j++) //矩阵指针列寻址
{
Grid = 0; //搜索范围置0
s = cvGet2D(img1, i, j); //获取对应像素点RGB值
blue = s.val[0]; //分别获取获取BGR的值
green = s.val[1];
red = s.val[2];
cout << " B: " << blue << " G: " << green << " R: " << red<for (int k = 0; k < 8; k++) //像素值所处范围
{
if ((blue >= Grid) && (blue < (Grid + 32)))
{
B[k]++;
cout << " 区域下限: " << Grid << " B像素点总数: "<if ((green >= Grid) && (green < (Grid + 32)))
{
G[k]++;
cout << " 区域下限: " << Grid << " G像素点总数: " << G[k] << endl;
}
if ((red >= Grid) && (red < (Grid + 32)))
{
R[k]++;
cout << " 区域下限: " << Grid << " R像素点总数: " << R[k] << endl;
}
Grid += 32; //跳转到下一块区域
}
number++;
}
}
for (int l = 0; l < 8; l++)
{
sprintf(str1, "%d", B[l]); //字符串格式化命令,格式化的数据写入某个字符串中
sprintf(str2, "%d", G[l]); //字符串格式化命令,格式化的数据写入某个字符串中
sprintf(str3, "%d", R[l]); //字符串格式化命令,格式化的数据写入某个字符串中
cvRectangle(img2,
cvPoint(Interval, 560), //两个对顶点
cvPoint(Interval + 10, 560-B[l]),
cvScalar(0xff, 0x00, 0x00), //BGR 设置为蓝色
CV_FILLED); //实心填充
cvRectangle(img2,
cvPoint(Interval+30, 560), //两个对顶点
cvPoint(Interval + 40, 560 - G[l]),
cvScalar(0x00, 0xff, 0x00), //BGR 设置为绿色
CV_FILLED); //实心填充
cvRectangle(img2,
cvPoint(Interval+60, 560), //两个对顶点
cvPoint(Interval + 70, 560 - R[l]),
cvScalar(0x00, 0x00, 0xff), //BGR 设置为红色
CV_FILLED); //实心填充
//输出相应的个数标签
cvPutText(img2, str1, cvPoint(Interval-5, 555-B[l]), &font, cvScalar(0, 0, 0));
cvPutText(img2, str2, cvPoint(Interval +25, 555 - G[l]), &font, cvScalar(0, 0, 0));
cvPutText(img2, str3, cvPoint(Interval +55, 555 - R[l]), &font, cvScalar(0, 0, 0));
Interval += 110;
}
cout << " 区域像素点总数: " << number;
}
int main(int argc, char* argv[])
{
box = cvRect(-1, -1, 0, 0); //(x,y,width,height)
IplImage* image = cvLoadImage("D:\\Template\\OpenCV\\Template23_Event_Trigger_Rect\\Debug\\4.jpg");
IplImage* temp = cvCloneImage(image); //克隆图像,复制图像随原图像变化
IplImage* temp1 = cvCloneImage(image); //克隆图像,复制图像随原图像变化
IplImage* histogram = cvCreateImage(cvSize(1000, 600), 8, 3);
IplImage* histogram_temp = cvCloneImage(histogram);
cvNamedWindow("Box Example");
cvNamedWindow("Box Example1");
cvNamedWindow("Box Histogram");
cvSetMouseCallback("Box Example", my_mouse_callback, (void*)temp); //注册回调函数
//事件产生窗口、回调函数名、事件产生影响的类
createCoordinate(histogram);
while (1)
{
if (drawing_box) //左键按下,开始绘制(左键按下时动态显示矩形)
{
cvCopyImage(image, temp1); //画之前恢复为原始图像
draw_box(temp1, box); //在temp1图像上画矩形
}
if (drawing_col) //左键按下,开始绘制(左键按下时动态显示矩形)
{
cvCopyImage(histogram, histogram_temp); //画之前恢复为原始图像
draw_column(temp1, box, histogram_temp);
drawing_col = false;
}
cvShowImage("Box Example", temp1); //显示temp1
cvShowImage("Box Example1", temp); //显示temp
cvShowImage("Box Histogram", histogram_temp); //显示柱状图
if (cvWaitKey(15) == 27) //15ms刷新
break;
}
cvReleaseImage(&image);
cvReleaseImage(&temp);
cvReleaseImage(&temp1);
cvReleaseImage(&histogram);
cvReleaseImage(&histogram_temp);
cvDestroyWindow("Box Example");
cvDestroyWindow("Box Example1");
cvDestroyWindow("Box Histogram");
}
void my_mouse_callback(int event, int x, int y, int flags, void* param)
{
IplImage* image = (IplImage*)param;
switch (event)
{
case CV_EVENT_MOUSEMOVE:
{
if (drawing_box) //左键已按下标志,开始绘制
{
box.width = x - box.x; //x为当前鼠标的位置
box.height = y - box.y; //box.x为左键按下时的位置
}
}
break;
case CV_EVENT_LBUTTONDOWN:
{
drawing_box = true;
box = cvRect(x, y, 0, 0); //记录矩形起始位置
}
break;
case CV_EVENT_LBUTTONUP:
{
drawing_box = false; //左键已松开标志,结束绘制
drawing_col = true;
if (box.width<0) //将width转化为正数,保证绘图从左向右进行
{
box.x += box.width; //box.x=box.x+box.width 回移起点位置
box.width *= -1; //box.x为左键按下时的位置
}
if (box.height<0) //将height转化为正数,保证绘图从上向下进行
{
box.y += box.height;
box.height *= -1;
}
draw_box(image, box); //在temp上画矩形
}
break;
}
}
程序中Box Example中图像可重绘,Box Example1中图像能反映所有画过的矩形,运行结果如下图(图2为灰度图是生成的颜色直方图,图3为RGB图像生成的颜色直方图):
本例完成的工作如下:
1. 读入并显示视频文件;
2. 滚动条控制播放位置,以10为步长跳进;
3. 另一滚动条控制停止/播放;
具体代码如下:
#include
#include
using namespace std;
int g_slider_position = 0; //滚动条位置全局变量
int slider_position = 0; //快进全局变量
CvCapture* g_capture = NULL; //确定要读入AVI文件的指针
int g_switch_value = 1;
void switch_off_function()
{
cout << "switch off" << endl;
cout << g_switch_value << endl;
}
void switch_on_function()
{
cout << "switch on" << endl;
cout << g_switch_value << endl;
}
void switch_callback(int pos)
{
if (pos == 0)
switch_off_function();
else
switch_on_function();
}
void onTrackbarSlide(int pos)
{
slider_position += 10; //以10为步长快进,点击一次滚动条,滚动条当前位置进10
g_slider_position = slider_position;
//cvSetCaptureProperty(g_capture, CV_CAP_PROP_POS_FRAMES, pos); //根据点击位置,以帧数设置读入
cvSetCaptureProperty(g_capture, CV_CAP_PROP_POS_FRAMES, g_slider_position); //以帧数设置读入
} //回调函数,滚动条被拖动时调用,位置以32位整型返回
int main(int argc, char** argv)
{
IplImage* frame;
g_capture = cvCreateFileCapture("D:\\Template\\OpenCV\\Template24_ShowAVI_Switch_Trackbar\\Debug\\好想告诉你.avi");
int frames = (int)cvGetCaptureProperty(g_capture, CV_CAP_PROP_FRAME_COUNT); //获取总帧数以设定滚动条
cvNamedWindow("Example3"); //创建窗口
if (frames != 0)
{
cvCreateTrackbar("Position","Example3",&g_slider_position,frames,onTrackbarSlide); //创建滚动条
cvCreateTrackbar("Switch", "Example3", &g_switch_value, 1, switch_callback);
} //将变量绑定到滚动条表征最大值和回调函数
while (1)
{
if (g_switch_value == 1)
{
frame = cvQueryFrame(g_capture);
if (!frame) break;
cvShowImage("Example3", frame);
g_slider_position++; //滚动条当前位置变量
slider_position++; //以十为步长快进变量
//滚动条随视频移动
cvCreateTrackbar("Position", "Example3", &g_slider_position, frames, onTrackbarSlide);
//char c = cvWaitKey(32); //在此处设置waitkey,开关会导致程序卡死
//if (c == 27) break;
}
char c = cvWaitKey(32);
if (c == 27) break;
}
cvReleaseCapture(&g_capture);
cvDestroyWindow("Example3");
return(0);
}
本例完成的工作如下:
1. 可以新建一个图像,并将图像中所有像素值设置为0,然后显示出来;
2. 鼠标左键画线、圆、椭圆、矩形,鼠标右键橡皮擦功能;
3. “逻辑画图”,滚动条选择AND,只显示原始像素数据为100的数据(由于1接近背景色不容易查看所以用100);
具体代码如下:
#include
#include
#include
#include
#include
using namespace std;
void my_mouse_callback(int event, int x, int y, int flags, void* param); //声明回调函数
IplImage* image; //新建的图像
CvRect box; //所画矩形的角点、宽高
CvRect erase; //所画矩形的角点、宽高
bool drawing_box = false; //判断左键是否按下,按下为true
bool drawing_erase = false; //判断右键是否按下
bool enAND = false; //判断AND功能是否使能
int g_switch_value = 0;
int g_logic_value = 0;
void create_image()
{
cout << "Create Image" << endl;
cout << g_switch_value << endl;
cvZero(image); //清零
}
void draw_line(IplImage* img, CvRect rect)
{
cvLine(img,
cvPoint(rect.x,rect.y),
cvPoint(rect.x + rect.width, rect.y + rect.height),
cvScalar(0x00, 0x00, 0x64)); //BGR 设置为R值设置为100,用于验证逻辑绘图中的AND
}
void draw_circle(IplImage* img, CvRect rect)
{
int radius = 0;
radius = (int)sqrt(rect.width*rect.width + rect.height*rect.height);
cvCircle(img,
cvPoint(rect.x, rect.y),
radius,
cvScalar(0x00, 0x00, 0xff)); //BGR 设置为红色
}
void draw_ellipse(IplImage* img, CvRect rect)
{
cvEllipse(img,
cvPoint(rect.x, rect.y),
cvSize(rect.width, rect.height),
0,0,360,
cvScalar(0x00, 0x00, 0xff));
}
void draw_rect(IplImage* img, CvRect rect)
{
cvRectangle(img,
cvPoint(rect.x, rect.y), //两个对顶点
cvPoint(rect.x + rect.width, rect.y + rect.height),
cvScalar(0x00, 0x00, 0xff)); //BGR 设置为红色
}
void Draw(IplImage* img, CvRect rect)
{
switch (g_switch_value)
{
case 0:
break;
case 1:
break;
case 2:
draw_line(img, rect);
break;
case 3:
draw_circle(img, rect);
break;
case 4:
draw_ellipse(img, rect);
break;
case 5:
draw_rect(img, rect);
break;
}
}
void eraser(IplImage* img, CvRect rect)
{
cvLine(img,
cvPoint(rect.x, rect.y),
cvPoint(rect.x + rect.width, rect.y + rect.height),
cvScalar(0x00, 0x00, 0x00),10); //BGR 设置为红色
}
void switch_callback(int pos)
{
if (g_switch_value==1)
create_image();
}
void Show_AND(IplImage* img)
{
int num;
for (int i = 0; i<400; i++) //矩阵指针行寻址
{
for (int j = 0; j<400; j++) //矩阵指针列寻址
{
uchar *ptr = cvPtr2D(img, i, j); //index1 行 index2 列
for (int k = 0; k < 3; k++)
{
if (ptr[k] == 0x64)
{
ptr[k] = 0x64;
}
else
ptr[k] = 0;
//num = ptr[k];
//cout << num << endl; //换行
}
}
}
}
void logic_callback(int pos)
{
switch (g_logic_value)
{
case 1:
enAND = true;
break;
default:
enAND = false;
break;
}
}
int main(int argc, char* argv[])
{
box = cvRect(-1, -1, 0, 0); //(x,y,width,height)
image = cvCreateImage(cvSize(400, 400), IPL_DEPTH_8U, 3); //200*200 8位 3通道
cvZero(image); //清零
IplImage* temp = cvCloneImage(image); //克隆图像,复制图像随原图像变化
cvNamedWindow("Box Example");
cvCreateTrackbar("Switch", "Box Example", &g_switch_value, 5, switch_callback); //创建绘图模式滚动条
cvCreateTrackbar("Logic", "Box Example", &g_logic_value, 1, logic_callback); //创建逻辑绘图滚动条
cvSetMouseCallback("Box Example", my_mouse_callback, (void*)image); //注册回调函数
//事件产生窗口、回调函数名、事件产生影响的类
while (1)
{
cvCopyImage(image, temp); //复制图像,不随原图像变化,(清除temp图像上左键按住时,实时显示的矩形)
if (drawing_box) //左键按下,开始绘制(左键按下时动态显示矩形)
Draw(temp, box); //在temp图像上画矩形(动态显示)
if (drawing_erase) //左键按下,开始绘制(左键按下时动态显示矩形)
eraser(temp, erase); //在temp图像上画矩形
if (enAND == true)
{
Show_AND(image);
enAND = false;
}
cvShowImage("Box Example", temp); //显示temp
if (cvWaitKey(15) == 27) //15ms刷新一次
break;
}
cvReleaseImage(&image);
cvReleaseImage(&temp);
cvDestroyWindow("Box Example");
}
void my_mouse_callback(int event, int x, int y, int flags, void* param)
{
IplImage* image = (IplImage*)param;
switch (event)
{
case CV_EVENT_MOUSEMOVE:
{
if (drawing_box) //左键已按下标志,开始绘制
{
box.width = x - box.x; //x为当前鼠标的位置
box.height = y - box.y; //box.x为左键按下时的位置
}
if (drawing_erase) //左键已按下标志,开始绘制
{
erase.width = x - erase.x; //x为当前鼠标的位置
erase.height = y - erase.y; //box.x为右键按下时的位置
}
}
break;
case CV_EVENT_LBUTTONDOWN:
{
drawing_box = true;
box = cvRect(x, y, 0, 0); //记录矩形起始位置
}
break;
case CV_EVENT_LBUTTONUP:
{
drawing_box = false; //左键已松开标志,结束绘制
Draw(image, box);
}
break;
case CV_EVENT_RBUTTONDOWN:
{
drawing_erase = true; //右键已松开标志,结束绘制
erase = cvRect(x, y, 0, 0); //记录矩形起始位置
}
break;
case CV_EVENT_RBUTTONUP:
{
drawing_erase = false; //右键已松开标志,结束绘制
eraser(image, erase);
}
break;
}
}
运行结果如下图(直线对应像素为100,用于表示逻辑画图,图1右上方有橡皮擦痕迹):
本例完成的工作如下:
1. 可以新建一个图像,并将图像中所有像素值设置为0,然后显示出来;
2. 用户点击一个位置,在此位置输出一个标签;
3. 允许使用Enter键进行编辑;
4. 正在编辑的标签可以使用backspace键修改原有内容;
具体代码如下:
#include
#include
#include
#include
#include
using namespace std;
void my_mouse_callback(int event, int x, int y, int flags, void* param); //声明回调函数
//IplImage* image; //新建的图像
CvRect box; //所画矩形的角点、宽高
bool drawing_tag = false; //判断左键是否按下,按下为true
bool drawing_next = false; //判断是否为新的一点
CvFont font; //字体变量
char display_text[40] = "|";
int main(int argc, char* argv[])
{
box = cvRect(-1, -1, 0, 0); //(x,y,width,height)
IplImage* image = cvCreateImage(cvSize(512, 512), IPL_DEPTH_8U, 3); //200*200 8位 3通道
cvZero(image); //清零
IplImage* temp = cvCloneImage(image); //克隆图像,复制图像随原图像变化
IplImage* temp1 = cvCloneImage(image); //克隆图像,复制图像随原图像变化
cvNamedWindow("Box Example");
cvSetMouseCallback("Box Example", my_mouse_callback, (void*)image); //注册回调函数
//事件产生窗口、回调函数名、事件产生影响的类
cvInitFont(&font, CV_FONT_HERSHEY_PLAIN, 1.0, 1.0, 1.0); //初始化字体相关参数
while (1)
{
cvCopyImage(temp, temp1); //复制图像,不随原图像变化,(清除temp图像上左键按住时,实时显示的矩形)
if (drawing_tag) //左键按下,开始绘制(左键按下时动态显示矩形)
cvPutText(temp1, display_text, cvPoint(box.x, box.y), &font, cvScalarAll(255));
//实时显示标签内容
cvShowImage("Box Example", temp1); //显示temp1
char c = cvWaitKey(15);
//判断是否为新的一点
if (drawing_next == true)
{
drawing_next = false;
cvCopyImage(temp,image); //存储上一标签的图像,(用于修改当前位置标签)
}
// 32 -- 126 可显示字符 AscII
if (drawing_tag == true && c < 126 && c > 32)
{
char tempstr[2] = { c };
if (display_text == "|")
{
strcpy(display_text, tempstr); //将字符串tempstr复制到diaplay_text
}
else
{
strcat(display_text, tempstr); //将字符串tempstr连接到diaplay_text结尾
}
}
// 13 -- Enter键 AscII
else if (c == 13)
{
if (drawing_tag == true) //设置标签
{
cout<<"完成标签"<255));
}
else //修改标签
{
cout << "修改标签" << endl;
cvCopyImage(image, temp); //清除图像上内容(用上一个标签的图像覆盖)
drawing_tag = true;
}
}
// 8 -- 退格键 AscII
else if (c == 8)
{
int len = strlen(display_text); //计算数组长度
printf("剩余字节数:%d\n", len-2);
display_text[len - 1] = '\0'; //NULL
}
else if (c == 27) break;
}
cvReleaseImage(&image);
cvReleaseImage(&temp);
cvReleaseImage(&temp1);
cvDestroyWindow("Box Example");
}
void my_mouse_callback(int event, int x, int y, int flags, void* param)
{
IplImage* image = (IplImage*)param;
switch (event)
{
case CV_EVENT_LBUTTONDOWN:
{
drawing_tag = true;
drawing_next = true;
box = cvRect(x, y, 0, 0); //记录矩形起始位置
strcpy(display_text, "|"); //将字符串 "|"复制到diaplay_text,每次重新设置标签
cvPutText(image, display_text, cvPoint(box.x, box.y), &font, cvScalarAll(255));
}
break;
}
}
本例完成的工作如下:
1. 读入一幅图像,并使用数字键1-8控制变换矩阵;
2. 按下数字键1-8任意一个键,透视变换数据会变大,同时按下Shift键,对应数据减小;
3. 添加放大、缩小功能;
4. 添加旋转功能;
具体代码如下:
#include
#include
#include
#include
#include
using namespace std;
int g_slider_position = 1;
int g_position = 0;
bool already = false;
bool already_rotate = false;
void onTrackbarSlide(int pos)
{
already = true; //标志是否进行过一次缩放
}
void onRotateSlide(int pos)
{
already_rotate = true; //标志是否进行过一次旋转
}
int main(int argc, char* argv[])
{
CvPoint2D32f srcQuad[4], dstQuad[4]; //变换的两个点集
CvMat* warp_matrix = cvCreateMat(3, 3, CV_32FC1); //变换矩阵
IplImage* src, *dst; //变换图像与原图像
cvNamedWindow("Original", 1);
cvNamedWindow("Perspective_Warp", 1);
src = cvLoadImage("D:\\Template\\OpenCV\\Template27_Perspective_Transformation\\Debug\\4.jpg");
if (src != 0)
{
cvCreateTrackbar("Position", "Original", &g_slider_position, 2, onTrackbarSlide);//缩放条
cvCreateTrackbar("Rotate", "Original", &g_position, 1, onRotateSlide); //旋转条
}
dst = cvCloneImage(src); //克隆图像
dst->origin = src->origin; //设置相同原点
cvZero(dst); //清零
srcQuad[0].x = 0; //左上
srcQuad[0].y = 0;
srcQuad[1].x = src->width-1; //右上
srcQuad[1].y = 0;
srcQuad[2].x = 0; //左下
srcQuad[2].y = src->height - 1;
srcQuad[3].x = src->width - 1; //右下
srcQuad[3].y = src->height - 1;
dstQuad[0].x = src->width*0.05; //左上
dstQuad[0].y = src->height*0.33;
dstQuad[1].x = src->width*0.9; //右上
dstQuad[1].y = src->height*0.25;
dstQuad[2].x = src->width*0.2; //左下
dstQuad[2].y = src->height*0.7;
dstQuad[3].x = src->width*0.8; //右下
dstQuad[3].y = src->height*0.9;
while (1)
{
char c=cvWaitKey(15);
// printf("当前字符:%d\n", c); //可用于打印测试shift+数字的值
switch (c)
{
case 49:
if (dstQuad[0].x < src->width)
dstQuad[0].x++;
else
dstQuad[0].x = src->width;
break;
case 50:
if (dstQuad[0].y < src->height)
dstQuad[0].y++;
else
dstQuad[0].y = src->height;
break;
case 51:
if (dstQuad[1].x < src->width)
dstQuad[1].x++;
else
dstQuad[1].x = src->width;
break;
case 52:
if (dstQuad[1].y < src->height)
dstQuad[1].y++;
else
dstQuad[1].x = src->height;
break;
case 53:
if (dstQuad[2].x < src->width)
dstQuad[2].x++;
else
dstQuad[2].x = src->width;
break;
case 54:
if (dstQuad[2].y < src->height)
dstQuad[2].y++;
else
dstQuad[2].y = src->height;
break;
case 55:
if (dstQuad[3].x < src->width)
dstQuad[3].x++;
else
dstQuad[3].x = src->width;
break;
case 56:
if (dstQuad[3].y < src->height)
dstQuad[3].y++;
else
dstQuad[3].y = src->height;
break;
/*****************************Shift+Num*********************************/
case 33:
if (dstQuad[0].x > 0)
dstQuad[0].x--;
else
dstQuad[0].x = 0;
break;
case 64:
if (dstQuad[0].y > 0)
dstQuad[0].y--;
else
dstQuad[0].y = 0;
break;
case 35:
if (dstQuad[1].x > 0)
dstQuad[1].x--;
else
dstQuad[1].x = 0;
break;
case 36:
if (dstQuad[1].y > 0)
dstQuad[1].y--;
else
dstQuad[1].x = 0;
break;
case 37:
if (dstQuad[2].x > 0)
dstQuad[2].x--;
else
dstQuad[2].x = 0;
break;
case 94:
if (dstQuad[2].y > 0)
dstQuad[2].y--;
else
dstQuad[2].y = 0;
break;
case 38:
if (dstQuad[3].x > 0)
dstQuad[3].x--;
else
dstQuad[3].x = 0;
break;
case 42:
if (dstQuad[3].y > 0)
dstQuad[3].y--;
else
dstQuad[3].y = 0;
break;
default:
break;
}
if (c == 27) break;
if ((g_slider_position == 0) && already) //缩小
{
dstQuad[0].x = srcQuad[0].x * 2; //左上
dstQuad[0].y = srcQuad[0].y * 2;
dstQuad[1].x = srcQuad[1].x*0.5; //右上
dstQuad[1].y = srcQuad[1].y*0.5;
dstQuad[2].x = srcQuad[2].x * 2; //左下
dstQuad[2].y = srcQuad[2].y*0.5;
dstQuad[3].x = srcQuad[3].x*0.5; //右下
dstQuad[3].y = srcQuad[3].y*0.5;
already = false;
}
if ((g_slider_position == 1) && already) //回复正常大小
{
dstQuad[0].x = srcQuad[0].x; //左上
dstQuad[0].y = srcQuad[0].y;
dstQuad[1].x = srcQuad[1].x; //右上
dstQuad[1].y = srcQuad[1].y;
dstQuad[2].x = srcQuad[2].x; //左下
dstQuad[2].y = srcQuad[2].y;
dstQuad[3].x = srcQuad[3].x; //右下
dstQuad[3].y = srcQuad[3].y;
already = false;
}
if ((g_slider_position == 2) && already) //放大
{
dstQuad[0].x = srcQuad[0].x * 0.5; //左上
dstQuad[0].y = srcQuad[0].y*0.5;
dstQuad[1].x = srcQuad[1].x * 2; //右上
dstQuad[1].y = srcQuad[1].y * 2;
dstQuad[2].x = srcQuad[2].x*0.5; //左下
dstQuad[2].y = srcQuad[2].y * 2;
dstQuad[3].x = srcQuad[3].x * 2; //右下
dstQuad[3].y = srcQuad[3].y * 2;
already = false;
}
if ((g_position == 0) && already_rotate) //逆时针旋转90°
{
dstQuad[0].x = srcQuad[0].x; //左上
dstQuad[0].y = srcQuad[0].y;
dstQuad[1].x = srcQuad[1].x; //右上
dstQuad[1].y = srcQuad[1].y;
dstQuad[2].x = srcQuad[2].x; //左下
dstQuad[2].y = srcQuad[2].y;
dstQuad[3].x = srcQuad[3].x; //右下
dstQuad[3].y = srcQuad[3].y;
already_rotate = false;
}
if ((g_position == 1) && already_rotate) //逆时针旋转90°
{
dstQuad[0].x = srcQuad[2].x; //左上
dstQuad[0].y = srcQuad[2].y;
dstQuad[1].x = srcQuad[0].x; //右上
dstQuad[1].y = srcQuad[0].y;
dstQuad[2].x = srcQuad[3].x; //左下
dstQuad[2].y = srcQuad[3].y;
dstQuad[3].x = srcQuad[1].x; //右下
dstQuad[3].y = srcQuad[1].y;
already_rotate = false;
}
cvGetPerspectiveTransform(srcQuad,
dstQuad,
warp_matrix ); //计算透视变换矩阵
cvWarpPerspective(src, dst, warp_matrix); //进行透视变换
cvShowImage("Original", src);
cvShowImage("Perspective_Warp", dst);
}
cvReleaseImage(&src);
cvReleaseImage(&dst);
cvReleaseMat(&warp_matrix);
cvDestroyWindow("Original");
cvDestroyWindow("Perspective_Warp");
}
本例完成的工作如下:
1. 编译一个facedetect.cpp代码;
2. 检测到人脸,将人脸矩形区域内的图像用目标图像代替;
3. 加入一个滑动条,在范围0.0~1.0之间设置10个位置,使用滚动条来调节矩形区域内图像与目标图像的Alpha融合;
4. 人脸识别部分程序参考 ID:未语愁眸 ;
blog:未语愁眸
源代码包含多个文件,笔者不在文中赘述,上传了加入Alpha融合的人脸识别代码,下载地址:源代码
本例完成的工作如下:
1. 编译lkdemo代码(运动追踪或光流法代码);
2. 在一个更大窗口中创建并显示视频图像;
3. 轻轻移动摄像机,并用光流法向量将图像显示在大窗口中,且保持画面稳定;
4. 部分程序参考 ID:Peak_Ding;
blog:Peak_Ding
具体代码如下:
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
Mat frame; //原始图像
Mat prevImg; //灰度图像1(光流金字塔第一个输入)
Mat nextImg; //灰度图像2(光流金字塔第二个输入)
Mat warp_matrix; //透视变换矩阵
Mat perspecttive_frame; //透视变换后显示的图像
vector prevPts; //跟踪标定点(定义point2f类型的向量),始终为nextPts的上一次值
vector nextPts; //光流追踪输出的点向量,第二幅图像中计算的输入特征的新点位置
vector initial; //标定跟踪点副本,用于画图显示最初点击的标定点位置
vector status; //输出状态向量(无符号char);相应的流特征被发现,向量的每个元素被设置为1,否则,被置为0.
vector<float> err; //输出错误向量;向量每个元素设为相应特征的一个错误,误差测量的类型可以在flags参数中设置
//如果无法找到流则会出现the error is not defined,此时使用status(状态)参数找出此情况
bool detect_station=false; //完成跟踪点标定标志
vector<int> test;
static void help()
{
// print a welcome message, and the OpenCV version
cout << "\nThis is a demo of Lukas-Kanade optical flow lkdemo(),\n"
"Using OpenCV version " << CV_VERSION << endl;
cout << "\nIt uses camera by default, but you can provide a path to video as an argument.\n";
cout << "\nHot keys: \n"
"\tESC once - delete all the points\n"
"\tESC twice - quit the program\n"
"\tTo add a feature point click it(LBUTTONDOWN)\n"
"\tRBUTTONDOWN - auto-initialize tracking\n" << endl;
}
void my_mouse_callback(int event, int x, int y, int flags, void* param)
{
switch (event){
case CV_EVENT_LBUTTONDOWN: //点击左键标定跟踪点
{
prevPts.push_back(cvPoint(x, y)); //标定跟踪点,向量尾部插入点元素
initial.push_back(cvPoint(x, y)); //标定跟踪点副本
}
break;
case CV_EVENT_RBUTTONDOWN:
{
detect_station = true; //点击右键确认完成跟踪点标定
}
break;
}
}
int main()
{
help(); //帮助信息
cvNamedWindow("OpticalFlow", 0); //0:可手动调整大小
cvNamedWindow("Perspective_Image", 1); //1:自动调整大小
cvSetMouseCallback("OpticalFlow", my_mouse_callback); //注册鼠标回调函数
CvCapture* capture = cvCreateCameraCapture(0); //读入摄像机视频流,0:随机选择一个
cvWaitKey(100);
while (capture)
{
frame = cvQueryFrame(capture); //下一帧图像载入内存填充或更新
imshow("OpticalFlow", frame); //显示图像矩阵
cvWaitKey(1);
if (detect_station == true) //判断是否完成标定
{
cvtColor(frame, nextImg, CV_BGR2GRAY); //原始图像BGR转化为灰度图,第二个光流金字塔输入图像
if (prevImg.empty()) //判断prevImg是否为空,为空则真
{
nextImg.copyTo(prevImg); //第一个光流金字塔输入图像,初次运行,光流金字塔两输入图像相同
}
}
while (detect_station == true) //判断是否完成标定
{
if (prevPts.size() == 0) //判断是否有标定点,标定点全部丢失则溢出
{
break;
}
//光流金字塔,计算稀疏光流向量
calcOpticalFlowPyrLK(
prevImg, //第一个8位输入图像,或通过buildOpticalFlowPyramid()建立的金字塔
nextImg, //第二个输入图像或者和prevImg相同尺寸和类型的金字塔
prevPts, //标定跟踪点,二维点向量存储找到的光流;(单精度浮点数)
nextPts, //输出二维点向量(用单精度浮点坐标),第二幅图像中计算的输入特征的新点位置
status, //输出状态向量(uchar);若对应流特征被发现,向量的每个元素被设置为1,否则,被置为0
err); //输出错误向量;向量每个元素设为相应特征的错误,误差测量的类型可在flags参数中设置
//若无法找到流则出现the error is not defined,此时使用status(状态)参数找出此情况
int k = 0; //追踪输出点存在的有效点数
for (int i = 0; i//轮询光流追踪输出点
{
if (status[i]) //输出状态向量,判断每个点向量所对应特征是否被发现,0:丢失
{
//prevPts[k++] = prevPts[i]; //标定跟踪点
initial[k] = initial[i]; //标定跟踪点副本(去除已丢失的追踪输出点对应的标定点)
/****************注意k++和++k的区别,k++是先执行再加1**********************/
nextPts[k++] = nextPts[i]; //光流追踪输出点,第一次赋值是给nextPts[0]
}
}
nextPts.resize(k); //重新设置光流追踪输出点长度(部分点可能已丢失)
//nextPts.resize(k);
initial.resize(k); //重新设置标定跟踪点长度
for (int i = 0; i//轮询光流追踪输出点,显示标定点与输出点之间的偏移
{
line(frame, initial[i], nextPts[i], Scalar(0, 0, 255)); //画线,由标定点指向输出点
circle(frame, nextPts[i], 3, Scalar(255, 0, 0)); //画圆,输出点为圆心,半径3
}
swap(prevPts, nextPts); //交换标定点和光流输出点,标定点prevPts始终是输出点nextPts的前一个值
swap(prevImg, nextImg); //交换两幅光流追踪输入图像,使prevImg始终是视频流中nextImg的前一帧图像
imshow("OpticalFlow", frame); //显示图像 跟踪点移动
//图像稳定(透视变换,基于标定点(4个)将新图像投影到原始图像的平面)
if (prevPts.size() == 4)
{
warp_matrix = getPerspectiveTransform(nextPts, prevPts); //(src,dst)计算透视变换矩阵
//之前swap()所以src、dst反转
warpPerspective(nextImg, //输入图像
perspecttive_frame, //输出图像
warp_matrix, //变换矩阵
cvSize(frame.cols, frame.rows)); //稀疏透视变换
imshow("Perspective_Image", perspecttive_frame); //显示变换的图像
}
//继续下一帧的处理
frame = cvQueryFrame(capture); //下一帧图像载入内存填充或更新
cvtColor(frame, nextImg, CV_BGR2GRAY); //nextImg第二个光流金字塔输入图像,装入新的一帧
char c = cvWaitKey(10); //第一次按下esc,清空现有跟踪标定点
if (c == 27)
{
detect_station = false; //跟踪标定完成失能
initial.resize(0); //清空跟踪标定点
prevPts.resize(0);
break;
}
}
char d = cvWaitKey(10); //第二次esc退出程序
if (d == 27) break;
}
cvDestroyWindow("OpticalFlow");
cvReleaseCapture(&capture);
return 0;
}