笔记系列
参考书籍:OpenCV3编程入门
作者:毛星云
版权方:电子工业出版社
出版日期:2015-02
笔记仅供本人参考使用,不具备共通性
笔记中代码均是OpenCV+Qt的代码,并非用vs开发,请勿混淆
HighGUI模块为高层GUI图形用户界面模块
它的功能包括
IplImage*
十分不好用OpenCV中的C++类和函数都是定义在命名空间cv之内的
我们有两种方法可以访问cv
using namespace cv;
这句代码以规定我们的程序位于词命名空间之内cv::
平常在写简单的OpenCV程序时,以下三句可以作为标配
#include
#include
using namespace cv;
Mat类
是用于保存图像以及其他矩阵数据的数据结构
默认情况下Mat类的尺寸为0
cv::Mat pic(320,640,cv::Scalar(100));
的代码来定义一个初始带尺寸的Mat类Mat最基本的用法如下
using namespace cv;
Mat srcImage = imread("需要加载的图像路径");//路径可以是绝对路径,也可以是相对路径
上面的代码表示将一张图片载入到Mat类型的srcImage变量中
代码中的imread()函数,是用于将图片读入Mat类型中,后面会详细讲解
图像载入用到的代码
imread()
图像显示用到的代码
imshow()
功能:用于读取文件中的图片到OpenCV中
函数的原型如下:
enum ImreadModes{
IMREAD_COLOR = 1, //!< If set, always convert image to the 3 channel BGR color image.};
//上面是截取的部分代码,下面是imread()函数的声明
Mat imread( const String& filename, int flags = IMREAD_COLOR );
//参数一:需要载入的图片路径名 参数二:载入标识符
第一个参数:const String& filename ---- 我们需要载入的图片路径名
在Windows操作系统下,OpenCV的imread函数支持如下类型的图像载入
图片类型 | 对应的文件后缀名 |
---|---|
Windows位图 | *.bmp, *.dib |
JPEG文件 | *.jpeg, *.jpg, *.jpe |
JPEG 2000文件 | *.jp2 |
PNG图片 | *.png |
便携文件格式 | *.pbm, *.pgm, *.ppm |
Sun rasters光栅文件 | *.sr, *.ras |
TIFF文件 | *.tiff *.tif |
第二个参数:int flags = IMREAD_COLOR
载入标识符,它指定一个加载图像的颜色类型和图片深度
flags默认值为IMREAD_COLOR,也就是1,所以有些时候这个参数在调用时可以忽略,此时表示载入三通道的彩色图图像
同时通过上面的代码展示可以看出来,此参数可以在OpenCV中标识图像格式的枚举体中取值
让我们转到定义,看看这个枚举体,如下
//! Imread flags
enum ImreadModes {
IMREAD_UNCHANGED = -1,
//!< If set, return the loaded image as is (with alpha channel, otherwise it gets cropped). Ignore EXIF orientation.
//如果设置,则按原样返回加载的图像(带有Alpha通道,否则将被裁剪)。 忽略EXIF方向
IMREAD_GRAYSCALE = 0,
//!< If set, always convert image to the single channel grayscale image (codec internal conversion).
//如果设置,则始终将图像转换为单通道灰度图像(编解码器内部转换)。
IMREAD_COLOR = 1,
//!< If set, always convert image to the 3 channel BGR color image.
//如果设置,则始终将图像转换为3通道BGR彩色图像。
IMREAD_ANYDEPTH = 2,
//!< If set, return 16-bit/32-bit image when the input has the corresponding depth, otherwise convert it to 8-bit.
//如果设置,则当输入具有相应的深度时返回16位/ 32位图像,否则将其转换为8位。
IMREAD_ANYCOLOR = 4,
//!< If set, the image is read in any possible color format.
//如果设置,将以任何可能的颜色格式读取图像。
IMREAD_LOAD_GDAL = 8,
//!< If set, use the gdal driver for loading the image.
//如果已设置,则使用gdal驱动程序加载图像。
IMREAD_REDUCED_GRAYSCALE_2 = 16,
//!< If set, always convert image to the single channel grayscale image and the image size reduced 1/2.
//如果设置,则始终将图像转换为单通道灰度图像,并且图像尺寸减小1/2。
IMREAD_REDUCED_COLOR_2 = 17,
//!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/2.
//如果设置,则始终将图像转换为3通道BGR彩色图像,并且图像尺寸减小1/2。
IMREAD_REDUCED_GRAYSCALE_4 = 32,
//!< If set, always convert image to the single channel grayscale image and the image size reduced 1/4.
//如果设置,则始终将图像转换为单通道灰度图像,并且图像尺寸缩小1/4
IMREAD_REDUCED_COLOR_4 = 33,
//!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/4.
//如果设置,则始终将图像转换为3通道BGR彩色图像,并且图像尺寸减小1/4。
IMREAD_REDUCED_GRAYSCALE_8 = 64,
//!< If set, always convert image to the single channel grayscale image and the image size reduced 1/8.
//如果设置,则始终将图像转换为单通道灰度图像,并且图像尺寸减小1/8。
IMREAD_REDUCED_COLOR_8 = 65,
//!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/8.
//如果设置,则始终将图像转换为3通道BGR彩色图像,并且图像尺寸减小1/8。
IMREAD_IGNORE_ORIENTATION = 128
//!< If set, do not rotate the image according to EXIF's orientation flag.
//如果已设置,则不要根据EXIF的方向标志旋转图像。
};
上述代码选择解释
枚举值 | 解释说明 |
---|---|
IMREAD_UNCHANGED | 等价取值为-1,该标识符在新版本中已被废置,忽略 |
IMREAD_GRAYSCALE | 等价取值为0,如果取这个标识的话,会将载入的图像转换成灰度图在返回 |
IMREAD_COLOR | 等价取值为1,若是取这个标识的话,会将图片转换成3通道BGR彩色图像并返回 |
IMREAD_ANYDEPTH | 等价取值为2,若是取这个标识的话,会先检查图像的深度,若图像深度为16位或32位,则返回对应深度的图像,否则,会将图像转换位8位图像再返回 |
注意:
如果输入有冲突的标志,则采用较小的数字值,例如:
flag = IMREAD_COLOR|IMREAD_ANYCOLOR
时,程序将载入三通道图
flag = IMREAD_ANYCOLOR|IMREAD_ANYDEPTH
flag = IMREAD_ANYCOLOR|IMREAD_ANYDEPTH
标识符所代表的意思是以任何可能的颜色格式,任何可能的图像深度来读取图像注意:下面的表格并不全对,这只是书上的描述,在最新的OpenCV3的版本中,有些许出入
因为flags是int型的变量,若我们不在这个枚举体中去固定的值,可以这样进行
flags取值 | 达到的效果 |
---|---|
flags > 0 | 返回一个3通道的彩色图像,但数值越大,图片就会呗压缩得越厉害! |
flags = 0 | 返回灰度图像 |
flags < 0 | 返回包含Alpha通道的加载图像 |
输出的图像默认情况下不返回Alpha通道,所以若想载入Alpha通道,这里就需要取负值
在书上,作者这样写道:通过表可知,flags = 1999也是可以的,它和flags = 1的效果一样,同样标识返回一个3通道的彩色图像
然而,最新的Opencv3的代码已经不太一样了,flag>1时,不一定会返回一个BGR三通道的图像,甚至flag在不同的数字区间中,所返回的图片大小也是不一样的(具体原因看上面的枚举体代码)
imread的一些常见用法
Mat image0 = imread("1.jpg",2|4);//载入无损的源图像
Mat image1 = imread("1.jpg",0);//载入灰度图
Mat image2 = imread("1.jpg",1);//载入3通道的彩色图像
imshow()函数用于在指定的窗口中显示一副图像
其函数原型如下
void imshow(const String& winname, InputArray mat);
//参数一:图像窗口的标识名称 参数二:需要显示的图像
第一个参数:const String& winname
第二个参数:InputArray mat
imshow()函数的显示规则
imshow()函数缩放图像,取决于图像的深度,具体如下
也就是说,值的范围是[0,255 x 256]映射到[0,255]
也就是说,值的范围是[0,1]映射到[0,255]
此外,若是在窗口创建的时候,程序设定支持OpenGL(WINDOW_OPENGL),那么imshow还支持olg::Buffer
、olg::Texture2D
和gpu::GpuMat
作为输入,其函数原型如下
namespace ogl
{
class CV_EXPORTS Buffer;
class CV_EXPORTS Texture2D;
class CV_EXPORTS Arrays;
}
void imshow(const String& winname, const ogl::Texture2D& tex);
关于imread()和imshow()函数最精简的示例程序,可以参考书本第一章中,1.3.8节的"最终的测试",或者1.4.1中"第一个程序:图像显示"中的代码.
找到InputArray类型的定义,再OpenCV 3.4.13版本中,该类型位于...\opencv2\core\mat.hpp
中
typedef const _InputArray& InputArray;
从此处可以看出:此处是一个类型声明引用,亦即_InputArray
和InputArray
是同一个变量,所以我们接着做定义跳转
跳到_InputArray的定义处
class CV_EXPORTS _InputArray
{
public:
enum {
KIND_SHIFT = 16,
FIXED_TYPE = 0x8000 << KIND_SHIFT,
FIXED_SIZE = 0x4000 << KIND_SHIFT,
KIND_MASK = 31 << KIND_SHIFT,
NONE = 0 << KIND_SHIFT,
MAT = 1 << KIND_SHIFT,
MATX = 2 << KIND_SHIFT,
STD_VECTOR = 3 << KIND_SHIFT,
STD_VECTOR_VECTOR = 4 << KIND_SHIFT,
STD_VECTOR_MAT = 5 << KIND_SHIFT,
EXPR = 6 << KIND_SHIFT, //!< removed
OPENGL_BUFFER = 7 << KIND_SHIFT,
CUDA_HOST_MEM = 8 << KIND_SHIFT,
CUDA_GPU_MAT = 9 << KIND_SHIFT,
UMAT =10 << KIND_SHIFT,
STD_VECTOR_UMAT =11 << KIND_SHIFT,
STD_BOOL_VECTOR =12 << KIND_SHIFT,
STD_VECTOR_CUDA_GPU_MAT = 13 << KIND_SHIFT,
STD_ARRAY =14 << KIND_SHIFT,
STD_ARRAY_MAT =15 << KIND_SHIFT
};
_InputArray();
_InputArray(int _flags, void* _obj);
_InputArray(const Mat& m);
_InputArray(const MatExpr& expr);
_InputArray(const std::vector<Mat>& vec);
template<typename _Tp> _InputArray(const Mat_<_Tp>& m);
template<typename _Tp> _InputArray(const std::vector<_Tp>& vec);
_InputArray(const std::vector<bool>& vec);
template<typename _Tp> _InputArray(const std::vector<std::vector<_Tp> >& vec);
_InputArray(const std::vector<std::vector<bool> >&);
template<typename _Tp> _InputArray(const std::vector<Mat_<_Tp> >& vec);
template<typename _Tp> _InputArray(const _Tp* vec, int n);
template<typename _Tp, int m, int n> _InputArray(const Matx<_Tp, m, n>& matx);
_InputArray(const double& val);
_InputArray(const cuda::GpuMat& d_mat);
_InputArray(const std::vector<cuda::GpuMat>& d_mat_array);
_InputArray(const ogl::Buffer& buf);
_InputArray(const cuda::HostMem& cuda_mem);
template<typename _Tp> _InputArray(const cudev::GpuMat_<_Tp>& m);
_InputArray(const UMat& um);
_InputArray(const std::vector<UMat>& umv);
#ifdef CV_CXX_STD_ARRAY
template<typename _Tp, std::size_t _Nm> _InputArray(const std::array<_Tp, _Nm>& arr);
template<std::size_t _Nm> _InputArray(const std::array<Mat, _Nm>& arr);
#endif
template<typename _Tp> static _InputArray rawIn(const std::vector<_Tp>& vec);
#ifdef CV_CXX_STD_ARRAY
template<typename _Tp, std::size_t _Nm> static _InputArray rawIn(const std::array<_Tp, _Nm>& arr);
#endif
Mat getMat(int idx=-1) const;
Mat getMat_(int idx=-1) const;
UMat getUMat(int idx=-1) const;
void getMatVector(std::vector<Mat>& mv) const;
void getUMatVector(std::vector<UMat>& umv) const;
void getGpuMatVector(std::vector<cuda::GpuMat>& gpumv) const;
cuda::GpuMat getGpuMat() const;
ogl::Buffer getOGlBuffer() const;
int getFlags() const;
void* getObj() const;
Size getSz() const;
int kind() const;
int dims(int i=-1) const;
int cols(int i=-1) const;
int rows(int i=-1) const;
Size size(int i=-1) const;
int sizend(int* sz, int i=-1) const;
bool sameSize(const _InputArray& arr) const;
size_t total(int i=-1) const;
int type(int i=-1) const;
int depth(int i=-1) const;
int channels(int i=-1) const;
bool isContinuous(int i=-1) const;
bool isSubmatrix(int i=-1) const;
bool empty() const;
void copyTo(const _OutputArray& arr) const;
void copyTo(const _OutputArray& arr, const _InputArray & mask) const;
size_t offset(int i=-1) const;
size_t step(int i=-1) const;
bool isMat() const;
bool isUMat() const;
bool isMatVector() const;
bool isUMatVector() const;
bool isMatx() const;
bool isVector() const;
bool isGpuMat() const;
bool isGpuMatVector() const;
~_InputArray();
protected:
int flags;
void* obj;
Size sz;
void init(int _flags, const void* _obj);
void init(int _flags, const void* _obj, Size _sz);
};
namedWindow函数用于创建一个窗口
若是想简单地进行图片显示,可以略去namedWindow()函数的调用
但如果需要在显示窗口之前就用到窗口名时(例如马上就要涉及到的滑动条的使用),就需要调用namedWindow函数先创建出窗口,显式的规定窗口名称了
neamdWindow()函数原型如下
void namedWindow(const String& winname, int flags = WINDOW_AUTOSIZE);
WINDOW_AUTOSIZE
,其他可选择的值可看下方代码enum WindowFlags {
WINDOW_NORMAL = 0x00000000,
//!< the user can resize the window (no constraint) / also use to switch a fullscreen window to a normal size.
//用户可以调整窗口大小(无限制),也可以将全屏窗口切换为正常大小。
WINDOW_AUTOSIZE = 0x00000001,
//!< the user cannot resize the window, the size is constrainted by the image displayed.
//用户无法调整窗口大小,该大小受所显示图像的限制。
WINDOW_OPENGL = 0x00001000,
//!< window with opengl support.
//opengl支持的窗口。
WINDOW_FULLSCREEN = 1,
//!< change the window to fullscreen.
//将窗口更改为全屏。
WINDOW_FREERATIO = 0x00000100,
//!< the image expends as much as it can (no ratio constraint).
//图片会尽可能多地支出(无比例限制)。
WINDOW_KEEPRATIO = 0x00000000,
//!< the ratio of the image is respected.
//图像的比例得到尊重。
WINDOW_GUI_EXPANDED=0x00000000,
//!< status bar and tool bar
//状态栏和工具栏
WINDOW_GUI_NORMAL = 0x00000010,
//!< old fashious way
//过去的方法
};
bool imwrite( const String& filename, InputArray img,
const std::vector<int>& params = std::vector<int>());
第一个参数:const String& filename
第二个参数:InputArray img
第三个参数:const std::vector& params = std::vector()
const std::vector& params表示为特定格式保存的参数编码
该参数有默认值std::vector
,所以一般情况下不需要填写
若是需要填写,需要了解下面的内容
图片格式 | 参数const std::vector 的含义 |
---|---|
JPGE | 表示从0到100的图片质量(CV_IMWRITE_JPEG_QUALITY),默认值是95 |
PNG | 表示压缩的级别(CV_IMWRITE_PNG_COMPRESSION)从0到9. 较高的值意味着更小的尺寸和更长的压缩时间,其默认值为3 |
PPM,PGM,PBM | 表示一个二进制格式标志(CV_IMWRITE_PXM_BINARY),取值为0或1,默认值为1 |
C++_vector操作
imwrite()函数用于将图片保存到指定的文件
示例程序:在OpenCV中生成一副PNG格式图片,并写入到当前工程目录下
前要知识
Mat(int rows, int cols, int type);
/** @overload
@param size 2D array size: Size(cols, rows) . In the Size() constructor, the number of rows and the
number of columns go in the reverse order.
@param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or
CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices.
*/
/*
/ ** @过载
@param size 2D数组大小:Size(cols,rows)。 在Size()构造函数中,行数和列数以相反的顺序排列。
@param type数组类型。 使用CV_8UC1,...,CV_64FC4创建1-4通道矩阵,或CV_8UC(n),...,CV_64FC(n)创建多通道(最多CV_CN_MAX通道)矩阵。
* /
*/
核心代码
//环境:Windows+Qt+OpenCV
//文件:main.cpp
#include
#include
#include
#include
using namespace cv;
using namespace std;
void creatAlphaMat(Mat &mat);
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//创建带Alpha通道的Mat
Mat mat(480,640,CV_8UC4);
creatAlphaMat(mat);
vector<int>compression_params;
compression_params.push_back(IMWRITE_PNG_COMPRESSION);
compression_params.push_back(9);
try
{
imwrite("Transparent Alpha value map.png",mat,compression_params);
imshow("Generated png image",mat);
qDebug()<<"PNG图片文件的alpha数据保存完毕\n您可以在工程目录下查看由imwrite()函数生成的图片\n";
waitKey(0);
}
catch(runtime_error& ex)
{
qDebug()<<"图像转换成PNG格式出错:"<<ex.what()<<endl;
return 1;
}
return a.exec();
}
void creatAlphaMat(Mat &mat)
{
for(int i = 0; i < mat.rows; ++i)
{
for (int j = 0;j < mat.cols;++j)
{
Vec4b& rgba = mat.at<Vec4b>(i,j);
rgba[0] = UCHAR_MAX;
rgba[1] = saturate_cast<uchar>((float (mat.cols - j))/
((float)mat.cols) * UCHAR_MAX);
rgba[2] = saturate_cast<uchar>((float (mat.rows - i))/
((float)mat.rows) * UCHAR_MAX);
rgba[3] = saturate_cast<uchar>(0.5 * (rgba[1] + rgba[2]));
}
}
}
代码运行效果图:
核心代码,文件:main.cpp
#include
#include
#include
using namespace cv;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//----------------------------------------【一、图像的载入和显示】----------------------------------------
//描述:以下三行代码用于完成图像的载入和显示
//----------------------------------------------------------------------------------------------------
//【1】载入图片
Mat girl = imread("D:/Study/StudyDocuments/Study-Documents-2021/Year-2021/Study_Note/Qt/Code/opencv/test.jpg");
//【2】创建一个名为[1]Anime的窗口,窗口大小无限制
namedWindow("[1]Anime",WINDOW_NORMAL);
//【3】显示名为[1]Anime的窗口
imshow("[1]Anime",girl);
//----------------------------------------【二、初级图像混合】----------------------------------------
//描述:初级图像混合
//----------------------------------------------------------------------------------------------------
//【1】载入图片
Mat image = imread("D:/Study/StudyDocuments/Study-Documents-2021/Year-2021/Study_Note/Qt/Code/opencv/test.jpeg");
Mat logo = imread("D:/Study/StudyDocuments/Study-Documents-2021/Year-2021/Study_Note/Qt/Code/opencv/data/opencv-logo-white.png");
//【2】载入后显示图片
namedWindow("[2]Original drawing");
imshow("[2]Original drawing",image);
namedWindow("[3]Logo");
imshow("[3]Logo",logo);
//【3】定义一个Mat类型,用于存放图像的ROI
//机器视觉、图像处理中,从被处理的图像以方框、圆、椭圆、不规则多边形等方式勾勒出需要处理的区域,称为感兴趣区域,ROI。
//方法一
//imageROI = image(Rect(image.cols-logo.cols,image.rows-logo.rows,logo.cols,logo.rows));
//方法二
//imageROI =image(Range(image.rows-logo.rows,image.rows),Range(image.cols-logo.cols,image.cols));
//方法三:方法二的改版
Mat imageROI(image,Range(image.rows-logo.rows,image.rows),Range(image.cols-logo.cols,image.cols));
//【4】将logo加到原图上
addWeighted(imageROI,0.5,logo,0.3,0.,imageROI);
//【5】显示结果
namedWindow("[4]OriginalPic+logoPic");
imshow("[4]OriginalPic+logoPic",image);
//----------------------------------------【三、图像的输出】----------------------------------------
//描述:将一个Mat图像输出到图像文件
//----------------------------------------------------------------------------------------------------
//输出一张jpg图片到工程目录下
imwrite("Final.jpg",image);
waitKey(0);
return a.exec();
}
代码效果如下
代码用到的函数
【1】Rect()函数,对应上述代码第40行
Rect函数参数列表如下
Rect(int _x,int _y,int _width,int _height);
int_x和int_y: 代表左上角点的坐标。
int_width和int_height:代表需要裁剪区域的尺寸 。
追踪Rect的定义,最终如下
template<typename _Tp> inline
Rect_<_Tp>::Rect_(const Point_<_Tp>& org, const Size_<_Tp>& sz)
: x(org.x), y(org.y), width(sz.width), height(sz.height) {}
【2】Range()函数,对应上述代码第42行
Rang()函数参数列表如下
Range::Range(int _start, int _end)
int _start:代表开始的位置
int _end:代表结束的位置
追踪Range的定义,如下
class CV_EXPORTS Range
{
public:
Range();
Range(int _start, int _end);
int size() const;
bool empty() const;
static Range all();
int start, end;
};
inline
Range::Range(int _start, int _end)
: start(_start), end(_end) {}
【3】Mat类的两个重载函数
//使用Range时使用的
Mat(const Mat& m, const Range& rowRange, const Range& colRange=Range::all());
//使用Rect时使用的
Mat(const Mat& m, const Rect& roi);
链接:C++的函数重载
createTrackbar()函数用于创建一个可以调整数值的滑动条(一般称为轨迹条),并将其附加到指定的窗口上
createTrackbar()函数一般会和一个回调函数配合起来使用
createTrackbar()函数的函数原型如下:
CV_EXPORTS int createTrackbar(const String& trackbarname, //轨迹条的名字
const String& winname,//窗口的名子
int* value, //滑动块的初始值
int count,//滑动块的最大值
TrackbarCallback onChange = 0,//指向回调函数的指针
void* userdata = 0);//传给回调函数的数据
第一个参数:const String& trackbarname
第二个参数:String& winname
第三个参数:int* value
第四个参数:int count
第五个参数:TrackbarCallback onChange = 0
void XXXX(int,void*)
int
是轨迹条的位置void*
是用户的数据,详看第六个参数NULL
第六个参数:void* userdata = 0
综上
特定名称
和范围
的轨迹条(Trackbar,或称滑块范围控制工具)轨迹条位置同步
的变量,而且要指定回调函数
显示在指定的窗口
上关于回调函数
定义
:如果我们把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数是,就成为回调函数这句话老夫没看懂
回调函数不由该函数的实现方(这是哪个瓜娃子?)
直接调用,而是在特定的事件或条件发生时由另外的一方(这又是哪个瓜娃子?)
调用,用于对该事件或条件进行响应createTrackbar()函数使用举例
createTrackbar("Contrast","Effect Image",&g_nContrastValue,
300,on_Change);
(在C/C++中,函数名为指向函数地址的指针)
createTrackbar()函数的完整示例
文件:main.cpp
#include
#include
#include
#include
using namespace cv;
#define WINDOW_NAME "[Linear]" //为窗口标题定义的宏
//--------------------------【全局变量声明部分】-------------------------
// 描述:全局变量声明
//--------------------------------------------------------------------
const int g_nMaxAlphaValue = 100;//Alpha值的最大值
int g_nAlphaValueSlider;//滑动条对应的变量
double g_dAlphaValue;
double g_dBetaValue;
char TrackbarName[50];
//声明存储图像的变量
Mat g_srcImage1;
Mat g_srcImage2;
Mat g_srcImage3;
Mat g_dstImage;
//-------------------------------------【on_Trackbar()函数】----------------------------------
// 描述:响应滑动条的回调函数
//-------------------------------------------------------------------------------------------
void on_Trackbar(int,void*)
{
//求出当前alpha值相对于最大值的比例
g_dAlphaValue = (double) g_nAlphaValueSlider/g_nMaxAlphaValue;
//则beta值为1减去alpha值
g_dBetaValue = (1.0 - g_dAlphaValue);
//根据alpha和beta值进行线性混合
addWeighted(g_srcImage1,g_dAlphaValue,g_srcImage2,g_dBetaValue,0.0,g_dstImage);
qDebug()<<getTrackbarPos(TrackbarName,WINDOW_NAME);//获取轨迹条位置
imshow(WINDOW_NAME,g_dstImage);
}
//---------------------------------------【main()函数】----------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始执行
//--------------------------------------------------------------------------------------------
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
g_srcImage1 = imread("D:/Study/StudyDocuments/Study-Documents-2021/Year-2021/Study_Note/Qt/Code/opencv/test.jpeg");
//g_srcImage2 = imread("D:/Study/StudyDocuments/Study-Documents-2021/Year-2021/Study_Note/Qt/Code/opencv/Final.jpg");
g_srcImage3 = imread("D:/Study/StudyDocuments/Study-Documents-2021/Year-2021/Study_Note/Qt/Code/opencv/test.jpg");
//加载的图片大小必须相等
g_srcImage2 = g_srcImage3(Rect(100,1000,500,500));
if(!g_srcImage1.data)
{
qDebug()<<"读取第一张图片出错,请确定imread指定的目录下有该图片存在!\n";
return -1;
}
if(!g_srcImage2.data)
{
qDebug()<<"读取第二张图片出错,请确定imread指定的目录下有该图片存在!\n";
return -1;
}
//将滑动条的初值设置为70
g_nAlphaValueSlider = 70;
//创建窗体
namedWindow(WINDOW_NAME);
//在创建的窗体中创建一个滑动条控件
sprintf(TrackbarName,"Transparency value:%d",g_nMaxAlphaValue);
createTrackbar(TrackbarName,WINDOW_NAME,&g_nAlphaValueSlider,g_nMaxAlphaValue,on_Trackbar);
//结果在回调函数中显示
on_Trackbar(g_nAlphaValueSlider, 0 );
//按任意键推出
waitKey(0);
return a.exec();
}
getTrackbarPos()函数用于获取当前轨迹条的位置并返回,该函数在上面的示例代码中有用到(Line36)
函数原型
CV_EXPORTS_W int getTrackbarPos(const String& trackbarname, const String& winname);
第一个参数:const String& trackbarname
第二个参数:const String& winname
实现效果(见控制台中刷新的数字)
OpenCV中的鼠标操作和滑动条的消息映射方式很类似,都是通过一个中介函数配合一个回调函数来实现
创建和指定鼠标操作回调函数的函数名为SetMouseCallback()
其函数原型如下
CV_EXPORTS void setMouseCallback(const String& winname, MouseCallback onMouse, void* userdata = 0);
第一个参数:const String& winname
第二个参数:MouseCallback onMouse
指定窗口里每次鼠标时间发生的时候,被调用的函数指针
回调函数的原型的大概形式为
void Foo(int event,int x,int y, int flags,void *param)
(注意不是窗口坐标系)
中的坐标值第三个参数:void* userdata = 0
实际示例
文件:main.cpp
//---------------------------------【预编译内容】----------------------------------
#include
#include
using namespace cv;
#define WINDOW_NAME "MOUSE"
//---------------------------------【全局函数声明】-----------------------------
void on_MouseHandle(int event,int x,int y,int flags,void *param);//鼠标处理函数(回调函数)
void DrawRectabgle(cv::Mat& img,cv::Rect box);//用于绘画的函数
//---------------------------------【全局变量声明】--------------------------------
Rect g_rectangle;//声明一个矩形,详情见下方链接
bool g_bDrawingBox = false;//是否进行绘制
RNG g_rng(12345);//用RNG产生随机数,详情见下面链接
//-------------------------------【main函数】-------------------------------------
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//【1】准备参数
g_rectangle = Rect(-1,-1,0,0);//矩形
Mat srcImage(600,800,CV_8UC3),tempImage;//画布
srcImage.copyTo(tempImage);//把srcImage的内容复制到tempImage里面
srcImage = Scalar::all(0);//给srcImage的每个通道都赋值0
//【2】设置鼠标操作回调函数
namedWindow(WINDOW_NAME);
setMouseCallback(WINDOW_NAME,on_MouseHandle,(void*)&srcImage);
//【3】程序主循环,当进行绘制的标识符为真时,进行绘制
while(1)
{
srcImage.copyTo(tempImage);//复制原图到临时变量
if(g_bDrawingBox)
DrawRectabgle(tempImage,g_rectangle);//当进行绘制的标识符为真时,进行绘制
imshow(WINDOW_NAME,tempImage);//显示临时变量
if(waitKey(10) == 27)
break;
//按下esc键,退出循环(但不会关闭窗口)
}
return a.exec();
}
//-------------------------------【on_MouseHandle函数】---------------------------------------
void on_MouseHandle(int event,int x,int y,int flags,void *param)
{
Mat &image = *(cv::Mat*) param;//这里创建了一个临时的Mat类
switch (event)
{
//鼠标移动事件(其实按照下方的代码定义,可以理解为鼠标左键按下之后,移动时的事件)
case EVENT_MOUSEMOVE:
{
if(g_bDrawingBox)//如果是否进行绘制的标识符为真,则记录下长和宽到Rect型变量中
{
g_rectangle.width = x-g_rectangle.x;
g_rectangle.height = y-g_rectangle.y;
}
}
break;
//鼠标左键按下消息
case EVENT_LBUTTONDOWN:
{
g_bDrawingBox = true;
g_rectangle = Rect(x,y,0,0);//记录下鼠标此刻的位置
}
break;
//鼠标抬起消息
case EVENT_LBUTTONUP:
{
g_bDrawingBox = false;
//对宽和高小于0的处理
if(g_rectangle.width < 0)
{
g_rectangle.x += g_rectangle.width;
g_rectangle.width *= -1;
}
if(g_rectangle.height < 0)
{
g_rectangle.y += g_rectangle.height;
g_rectangle.height *= -1;
}
//调用绘制函数
DrawRectabgle(image,g_rectangle);
}
break;
}
}
//-------------------------------------【DrawRectabgle()】-------------------------------------------
void DrawRectabgle(cv::Mat& img,cv::Rect box)
{
rectangle(img,box.tl(),box.br(),Scalar(g_rng.uniform(0,255),
g_rng.uniform(0,255),g_rng.uniform(0,255)));//随机颜色
}
代码运行效果
代码分析
opencv - CvType.CV_8UC4的用途是什么
opencv中用RNG产生随机数
opencv Rect类用法
Rect(int _x,int _y,int _width,int _height);
参数意思为:左上角x坐标
左上角y坐标
矩形的宽
矩形的高
一般的用法为Rect g_rectangle;
g_rectangle=Rect(a,b,c,d);
copyTo()函数
copyTo最一般的用法是src.copyTo(dst),将src复制到dst矩阵中。
后面一个参数可以表示复制的部分,如上面的代码src.copyTo( dst, detected_edges);
是将src中detected_edges矩阵对应的非零部分(即边缘检测结果)复制到dst中。
所以最终显示的边缘和原图颜色一样,也可以直接显示detected_edges矩阵(黑白)
Mat() opencv Scalar::all(0)
本章核心函数清单
函数名称 | 用途 | 讲解章节 |
---|---|---|
imread | 用于读取文件中的图片到OpenCV中 | 3.1.4 |
imshow | 在指定的窗口中显示一副图像 | 3.1.5 |
namedWindow | 用于创建一个窗口 | 3.1.7 |
imwrite | 输出图像到文件 | 3.1.8 |
creatTrackbar | 用于创建一个可以调整数值的轨迹条 | 3.2.1 |
getTrackbarPos | 用于获得轨迹条的当前位置 | 3.2.2 |
SetMouseCallback | 为指定的窗口设置鼠标回调函数 | 3.3 |