【图像处理OpenCV(C++版)】——4.2 对比度增强之线性变换

news/2024/7/21 7:25:01 标签: opencv, 图像处理, c++, 计算机视觉, 算法

前言

😊😊😊欢迎来到本博客😊😊😊

🌟🌟🌟 本专栏主要结合OpenCV和C++来实现一些基本的图像处理算法并详细解释各参数含义,适用于平时学习、工作快速查询等,随时更新。

😊😊😊 具体食用方式:可以点击本专栏【OpenCV快速查找(更新中)】–>搜索你要查询的算子名称或相关知识点,或者通过这篇博客👉通俗易懂OpenCV(C++版)详细教程——OpenCV函数快速查找(不断更新中)]查阅你想知道的知识,即可食用。

🎁🎁🎁支持:如果觉得博主的文章还不错或者您用得到的话,可以悄悄关注一下博主哈,如果三连收藏支持就更好啦!这就是给予我最大的支持!😙😙😙


文章目录

    • 学习目标
    • 一、概念及原理
    • 二、代码实现
      • 2.1、方式一
      • 2.2、方式二
      • 2.3、方式三
    • 三、 总结

学习目标

  • 熟悉线性变换概念及原理
  • C++实现线性变换案例

一、概念及原理

  图像的线性变换可以用以下公式定义:

其中,输入图像为I,宽为W、高为H,输出图像记为O

  如下图所示,当a=1b=0时,OI 的一个副本;如果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的数据类型,参数alphabeta分别可以理解为线性变换中ab简单案例如下:

#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+betadst数据类型和输入矩阵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有一种基于当前图像情况自动选取ab的值的方法,下一节就介绍一种显而易见但却很有效的方法,称为直方图正规化


三、 总结

  最后,长话短说,大家看完就好好动手实践一下,切记不能三分钟热度、三天打鱼,两天晒网。OpenCV是学习图像处理理论知识比较好的一个途径,大家也可以自己尝试写写博客,来记录大家平时学习的进度,可以和网上众多学者一起交流、探讨,有什么问题希望大家可以积极评论交流,我也会及时更新,来督促自己学习进度。希望大家觉得不错的可以点赞、关注、收藏。


🚶🚶🚶 今天的文章就到这里啦~
喜欢的话,点赞👍、收藏⭐️、关注💟哦 ~

http://www.niftyadmin.cn/n/27086.html

相关文章

Makefile从入门到....

本文参考正点原子的I.MX6U 嵌入式 Linux 驱动开发指南 原子哥在线教学:www.yuanzige.com 论坛:www.openedv.com 文章目录Makefile 的引入Makefile 语法Makefile 规则格式Makefile 变量赋值符“”赋值符“:”赋值符“?”变量追加“”Makefile 模式规则Makefile 自动化变量Makef…

【蓝桥杯】时间显示(省赛)Java

【问题描述】 小蓝要和朋友合作开发一个时间显示的网站。在服务器上&#xff0c;朋友已经获取了当前的时间&#xff0c;用一个整数表示&#xff0c;值为从1970年1月1日O0:00:00到当前时刻经过的毫秒数。 现在&#xff0c;小蓝要在客户端显示出这个时间。小蓝不用显示出年月日&a…

MATLAB实现费诺编码的计算与分析

一、实验目的 1、理解霍费诺编码的原理。 2、掌握费诺编码的方法和步骤。 3、熟悉费诺编码的效率。 4、本实验用Matlab语言编程实现费诺&#xff08;Fano&#xff09;编码。 二、实验环境 windows XP&#xff0c;MATLAB 7 三、实验原理 费诺编码算法如下&#xff1a;在信源…

Shiro【授权、整合Spirng、Shiro过滤器】

前言 本文主要讲解的知识点有以下&#xff1a; Shiro授权的方式简单介绍与Spring整合初始Shiro过滤器 一、Shiro授权 上一篇我们已经讲解了Shiro的认证相关的知识了&#xff0c;现在我们来弄Shiro的授权 Shiro授权的流程和认证的流程其实是差不多的&#xff1a; 1.1Shiro支…

商品详情的APP原数据接口测试

一、原数据接口的来源&#xff1a; 原数据接口来源于手机端&#xff0c;随着智能化的发展与普及&#xff0c;越来越多的人都是使用智能手机&#xff0c;这样极大的方便了人民的生活&#xff0c;各大电商平台看准了这个商家&#xff0c;把目光都瞄准这个商机&#xff0c;伴随而…

DVWA靶机CSRF全难度(未完)

目录 Low难度 medium难度 Cross Site Request Forgery跨站的请求伪造 原理&#xff1a;利用受害者尚未失效的身份认证信息、会话&#xff1b;诱骗其访问黑客设计号的页面&#xff0c;在受害人不知情的情况下以受害人的身份向服务器发送请求完成非法操作 Low难度 源代码 &l…

【软件测试】软件测试基础知识

1. 什么是软件测试 软件测试就是验证软件产品特性是否满足用户的需求 2. 调试与测试的区别 目的不同 调试&#xff1a;发现并解决软件中的缺陷测试&#xff1a;发现软件中的缺陷 参与角色不同 调试&#xff1a;开发人员测试&#xff1a;测试人员&#xff0c;开发人员等&a…

ESP-IDF:链表例程实现创建,增加,打印数据成员,释放链表空间等功能

链表例程&#xff1a; typedef struct LISTNODE { void *data_p; LISTNODE *next; } mlistnode; typedef struct MYLIST { int size; mlistnode *head; } mylist; mylist *initial_mylist() { mylist *p (mylist *)malloc(sizeof(mylist)); p->size 0; p->head (ml…