紧接着(一)(二)(三)中的程序我们继续利用C++以及opencv的基本方法对图像进行处理,头文件和源文件还是用的(一)中的,在此基础上进行扩充。
void QuickDemo::drawing_demo(Mat& image) {
Rect rect;
rect.x = 200;
rect.y = 200;
rect.width = 250;
rect.height = 300;
//生成一个模板,我们想在bg上面绘制
Mat bg = Mat::zeros(image.size(), image.type());
rectangle(bg, rect, Scalar(0, 0, 255),2, 8, 0);
//在图像(350,400)的位置画一个半径为15的圆,颜色为蓝
circle(bg, Point(350,400),15,Scalar(255,0,0),2,0);
Mat dst;
addWeighted(image, 0.7, bg, 0.3, 0, dst);
imshow("绘制演示", dst);
}
imshow改为bg输出的就是模板上的圆和0矩形。有兴趣的可以自己更改。
addWeighted()函数是将两张相同大小,相同类型的图片融合的函数。他可以实现图片的特效,不多说了,直接上图。
API详解:void cvAddWeighted( const CvArr* src1, double alpha,const CvArr* src2, double beta,double gamma, CvArr* dst );
参数1:src1,第一个原数组.
参数2:alpha,第一个数组元素权重
参数3:src2第二个原数组
参数4:beta,第二个数组元素权重
参数5:gamma,图1与图2作和后添加的数值。不要太大,不然图片一片白。总和等于255以上就是纯白色了。
参数6:dst,输出图片
输出结果:
图3是由1,2融合而成。
只有当按键盘ESC才会停止。程序如下所示:
void QuickDemo::random_drawing_demo() {
//随机绘制
Mat canvas = Mat::zeros(Size(512, 512), CV_8UC3);
int w = canvas.cols;
int h = canvas.rows;
RNG rng(12345);//OpenCV随机数产生器,12345是随机数种子,默认产生的是系统时间
while(true){//无限循环一直画
//键盘的响应操作,只有按键盘的ESC键退出才不会一直画,否则会一直画线
int c = waitKey(10);
if (c == 27) {
break;
}
//线段有两个点,设定点的X,Y坐标
int x1 = rng.uniform(0, w);//产生统一分布的a,b之间的数
int y1 = rng.uniform(0, h);
int x2 = rng.uniform(0, w);
int y2 = rng.uniform(0, h);
//颜色也随机产生
int b = rng.uniform(0, 255);
int g = rng.uniform(0, 255);
int r = rng.uniform(0, 255);
line(canvas, Point(x1, y1), Point(x2, y2), Scalar(b, g, r), 1, LINE_4, 0);
imshow("随机绘制演示:", canvas);
}
}
鼠标的基本基本操作主要有如下:
setMouseCallback()函数是用来处理鼠标动作的函数,我们可以利用它来做有用的操作
OpenCV处理鼠标动作,首先需要创建一个回调函数,当鼠标事件触发时,该函数执行
1、OnMouse()回调函数: def OnMouse(event,x,y,flags,param):
2、setMouseCallback()函数: cv2.setMouseCallback('image',OnMouse)
Event:
EVENT_MOUSEMOVE 0 #滑动
EVENT_LBUTTONDOWN 1 #左键点击
EVENT_RBUTTONDOWN 2 #右键点击
EVENT_MBUTTONDOWN 3 #中键点击
EVENT_LBUTTONUP 4 #左键放开
EVENT_RBUTTONUP 5 #右键放开
EVENT_MBUTTONUP 6 #中键放开
EVENT_LBUTTONDBLCLK 7 #左键双击
EVENT_RBUTTONDBLCLK 8 #右键双击
EVENT_MBUTTONDBLCLK 9 #中键双击
x
,y
,代表鼠标位于窗口的(x,y)坐标位置,flags
,代表鼠标的拖拽事件,以及键盘鼠标联合事件。
flags:
EVENT_FLAG_LBUTTON 1 #左鍵拖曳
EVENT_FLAG_RBUTTON 2 #右鍵拖曳
EVENT_FLAG_MBUTTON 4 #中鍵拖曳
EVENT_FLAG_CTRLKEY 8 #(8~15)按Ctrl不放事件
EVENT_FLAG_SHIFTKEY 16 #(16~31)按Shift不放事件
EVENT_FLAG_ALTKEY 32 #(32~39)按Alt不放事件
在使用之前我们需要定义一个OnMouse()回调函数:
def OnMouse(event,x,y,flags,param):
global x0,y0,x1,y1
if event==cv2.EVENT_LBUTTONDOWN:#左键单击,这里可以设置为其他的鼠标动作
x0, y0 = x, y
cv2.circle(img, (x, y), 20, (255, 245, 190), 2)
然后在主函数中调用,调用方法如下:
img = 255*np.ones((480,480,3),np.uint8)
cv2.namedWindow('image')
cv2.setMouseCallback('image',OnMouse)
while(1):
cv2.imshow('image',img)
if k==ord('q'):#直到按键盘上的'q'键才退出图像
break
cv2.destroyAllWindows()
这样操作后,每次点一下屏幕,就会绘制一个固定半径的圆,至于怎么绘制不固定半径的圆
按鼠标左键在图像上画矩形的代码函数如下所示:(鼠标点击的方式绘制矩形)
Point sp(-1, -1); //鼠标开始的位置
Point ep(-1, -1);//鼠标结束的位置
static void on_draw(int event, int x, int y, int flag, void* userdata) {
Mat image = *((Mat*)userdata);
if (event == EVENT_LBUTTONDOWN) {//左键点击
sp.x = x;
sp.y = y;
std::cout << "start point" << sp << std::endl;
}
else if (event == EVENT_LBUTTONUP) {//左键放开
ep.x = x;
ep.y = y;
int dx = ep.x - sp.x;
int dy = ep.y - sp.y;
if (dx > 0 && dy > 0) {
Rect box(sp.x, sp.y, dx, dy);//可以用鼠标画矩形,但是我们希望在图像上画,所以需要添加如下代码
rectangle(image, box, Scalar(0, 0, 255), 2, 8, 0);
imshow("鼠标绘制显示:", image);//绘制了要急时更新
}
}
}
void QuickDemo::mouse_drawing_demo(Mat &image) {
//鼠标的基本操作:使用鼠标画图
namedWindow("鼠标绘制", WINDOW_AUTOSIZE);
setMouseCallback("鼠标绘制", on_draw, (void*)(&image));
imshow("鼠标绘制显示:", image);
}
鼠标移动的方式绘制矩形代码:
Point sp(-1, -1); //鼠标开始的位置
Point ep(-1, -1);//鼠标结束的位置
static void on_draw(int event, int x, int y, int flag, void* userdata) {
Mat image = *((Mat*)userdata);
if (event == EVENT_LBUTTONDOWN) {//左键点击
sp.x = x;
sp.y = y;
std::cout << "start point" << sp << std::endl;
}
else if (event == EVENT_LBUTTONUP) {//左键放开
ep.x = x;
ep.y = y;
int dx = ep.x - sp.x;
int dy = ep.y - sp.y;
if (dx > 0 && dy > 0) {
Rect box(sp.x, sp.y, dx, dy);//可以用鼠标画矩形,但是我们希望在图像上画,所以需要添加如下代码
rectangle(image, box, Scalar(0, 0, 255), 2, 8, 0);
imshow("鼠标绘制显示:", image);//绘制了要急时更新
//一个矩形绘制好了以后要把鼠标的初始位置还是置为-1,为了下一次绘制做准备
sp.x = -1;
sp.y = -1;
}
}
else if (event == EVENT_MOUSEMOVE) {
if (sp.x > 10 && sp.y > 0) {//表示鼠标的左键要按下去
ep.x = x;
ep.y = y;
int dx = ep.x - sp.x;
int dy = ep.y - sp.y;
if (dx > 0 && dy > 0) {
Rect box(sp.x, sp.y, dx, dy);
rectangle(image, box, Scalar(0, 0, 255), 2, 8, 0);
imshow("鼠标绘制", image);
}
}
}
}
void QuickDemo::mouse_drawing_demo(Mat &image) {
//鼠标的基本操作:使用鼠标画图
namedWindow("鼠标绘制", WINDOW_AUTOSIZE);
setMouseCallback("鼠标绘制", on_draw, (void*)(&image));
imshow("鼠标绘制显示:", image);
}
效果如下所示:
我们可以看到断断续续的效果,这是由于他把所有的矩形都给绘制出来了,变成了这种叠加的效果。如果我们要擦除前面所画的矩形,只需保留最后的即可。可以如下所示:
如果我还想提取box中的ROI区域,只需要在上面程序中增加一句就可以完成。
增加的代码如下所示: