目標(biāo)
在本教程中,將學(xué)習(xí)如何:
訪問(wèn)像素值
用零初始化矩陣
了解cv :: saturate_cast是什么
獲取關(guān)于像素變換的一些很酷的信息
在實(shí)際的例子中提高圖像的亮度
理論
下面的解釋屬于Richard Szeliski 的“ 《計(jì)算機(jī)視覺(jué):算法與應(yīng)用》 ”一書(shū)
圖像處理
一般的圖像處理算子是采用一個(gè)或多個(gè)輸入圖像并產(chǎn)生輸出圖像的函數(shù)。
圖像變換可以看作:
點(diǎn)運(yùn)算(像素變換)
鄰域(區(qū)域)運(yùn)算
像素變換
在這種圖像處理變換中,每個(gè)輸出像素的值只取決于相應(yīng)的輸入像素值(可能是一些全局采集的信息或參數(shù))。
這些操作的示例包括亮度和對(duì)比度調(diào)整以及顏色校正和變換。
亮度和對(duì)比度調(diào)整
- 兩個(gè)常用的點(diǎn)處理過(guò)程是乘法和加法:

- 參數(shù)α > 0和β通常被稱(chēng)為增益和偏置參數(shù); 有時(shí)這些參數(shù)據(jù)分別控制對(duì)比度和亮度。
- 你可以想到f(x )作為源圖像像素和g(x )作為輸出圖像像素。然后,我們可以更方便地將表達(dá)式寫(xiě)成:

i 和 j 表示像素位于第i行和第j列。
代碼實(shí)現(xiàn)
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
double alpha = 1.0; /*< Simple contrast control */
int beta = 0; /*< Simple brightness control */
String imageName("lena.bmp"); // by default
if (argc > 1)
{
imageName = argv[1];
}
Mat image = imread(imageName);
Mat new_image = Mat::zeros(image.size(), image.type());
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;
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>(alpha*(image.at<Vec3b>(y, x)[c]) + beta);
}
}
}
namedWindow("Original Image", WINDOW_AUTOSIZE);
namedWindow("New Image", WINDOW_AUTOSIZE);
imshow("Original Image", image);
imshow("New Image", new_image);
waitKey();
return 0;
}
解釋
- 要訪問(wèn)圖像中的每個(gè)像素,使用這種語(yǔ)法:image.at <Vec3b>(y,x)[c]其中y是行,x是列,c是R,G或B(0,1或2)。
- 由于操作α·p(i,j)+β可以給出值超出范圍或不是整數(shù)(如果α是float),我們使用cv :: saturate_cast來(lái)確保這些值是有效的。
- 如果不使用for循環(huán)訪問(wèn)每個(gè)像素,我們可以簡(jiǎn)單地使用這個(gè)命令:
image.convertTo(new_image,-1,alpha,beta);
其中cv :: Mat :: convertTo將有效地執(zhí)行* new_image = a * image + beta *。
但是,我想告訴你如何訪問(wèn)每個(gè)像素。在任何情況下,兩種方法都給出相同的結(jié)果,但是convertTo是更優(yōu)化的,并且工作得更快。
結(jié)果

聯(lián)系實(shí)際
在本段中,將通過(guò)調(diào)整圖像的亮度和對(duì)比度來(lái)實(shí)踐我們所學(xué)到的來(lái)校正曝光不足的圖像。我們還將看到另一種技術(shù)來(lái)校正稱(chēng)為伽瑪校正圖像亮度。
亮度和對(duì)比度調(diào)整
增加(/減少)β值將為每個(gè)像素添加(/減少)常量值。像素值超出[0; 255]范圍將飽和(即像素值高于0-255)將被鉗位到0-255中。

直方圖表示每個(gè)顏色級(jí)別具有該顏色級(jí)別的像素?cái)?shù)。黑暗的圖像將具有許多具有低顏色值的像素,因此直方圖將在其左部分呈現(xiàn)峰值。當(dāng)添加恒定偏差時(shí),直方圖向右移動(dòng),因?yàn)槲覀兿蛩邢袼靥砑恿艘粋€(gè)恒定的偏置。
該α參數(shù)將修改水平如何傳播。如果α < 1,顏色級(jí)別將被壓縮,結(jié)果將是對(duì)比度較小的圖像。

請(qǐng)注意,這些直方圖是使用Gimp軟件中的Brightness-Contrast工具獲得的。亮度工具應(yīng)與β相同偏差參數(shù),但對(duì)比度工具似乎不同于α 輸出范圍似乎以Gimp為中心的增益(可以注意到先前的直方圖)。
可以發(fā)生的是修改β偏差將提高亮度,但是同時(shí),隨著對(duì)比度的降低,圖像會(huì)出現(xiàn)輕微的面紗。該α 增益可以用來(lái)減少這種影響,但由于飽和度,我們將在原來(lái)的明亮區(qū)域中丟失一些細(xì)節(jié)。
伽瑪校正
伽馬校正可用于通過(guò)使用輸入值和映射的輸出值之間的非線性變換來(lái)校正圖像的亮度:

由于該關(guān)系是非線性的,因此對(duì)于所有像素的效果將不同,并且將取決于它們的原始值。

當(dāng)γ< 1,原始的黑暗區(qū)域會(huì)更亮,直方圖將向右移動(dòng),而與γ相反> 1。
糾正曝光不足的圖像
以下圖像已更正:α = 1.3,β= 40

總體亮度得到改善,但您可以注意到,由于所使用的實(shí)現(xiàn)的數(shù)值飽和度(在攝影中
突出顯示剪切),云層現(xiàn)在已經(jīng)飽和了。
以下圖像已被更正:γ= 0.4。

伽馬校正應(yīng)傾向于增加較少的飽和效應(yīng),因?yàn)橛成涫欠蔷€性的,并且沒(méi)有像以前的方法那樣的數(shù)值飽和。

上圖比較了三個(gè)圖像的直方圖(三個(gè)直方圖之間的y范圍不一樣)??梢宰⒁獾?,大多數(shù)像素值都在原始圖像的直方圖的較低部分。經(jīng)過(guò)α,β校正,由于飽和度以及右側(cè)偏移,我們可以在255觀察到一個(gè)高峰。在伽馬校正之后,直方圖向右移動(dòng),但暗區(qū)域中的像素比明亮區(qū)域中的像素更大偏移(參見(jiàn)伽馬曲線圖)。
伽馬校正代碼:
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);
查詢(xún)表用于提高計(jì)算性能,因?yàn)橹恍枰?jì)算256個(gè)值。