圖像亮度/對比度的調整

要調整正圖片的亮度和對比度, 首先要知道亮度和對比度的定義:

“明度”(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;
}
//---------------------------------------------------------------------------

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章