#include <opencv2\opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
Mat src;
src = imread("D:/demo01.jpg");
if (src.empty())
{
cout << "could not find the image resource..." << endl;
return -1;
}
namedWindow("My Image", CV_WINDOW_AUTOSIZE);
imshow("My Image", src);
Mat grayImage;
//函数可以做下面类型的转换,需要说明的是在opencv2.x时颜色空间转换code用的宏定义是CV_前缀开头,而在opencv3.x版本其颜色空间转换code宏定义更改为COLOR_开头
cvtColor(src,grayImage,COLOR_BGR2GRAY);
namedWindow("单通道灰度图像", CV_WINDOW_AUTOSIZE);
imshow("单通道灰度图像", grayImage);
//1).empty() 判断文件读取是否正确
//2).rows 获取图像行数(高度)
//3).cols 获取图像列数(长度)
//4).channels() 获取图像通道数
//5).depth() 获取图像位深度
//单通道 做反色处理
int height = grayImage.rows;
int width = grayImage.cols;
//Mat.ptr<uchar>(row):获取第row行的图像像素指针。图像的行数从0开始计数
//获取点P(row, col)的像素值:P(row.col) = Mat.ptr<uchar>(row)[col]
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
int gray = grayImage.at<uchar>(row,col);
grayImage.at<uchar>(row, col) = 255 - gray;
}
}
namedWindow("单通道做反色处理", CV_WINDOW_AUTOSIZE);
imshow("单通道做反色处理", grayImage);
//多通道
Mat dst;
dst.create(src.size(),src.type());
//height = src.rows;
//width = src.cols;
//int nc = src.channels();
//for (int row = 0; row < height; row++)
//{
// for (int col = 0; col < width; col++)
// {
// if (1 == nc)
// {
// int gray = grayImage.at<uchar>(row, col);
// grayImage.at<uchar>(row, col) = 255 - gray;// 表示对当前图像取反
// }
// else if (3 == nc)
// {
// //src.at<Vec3b>(row, col)[0];
// //意味着从彩色3通道图像中row行col列第0个通道的颜色点
// //Vec3b 图像像素值类型
// int b = src.at<Vec3b>(row, col)[0];
// int g = src.at<Vec3b>(row, col)[1];
// int r = src.at<Vec3b>(row, col)[2];
// dst.at<Vec3b>(row, col)[0] = 255 - b;
// dst.at<Vec3b>(row, col)[1] = 255 - g;
// dst.at<Vec3b>(row, col)[2] = 255 - r;
// }
// }
//}
bitwise_not(src, dst); //这个是利用位取反函数对图像进行取反。
namedWindow("三通道做反色处理", CV_WINDOW_AUTOSIZE);
imshow("三通道做反色处理", dst);
waitKey(0);
return 0;
}
.图像基本运算
图像的基本运算有很多种,比如两幅图像可以相加、相减、相乘、相除、位运算、平方根、对数、绝对值等;图像也可以放大、缩小、旋转,还可以截取其中的一部分作为ROI(感兴趣区域)进行操作,各个颜色通道还可以分别提取及对各个颜色通道进行各种运算操作。总之,对于图像可以进行的基本运算非常的多,只是挑了些常用的操作详解。
-
void add(InputArray src1, InputArray src2, OutputArray dst,InputArray mask=noArray(), int dtype= -1); //dst = src1 + src2
-
void subtract(InputArray src1, InputArray src2, OutputArray dst,InputArray mask=noArray(), int dtype= -1); //dst = src1 - src2
-
void multiply(InputArray src1, InputArray src2,OutputArray dst, double scale=1, int dtype=-1); //dst = scale*src1*src2
-
void divide(InputArray src1, InputArray src2, OutputArray dst,double scale=1, int dtype=-1); //dst = scale*src1/src2
-
void divide(double scale, InputArray src2,OutputArray dst, int dtype=-1); //dst = scale/src2
-
void scaleAdd(InputArray src1, double alpha, InputArray src2, OutputArray dst); //dst = alpha*src1 + src2
-
void addWeighted(InputArray src1, double alpha, InputArray src2,double beta, double gamma, OutputArray dst, int dtype=-1); //dst = alpha*src1 + beta*src2 + gamma
-
void sqrt(InputArray src, OutputArray dst); //计算每个矩阵元素的平方根
-
void pow(InputArray src, double power, OutputArray dst); //src的power次幂
-
void exp(InputArray src, OutputArray dst); //dst = e**src(**表示指数的意思)
-
void log(InputArray src, OutputArray dst); //dst = log(abs(src))
上述的基本操作中都属于将基础数学运算应用于图像像素的处理中,下面将着重介绍
-
bitwise_and、bitwise_or、bitwise_xor、bitwise_not这四个按位操作函数。
-
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 = src1 ^ src2
-
void bitwise_not(InputArray src, OutputArray dst,InputArray mask=noArray()); //dst = ~src
bitwise_or是对二进制数据进行“或”操作,即对图像(灰度图像或彩色图像均可)每个像素值进行二进制“或”操作,1|1=1,1|0=0,0|1=0,0|0=0
bitwise_xor是对二进制数据进行“异或”操作,即对图像(灰度图像或彩色图像均可)每个像素值进行二进制“异或”操作,1^1=0,1^0=1,0^1=1,0^0=0
bitwise_not是对二进制数据进行“非”操作,即对图像(灰度图像或彩色图像均可)每个像素值进行二进制“非”操作,~1=0,~0=1
src.convertTo(dst,CV_32F);
6-两幅图像融合简单实现1;addWeighted()函数;两幅图像尺寸相同时;
在图像处理的操作中经常会遇到将两幅图像融合成一张图像的问题,在opnecv里利用addWeighted()函数可以很容易实现这个功能。
- 在addWeighted()函数中,输入两个图像image1 和 image2。
- 两个图像可以是任何象素类型,只要它们的类型相同。它们可以是单通道或是三通道,只要它们相符。
- 且图像尺寸大小必须相同,否则编译会出错!!!
1.addWeighted()函数
首先看一下addWeighted()函数定义:
-
void addWeighted(InputArray src1, double alpha, InputArray src2,
-
double beta, double gamma, OutputArray dst, int dtype= -1);
参数说明:
- 第一个参数:src1,表示进行加权操作的第一个图像对象,即输入图片1。
- 第二个参数:double型的alpha,表示第一个图像的加权系数,即图片1的融合比例。
- 第三个参数:src2,表示进行加权操作的第二个图像对象,即输入图片2。
- 第四个参数:double型的beta,表示第二个图像的加权系数,即图片2的融合比例。很多情况下,有关系 alpha+beta=1.0。
- 第五个参数:double型的gamma,表示一个作用到加权和后的图像上的标量,可以理解为加权和后的图像的偏移量。
- 第六个参数:dst,表示两个图像加权和后的图像,尺寸和图像类型与src1和src2相同,即输出图像。
- 第七个参数:输出阵列的可选深度,有默认值-1。当两个输入数组具有相同的深度时,这个参数设置为-1(默认值),即等同于src1.depth()。
- dst = src1[I] * alpha + src[2] * beta + gamma;
其中:alpha + beta =1
程序说明
注意:要融合的两个图像的尺寸与类型必须一致!!!
我们说到在两幅图像融合时,我们要求这两幅图像必须类型尺寸一致。但是,若遇到两幅图像尺寸不相同时,我们怎么处理呢?
这节,我们通过两种方式解决这个问题。
- 重置其中一副图像的尺寸大小,使其两幅图像尺寸一致;
- 在较大的图像中设置感兴趣区域ROI,获得与较小的那个图像尺寸一致的区域;
-
void addWeighted(InputArray src1, double alpha, InputArray src2,
-
double beta, double gamma, OutputArray dst, int dtype= -1);
参数说明:
- 第一个参数:src1,表示进行加权操作的第一个图像对象,即输入图片1;
- 第二个参数:double型的alpha,表示第一个图像的加权系数,即图片1的融合比例;
- 第三个参数:src2,表示进行加权操作的第二个图像对象,即输入图片2;
- 第四个参数:double型的beta,表示第二个图像的加权系数,即图片2的融合比例。很多情况下,有关系 alpha+beta=1.0;
- 第五个参数:double型的gamma,表示一个作用到加权和后的图像上的标量,可以理解为加权和后的图像的偏移量;
- 第六个参数:dst,表示两个图像加权和后的图像,尺寸和图像类型与src1和src2相同,即输出图像;
- 第七个参数:输出阵列的可选深度,有默认值-1。当两个输入数组具有相同的深度时,这个参数设置为-1(默认值),即等同于src1.depth()。
1-1-第一种方法代码演示
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
//我们说到在两幅图像融合时,我们要求这两幅图像必须类型尺寸一致。但是,若遇到两幅图像尺寸不相同时,我们怎么处理呢?
//这节,我们通过两种方式解决这个问题。
//1.重置其中一副图像的尺寸大小,使其两幅图像尺寸一致;
//2.在较大的图像中设置感兴趣区域ROI,获得与较小的那个图像尺寸一致的区域;
//注意:第一种方式会造成其中一副图像发生形变;而第二种方式不会改变两幅图像的尺寸;
int main()
{
//1 定义相关变量
Mat src1, src2, dst;
//2 读取原始图像并检查图像是否读取成功
src1 = imread("D:/demo01.jpg");
//"D:/demo01.jpg" 或者"D:\\demo01.jpg"
if (!src1.data)
{
cout << "读取图像有误,请重新输入正确路径!\n";
return -1;
}
src2 = imread("D:/demo02.jpg");
if (src2.empty())//如果数组没有元素 返回true
{
cout << "读取图像有误,请重新输入正确路径!\n";
return -1;
}
//3 显示原始图像
namedWindow("原始图像1",CV_WINDOW_AUTOSIZE);
imshow("原始图像1",src1);
//1.重置其中一副图像的尺寸大小,使其两幅图像尺寸一致;
//调整src2的大小与src1的大小一致 融合函数addWeighted()要求输入的两个图像尺寸类型必须相同
// OpenCV提供了resize函数来改变图像的大小,函数原型如下:
//void resize(InputArray src, OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR );
//src:输入,原图像,即待改变大小的图像;
//dst:输出,改变大小之后的图像,这个图像和原图像具有相同的内容,只是大小和原图像不一样而已;
//dsize:输出图像的大小。如果这个参数不为0,那么就代表将原图像缩放到这个Size(width,height)指定的大小;如果这个参数为0,那么原图像缩放之后的大小就要通过下面的公式来计算:
//dsize = Size(round(fx*src.cols), round(fy*src.rows))
// 其中,fx和fy就是下面要说的两个参数,是图像width方向和height方向的缩放比例。
// fx:width方向的缩放比例,如果它是0,那么它就会按照(double)dsize.width / src.cols来计算;
// fy:height方向的缩放比例,如果它是0,那么它就会按照(double)dsize.height / src.rows来计算;
resize(src2,src2,Size(src1.cols,src1.rows));
namedWindow("原始图像2", CV_WINDOW_AUTOSIZE);
imshow("原始图像2", src2);
//bool imwrite(const String& filename, InputArray img, const std::vector<int>& params = std::vector<int>());
//imwrite("D:/demo02.jpg",src2);
//利用addWeighted()函数对两幅图像进行融合
addWeighted(src1,0.6,src2,0.4,0,dst);
namedWindow("图像1与图像2融合效果图");
imshow("图像1与图像2融合效果图", dst);
//保持等待状态
waitKey(0);
return 0;
}
第一种方法程序说明
OpenCV提供了resize函数来改变图像的大小,函数原型如下:
void resize(InputArray src, OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR );
先解释一下各个参数的意思:
src:输入,原图像,即待改变大小的图像;
dst:输出,改变大小之后的图像,这个图像和原图像具有相同的内容,只是大小和原图像不一样而已;
dsize:输出图像的大小。如果这个参数不为0,那么就代表将原图像缩放到这个Size(width,height)指定的大小;如果这个参数为0,那么原图像缩放之后的大小就要通过下面的公式来计算:
dsize = Size(round(fx*src.cols), round(fy*src.rows))
其中,fx和fy就是下面要说的两个参数,是图像width方向和height方向的缩放比例。
fx:width方向的缩放比例,如果它是0,那么它就会按照(double)dsize.width/src.cols来计算;
fy:height方向的缩放比例,如果它是0,那么它就会按照(double)dsize.height/src.rows来计算;
interpolation:这个是指定插值的方式,图像缩放之后,肯定像素要进行重新计算的,就靠这个参数来指定重新计算像素的方式,有以下几种:
- INTER_NEAREST - 最邻近插值
- INTER_LINEAR - 双线性插值,如果最后一个参数你不指定,默认使用这种方法
- INTER_AREA - resampling using pixel area relation. It may be a preferred method for image decimation, as it gives moire’-free results. But when the image is zoomed, it is similar to the INTER_NEAREST method.
- INTER_CUBIC - 4x4像素邻域内的双立方插值
- INTER_LANCZOS4 - 8x8像素邻域内的Lanczos插值
使用注意事项:
1. dsize和fx/fy不能同时为0,要么你就指定好dsize的值,让fx和fy空置直接使用默认值,就像
resize(img, imgDst, Size(30,30));
要么你就让dsize为0,指定好fx和fy的值,比如fx=fy=0.5,那么就相当于把原图两个方向缩小一倍!
2. 至于最后的插值方法,正常情况下使用默认的双线性插值就够用了。
几种常用方法的效率是:最邻近插值>双线性插值>双立方插值>Lanczos插值;
但是效率和效果成反比,所以根据自己的情况酌情使用。
3. 正常情况下,在使用之前dst图像的大小和类型都是不知道的,类型从src图像继承而来,大小也是从原图像根据参数计算出来。但是如果你事先已经指定好dst图像的大小,那么你可以通过下面这种方式来调用函数:
resize(src, dst, dst.size(), 0, 0, interpolation);
保存图像:imwrite()函数
2-1-第二种方法代码演示
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
//我们说到在两幅图像融合时,我们要求这两幅图像必须类型尺寸一致。但是,若遇到两幅图像尺寸不相同时,我们怎么处理呢?
//这节,我们通过两种方式解决这个问题。
//1.重置其中一副图像的尺寸大小,使其两幅图像尺寸一致;
//2.在较大的图像中设置感兴趣区域ROI,获得与较小的那个图像尺寸一致的区域;
//注意:第一种方式会造成其中一副图像发生形变;而第二种方式不会改变两幅图像的尺寸;
int main()
{
//1 定义相关变量
Mat src1, src2, dst;
//2 读取原始图像并检查图像是否读取成功
src1 = imread("D:/demo01.jpg");
//"D:/demo01.jpg" 或者"D:\\demo01.jpg"
if (!src1.data)
{
cout << "读取图像有误,请重新输入正确路径!\n";
return -1;
}
src2 = imread("D:/demo02.jpg");
if (src2.empty())//如果数组没有元素 返回true
{
cout << "读取图像有误,请重新输入正确路径!\n";
return -1;
}
//3 显示原始图像
namedWindow("原始图像1",CV_WINDOW_AUTOSIZE);
imshow("原始图像1",src1);
namedWindow("原始图像2", CV_WINDOW_AUTOSIZE);
imshow("原始图像2", src2);
//第二种方法代码演示
//2.在较大的图像中设置感兴趣区域ROI,获得与较小的那个图像尺寸一致的区域;
//利用ROI,获取将要较小的那个图像尺寸一致的区域
Mat imageROI;
imageROI = src2(Rect(20, 40, src1.cols, src1.rows));//在src2图像左上角(20,40)处(即起点位置),获取同src1图像尺寸一致的区域
//利用addWeighted()函数对两幅图像进行融合
//void addWeighted(InputArray src1, double alpha, InputArray src2,
//double beta, double gamma, OutputArray dst, int dtype = -1);
addWeighted(src1, 0.6, imageROI, 0.4, 0., imageROI);
namedWindow("图像1与图像2融合效果图");
imshow("图像1与图像2融合效果图", src2);
//保持等待状态
waitKey(0);
return 0;
}
第二种方法程序说明
最近发现OpenCV中的Rect类非常神奇,其中很多函数使用起来极其方便。一下列举一些比较实用的函数:
- size()函数返回矩形的尺寸大小。返回类型为cv::Size。
- area()函数返回矩形的面积,也就是矩形包含的像素点个数。也就是矩形的(宽*高)的值。
- contains(Point)能检测点是否在矩形内。
- inside(Rect)检测矩形是否在矩形内。
- tl()返回矩形左上角的点坐标。即top-left。
- br()返回矩形右下角点坐标。即bottom-right。
- Rect rect = rect1 & rect2;
- Rect rect = rect1 | rect2;
如果想将Rect平移,可以这样写:
- Rect r1(0, 0, 5, 5);
- Point p(2, 3);
- Rect r2 = r1 + p;<span style="white-space:pre"> </span>//平移
如果想改变矩形的尺寸大小,可以这样写:
- Rect r1(0, 0, 5, 5);
- Size s(-1, -1);
- Rect r2 = r1 + s;<span style="white-space:pre"> </span>//改变尺寸大小
但是据我观察,rect裁出来的数据只是加上了坐标限制,但是data里存的数据还是全部的数据,所以如果需要访问data的话还是要单点拷贝了,就是写一个循环一个一个赋值。
//Rect类对象的创建示例
//Rect rect(40, 40, 60, 30);
//参数解释:
//参数1:创建矩形的最左角的 x - 坐标 ;
//参数2:创建矩形的最左角的 y - 坐标;
//参数3:创建矩形的宽;
//参数4:创建矩形的高;
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
//【0】定义相关变量
Rect rect(40, 40, 60, 30);
Point point(10, 10); //定义坐标在(10,10)的点
Point point2(70, 50);
Rect rect1(20, 20, 30, 40);
Rect rect2(90, 30, 20, 10);
Size size(20, 20);
//【1】Rect类的相关操作
cout << "矩形rect左上角的横坐标:" << rect.x << endl;
cout << "矩形rect左上角的纵坐标:" << rect.y << endl;
cout << "矩形rect的宽度:" << rect.width << endl;
cout << "矩形rect的高度:" << rect.height << endl;
cout << "矩形rect的尺寸:" << rect.size() << endl;
cout << "矩形rect的面积:" << rect.area() << endl;
//tl()返回矩形左上角的点坐标。即top-left。
cout << "\n矩形rect左上角的点坐标:" << rect.tl() << endl;
//br()返回矩形右下角点坐标。即bottom-right
cout << "矩形rect右下角的点坐标:" << rect.br() << endl;
//该点在里面则为1,否则为0
//contains(Point)能检测点是否在矩形内
cout << "\n判断(10, 10)这个点在不在矩形rect内:" << rect.contains(point) << endl;
cout << "判断(70, 50)这个点在不在矩形rect内:" << rect.contains(point2) << endl;
cout << "\n矩阵rect与矩阵rect1的交集:" << (rect1 & rect) << endl;
cout << "矩阵rect与矩阵rect1的并集:" << (rect | rect2) << endl;
cout << "\n矩阵rect2进行平移操作:" << (rect2 + point) << endl;
cout << "矩阵rect2进行平移操作:" << (rect2 - point) << endl;
cout << "矩阵rect2改变尺寸大小操作:" << (rect2 + size) << endl;
system("pause"); //作用:暂停黑窗口,否则窗口一闪而过,看不见信息
return 0;
}