1. 程式人生 > >影象gamma校正

影象gamma校正

                                   影象gamma校正

1.為什麼要進行Gamma校正

 (Gamma Correction,伽瑪校正):所謂伽瑪校正就是對影象的伽瑪曲線進行編輯,以對影象進行非線性色調編輯的方法,檢出影象訊號中的深色部分和淺色部分,並使兩者比例增大,從而提高影象對比度效果。計算機繪圖領域慣以此螢幕輸出電壓與對應亮度的轉換關係曲線,稱為伽瑪曲線(Gamma Curve)。

我們使用顯示器去呈現,檢查和調節照片或視訊。它們是一個特殊的非線性系統可以顯示數字影象資料。由於非線性,輸入端的數字亮度值和顯示端不是相應的線性關係。更進一步的考慮,這些系統產生連續的曲線,也就是其特徵曲線。這個梯度曲線的線性部分被定義為伽馬值。

 

範例

 

  假設一張圖片的訊號和顯示器是線性關係(左上方的圖片),這會是RAW影象。顯示器描繪出輸入的資料,在這個例子中輸入的訊號是被顯示器的特徵曲線所控。考慮的更進一步,對角線的曲線轉換為一條曲線(右上方影象)。輸入的亮度值也就和監視器上的輸出成非線性關係。導致的結果就是在監視器上影象看起來很黑暗。

  要確保輸出端的線性這樣才能確保一個真實的還原。在非線性顯示的操作情況下,對系統的失真進行補償是必要的。可以通過輸入線性訊號的伽馬校正做到。這種調整就導致了輸入訊號的非線性行為(左下圖片),這和監視器上是相反的(中下圖片)。這兩個一起計算,監視器輸出端就仍能保持線性(右下圖片)。

 

 

  許多相機都可以產生RAW格式的圖片。產生的資料可以用不同的影象編輯程式的伽馬校正去開發並進行相應的調整。相機還使用非線性的伽馬校正內部產生的JPG格式的圖片。除了在非線性顯示器上顯示的真實亮度,輸入訊號的校正提供了一個額外的好處。雖然原始資料的位深為12到14位,JPG只有8位。8位的是使用256亮度等級代替了4096(12位)的亮度等級水平。

  在不使用伽馬校正去產生JPG影象意味著亮度等級水平在進行8位的轉換時會有一定的損失,就會產生“臺階”效果。通過調校亮度的分佈就是進行伽馬校正意味著那些效果會得到補償修復。gamma校正的意義就在於將人眼感受到的灰度值, 轉換到自然界真正的灰度值(因為人不是測量器, 人只能感受和比較)

2.色彩校正中的 gamma 值是什麼

韋伯定律:即感覺的差別閾限隨原來刺激量的變化而變化,而且表現為一定的規律性,用公式來表示,就是△Φ/Φ=C,其中Φ為原刺激量,△Φ為此時的差別閾限,C為常數,又稱為韋伯率。

這個0-1區間的曲線,就是所謂的Gamma曲線。我們若定義黑是0,白是1,那麼在0-1區間,我們是可以用一個冪函式來描述客觀自然數值和主觀心理感知的對應關係的(等比數列通項公式就是冪函式):

 

 

  • Gamma=2.2怎麼來的?
    將人眼感受到的灰階轉換成自然界真實的灰階
    比如 (0.5)^2.2=0.2

 3.gamma校正原理

假設影象中有一個畫素,值是 200 ,那麼對這個畫素進行校正必須執行如下步驟: 
  1. 歸一化 :將畫素值轉換為  0 ~ 1  之間的實數。 演算法如下 : ( i + 0. 5)/256  這裡包含 1 個除法和 1 個加法操作。對於畫素  A  而言  , 其對應的歸一化值為  0. 783203 。 

  2. 預補償 :根據公式  , 求出畫素歸一化後的 資料以  1 /gamma  為指數的對應值。這一步包含一個 求指數運算。若  gamma  值為  2. 2 ,  則  1 /gamma  為  0. 454545 , 對歸一化後的  A  值進行預補償的結果就 是  0. 783203 ^0. 454545 = 0. 894872 。 

  3. 反歸一化 :將經過預補償的實數值反變換為  0  ~  255  之間的整數值。具體演算法為 : f*256 - 0. 5  此步驟包含一個乘法和一個減法運算。續前 例  , 將  A  的預補償結果  0. 894872  代入上式  , 得到  A  預補償後對應的畫素值為  228 , 這個  228  就是最後送 入顯示器的資料。

  
  如上所述如果直接按公式程式設計的話,假設影象的解析度為 800*600 ,對它進行 gamma 校正,需要執行 48 萬個浮點數乘法、除法和指數運算。效率太低,根本達不到實時的效果。 
  針對上述情況,提出了一種快速演算法,如果能夠確知影象的畫素取值範圍  , 例如  , 0 ~ 255 之間的整數  , 則影象中任何一個畫素值只能 是  0  到  255  這  256  個整數中的某一個 ; 在  gamma 值 已知的情況下  ,0 ~ 255  之間的任一整數  , 經過“歸一 化、預補償、反歸一化”操作後 , 所對應的結果是唯一的  , 並且也落在  0 ~ 255  這個範圍內。
  如前例  , 已知  gamma  值為  2. 2 , 畫素  A  的原始值是  200 , 就可求得 經  gamma  校正後  A  對應的預補償值為  228 。基於上述原理  , 我們只需為  0 ~ 255  之間的每個整數執行一次預補償操作  , 將其對應的預補償值存入一個預先建立的  gamma  校正查詢表 (LUT:Look Up Table) , 就可以使用該表對任何畫素值在  0 ~ 255  之 間的影象進行  gamma  校正。
 

4.Gamma校正實現

--c++程式碼

#include <iostream>  
#include <opencv2\core\core.hpp>  
#include <opencv2\highgui\highgui.hpp>  
#include <opencv2\imgproc\imgproc.hpp>  
#include<cmath>
using namespace cv;
 
Mat gammaTransform(Mat &srcImage, float kFactor)
{
	
	unsigned char LUT[256];
	for (int i = 0; i < 256; i++)
	{
		float f = (i + 0.5f) / 255;
		f = (float)(pow(f, kFactor));
		LUT[i] = saturate_cast<uchar>(f*255.0f - 0.5f);
	}
	Mat resultImage = srcImage.clone();
	
	if (srcImage.channels() == 1)
	{
		
		MatIterator_<uchar> iterator = resultImage.begin<uchar>();
		MatIterator_<uchar> iteratorEnd = resultImage.end<uchar>();
		for (; iterator != iteratorEnd; iterator++)
		{
			*iterator = LUT[(*iterator)];
		}
	}
	else
	{
		
		
		MatIterator_<Vec3b> iterator = resultImage.begin<Vec3b>();
		MatIterator_<Vec3b> iteratorEnd = resultImage.end<Vec3b>();
		for (; iterator != iteratorEnd; iterator++)
		{
			(*iterator)[0] = LUT[((*iterator)[0])];//b
			(*iterator)[1] = LUT[((*iterator)[1])];//g
			(*iterator)[2] = LUT[((*iterator)[2])];//r
		}
	}
	return resultImage;
}
int main()
{
	Mat srcImage = imread("lakeWater.jpg");
	if (!srcImage.data)
	{
		printf("could not load image...\n");
		return -1;
	}
	//取兩種不同的gamma值
	float gamma1 = 3.33f;
	float gamma2 = 0.33f;
	float kFactor1 = 1 / gamma1;
	float kFactor2 = 1 / gamma2;
	Mat result1 = gammaTransform(srcImage, kFactor1);
	Mat result2 = gammaTransform(srcImage, kFactor2);
	imshow("srcImage", srcImage);
	imshow("res1", result1);
	imshow("res2", result2);
	waitKey(0);
	return 0;
}

--python程式碼

#-*-coding: UTF-8-*-
############gamma增強
import cv2
import numpy as np

img = cv2.imread('lenna.bmp')

img1 = np.power(img/float(np.max(img)), 1/2.2)
img2 = np.power(img/float(np.max(img)), 2.2)

cv2.imshow('src',img)
cv2.imshow('gamma',img1)
cv2.imshow('gamma_process',img2)

#由於後面需要進行型別轉換,轉換成uint8,所以需要對其乘以255處理
img2 = img2 * 255 
cv2.imwrite('gamma_process.jpg',img2)

cv2.waitKey(0)
cv2.destroyAllWindows()

5.實驗結果

從左到右分別為原圖,gamma=2.2,gamma=1/2.2的圖片。

簡單來說,也就是調整一個引數,對圖片中的每個畫素根據這個gamma引數做出調整,而這個調整

的原理就是原畫素的灰度值的gamma次方,所以就可以解釋圖片中的三個結果了,當gamma小於1的時

候,很明顯a^gamma > a(a<1, a是通過運算得到a = gray / 255.0),這樣就會讓整體的亮度提升,並且

也可以預見到,當畫素的灰度值越低(暗),運算的結果就越明顯,而比較亮的部分(灰度值比較高)

的整體的提升就比較少。同樣的對於gamma大於1的時候就就會有相反的效果。
 

【轉載】:https://blog.csdn.net/linqianbi/article/details/78617615

                  https://blog.csdn.net/qq_35044509/article/details/78713289

                  https://www.cnblogs.com/qiqibaby/p/5325193.html

                  https://www.jianshu.com/p/52fc2192ae7b

                  https://www.zhihu.com/question/27467127

                  http://www.52rd.com/s_txt/2017_5/txt95785.htm

                  http://www.cnblogs.com/denny402/p/5122328.html(影象型別轉換)