在数学中我们学过线性理论,在图像亮度和对比度调节中同样适用,看下面这个公式:
在图像像素中其中:
在opencv中图像数据是存放在Mat数据类型中,我们知道一个像素有rgb构成,所以Mat是个三维数组,一下就是简单的获取mat中图像像素。
//三个for循环,执行运算 new_image(i,j) =a*image(i,j) + b for(int y = 0; y < image.rows; y++ ) { for(int x = 0; x < image.cols; x++ ) { for(int c = 0; c < 3; c++ ) { new_image.at<Vec3b>(y,x)[c]= saturate_cast<uchar>( (g_nContrastValue*0.01)*(image.at<Vec3b>(y,x)[c] ) + g_nBrightValue ); } } }
上述代码中image.at<Vec3b>(y,x)[c] 其中,y是像素所在的行, x是像素所在的列, c是R、G、B(对应0、1、2)其中之一。
saturate_cast为了安全转换,运算结果可能超出像素取值范围(溢出),还可能是非整数(如果是浮点数的话),用saturate_cast对结果进行转换,以确保它为有效值。
效果图:
#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include "opencv2/imgproc/imgproc.hpp" using namespace std; using namespace cv; static void ContrastAndBright(int, void *); int g_nContrastValue; //对比度值 int g_nBrightValue; //亮度值 Mat g_srcImage, g_dstImage; int main() { // 读入用户提供的图像 g_srcImage = imread("0004.bmp"); g_dstImage = Mat::zeros(g_srcImage.size(), g_srcImage.type()); //设定对比度和亮度的初值 g_nContrastValue = 80; g_nBrightValue = 80; //创建窗口 namedWindow("【效果图窗口】", 1); //创建轨迹条 createTrackbar("对比度:", "【效果图窗口】", &g_nContrastValue, 300, ContrastAndBright); createTrackbar("亮 度:", "【效果图窗口】", &g_nBrightValue, 200, ContrastAndBright); //调用回调函数 ContrastAndBright(g_nContrastValue, 0); ContrastAndBright(g_nBrightValue, 0); waitKey(0); //输出一些帮助信息 return 0; } //-----------------------------【ContrastAndBright( )函数】------------------------------------ // 描述:改变图像对比度和亮度值的回调函数 //----------------------------------------------------------------------------------------------- static void ContrastAndBright(int, void *) { // 三个for循环,执行运算 g_dstImage(i,j) = a*g_srcImage(i,j) + b for (int y = 0; y < g_srcImage.rows; y++) { for (int x = 0; x < g_srcImage.cols; x++) { for (int c = 0; c < 3; c++) { g_dstImage.at<Vec3b>(y, x)[c] = saturate_cast<uchar>((g_nContrastValue*0.01)*(g_srcImage.at<Vec3b>(y, x)[c]) + g_nBrightValue); } } } // 显示图像 imshow("【原始图窗口】", g_srcImage); imshow("【效果图窗口】", g_dstImage); }
注意:
功能:防止数据溢出
为什么上面的函数会用到saturate_cast呢,因为无论是加是减,乘除,都会超出一个像素灰度值的范围(0~255)所以,所以当运算完之后,结果为负,则转为0,结果超出255,则为255。
这样已经完成了更改亮度和对比度的需求,但是用for循环执行效率有点低,图像处理起来也不是特别流畅,opencv给出了非常合适的函数
#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include "opencv2/imgproc/imgproc.hpp" using namespace std; using namespace cv; static void ContrastAndBright(int, void *); int g_nContrastValue; //对比度值 int g_nBrightValue; //亮度值 Mat g_srcImage, g_dstImage; int main() { // 读入用户提供的图像 g_srcImage = imread("0004.bmp"); g_dstImage = Mat::zeros(g_srcImage.size(), g_srcImage.type()); //设定对比度和亮度的初值 g_nContrastValue = 80; g_nBrightValue = 80; //创建窗口 namedWindow("【效果图窗口】", 1); //创建轨迹条 createTrackbar("对比度:", "【效果图窗口】", &g_nContrastValue, 300, ContrastAndBright); createTrackbar("亮 度:", "【效果图窗口】", &g_nBrightValue, 200, ContrastAndBright); //调用回调函数 ContrastAndBright(g_nContrastValue, 0); ContrastAndBright(g_nBrightValue, 0); waitKey(0); //输出一些帮助信息 return 0; } //-----------------------------【ContrastAndBright( )函数】------------------------------------ // 描述:改变图像对比度和亮度值的回调函数 //----------------------------------------------------------------------------------------------- static void ContrastAndBright(int, void *) { // 三个for循环,执行运算 g_dstImage(i,j) = a*g_srcImage(i,j) + b //for (int y = 0; y < g_srcImage.rows; y++) //{ // for (int x = 0; x < g_srcImage.cols; x++) // { // for (int c = 0; c < 3; c++) // { // g_dstImage.at<Vec3b>(y, x)[c] = saturate_cast<uchar>((g_nContrastValue*0.01)*(g_srcImage.at<Vec3b>(y, x)[c]) + g_nBrightValue); // } // } //} g_srcImage.convertTo(g_dstImage, -1, g_nContrastValue*0.01, g_nBrightValue); // 显示图像 imshow("【原始图窗口】", g_srcImage); imshow("【效果图窗口】", g_dstImage); }
原文:https://www.cnblogs.com/aiguona/p/9934796.html