一、图像混合、叠加
图像线性混合的数学原理 :G(x)=(1-a)F(x)+aQ(x)
注意事项:
1,a的取值范围为0到1之间
2,F(x)和Q(x)为参与混合的两幅图像,G(x)表示输出图像
3,通过对两幅图像的每个像素值做线性加权得到最终的输出图像
4,两幅图像的大小和类型必须完全一致,如果把图像当成一个矩阵则两个矩阵相加的前提是维度必须一致,否则没有相加的意义。
在OpenCV里面我们使用的API是addWeighted()函数:
C++ void addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype=-1);
- 第一个参数,InputArray类型的src1,表示需要加权的第一个数组,常常填一个Mat。
- 第二个参数,alpha,表示第一个数组的权重
- 第三个参数,src2,表示第二个数组,它需要和第一个数组拥有相同的尺寸和通道数。
- 第四个参数,beta,表示第二个数组的权重值。
- 第五个参数,dst,输出的数组,它和输入的两个数组拥有相同的尺寸和通道数。
- 第六个参数,gamma,一个加到权重总和上的标量值。看下面的式子自然会理解。
- 第七个参数,dtype,输出阵列的可选深度,有默认值-1。;当两个输入数组具有相同的深度时,这个参数设置为-1(默认值),即等同于src1.depth()。
通过这个函数进行加权叠加就可以制造出一些奇特的画面叠加效果,下面这个这个实验我选的图不是很好,没有理想的效果,大家可以自行选取图片实验。程序里面另一个函数是选取ROI后再进行叠加的效果;
示例程序:
//图像的线性混合
#include "stdafx.h"
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
using namespace std;
using namespace cv;
Mat src1, src2, mixed_image,Roi_mixed;
bool LinearBlending();
bool Roi_LinearBlending();
int main(int agrc, char**agrv)
{
LinearBlending();
Roi_LinearBlending();
waitKey(0);
return 0;
}
//线性混合
bool LinearBlending()
{
src1 = imread("C:/Users/59235/Desktop/image/girl3.jpg");
src2 = imread("C:/Users/59235/Desktop/image/national customs2.jpg");
if (!src1.data)
{
cout << "could not load image1...%d\n" << endl;
return -1;
}
if (!src2.data)
{
cout << "could not load image2...%d\n" << endl;
return -1;
}
if (src1.rows == src2.rows &&src1.cols == src2.cols)
{
double alpha = 0.5;//图像的线性混合按照不同的权重进行混合src1权重0.5,src2权重0.5
addWeighted(src1, alpha, src2, (1 - alpha), 0.0, mixed_image);
namedWindow("mixed_image", CV_WINDOW_AUTOSIZE);
imshow("mixed_image", mixed_image);
}
else
{
printf("image is not same size...\n");
return -1;
}
return true;
}
//Roi区域线性混合
bool Roi_LinearBlending()
{
src1 = imread("C:/Users/59235/Desktop/image/test.jpg");
src2 = imread("C:/Users/59235/Desktop/image/heart.jpg");
if (!src1.data)
{
cout << "could not load image1...%d\n" << endl;
return -1;
}
if (!src2.data)
{
cout << "could not load image2...%d\n" << endl;
return -1;
}
//定义一个ROI感兴趣区域
Mat imageROI1 = src1(Rect(425, 215, 90, 65));
Mat imageROI2 = src2(Rect(230, 500, 90, 65));
addWeighted(imageROI1, 0.3, imageROI2, 0.7, 0.0, imageROI1);
namedWindow("Roi_mixed_image", CV_WINDOW_AUTOSIZE);
imshow("Roi_mixed_image", src1);
return true;
}
效果图:
(原图)
(效果图)
(原图) (效果图)
二、分离、合并颜色通道
我们知道一般的图像形式分为单通道(灰度)图像和多通道(彩色)图像。而且像我们最常见的图像RGB、HSV、HSI等都是三通道图像。当我们要对一个通道进行单独处理时,就需要进行分离,如HSV中V是亮度,我们将其置为0时可以有效抗击光照的影响。同时有时候,我们也需要将多个单通道图像合并成一个多通道图像。
OpenCV里面提供的API分别为split();merge()函数:
c++: void split(const mat& src, mat* mvbegin);
c++: void split(inputarray m, outputarrayofarrays mv);
第一个参数,inputarray类型的m或者const mat&类型的src,填我们需要进行分离的多通道数组。
第二个参数,outputarrayofarrays类型的mv,填函数的输出数组或者输出的vector容器
merge()函数是split()函数的逆向操作,将多个数组合并成一个多通道的数组。它通过组合一些给定的单通道数组,将这些孤立的单通道数组合并成一个多通道的数组,从而创建出一个由多个单通道阵列组成的多通道阵列。
C++; void merge(const Mat* mv, size_tcount, OutputArray dst)
C++: void merge(InputArrayOfArrays mv, OutputArray dst)
第一个参数,mv。填需要被合并的输入矩阵或vector容器的阵列,这个mv参数中所有的矩阵必须有着一样的尺寸和深度。
第二个参数,count。当mv为一个空白的C数组时,代表输入矩阵的个数,这个参数显然必须大于1
第三个参数,dst。即输出矩阵,和mv[0]拥有一样的尺寸和深度,并且通洞数量是矩阵阵列中的通道的总数。
示例程序:
//分离颜色通道,多通道融合
#include "stdafx.h"
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src = imread("C:\\Users\\59235\\Desktop\\image\\national customs3.jpg");
if (src.empty())
{
cout << "could not load image...\n" << endl;
return -1;
}
namedWindow("original image", CV_WINDOW_AUTOSIZE);
imshow("original image", src);
//把一个三通道图像,转换成3个单通道图像
vector<Mat> channels;
Mat BImg, GImg, RImg;
split(src, channels);//分离色彩通道
BImg = channels.at(0);
GImg = channels.at(1);
RImg = channels.at(2);
imshow("蓝色分量", BImg);
imshow("绿色分量", GImg);
imshow("红色分量", RImg);
//merge功能:将一些数组合并成一个多通道数组
Mat dst;
merge(channels, dst);
waitKey(0);
return 0;
}
三、对比度、亮度调整
一般的图像处理算子都是一个函数,它接受一个或多个输入图像,并产生输出图像。算子的一般形式如下:
g(x)=h(f(x))或者g(x)=h(f0(x)……fn(x))
图像亮度和对比度的调整操作,属于图像处理变换中一种——点操作。 点操作特点:仅仅根据输入像素值(有时加上某些全局信息或参数),来计算相应的输出像素值。这类算子包括亮度和对比度、颜色校正和变换。 两种最常用的点操作(或者说点算子)是乘上一个常数(对应对比度的调节)以及加上一个常数(对应亮度值的调节),公式如下:
g(x)=a*f(x)+b
- 参数f(x)表示源图像像素
- 参数g(x)表示输出图像像素
- 参数a(需要满足a>0)被称为增益(gain),常常被用来控制图像的对比度
- 参数b通常被称为偏置,常常被用来控制图像的亮度 可以更进一步写成: g(i,j)=a*f(i,j)+b
示例程序:
//图像的对比度和亮度调整
#include "stdafx.h"
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int agrc, char**argv)
{
Mat src;
src = imread("C:\\Users\\59235\\Desktop\\image\\national customs3.jpg");
if (!src.data)
{
printf("could not load image...\n");
return -1;
}
char input[] = "input image";
namedWindow(input, CV_WINDOW_AUTOSIZE);
imshow(input, src);
int height = src.rows;
int width = src.cols;
int t = src.channels();
Mat dst = Mat::zeros(src.size(), src.type());
double alpha = 0.6;//对比度系数
double beta = 30;//亮度系数
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
if (t == 3)
{
float b = src.at<Vec3b>(row, col)[0];//blue
float g = src.at<Vec3b>(row, col)[1];//green
float r = src.at<Vec3b>(row, col)[2];//red
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
{
float gray = src.at<uchar>(row, col);
dst.at<uchar>(row, col) = saturate_cast<uchar>(gray*alpha + beta);
}
}
}
char output[] = "output image";
namedWindow(output, CV_WINDOW_AUTOSIZE);
imshow(output, dst);
waitKey(0);
return 0;
}