OpenCV学习笔记(三)——图像简单处理
一、图像线性混合
在OpenCV中提供了一个API可以实现两张图片的线性融合,此函数的声明如下:
可以看出这个函数最小需要6个参数,其中参数解释如下:
第一个参数,InputArray类型的src1,表示需要加权的第一个数组,常常填一个Mat。
第二个参数,alpha,表示第一个数组的权重。
第三个参数,src2,表示第二个数组,它需要和第一个数组拥有相同的尺寸和通道数。
第四个参数,beta,表示第二个数组的权重值。
第五个参数,dst,输出的数组,它和输入的两个数组拥有相同的尺寸和通道数。
第六个参数,gamma,一个加到权重总和上的标量值。看下面的式子自然会理解。
第七个参数,dtype,输出阵列的可选深度,有默认值-1。;当两个输入数组具有相同的深度时,这个参数设置为-1(默认值),即等同于src1.depth()。
那么它的原理是什么呢?其实很简单:如果用数学公式来表达,addWeighted函数计算如下两个数组(src1和src2)的加权和,得到结果输出给第四个参数。即addWeighted函数的作用可以被表示为为如下的矩阵表达式为:
其中的,是多维数组元素的索引值。而且,在遇到多通道数组的时候,每个通道都需要独立地进行处理。另外需要注意的是,当输出数组的深度为CV_32S时,这个函数就不适用了,这时候就会内存溢出或者算出的结果压根不对。
理论和函数的讲解就是上面这些,接着我们来看代码实例:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
double alpha = 0.5;
Mat img1, img2,dst;
img1 = imread("test5.jpg");
img2 = imread("test6.jpg");
if (img1.empty())
{
printf("could not load the picture1...");
}
else if (img2.empty())
{
printf("could not load the picture2...");
}
if (img1.rows == img2.rows&&img1.cols == img2.cols&&img1.type() == img2.type())
{
addWeighted( img1,alpha,img2,(1.0-alpha),0.0,dst);
namedWindow("图片1:", CV_WINDOW_AUTOSIZE);
imshow("图片1:", img1);
namedWindow("图片2:", CV_WINDOW_AUTOSIZE);
imshow("图片2:", img2);
namedWindow("混合后:", CV_WINDOW_AUTOSIZE);
imshow("混合后:", dst);
}
else
{
printf( "could not linear blend...");
}
waitKey(0);
system("PAUSE");
return 0;
}
运行程序,结果如下:
二、亮度和对比度增强
图像对比度是通过灰度级范围来度量的,而灰度级范围可通过观察灰度直方图得到,灰度级范围越大代表对比度越高;反之,对比度越低,低对比度的图像在视觉上给人的感觉是看起来不够清晰的,所以通过算法调整图像的灰度值,从而调整图像的对比度是有必要的。最简单的一种对比度增强方法是通过灰度值的线性变换来实现的。
假设输入图像为,宽为、高为,输出图像记为,图像的线性变换可以利用以下公式定义:
因此,当时,为的一个副本;如果,则输出图像的对比度比有所增大;如果,则输出图像的对比度比有所减小。而值的改变,影响的是输出图像的亮度,当时,亮度增加;时,亮度减小。
举例:假设图像的灰度级范围是[50,100],通过的线性变换,可以将输出图像的灰度级拉伸到[100,200],灰度级范围有所增加,从而提高了对比度;而如果令,则输出图像的灰度级会压缩到[25,50],灰度级范围有所减小,则降低了对比度。
这就是为什么我们拿到彩色图像以后往往会转化为灰度图进行处理,这位这样相当于只要处理一个通道。如果是三通道,原理是一样的,只不过我们需要对每一个通道都做上述转换步骤。接下来我们对三通道的彩色图像进行线性变换:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
Mat src, dst;
src = imread("test3.png");
if (src.empty())
{
printf("could not load image...\n");
return -1;
}
// 亮度和对比度调整
int height = src.rows;
int width = src.cols;
dst = Mat::zeros(src.size(), src.type());
float alpha = 1.5;
float beta = 30;
Mat m1;
if (src.depth() != CV_32F)
{
src.convertTo(m1, CV_32F);
}
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
if (src.channels() == 3)
{
float b = m1.at<Vec3f>(row, col)[0];//蓝通道
float g = m1.at<Vec3f>(row, col)[1]; // 绿通道
float r = m1.at<Vec3f>(row, col)[2]; // 红通道
dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(b*alpha + beta);
dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(g*alpha + beta);
dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(r*alpha + beta);
}
else if (src.channels() == 1)
{
float v = src.at<uchar>(row, col);
dst.at<uchar>(row, col) = saturate_cast<uchar>(v*alpha + beta);
}
}
}
namedWindow("调整前:", CV_WINDOW_AUTOSIZE);
imshow("调整前:", src);
namedWindow("调整后:", CV_WINDOW_AUTOSIZE);
imshow("调整后:", dst);
waitKey(0);
system("PAUSE");
return 0;
}
运行程序,如下所示:
三、线性变换API
其实,在OpenCV中,就提供了API接口来实现上述的线性变换,其函数声明如下:
其中参数的关系为, 的数据类型和输入矩阵的数据类型是相同的。
我们用上面介绍的API接口来实现同样的操作:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
Mat src, dst;
src = imread("test3.png");
if (src.empty())
{
printf("could not load image...\n");
return -1;
}
// 亮度和对比度调整
int height = src.rows;
int width = src.cols;
dst = Mat::zeros(src.size(), src.type());
float alpha = 1.5;
float beta = 30;
convertScaleAbs(src, dst, 1.5, 30); //调用API接口
namedWindow("调整前:", CV_WINDOW_AUTOSIZE);
imshow("调整前:", src);
namedWindow("调整后:", CV_WINDOW_AUTOSIZE);
imshow("调整后:", dst);
waitKey(0);
system("PAUSE");
return 0;
}
推荐阅读