2013-11-10 14:10:34 jiangyinglin198812 阅读数 8516

要调整正图片的亮度和对比度, 首先要知道亮度和对比度的定义:

“明度”(Brightness)原来用做光度测定术语照度和(错误的)用于辐射测定术语辐射度的同义词。按美国联邦通信术语表(FS-1037C)的规定,明度现在只应用于非定量的提及对光的生理感觉和感知。[1]

一个给定目标亮度在不同的场景中可以引起不同的明度感觉;比如White错觉Wertheimer-Benary错觉

在 RGB 色彩空间中,明度可以被认为是 R(红色),G(绿色)和B(蓝色)座标的算术平均 μ(尽管这三个成分中的某个要比其他看起来更明亮,但这可以被某些显示系统自动补偿):

 \mu = {R + G + B \over 3 }
即: dst[i] = (src[i + 0] + src[i + 1] + src[i + 2]) / 3 (三通道图片)

明度也是 HSB 或 HSV 色彩空间色相饱和度和明度)中的颜色坐标,它的值是这个颜色的 RG 和 B 三者中的极大值。

 对比度,具体的概念解释可以参考Wiki或者百度百科。简单的讲对比度反应了图片上亮区域和暗区域的层次感。而反应到图像编辑上,调整对比度就是在保证平均亮度不变的情况下,扩大或缩小亮的点和暗的点的差异。既然是要保证平均亮度不变,所以对每个点的调整比例必须作用在该值和平均亮度的差值之上,这样才能够保证计算后的平均亮度不变,故有调整公式:

                                  Out = Average + (In – Average) * ( 1 + percent)

其中In表示原始像素点亮度,Average表示整张图片的平均亮度,Out表示调整后的亮度,而percent即调整范围[-1,1]。证明这个公式的正确性相当简单:

设图上有n个像素点,各个点亮度为Ai,平均亮度为A,变化率为alpha,则有:

CodeCogsEqn (1)

    但是实际处理中,并没有太多的必要去计算一张图的平均亮度:一来耗时间,二来在平均亮度上的精确度并不会给图像的处理带来太多的好处—-一般就假设一张图的平均亮度为128,即一半亮度,而一张正常拍照拍出来的图平均亮度应该是在[100,150]。在肉眼看来两者基本没有任何区别,而如果真实地去计算平均亮度还会带来很大的计算量。、

即: dst[i] = 128 + (src[i] – 128) * (nPercent)    // nPercent = 1 + percent

简单示例:

#include "highgui.h"
#pragma comment(lib,"cv200d.lib")
#pragma comment(lib,"cxcore200d.lib")
#pragma comment(lib,"highgui200d.lib")
 
int BrightnessAdjust(const IplImage* srcImg,
                     IplImage* dstImg,
                     float brightness)
{
    assert(srcImg != NULL);
    assert(dstImg != NULL);
 
    int x,y,i;
    float val;
    for (i = 0; i < 3; i++)//彩色图像需要处理3个通道,灰度图像这里可以删掉
    {
        for (y = 0; y < srcImg->height; y++)
        {
            for (x = 0; x < srcImg->width; x++)
            {
 
                val = ((uchar*)(srcImg->imageData + srcImg->widthStep*y))[x*3+i];
                val += brightness;
                //对灰度值的可能溢出进行处理
                if(val>255)    val=255;
                if(val<0) val=0;
                ((uchar*)(dstImg->imageData + dstImg->widthStep*y))[x*3+i] = (uchar)val;
            }
        }
    }
 
    return 0;
}
 
int ContrastAdjust(const IplImage* srcImg,
                   IplImage* dstImg,
                   float nPercent)
{
    assert(srcImg != NULL);
    assert(dstImg != NULL);
 
    int x,y,i;
    float val;
    for (i = 0; i < 3; i++)//彩色图像需要处理3个通道,灰度图像这里可以删掉
    {
        for (y = 0; y < srcImg->height; y++)
        {
            for (x = 0; x < srcImg->width; x++)
            {
 
                val = ((uchar*)(srcImg->imageData + srcImg->widthStep*y))[x*3+i];
                val = 128 + (val - 128) * nPercent;
                //对灰度值的可能溢出进行处理
                if(val>255) val=255;
                if(val<0) val=0;
                ((uchar*)(dstImg->imageData + dstImg->widthStep*y))[x*3+i] = (uchar)val;
            }
        }
    }
    return 0;
}
 
int main(int argc, char** argv)
{
    IplImage* srcImg = cvLoadImage("lena.jpg");
    assert( srcImg != NULL );
 
    IplImage* brightnessImg = cvCloneImage(srcImg);
    //亮度变换,最后数值取值为正时变亮,负则变暗
    BrightnessAdjust(srcImg, brightnessImg, 80.0f);
 
    IplImage* contrastImg = cvCloneImage(srcImg);
    //对比度变换,数值小于1降低对比度,大于1增强对比度
    ContrastAdjust(srcImg, contrastImg, 1.3f);
 
    cvNamedWindow("Source",CV_WINDOW_AUTOSIZE);
    cvNamedWindow("BrightnessAdjust",CV_WINDOW_AUTOSIZE);
    cvNamedWindow("ContrastAdjust",CV_WINDOW_AUTOSIZE);
    cvShowImage("Source",srcImg);
    cvShowImage("BrightnessAdjust",brightnessImg);
    cvShowImage("ContrastAdjust",contrastImg);
    cvWaitKey(0);
    cvReleaseImage(&srcImg);
    cvReleaseImage(&brightnessImg);
    cvReleaseImage(&contrastImg);
    cvDestroyWindow("Source");
    cvDestroyWindow("BrightnessAdjust");
    cvDestroyWindow("ContrastAdjustrast");
 
    return 0;
}

结果:

并且与photoshop的调整效果对比过
亮度变换与ps旧版效果一致,貌似ps对亮度变换的公式进行过调整,新版不是这么单纯的加减灰度值对比度就几乎都差不多了


在《Delphi图像处理 -- 亮度/对比度调整》一文实现了Photoshop的亮度/对比度调整功能,这是其C/C++版。

    还是先简单介绍一下Photoshop图像亮度/对比度调整的原理:

 一、Photoshop对比度算法。可以用下面的公式来表示:

    (1)、nRGB = RGB + (RGB - Threshold) * Contrast / 255

    公式中,nRGB表示图像像素新的R、G、B分量,RGB表示图像像素R、G、B分量,Threshold为给定的阀值,Contrast为处理过的对比度增量。

    Photoshop对于对比度增量,是按给定值的正负分别处理的:

    当增量等于-255时,是图像对比度的下端极限,此时,图像RGB各分量都等于阀值,图像呈全灰色,灰度图上只有1条线,即阀值灰度;

    当增量大于-255且小于0时,直接用上面的公式计算图像像素各分量;

    当增量等于 255时,是图像对比度的上端极限,实际等于设置图像阀值,图像由最多八种颜色组成,灰度图上最多8条线,即红、黄、绿、青、蓝、紫及黑与白;

    当增量大于0且小于255时,则先按下面公式(2)处理增量,然后再按上面公式(1)计算对比度:

    (2)、nContrast = 255 * 255 / (255 - Contrast) - 255

    公式中的nContrast为处理后的对比度增量,Contrast为给定的对比度增量。

    二、图像亮度调整。本文采用的是最常用的非线性亮度调整(Phoposhop CS3以下版本也是这种亮度调整方式,CS3及以上版本也保留了该亮度调整方式的选项),本文亮度调整采用MMX,对亮度增量分正负情况分别进行了处理,每次处理2个像素,速度相当快,比常规BASM代码的亮度处理过程还要快几倍(参见《GDI+ 在Delphi程序的应用 -- 调整图像亮度》)。

    三、图像亮度/对比度综合调整算法。这个很简单,当亮度、对比度同时调整时,如果对比度大于0,先调整亮度,再调整对比度;当对比度小于0时,则相反,先调整对比度,再调整亮度。

 下面是用BCB2007和GDI+位图数据写的Photoshop图像亮度/对比度调整代码,包括例子代码:

//---------------------------------------------------------------------------
FORCEINLINE
INT CheckValue(INT value)
{
	return (value & ~0xff) == 0? value : value > 255? 255 : 0;
}
//---------------------------------------------------------------------------
// 亮度/对比度调整
VOID BrightAndContrast(BitmapData *data, INT bright, INT contrast, BYTE threshold)
{
	if (bright == 0 && contrast == 0)
		return;
	FLOAT cv = contrast <= -255? -1.0f : contrast / 255.0f;
	if (contrast > 0 && contrast < 255)
		cv = 1.0f / (1.0f - cv) - 1.0f;
	BYTE values[256];
	for (INT i = 0; i < 256; i ++)
	{
		INT v = contrast > 0? CheckValue(i + bright) : i;
		if (contrast >= 255)
			v = v >= threshold? 255 : 0;
		else
			v = CheckValue(v + (INT)((v - threshold) * cv + 0.5f));
		values[i] = contrast <= 0? CheckValue(v + bright) : v;
	}
	PARGBQuad p = (PARGBQuad)data->Scan0;
	INT offset = data->Stride - data->Width * sizeof(ARGBQuad);
	for (UINT y = 0; y < data->Height; y ++, (BYTE*)p += offset)
	{
		for (UINT x = 0; x < data->Width; x ++, p ++)
		{
			p->Blue		= values[p->Blue];
			p->Green	= values[p->Green];
			p->Red		= values[p->Red];
		}
	}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
	Gdiplus::Bitmap *bmp =  new Gdiplus::Bitmap(L"..\\..\\media\\source1.jpg");
	Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);
	g->DrawImage(bmp, 0, 0);
	BitmapData data;
	LockBitmap(bmp, &data);
	BrightAndContrast(&data, 0, 255, 121);
	UnlockBitmap(bmp, &data);
	g->DrawImage(bmp, data.Width, 0);
	delete g;
	delete bmp;
}
//---------------------------------------------------------------------------

2019-01-05 13:30:06 qq_42887760 阅读数 445

理论

  • 图像变换可以看作如下:
    • 像素变换 – 点操作
    • 邻域操作 – 区域
      调整图像亮度和对比度属于像素变换-点操作
      g(i,j)=αf(i,j)+βg(i,j) = αf(i,j) + β (其中 α>0,β是增益变量)
  • 重要的API
    • Mat new_image = Mat::zeros( image.size(), image.type() ); :创建一张跟原图像大小和类型一致的空白图像、像素值初始化为0
    • saturate_cast(value);:确保值大小范围为0~255之间
    • Mat.at(y,x)[index]=value; :给每个像素点每个通道赋值

代码:

#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>

using namespace std;
using namespace cv;

int main(int argc,char** argv){
	Mat src=imread("E:/Experiment/OpenCV/Pictures/test.jpg");
	if(!src.data){
		cout<<"could not load image ..."<<endl;
		return -1;
	}
	char windows_name[]="input Image";
	namedWindow(windows_name,CV_WINDOW_AUTOSIZE);
	imshow(windows_name,src);

	//调整图像亮度与对比度
	int height=src.rows;
	int width=src.cols;

	float alpha = 1.2f;
    float beta = 10.f;

	Mat dst,convert, dst_convert;
	//cvtColor(src,src,CV_RGB2GRAY);//将src灰度处理
	//imshow(windows_name,src);

	dst=Mat::zeros(src.size(),src.type());//初始化对象

    src.convertTo(convert, CV_32F);//将其转化为 float 型数据
	dst_convert=Mat::zeros(src.size(),src.type());

	for(int row= 0; row < height; row++){
		for (int col= 0; col < width; col++){
			if(src.channels()==1){//单通道图像的处理
				int v=src.at<uchar>(row,col);
				dst.at<uchar>(row,col)=saturate_cast<uchar>(alpha*v + beta);//灰度图的计算
			}
			else if(src.channels()==3){//3通道图像处理
				int b = src.at<Vec3b>(row, col)[0];//注意只能用<Vec3b>,而不能用<Vec3f>
                int g = src.at<Vec3b>(row, col)[1];
                int r = src.at<Vec3b>(row, col)[2];
                dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(alpha*b + beta);//调整对比度与亮度,公式: g(i,j) = α*f(i,j)+β  其中 α>0, β是增益变量
                dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(alpha*g + beta);//图像越亮,颜色值越往255靠近
                dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(alpha*r + beta);//对比度: 就是两个像素点之间的差值,差值越大对比度越高,反之越低

                float f_b = convert.at<Vec3f>(row, col)[0];
                float f_g = convert.at<Vec3f>(row, col)[1];
                float f_r = convert.at<Vec3f>(row, col)[2];
                dst_convert.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(alpha*f_b + beta);//用float计算会比uchar精度高一些,值会大一点点
                dst_convert.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(alpha*f_g + beta);
                dst_convert.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(alpha*f_r + beta);
				
			}
		}
	}

	imshow("dst_Image",dst);
	imshow("dst_convert Image",dst_convert);

	waitKey(0);
	return 0;
}

运行结果

在这里插入图片描述

在这里插入图片描述

2020-01-06 14:25:44 yuhengyue 阅读数 56

网上有很多资料,在这里讲讲自己的理解。

一、概念。

a.rgb

假设一副图像的色彩模型是rgb,那么每个像素占24bit(不考虑压缩),r,g,b各占8bit,值从0~255。全0是黑,全255是白。

当r,g,b值相等时,是一条灰色渐变带,如(50,50,50)  ,(127,127,127)等等。

每个像素点从视觉来看有其对应的亮度值,r,g,b等量的相加或减少,可以改变这个点的亮度,而不改变色相,这便是调节亮度的原理。

b.对比度

对比度反应了图片上亮区域和暗区域的层次感。而反应到图像编辑上,调整对比度就是在保证平均亮度不变的情况下,扩大或缩小亮的点和暗的点之间的差异。

 

二、算法

给个photoshop调节对比度的公式,

nRGB = RGB + (RGB - Threshold) * Contrast / 255

其中nRGB代表了r,g,b各自重新计算后的值。Threshold是平均亮度,一般我们不会特意算图像的平均亮度,一来耗时,二来大部分图片是在100~150之间。我们取127,与实际相近。Contrast取值范围为[-255,255]。

所以当Contrast为0时,rgb值不变,表示当前的一个对比度。

为-255时,rgb都为127,亮度都一样,没有差别,呈灰色。

为255时,在Threshold之上的像素点会变得更亮,反之变得更暗。

2019-05-12 21:32:15 qq_32285693 阅读数 596

今天呢,我们一起学习一下,如何调整图像的对比度以及亮度。

一、原理

调整图像亮度与对比度所用的公式
g(i,j) = a * f(i,j) + b
其中,i 和 j 表示像素位于第i行 和 第j列 。

  • 参数f(i,j)表示源图像像素。

  • 参数g(i,j) 表示输出图像像素。

  • 参数a(需要满足a>0)被称为增益(gain),常常被用来控制图像的对比度。

  • 参数b通常被称为偏置(bias),常常被用来控制图像的亮度。

二、访问图像像素的方式

1、通过指针进行遍历

//使用ptr遍历Mat 变量mat是一幅图像
for (int row = 0; row < mat.rows; row++)
{
    for (int col = 0; col < mat.cols; col++)
    {
        Vec3b *c = mat.ptr<Vec3b>(row, col);
        c->val[0] = 0;      //B
        c->val[1] = 255;    //G
        c->val[2] = 0;      //R
    }
}

2、通过at方法遍历

try
{
    //使用at来遍历
    for (int row = 0; row < mat.rows; row++)
    {
        for (int col = 0; col < mat.cols; col++)
        {
            Vec3b &m = mat.at<Vec3b>(row, col);
            m[0] = 100;
            m[1] = 100;
            m[2] = 100;
        }
    }
}
catch (Exception &ex)
{
    cout << ex.what() << endl;
}

3、通过迭代器遍历

auto it = mat.begin<Vec3b>();
auto it_end = mat.end<Vec3b>();
for (; it != it_end; it++)
{
    (*it).val[0] = 0;   //B
    (*it).val[1] = 0;   //G
    (*it).val[2] = 255; //R
}

为了访问图像的每一个像素,我们使用这样的语法: mat.at<Vec3b>(row,col)[c]
其中,row是像素所在的行, col是像素所在的列, c是R、G、B(对应0、1、2)其中之一。

二、图像对比度亮度调整实例

/////////////改变图像的对比度和亮度/////////////////////////////////////
///@para a float 对比度 1.0~3.0
///@para b int 亮度 0~100
void ChangeGain(Mat &src, Mat &des, float a, int b)
{
    //g(r,c) = a*f(r,c) + b
    des.create(src.rows, src.cols, src.type());
    for (int r = 0; r < src.rows; r++)
    {
        for (int c = 0; c < src.cols; c++)
        {
            for (int i = 0; i < 3; i++)
            {
                des.at<Vec3b>(r, c)[i] = 
                    saturate_cast<uchar>(a * src.at<Vec3b>(r, c)[i] + b);
            }
        }
    }
}

因为我们的运算结果可能超出像素取值范围(溢出),还可能是非整数(如果是浮点数的话),所以我们要用saturate_cast对结果进行转换,以确保它为有效值。

实例:

头文件、变量、函数声明

头文件、变量、函数声明

调整图像对比度与亮度

调整图像对比度与亮度

main函数调用

 

好了,今天的OpenCV学到这里就结束了,喜欢的朋友可以给我点个赞哦!!!

我是奕双,现在已经毕业将近两年了,从大学开始学编程,期间学习了C语言编程,C++语言编程,Win32编程,MFC编程,毕业之后进入一家图像处理相关领域的公司,掌握了用OpenCV对图像进行处理,如果大家对相关领域感兴趣的话,可以关注我,我这边会为大家进行解答哦!如果大家需要相关学习资料的话,可以私聊我哦!

2019-01-22 14:45:02 hhaowang 阅读数 1254

目标

在本教程中,您将学习如何:

  • 访问像素值
  • 用零初始化矩阵
  • 了解cv :: saturate_cast的作用以及它有用的原因
  • 获取有关像素转换的一些很酷的信息
  • 在实际例子中提高图像的亮度

理论

注意,以下解释属于Richard Szeliski 所着的Computer Vision:Algorithms and Applications一

图像处理

  • 一般图像处理操作符是获取一个或多个输入图像并产生输出图像的功能。
  • 图像变换可以看作:
    • 点运算(像素变换)-
    • Point operators (pixel transforms)
    • 邻域操作
    • Neighborhood (area-based) operators

像素转换

  • 在这种图像处理变换中,每个输出像素的值仅取决于相应的输入像素值。
  • 这种算子的例子包括亮度对比度调整以及颜色校正和变换

亮度和对比度调整

  • 两个常用的点过程是乘法加法与常量:

  • 参数α>0和β通常称为增益偏差参数(the gain and bias parameters),这些参数分别控制对比度亮度
  • 假设f(x)作为源图像像素,g(x)作为输出图像像素,上述表达式可以改写为:

    其中,i和j表示像素位于i行和第j

     

Coding

C ++

  • 完整代码:
/**
 * @file BasicLinearTransforms.cpp
 * @brief Simple program to change contrast and brightness
 * @author OpenCV team
 */

#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>

// we're NOT "using namespace std;" here, to avoid collisions between the beta variable and std::beta in c++17
using std::cin;
using std::cout;
using std::endl;
using namespace cv;

/**
 * @function main
 * @brief Main function
 */
int main( int argc, char** argv )
{
    /// Read image given by user
    //! [basic-linear-transform-load]
    CommandLineParser parser( argc, argv, "{@input | ../data/lena.jpg | input image}" );
    Mat image = imread( parser.get<String>( "@input" ) );
    if( image.empty() )
    {
      cout << "Could not open or find the image!\n" << endl;
      cout << "Usage: " << argv[0] << " <Input image>" << endl;
      return -1;
    }
    //! [basic-linear-transform-load]

    //! [basic-linear-transform-output]
    Mat new_image = Mat::zeros( image.size(), image.type() );
    //! [basic-linear-transform-output]

    //! [basic-linear-transform-parameters]
    double alpha = 1.0; /*< Simple contrast control */
    int beta = 0;       /*< Simple brightness control */

    /// Initialize values
    cout << " Basic Linear Transforms " << endl;
    cout << "-------------------------" << endl;
    cout << "* Enter the alpha value [1.0-3.0]: "; cin >> alpha;
    cout << "* Enter the beta value [0-100]: ";    cin >> beta;
    //! [basic-linear-transform-parameters]

    /// Do the operation new_image(i,j) = alpha*image(i,j) + beta
    /// Instead of these 'for' loops we could have used simply:
    /// image.convertTo(new_image, -1, alpha, beta);
    /// but we wanted to show you how to access the pixels :)
    //! [basic-linear-transform-operation]
    for( int y = 0; y < image.rows; y++ ) {
        for( int x = 0; x < image.cols; x++ ) {
            for( int c = 0; c < image.channels(); c++ ) {
                new_image.at<Vec3b>(y,x)[c] =
                  saturate_cast<uchar>( alpha*image.at<Vec3b>(y,x)[c] + beta );
            }
        }
    }
    //! [basic-linear-transform-operation]

    //! [basic-linear-transform-display]
    /// Show stuff
    imshow("Original Image", image);
    imshow("New Image", new_image);

    /// Wait until user press some key
    waitKey();
    //! [basic-linear-transform-display]
    return 0;
}

说明 
 

  • 我们使用cv :: imread加载图像并将其保存在Mat对象中:
  CommandLineParser parser( argc, argv, "{@input | ../data/lena.jpg | input image}" );
    Mat image = imread( parser.get<String>( "@input" ) );
    if( image.empty() )
    {
      cout << "Could not open or find the image!\n" << endl;
      cout << "Usage: " << argv[0] << " <Input image>" << endl;
      return -1;
    }
  • 现在,由于我们将对此图像进行一些转换,因此我们需要一个新的Mat对象来存储它。此外,我们希望它具有以下功能:
    • 初始像素值等于零
    • 与原始图像的大小和类型相同
 Mat new_image = Mat::zeros( image.size(), image.type() );  
// the same size and type of Mat img

我们观察到cv :: Mat :: zeros返回一个基于image.size()image.type()的Matlab样式的零初始值设定项

  • 我们现在问α的值和β 由用户输入:
 double alpha = 1.0; /*< Simple contrast control */
    int beta = 0;       /*< Simple brightness control */
    cout << " Basic Linear Transforms " << endl;
    cout << "-------------------------" << endl;
    cout << "* Enter the alpha value [1.0-3.0]: "; cin >> alpha;
    cout << "* Enter the beta value [0-100]: ";    cin >> beta;
  • 现在,执行操作g(i,j)= α · f(i,j)+ b我们将访问图像中的每个像素。由于我们使用BGR图像进行操作,因此每个像素(B,G和R)将有三个值,因此我们也将单独访问它们。
  • 代码:
 for( int y = 0; y < image.rows; y++ ) {
        for( int x = 0; x < image.cols; x++ ) {
            for( int c = 0; c < image.channels(); c++ ) {
                new_image.at<Vec3b>(y,x)[c] =
                  saturate_cast<uchar>( alpha*image.at<Vec3b>(y,x)[c] + beta );
            }
        }
    }

请注意以下内容(仅限C ++代码):

  • 要访问图像中的每个像素,我们使用以下语法:image.at <Vec3b>(y,x)[c]其中y是行,x是列,c是R,G或B(0,1或2)。
  • 由于操作α&CenterDot;&p(i,j)+ b可以给出超出范围的值或不是整数(如果α是浮动),我们使用cv :: saturate_cast来确保值是有效的。
  • 最后,我们创建窗口并以通常的方式显示图像。

 

    imshow("Original Image", image);
    imshow("New Image", new_image);
    waitKey();

注意

我们可以简单地使用此命令,而不是使用for循环来访问每个像素:

image.convertTo(new_image,-1,alpha,beta);

其中cv :: Mat :: convertTo将有效地执行* new_image = a * image + beta *。但是,我们想向您展示如何访问每个像素。在任何情况下,两种方法都给出相同的结果,但convertTo更优化,工作速度更快。


结果

  • 运行我们的代码并使用α=2.2和β= 50

    $ ./BasicLinearTransforms lena.jpg

    基本线性变换

    -------------------------

    *输入alpha值[1.0-3.0]:2.2

    *输入beta值[0-100]:50

  • 我们得到这个:

    Basic_Linear_Transform_Tutorial_Result_big.jpg

例子

在本段中,我们将通过调整图像的亮度和对比度来实践我们所学到的校正曝光不足图像的方法。我们还将看到另一种技术来校正称为伽马校正图像亮度。

亮度和对比度调整

增加(/减少)β将为每个像素添加(/减去)一个常量值。像素值在[0; 255]范围将饱和(即,高于(/小于)255(/ 0)的像素值将被截止到255(/ 0))。

Basic_Linear_Transform_Tutorial_hist_beta.png

原始图像的直方图,当Gimp中亮度= 80时为深灰色

直方图针对每个颜色级别表示具有该颜色级别的像素的数量。暗图像将具有许多具有低颜色值的像素,因此直方图将在其左侧部分呈现峰值。当添加恒定偏置时,直方图向右移动,因为我们已经为所有像素添加了恒定偏置。

在一个参数将修改级别的传播方式。如果α<1,颜色等级将被压缩,结果将是对比度较低的图像。

Basic_Linear_Transform_Tutorial_hist_alpha.png

原始图像的直方图,当在Gimp中对比度<0时为深灰色

请注意,这些直方图是使用Gimp软件中的亮度 - 对比度工具获得的。亮度工具应与β相同偏差参数但对比工具似乎与α不同 输出范围似乎以Gimp为中心的增益(正如您在前面的直方图中所注意到的那样)。

β可能会发生偏差会改善亮度,但同时图像会出现轻微的面纱,因为对比度会降低。该α 增益可以用来减少这种效果,但由于饱和,我们将失去原始明亮区域的一些细节。


伽玛校正

通过在输入值和映射的输出值之间使用非线性变换Gamma校正可用于校正图像的亮度:

 

 

由于此关系是非线性的,因此对于所有像素,效果将不相同,并且将取决于它们的原始值。

Basic_Linear_Transform_Tutorial_gamma.png

绘制不同的伽玛值,当γ< 1中,原始的暗区将是明亮,直方图将被向右移位,而这将是与相对的γ> 1。

纠正曝光不足的图像

以下图像已更正为:α=1.3和β= 40。

 

整体亮度已得到改善,但您可以注意到,经过对比度调整之后,暗部细节得到了明显改善,与此同时,高亮度部分的云已经过饱和,使得天空颜色失真。

以下图像已用:γ校正= 0.4。

 

     伽马校正应该倾向于增加图像的动态对比度,因为映射是非线性的并且不存在如先前方法中那样的数值饱和。

Basic_Linear_Transform_Tutorial_histogram_compare.png

                             左:alpha,beta校正后的直方图;  中:原始图像的直方图; 右:伽马校正后的直方图

上图比较了三幅图像的直方图(三个直方图之间的y范围不同)。您可以注意到,大多数像素值位于原始图像直方图的下半部分。在α之后,b校正,由于饱和以及右移,我们可以观察到255处的大峰值。在伽马校正之后,直方图向右移动但是暗区域中的像素比亮区域中的像素更加偏移(参见伽马曲线)。


伽马变换代码段:

C++

 Mat lookUpTable(1, 256, CV_8U);
    uchar* p = lookUpTable.ptr();
    for( int i = 0; i < 256; ++i)
        p[i] = saturate_cast<uchar>(pow(i / 255.0, gamma_) * 255.0);
    Mat res = img.clone();
    LUT(img, lookUpTable, res);

完整代码:

#include <iostream>
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"

// we're NOT "using namespace std;" here, to avoid collisions between the beta variable and std::beta in c++17
using std::cout;
using std::endl;
using namespace cv;

namespace
{
/** Global Variables */
int alpha = 100;
int beta = 100;
int gamma_cor = 100;
Mat img_original, img_corrected, img_gamma_corrected;

void basicLinearTransform(const Mat &img, const double alpha_, const int beta_)
{
    Mat res;
    img.convertTo(res, -1, alpha_, beta_);

    hconcat(img, res, img_corrected);
    imshow("Brightness and contrast adjustments", img_corrected);
}

void gammaCorrection(const Mat &img, const double gamma_)
{
    CV_Assert(gamma_ >= 0);
    //! [changing-contrast-brightness-gamma-correction]
    Mat lookUpTable(1, 256, CV_8U);
    uchar* p = lookUpTable.ptr();
    for( int i = 0; i < 256; ++i)
        p[i] = saturate_cast<uchar>(pow(i / 255.0, gamma_) * 255.0);

    Mat res = img.clone();
    LUT(img, lookUpTable, res);
    //! [changing-contrast-brightness-gamma-correction]

    hconcat(img, res, img_gamma_corrected);
    imshow("Gamma correction", img_gamma_corrected);
}

void on_linear_transform_alpha_trackbar(int, void *)
{
    double alpha_value = alpha / 100.0;
    int beta_value = beta - 100;
    basicLinearTransform(img_original, alpha_value, beta_value);
}

void on_linear_transform_beta_trackbar(int, void *)
{
    double alpha_value = alpha / 100.0;
    int beta_value = beta - 100;
    basicLinearTransform(img_original, alpha_value, beta_value);
}

void on_gamma_correction_trackbar(int, void *)
{
    double gamma_value = gamma_cor / 100.0;
    gammaCorrection(img_original, gamma_value);
}
}

int main( int argc, char** argv )
{
    CommandLineParser parser( argc, argv, "{@input | ../data/lena.jpg | input image}" );
    img_original = imread( parser.get<String>( "@input" ) );
    if( img_original.empty() )
    {
      cout << "Could not open or find the image!\n" << endl;
      cout << "Usage: " << argv[0] << " <Input image>" << endl;
      return -1;
    }

    img_corrected = Mat(img_original.rows, img_original.cols*2, img_original.type());
    img_gamma_corrected = Mat(img_original.rows, img_original.cols*2, img_original.type());

    hconcat(img_original, img_original, img_corrected);
    hconcat(img_original, img_original, img_gamma_corrected);

    namedWindow("Brightness and contrast adjustments");
    namedWindow("Gamma correction");

    createTrackbar("Alpha gain (contrast)", "Brightness and contrast adjustments", &alpha, 500, on_linear_transform_alpha_trackbar);
    createTrackbar("Beta bias (brightness)", "Brightness and contrast adjustments", &beta, 200, on_linear_transform_beta_trackbar);
    createTrackbar("Gamma correction", "Gamma correction", &gamma_cor, 200, on_gamma_correction_trackbar);

    on_linear_transform_alpha_trackbar(0, 0);
    on_gamma_correction_trackbar(0, 0);

    waitKey();

    imwrite("linear_transform_correction.png", img_corrected);
    imwrite("gamma_correction.png", img_gamma_corrected);

    return 0;
}


 


Python

lookUpTable = np.empty((1,256), np.uint8)
    for i in range(256):
        lookUpTable[0,i] = np.clip(pow(i / 255.0, gamma) * 255.0, 0, 255)
    res = cv.LUT(img_original, lookUpTable)

 

完整代码:

from __future__ import print_function
from __future__ import division
import cv2 as cv
import numpy as np
import argparse

alpha = 1.0
alpha_max = 500
beta = 0
beta_max = 200
gamma = 1.0
gamma_max = 200

def basicLinearTransform():
    res = cv.convertScaleAbs(img_original, alpha=alpha, beta=beta)
    img_corrected = cv.hconcat([img_original, res])
    cv.imshow("Brightness and contrast adjustments", img_corrected)

def gammaCorrection():
    ## [changing-contrast-brightness-gamma-correction]
    lookUpTable = np.empty((1,256), np.uint8)
    for i in range(256):
        lookUpTable[0,i] = np.clip(pow(i / 255.0, gamma) * 255.0, 0, 255)

    res = cv.LUT(img_original, lookUpTable)
    ## [changing-contrast-brightness-gamma-correction]

    img_gamma_corrected = cv.hconcat([img_original, res]);
    cv.imshow("Gamma correction", img_gamma_corrected);

def on_linear_transform_alpha_trackbar(val):
    global alpha
    alpha = val / 100
    basicLinearTransform()

def on_linear_transform_beta_trackbar(val):
    global beta
    beta = val - 100
    basicLinearTransform()

def on_gamma_correction_trackbar(val):
    global gamma
    gamma = val / 100
    gammaCorrection()

parser = argparse.ArgumentParser(description='Code for Changing the contrast and brightness of an image! tutorial.')
parser.add_argument('--input', help='Path to input image.', default='lena.jpg')
args = parser.parse_args()

img_original = cv.imread(cv.samples.findFile(args.input))
if img_original is None:
    print('Could not open or find the image: ', args.input)
    exit(0)

img_corrected = np.empty((img_original.shape[0], img_original.shape[1]*2, img_original.shape[2]), img_original.dtype)
img_gamma_corrected = np.empty((img_original.shape[0], img_original.shape[1]*2, img_original.shape[2]), img_original.dtype)

img_corrected = cv.hconcat([img_original, img_original])
img_gamma_corrected = cv.hconcat([img_original, img_original])

cv.namedWindow('Brightness and contrast adjustments')
cv.namedWindow('Gamma correction')

alpha_init = int(alpha *100)
cv.createTrackbar('Alpha gain (contrast)', 'Brightness and contrast adjustments', alpha_init, alpha_max, on_linear_transform_alpha_trackbar)
beta_init = beta + 100
cv.createTrackbar('Beta bias (brightness)', 'Brightness and contrast adjustments', beta_init, beta_max, on_linear_transform_beta_trackbar)
gamma_init = int(gamma * 100)
cv.createTrackbar('Gamma correction', 'Gamma correction', gamma_init, gamma_max, on_gamma_correction_trackbar)

on_linear_transform_alpha_trackbar(alpha_init)
on_gamma_correction_trackbar(gamma_init)

cv.waitKey()

查找线性表用于提高计算性能,因为只需要计算一次256个值。

没有更多推荐了,返回首页