前言:
😊😊😊欢迎来到本博客😊😊😊
🌟🌟🌟 本专栏主要结合OpenCV和C++来实现一些基本的图像处理算法并详细解释各参数含义,适用于平时学习、工作快速查询等,随时更新。
😊😊😊 具体食用方式:可以点击本专栏【OpenCV快速查找(更新中)】–>搜索你要查询的算子名称或相关知识点,或者通过这篇博客👉通俗易懂OpenCV(C++版)详细教程——OpenCV函数快速查找(不断更新中)]查阅你想知道的知识,即可食用。
🎁🎁🎁支持:如果觉得博主的文章还不错或者您用得到的话,可以悄悄关注一下博主哈,如果三连收藏支持就更好啦!这就是给予我最大的支持!😙😙😙
文章目录
- 学习目标
- 一、概念及原理
- 二、代码实现
- 2.1、方式一
- 2.2、方式二
- 2.3、方式三
- 三、 总结
学习目标
- 熟悉线性变换概念及原理
- C++实现线性变换案例
一、概念及原理
图像的线性变换可以用以下公式定义:
其中,输入图像为I
,宽为W
、高为H
,输出图像记为O
。
如下图所示,当a=1
,b=0
时,O
为I
的一个副本;如果a>1
,那么输出图像O
的对比度比I
有所增大;如果0<a<1
,那么O
的对比度比I
有所减小。而b
值的改变,影响的是输出图像的亮度,当b>0
时,亮度增加;当b<0时,亮度减小。
举例:假设图像的灰度级范围是[50,100],通过a=2,b=0
的线性变换,可以将输出图像的灰度级拉伸到[100,200],灰度级范围有所增加,从而提高了对比度;而如果令a=0.5,b=0
,则输出图像的灰度级会压缩到[25,50],灰度级范围有所减小,则降低了对比度。
下面介绍线性变换的代码实现,从处理图像的效果上可以更直观地理解线性变换的作用。
二、代码实现
在OpenCV中实现一个常数与矩阵相乘有多种方式。
2.1、方式一
线性变换的第一种方式,通过Mat的成员函数:
convertTo(OutputArray m,int rtype,double alpha=1,double beta=0)
其中参数m
代表输出矩阵,参数rtype
是输出矩阵m的数据类型,参数alpha
和beta
分别可以理解为线性变换中的a
和b
。简单案例如下:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
int main() {
Mat I = (Mat_<uchar>(2,2) << 0, 200, 35, 8);//输入矩阵/原图
Mat O;//输出矩阵
I.convertTo(O, CV_8UC1, 2.0, 0);
cout << "O=:" << O << endl;
return 0;
}
其中输入的I
的数据类型为uchar
,当输出矩阵的数据类型是CV_8U
时,大于255的值会自动截断为255。
我们可以再用一张图形象显示:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
int main() {
Mat image = imread("D:/VSCodeFile/OpenCV_CSDN/image/logo.jpeg", IMREAD_COLOR);
Mat Outiamge;
image.convertTo(Outiamge, CV_8UC1, 2.0, 5);
imshow("image", image);
imshow("Outiamge【alpha=1.5,beta=0】", Outiamge);
waitKey(0);
return 0;
}
其中,通过a=2,b=5
的线性变换,可以将输出图像的灰度级进行拉伸,从而提高了对比度;b=5>0
,亮度也有所增加。
2.2、方式二
线性变换的第二种方式,使用乘法运算符‘ * ’
,即:
Mat O=4.0*I;
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
int main() {
Mat I = (Mat_<uchar>(2,2) << 0, 200, 35, 8);//输入矩阵/原图
Mat O = 4.0 * I;
cout << "O=:" << O << endl;
return 0;
}
输出矩阵O的值为[[0,255],[140,32]],使用乘法运算符‘ * ’
,无论常数是什么数据类型,输出矩阵的数据类型总是和输入矩阵的数据类型相同,当数据类型是CV_8U
时,在返回值中将大于255的值自动截断为255。
同样,用一张图形象显示:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
int main() {
Mat image = imread("D:/VSCodeFile/OpenCV_CSDN/image/logo.jpeg", IMREAD_COLOR);
Mat Outiamge=2.0*image+5;
imshow("image", image);
imshow("Outiamge【alpha=1.5,beta=0】", Outiamge);
waitKey(0);
return 0;
}
2.3、方式三
线性变换的第三种方式,利用OpenCV提供的函数:
convertScaleAbs(InputArray src,OutputArray dst,double alpha=1,double beta=0))
其中,dst=alpha*src+beta
,dst
的数据类型和输入矩阵src的数据类型是相同的。
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
int main() {
Mat I = (Mat_<uchar>(2,2) << 0, 200, 35, 8);//输入矩阵/原图
Mat O;
convertScaleAbs(I,O, 2.0 ,0);
cout << "O=:" << O << endl;
return 0;
}
同样,用一张图形象显示:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
int main() {
Mat image = imread("D:/VSCodeFile/OpenCV_CSDN/image/logo.jpeg", IMREAD_COLOR);
Mat Outiamge;
//Mat Outiamge=2.0*image+5;
convertScaleAbs(image, Outiamge, 2.0, 5);
imshow("image", image);
imshow("Outiamge【alpha=1.5,beta=0】", Outiamge);
waitKey(0);
return 0;
}
以上线性变换是对整个灰度级范围使用了相同的参数,有的时候也需要针对不同的灰度级范围进行不同的线性变换,这就是常用的分段线性变换。
分段线性变换应用场景:经常用于降低较亮或较暗区域的对比度来增强灰度级处于中间范围的对比度,或者压低中间灰度级处的对比度来增强较亮或较暗区域的对比度。
线性变换的参数需要根据不同的应用及图像自身的信息进行合理的选择,可能需要进行多次测试,所以选择合适的参数是相当麻烦的。其实OpenCV有一种基于当前图像情况自动选取a
和b
的值的方法,下一节就介绍一种显而易见但却很有效的方法,称为直方图正规化。
三、 总结
最后,长话短说,大家看完就好好动手实践一下,切记不能三分钟热度、三天打鱼,两天晒网。OpenCV是学习图像处理理论知识比较好的一个途径,大家也可以自己尝试写写博客,来记录大家平时学习的进度,可以和网上众多学者一起交流、探讨,有什么问题希望大家可以积极评论交流,我也会及时更新,来督促自己学习进度。希望大家觉得不错的可以点赞、关注、收藏。