简 介: ※
关键词
: GUI,鼠标事件,滑动条
本文根据 Mouse and Trackbar in OpenCV GUI 中的内容进行整理而成,用于学习和工程开发。
§00 前 言
在图像用户界面(GUI:Graphical User Interface)中,鼠标是一个重要元素,如果缺少它,你几乎无法想象如何使用图像交互界面。因此,我们对OpenCV内置的鼠标盒滑动条函数进一步介绍,展示如何利用鼠标来对图像进行标注,利用滑动条控制图像的尺寸。
本文也是“开始学习OpenCV”系列博文的一篇,下面的图像是后面软件实验中的展示OpenCV中鼠标与滑动条能力的测试图像。
▲ 图1 用于博文中输入图像
§01 利用鼠标标注图像
OpenCV中提供了鼠标事件驱动机制来检测不同的鼠标操作,比如点击左键,点击右键。在第一个例子中,我们给你展示如何利用鼠标绘制一个矩形,对于命名窗口中的图像进行标注。
下面就是代码:
- Python
# Import packages
import cv2
# Lists to store the bounding box coordinates
top_left_corner=[]
bottom_right_corner=[]
# function which will be called on mouse input
def drawRectangle(action, x, y, flags, *userdata):
# Referencing global variables
global top_left_corner, bottom_right_corner
# Mark the top left corner when left mouse button is pressed
if action == cv2.EVENT_LBUTTONDOWN:
top_left_corner = [(x,y)]
# When left mouse button is released, mark bottom right corner
elif action == cv2.EVENT_LBUTTONUP:
bottom_right_corner = [(x,y)]
# Draw the rectangle
cv2.rectangle(image, top_left_corner[0], bottom_right_corner[0], (0,255,0),2, 8)
cv2.imshow("Window",image)
# Read Images
image = cv2.imread("../Input/sample.jpg")
# Make a temporary image, will be useful to clear the drawing
temp = image.copy()
# Create a named window
cv2.namedWindow("Window")
# highgui function called when mouse events occur
cv2.setMouseCallback("Window", drawRectangle)
k=0
# Close the window when key q is pressed
while k!=113:
# Display the image
cv2.imshow("Window", image)
k = cv2.waitKey(0)
# If c is pressed, clear the window, using the dummy image
if (k == 99):
image= temp.copy()
cv2.imshow("Window", image)
cv2.destroyAllWindows()
- C++
// Import packages
#include <opencv2/opencv.hpp>
#include <vector>
#include <iostream>
//Using namespace to nullify use of cv::function(); syntax
using namespace cv;
using namespace std;
// Points to store the bounding box coordinates
Point top_left_corner, bottom_right_corner;
// image image
Mat image;
// function which will be called on mouse input
void drawRectangle(int action, int x, int y, int flags, void *userdata)
{
// Mark the top left corner when left mouse button is pressed
if( action == EVENT_LBUTTONDOWN )
{
top_left_corner = Point(x,y);
}
// When left mouse button is released, mark bottom right corner
else if( action == EVENT_LBUTTONUP)
{
bottom_right_corner = Point(x,y);
// Draw rectangle
rectangle(image, top_left_corner, bottom_right_corner, Scalar(0,255,0), 2, 8 );
// Display image
imshow("Window", image);
}
}
// Main function
int main()
{
image = imread("../../Input/sample.jpg");
// Make a temporary image, which will be used to clear the image
Mat temp = image.clone();
// Create a named window
namedWindow("Window");
// highgui function called when mouse events occur
setMouseCallback("Window", drawRectangle);
int k=0;
// loop until q character is pressed
while(k!=113)
{
imshow("Window", image );
k= waitKey(0);
// If c is pressed, clear the window, using the dummy image
if(k == 99)
{
temp.copyTo(image);
}
}
destroyAllWindows();
return 0;
}
我们假设你已经在系统中正确安装了OpenCV,如果还没有可以参见如下链接:
开始先导入所需要的软件包:
- Python
# Import packages
import cv2
- C++
// Import packages
#include <opencv2/opencv.hpp>
#include <vector>
#include <iostream>
// Using namespace to nullify use of cv::function(); syntax
using namespace cv;
using namespace std;
接下来初始化两个列表用于存储要绘制的矩形。在C++中,我们同要需要初始化一个用于存储图像数据的矩阵(这在Python不需要)
- Python
# Lists to store the points
top_left_corner=[]
bottom_right_corner=[]
- C++
// Points to store the center of the circle and a point on the circumference
Point top_left_corner, bottom_right_corner;
// image
Mat image;
首先定义一个回调函数,它在命名窗口图像上绘制矩形。在特定用户界面事件被检测到时被调用,这类函数成为“回调”函数。这里,这种特殊的函数与鼠标事件向管理,所以我们将其定义为MouseCallback函数,当然你也可以给他们起别的你所喜欢的名称。本例子中我们给他们起名为 drawRectangle()。 这在我们使他们与鼠标事件相关联时起作用。你无需给函数指明任何参数,在用户使用鼠标进行交互时比较流行这么做。
我们需要弄懂如何将函数与特定鼠标事件关联起来。从下面例子中,我们将创建一个逻辑基于特定鼠标事件来绘制矩形。这个链接中给出了可以使用的 事件列表 。当使用鼠标进行程序交互时,鼠标在显示图片上就会产生鼠标事件:鼠标事件种类和事件标志将会被记录,同时记录的还有鼠标x,y坐标。这些信息将会被传递到处理函数。 * userdata 是可选参数用于未来回调函数使用(这个函数中并不需要这个参数。)当LEFTBUTTONDOWN, LEFTBUTTONUP事件被触发是,而我们将会存储在各自的变量中存储坐标用于绘制矩形。
如果对于图片标注不熟悉,可以参照当前 这个链接 。
- Python
def drawRectangle(action, x, y, flags, *userdata):
# Referencing global variables
global top_left_corner, bottom_right_corner
# Mark the top left corner, when left mouse button is pressed
if action == cv2.EVENT_LBUTTONDOWN:
top_left_corner = [(x,y)]
# When left mouse button is released, mark bottom right corner
elif action == cv2.EVENT_LBUTTONUP:
bottom_right_corner = [(x,y)]
# Draw the rectangle
cv2.rectangle(image, top_left_corner[0], bottom_right_corner[0], (0,255,0),2,8)
cv2.imshow("Window",image)
- C++
void drawRectangle(int action, int x, int y, int flags, void *userdata)
{
// Mark the top left corner when left mouse button is pressed
if( action == EVENT_LBUTTONDOWN )
{
top_left_corner = Point(x,y);
}
// When left mouse button is released, mark bottom right corner
else if( action == EVENT_LBUTTONUP)
{
bottom_right_corner = Point(x,y);
rectangle(image, top_left_corner, bottom_right_corner, Scalar(0,255,0), 2, 8 );
imshow("Window", image);
}
}
定义完鼠标回调函数之后,进一步执行下面的操作:
- 通过 imshow() 磁盘读入样例图像;
- 通过 copy() 函数拷贝图像,在C++使用 clone() 函数;
- 创建命名窗口;
调用 setMouseCallback() 函数注册上面我们定义的鼠标回调函数。使用 drawRectangle() 函数在命名窗口处理鼠标事件。
setMouseCallback() 调用语法如下:
Python:
cv2.setMouseCallback(winname, onm ouse, userdata)
C++:
void cv::setMouseCallback(const String & winname, MouseCallback onm ouse, void * userdata = 0);
调用参数:
-
winname: Name of the window
-
onMouse: Callback function for mouse events
-
userdata: Optional argument passed to the callback
-
Python
# Read Images
image = cv2.imread("../Input/sample.jpg")
# Make a temporary image, will be useful to clear the drawing
temp = image.copy()
# Create a named window.
cv2.namedWindow("Window")
# highgui function called when mouse events occur
cv2.setMouseCallback("Window", drawRectangle)
- C++
image = imread("../Input/sample.jpg");
// Make a temp image, which will be used to clear the image
Mat temp = image.clone();
// Create a named window
namedWindow("Window");
// highgui function called when mouse events occur
setMouseCallback("Window", drawRectangle);
在最后一步,我们需要显示循环,允许用户与命名窗口进行交互。因此,在下面的代码:
- 创建一个 while 循环连续显示图像,指导用户按动 ‘q’’ (ASCII编码:113)退出程序脚本;
- 在循环中完成清楚前期标注。用户可以在命名窗口中复位图片,显示原始图片的拷贝版本。检测键盘 ‘c’ (ASCII: 99)完成这个功能;
- 当用于退出循环,调用 destroyAllWindows() 关闭所有的窗口。
如果你对于 waitKey() 不熟悉,可以参考这个系列博文的第一篇。
- Python
k=0
while k!=113:
cv2.imshow("Window", image)
k = cv2.waitKey(0)
if (k == 99):
image= temp.copy()
cv2.imshow("Window", image)
cv2.destroyAllWindows()
- C++
int k = 0;
while(k!=113)
{
imshow("Window", image );
k= waitKey(0);
if(k == 99)
{
temp.copyTo(image);
}
}
destroyAllWindows();
运行代码后,用户可以在图像上绘制一个或者多个矩形,结果如下所示。注意, 图像上的矩形只是对使用鼠标指针的简单演示。一旦你可以检测到用户与图像之间的交互并提供应用所需要的功能时,才能够显示出这个办法的强大之处。
▲ 图1.1 利用鼠标绘制矩形结果
§02 利用滑动条改动图像尺寸
现在我们演示如何通过滑动条改变图像尺寸。先看看下面的代码。
- Python
# Import dependancies
import cv2
maxScaleUp = 100
scaleFactor = 1
windowName = "Resize Image"
trackbarValue = "Scale"
# read the image
image = cv2.imread("../Input/sample.jpg")
# Create a window to display results and set the flag to Autosize
cv2.namedWindow(windowName, cv2.WINDOW_AUTOSIZE)
# Callback functions
def scaleImage(*args):
# Get the scale factor from the trackbar
scaleFactor = 1+ args[0]/100.0
# Resize the image
scaledImage = cv2.resize(image, None, fx=scaleFactor, fy = scaleFactor, interpolation = cv2.INTER_LINEAR)
cv2.imshow(windowName, scaledImage)
# Create trackbar and associate a callback function
cv2.createTrackbar(trackbarValue, windowName, scaleFactor, maxScaleUp, scaleImage)
# Display the image
cv2.imshow(windowName, image)
c = cv2.waitKey(0)
cv2.destroyAllWindows()
- C++
// Import Packages
#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>
// Using namespace to nullify use of cv::function(); syntax
using namespace std;
using namespace cv;
int maxScaleUp = 100;
int scaleFactor = 1;
string windowName = "Resize Image";
string trackbarValue = "Scale";
// Callback functions
void scaleImage(int, void*)
{
// Read the image
Mat image = imread("../../Input/sample.jpg");
// Get the Scale factor from the trackbar
double scaleFactorDouble = 1 + scaleFactor/100.0;
// Set the factor to 1 if becomes 0
if (scaleFactorDouble == 0)
{
scaleFactorDouble = 1;
}
Mat scaledImage;
// Resize the image
resize(image, scaledImage, Size(), scaleFactorDouble, scaleFactorDouble, INTER_LINEAR);
// Display the image
imshow(windowName, scaledImage);
}
int main()
{
// load an image
Mat image = imread("../../Input/sample.jpg");
// Create a window to display results and set the flag to Autosize
namedWindow(windowName, WINDOW_AUTOSIZE);
// Create Trackbars and associate a callback function
createTrackbar(trackbarValue, windowName, &scaleFactor, maxScaleUp, scaleImage);
scaleImage(25,0);
// Display image
imshow(windowName, image);
waitKey(0);
destroyAllWindows();
return 0;
}
实际上你已经学会在OpenCV中改变图像的尺寸,通过这个链接重新看一下相关的细节。 这个链接 我们主要注意力放在如何利用OpenCV中的滑动条创建一个用户回调函数。下面的例子 与前面绘制矩形傣妹相似,所以我们主要就如何利用滑动条的代码部分。
首先导数相应的软件包。
- Python
# Import dependencies
import cv2
- C++
// Import Packages
#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>
// Using namespace to nullify use of cv::function(); syntax
using namespace std;
using namespace cv;
接下来初始化一个变量和字符串。使用这些创建滑动条以及实现我们的回调函数。
-
caleFactor : will be used in the callback function that we are going to define for scaling the image.
-
axScaleUp : is the maximum value that the trackbar position will record. It’s best to have 100 as the maximum value for we can then use the trackbar position to directly scale something as a percentage.
-
Python
maxScaleUp = 100
scaleFactor = 1
windowName = "Resize Image"
trackbarValue = "Scale"
- C++
int maxScaleUp = 100;
int scaleFactor = 1;
string windowName = "Resize Image";
string trackbarValue = "Scale";
现在使用imread() 来读入样例图像,并创建 一个命名窗口,在 namedWindow() 函数里,我们传入 WINDOW_AUTOSIZE 标志。这对于改动图像尺寸很重要。
- Python
# load an image
image = cv2.imread("../Input/sample.jpg")
# Create a window to display results
cv2.namedWindow(windowName, cv2.WINDOW_AUTOSIZE)
- C++
// load an image
Mat image = imread("../Input/sample.jpg");
// Create a window to display results and set the flag to Autosize
namedWindow(windowName, WINDOW_AUTOSIZE);
下面我们谈谈回调函数,当用户操作滑动条时回调函数被调用。与鼠标事件回调函数相同,滑动条的回调函数定义方式相同。只要符合函数 signature, 将滑动条回调函数进行惯例。
- Python
def scaleImage(*args):
# Get the scale factor from the trackbar
scaleFactor = 1+ args[0]/100.0
# Resize the image
scaledImage = cv2.resize(image, None, fx=scaleFactor, fy = scaleFactor, interpolation = cv2.INTER_LINEAR)
cv2.imshow(windowName, scaledImage)
- C++
void scaleImage(int, void*)
{
Mat image = imread("../Input/sample.jpg");
// Get the Scale factor from the trackbar
double scaleFactorDouble = 1 + scaleFactor/100.0;
Mat scaledImage;
// Resize the image
resize(image, scaledImage, Size(), scaleFactorDouble, scaleFactorDouble, INTER_LINEAR);
imshow(windowName, scaledImage);
}
cv2.createTrackbar( trackbarName, windowName, value, count, onChange)
int cv::createTrackbar (const String & trackbarname, const String & winname, int * value, int count, TrackbarCallback onChange = 0, void * userdata = 0)
调用参数:
- trackbarname: Name of the created trackbar.
- winname: Name of the parent window of the created trackbar.
- value: Default position of the slider. This is optional.
- count: Till what value the slider will go.
- onChange: Callback function.
- userdata: User data that is passed to the callback function. It can be used to handle trackbar events, without using global variables.
实现的代码如下:
- Python
cv2.createTrackbar(trackbarValue, windowName, scaleFactor, maxScaleUp, scaleImage)
- C++
imshow(windowName, image);
waitKey(0);
destroyAllWindows();
▲ 图2.1 滑动条应用程序界面
※ 总 结 ※
本文中介绍了回调函数,与鼠标事件相关联。使用 setMouseCallback(), createTrackbar() 进行注册回调函数。通过这些基础组件你可以创建有趣的应用。到此为止为何不通过滑动条改变一下显示图像的亮度呢?
■ 相关文献链接:
- Mouse and Trackbar in OpenCV GUI
- Install OpenCV on Windows
- Install OpenCV 4 on macOS | LearnOpenCV
- Install OpenCV on Ubuntu
- 事件列表
- 这个链接
- 这个链接
● 相关图表链接: