opencv 画检测框 opencv绘制矩形框_opencv rect画旋转矩形


时间为友,记录点滴。

聊了这么多的图片运算,忽然有个想法,为什么非要用静态的图片来展示运算呢?我们可以用自己做的图片来经过运算后显示出来。说干就干。


先想一下需求:

  1. 整体环境非黑即白。
  2. 可以用鼠标在背景上画出矩形,矩形内容要填充成反色。
  3. 可以通过鼠标画的矩形展示图片的逻辑操作。

好就这吧,怎么而实现呢?老规矩,大象放冰箱的三步走:

OpenCV中的矩形类实现

rect

创建一个矩形:rect(X0, Y0, width, height), X0/Y0代表矩形的起点,width, height代表矩形的宽和高。这四个变量就把矩形的位置和大小限定了。

  • area(): 面积
  • size(): 尺寸,及[width, height]
  • tl():
  • br():
  • width():
  • heigth():
  • contains(Point(x, y)):
  • &:
  • | :
  • ==:
  • 平移:rect = rect + Point(x, y); 其实就是对左上顶点坐标做加法
  • 缩放:rect = rect + Size(x, y);其实就是对width和height做加法。

rectangle


void rectangle(InputOutputArray img, Point pt1, Point pt2,
                          const Scalar& color, int thickness = 1,
                          int lineType = LINE_8, int shift = 0);


  • img: 可以认为是Mat 类型的Image.
  • pt1: 矩形的一个顶点,比如:rect.tl
  • pt2:矩形的pt1的对立顶点,比如:rect.br
  • color: 矩形的颜色值或者灰度值。
  • thickness: 是否需要填充 thickness>0?不填充:填充
  • lineType: 线的类型,参考LineTypes
  • shift Number of fractional bits in the point coordinates.

OpenCV中的鼠标实现画矩形

鼠标先知道一个函数就够了


void setMousecallback(const string& winname, MouseCallback onMouse, void* userdata=0)


  • winname:窗口的名字
  • onMouse:鼠标响应函数,回调函数。指定窗口里每次鼠标时间发生的时候,被调用的函数指针。 这个函数的原型应该为void on_Mouse(int event, int x, int y, int flags, void* param);
  • userdate:传给回调函数的参数
void on_Mouse(int event, int x, int y, int flags, void* param);


  • event:

常见的event:

cv2_EVENT_MOUSEMOVE 鼠标滑动
cv2_EVENT_LBUTTONDOWN 左键点击
cv2_EVENT_RBUTTONDOWN 右键点击
cv2_EVENT_MBUTTONDOWN 中间点击
cv2_EVENT_LBUTTONUP 左键释放
cv2_EVENT_RBUTTONUP 右键释放
cv2_EVENT_MBUTTONUP 中间释放
cv2_EVENT_LBUTTONDBLCLK 左键双击
cv2_EVENT_RBUTTONDBLCLK 右键双击
cv2_EVENT_MBUTTONDBLCLK 中间双击

  • x/y: 鼠标指针在图像坐标系的坐标(不是窗口坐标系)
  • flags: CV_EVENT_FLAG的组合, param是用户定义的传递到setMouseCallback函数调用的参数。

cv2_EVENT_FLAG_LBUTTON 左键拖拽
cv2_EVENT_FLAG_RBUTTON 右键拖拽

OpenCV中的图片逻辑操作


//逻辑运算:
//dst = src1 & src2
void bitwise_and(InputArray src1, InputArray src2,OutputArray dst, InputArray mask=noArray());
//dst = src1 | src2
void bitwise_or(InputArray src1, InputArray src2,OutputArray dst, InputArray mask=noArray());
//dst = src1 ^ src2
void bitwise_xor(InputArray src1, InputArray src2,OutputArray dst, InputArray mask=noArray());
//dst = ~src
void bitwise_not(InputArray src, OutputArray dst,InputArray mask=noArray());


好了,完成了构思,和基本的概念,coding。。。

第一步:先实现一个矩形类,并且把它画出来,看看基本的&|操作是否有用。


static void drawRectWindow(Mat img, Rect rect)
{
	rectangle(img, rect, Scalar(255), -1);
}


static void rectTest()
{
	Mat imgBackGround = Mat::zeros(600, 800, CV_8UC1);
	Rect test1(100, 100, 200, 200);
	Rect test2(200, 200, 200, 200);
	Rect test3 = test1 | test2;
	Rect test4 = test1 & test2;
	showRectPara("test1", test1);
	showRectPara("test2", test2);
	showRectPara("test1 | test2", test3);
	showRectPara("test1 & test2", test4);
	drawRectWindow(imgBackGround, test4);
	namedWindow("Test");
	imshow("Test", imgBackGround);
}


static void showRectPara(String name, Rect rect)
{
	static uint number = 0;
	cout << "n-------------------" << name << "-" << number << "--------------------" << endl;
	cout << "area: " << rect.area() << endl;
	cout << "size: " << rect.size() << endl;
	cout << "TopLeft: " << rect.tl() << endl;
	cout << "BottomRight: " << rect.br() << endl;
	cout << "width: " << rect.width << endl;
	cout << "height " << rect.height << endl;
	number++;
 }


打印输出:


opencv 画检测框 opencv绘制矩形框_Test_02

矩形的一些属性


opencv 画检测框 opencv绘制矩形框_opencv rect画旋转矩形_03

图片输出的是test1 &amp;amp;amp;amp; test2

第二步:构造鼠标操作

注意点:

  1. 为了实现实时更新矩形,需要在while(1)的线程和callback函数中对矩形做动态调整。
  2. Not/And/Or的操作分别在“EVENT_LBUTTONUP” “EVENT_LBUTTONDOWN”时有操作的不同。
#include "pch.h"
#include <iostream>
#include <string>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

//宏定义
#define NOT_WINDOWS	"NOT"
#define AND_WINDOWS	"AND"
#define OR_WINDOWS	"OR"

//全局变量
Rect gNotRectangle;
Rect gOrRectangle;
Rect gAndRectanglePre;
Rect gAndRectangleNew;
Rect gAndRectangle;
bool gbNotDrawingBox = false;
bool gbOrDrawingBox = false;
bool gbAndDrawingBox = false;

//Functions List
static void showRectPara(String name, Rect rect);
static void notMouseCallBall(int event, int x, int y, int flags, void* param);
static void orMouseCallBall(int event, int x, int y, int flags, void* param);
static void andMouseCallBall(int event, int x, int y, int flags, void* param);
static void drawRectWindow(Mat img, Rect rect);
static void rectTest();

int main()
{
	Mat imgBackGround = Mat::zeros(600, 800, CV_8UC1);
	Mat notImgTemp = imgBackGround.clone();
	Mat orImgTemp = imgBackGround.clone();
	Mat andImgTemp = imgBackGround.clone();

	gNotRectangle = Rect(-1, -1, 0, 0);
	gOrRectangle = Rect(-1, -1, 0, 0);
	gAndRectanglePre = Rect(-1, -1, 0, 0);
	gAndRectangleNew = Rect(-1, -1, 0, 0);
	gAndRectangle = Rect(-1, -1, 0, 0);

	namedWindow(NOT_WINDOWS);
	namedWindow(OR_WINDOWS);
	namedWindow(AND_WINDOWS);


	imshow(NOT_WINDOWS, imgBackGround);
	imshow(OR_WINDOWS, imgBackGround);
	imshow(AND_WINDOWS, imgBackGround);

	setMouseCallback(NOT_WINDOWS, notMouseCallBall, (void*)¬ImgTemp);
	setMouseCallback(OR_WINDOWS, orMouseCallBall, (void*)&orImgTemp);
	setMouseCallback(AND_WINDOWS, andMouseCallBall, (void*)&andImgTemp);


	//rectTest();


	while (true)
	{
		if (gbNotDrawingBox)
		{
			drawRectWindow(notImgTemp, gNotRectangle);
			imshow(NOT_WINDOWS, notImgTemp);
		}
		if (gbOrDrawingBox)
		{
			drawRectWindow(orImgTemp, gOrRectangle);
			imshow(OR_WINDOWS, orImgTemp);
		}
		if (gbAndDrawingBox)
		{
			drawRectWindow(andImgTemp, gAndRectangleNew);
			imshow(AND_WINDOWS, andImgTemp);
		}
		if (waitKey(10) == 27)
		{
			break;
		}
	}
	
	waitKey(0);

	return true;
}


static void notMouseCallBall(int event, int x, int y, int flags, void* param)
{
	Mat& img = *(Mat*)param;
	Mat notImgTemp = img.clone();

	int deltaX, deltaY;
	static int oriX = 0;
	static int oriY = 0;

	switch (event)
	{
	case EVENT_MOUSEMOVE:
		if (gbNotDrawingBox)
		{
			deltaX = x - oriX;
			deltaY = y - oriY;

			if (deltaX >= 0 && deltaY >= 0)
			{
				gNotRectangle = Rect(oriX, oriY, deltaX, deltaY);
			}
			else if (deltaX >= 0 && deltaY < 0)
			{
				gNotRectangle = Rect(oriX, y, deltaX, -1*deltaY);
			}
			else if (deltaX < 0 && deltaY >= 0)
			{
				gNotRectangle = Rect(x, oriY, -1*deltaX, deltaY);
			}
			else if (deltaX < 0 && deltaY < 0)
			{
				gNotRectangle = Rect(x, y, -1*deltaX, -1 * deltaY);
			}
		}
		break;

	case EVENT_LBUTTONDOWN:
		gbNotDrawingBox = true;
		oriX = x;
		oriY = y;
		img = 0;
		//gNotRectangle = Rect(x, y, 0, 0);

		break;

	case EVENT_LBUTTONUP:
		gbNotDrawingBox = false;
		bitwise_not(img, notImgTemp);
		//notImgTemp.copyTo(img);
		imshow(NOT_WINDOWS, notImgTemp);
		break;

	default:
		break;
	}

}

static void andMouseCallBall(int event, int x, int y, int flags, void* param)
{
	int deltaX, deltaY;
	static int oriX = 0;
	static int oriY = 0;
	static bool isInit = false;
	Mat& img = *(Mat*)param;

	switch (event)
	{
	case EVENT_MOUSEMOVE:
		if (gbAndDrawingBox)
		{
			deltaX = x - oriX;
			deltaY = y - oriY;

			if (deltaX >= 0 && deltaY >= 0)
			{
				gAndRectangleNew = Rect(oriX, oriY, deltaX, deltaY);
			}
			else if (deltaX >= 0 && deltaY < 0)
			{
				gAndRectangleNew = Rect(oriX, y, deltaX, -1 * deltaY);
			}
			else if (deltaX < 0 && deltaY >= 0)
			{
				gAndRectangleNew = Rect(x, oriY, -1 * deltaX, deltaY);
			}
			else if (deltaX < 0 && deltaY < 0)
			{
				gAndRectangleNew = Rect(x, y, -1 * deltaX, -1 * deltaY);
			}
		}
		break;

	case EVENT_LBUTTONDOWN:
		gbAndDrawingBox = true;
		oriX = x;
		oriY = y;
		gAndRectangleNew = Rect(x, y, 0, 0);
		break;

	case EVENT_LBUTTONUP:
		cout << "isInit: " << isInit << endl;
		if (false == isInit)
		{
			gAndRectanglePre = gAndRectangleNew;
			isInit = true;
		}
		gbAndDrawingBox = false;

		gAndRectangle = gAndRectangleNew & gAndRectanglePre;
		showRectPara("Pre", gAndRectanglePre);
		showRectPara("New", gAndRectangleNew);
		showRectPara("Rect", gAndRectangle);


		gAndRectanglePre = gAndRectangle;
		img = 0;
		drawRectWindow(img, gAndRectangle);
		imshow(AND_WINDOWS, img);

		break;

	default:
		break;
	}

}
static void orMouseCallBall(int event, int x, int y, int flags, void* param)
{
	int deltaX, deltaY;
	static int oriX = 0;
	static int oriY = 0;

	switch (event)
	{
	case EVENT_MOUSEMOVE:
		if (gbOrDrawingBox)
		{
			deltaX = x - oriX;
			deltaY = y - oriY;

			if (deltaX >= 0 && deltaY >= 0)
			{
				gOrRectangle = Rect(oriX, oriY, deltaX, deltaY);
			}
			else if (deltaX >= 0 && deltaY < 0)
			{
				gOrRectangle = Rect(oriX, y, deltaX, -1 * deltaY);
			}
			else if (deltaX < 0 && deltaY >= 0)
			{
				gOrRectangle = Rect(x, oriY, -1 * deltaX, deltaY);
			}
			else if (deltaX < 0 && deltaY < 0)
			{
				gOrRectangle = Rect(x, y, -1 * deltaX, -1 * deltaY);
			}
		}
		break;

	case EVENT_LBUTTONDOWN:
		gbOrDrawingBox = true;
		oriX = x;
		oriY = y;
		//gOrRectangle = Rect(x, y, 0, 0);
		break;

	case EVENT_LBUTTONUP:
		gbOrDrawingBox = false;
		break;

	default:
		break;
	}

}

static void drawRectWindow(Mat img, Rect rect)
{
	rectangle(img, rect, Scalar(255), -1);
}


static void rectTest()
{
	Mat imgBackGround = Mat::zeros(600, 800, CV_8UC1);
	Rect test1(100, 100, 200, 200);
	Rect test2(200, 200, 200, 200);
	Rect test3 = test1 | test2;
	Rect test4 = test1 & test2;
	showRectPara("test1", test1);
	showRectPara("test2", test2);
	showRectPara("test1 | test2", test3);
	showRectPara("test1 & test2", test4);
	drawRectWindow(imgBackGround, test4);
	namedWindow("Test");
	imshow("Test", imgBackGround);
}


static void showRectPara(String name, Rect rect)
{
	static uint number = 0;
	cout << "n-------------------" << name << "-" << number << "--------------------" << endl;
	cout << "area: " << rect.area() << endl;
	cout << "size: " << rect.size() << endl;
	cout << "TopLeft: " << rect.tl() << endl;
	cout << "BottomRight: " << rect.br() << endl;
	cout << "width: " << rect.width << endl;
	cout << "height " << rect.height << endl;
	number++;
 }


运行结果:


opencv 画检测框 opencv绘制矩形框_Test_04

四个弹窗:log输出;&amp;amp;amp;运算、|运算、非运算