`
阿尔萨斯
  • 浏览: 4168160 次
社区版块
存档分类
最新评论

图像亮度/对比度的调整

 
阅读更多

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

“明度”(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 色彩空间色相饱和度和明度)中的颜色坐标,它的值是这个颜色的RGB三者中的极大值。

对比度,具体的概念解释可以参考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;
}
//---------------------------------------------------------------------------

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics