這篇文章主要闡述了如何使用Qt在像素級(jí)別上對(duì)圖像進(jìn)行操作,并實(shí)現(xiàn)了一些圖像效果,這些效果主要有:灰度,模糊,銳化,添加相框,金屬質(zhì)感,改變圖像飽和度,亮度還有白平衡。
scanLine 返回某一行數(shù)據(jù),轉(zhuǎn)換為QRgb指針可進(jìn)行直接有效的像素存取操作。
介紹
文章中,我們將討論在Qt中修改圖像的一些技術(shù)和算法,在這之前,你必須知道在Qt中操作圖像的一些方法。
.在Qt中有兩種表示圖像的類(lèi),Qt:QImage和QPixmap,還有QBitmap來(lái)存儲(chǔ)單色的圖像,比如遮罩,QPicture在存儲(chǔ)QPainter的一些操作指令。
? 當(dāng)我們想要在屏幕上繪制圖像的時(shí)候,最快的方法就是使用QPixmap,不過(guò)壞處就是無(wú)法訪問(wèn)和修改像素;
QImage在IO操作中有很快的速度,并且給出了訪問(wèn)像素的接口,這篇文章中我們就使用這個(gè)類(lèi)。
.如果你是要處理大的圖片,比如攝像頭拍攝的照片,這種情況最好是將原圖縮小之后作為預(yù)覽圖顯示在屏幕上,除非我們?cè)试S用戶(hù)縮放圖像。有兩種加載并縮放圖像的方法。
。將圖像加載進(jìn)QImage或者QPixmap,然后調(diào)整大小:
?
[cpp]?view plain?copy
QImage?image("sample.png");??image?=?image.scaled(width,?height);??
使用QImageReader來(lái)讀取和縮放圖片,然后再加載進(jìn)QImage中。QImageReader無(wú)法將一張圖片加載進(jìn)QPixmap中去,但是可以使用靜態(tài)方法 QPixmap::fromImage(QImage img)從QImage中加載進(jìn)QPixmap。這個(gè)方法非常快,并且不需要加載大圖的內(nèi)存開(kāi)銷(xiāo):
?
?
[cpp]?view plain?copy
QImageReader?imgReader("sample.png");??imgReader.setScaledSize(QSize(width,?height));??QImage?*?image;??imgReader.read(image);??
。每一張圖片都是由像素點(diǎn)組成,每一個(gè)像素都有三個(gè)通道:紅,綠,藍(lán),還有一個(gè)alpha通道來(lái)保存透明度(JPEG格式的圖片不支持透明)。每個(gè)通道的值是0-255,三個(gè)通道都是0的話,表示黑色,都是255表示白色。這篇文章中我們用RGB來(lái)表示一種顏色,也就是三個(gè)通道的值。
?
?
。相比于一個(gè)像素一個(gè)像素地讀取,uchar * ?QImage::scanLine(int i)可以一次讀取整行的像素值,會(huì)更加高效,下面的例子就是按行讀取的例子,也是我們將要講的第一個(gè)例子,轉(zhuǎn)灰度圖。
?
[cpp]?view plain?copy
QImage?*?MainWindow::greyScale(QImage?*?origin){??????QImage?*?newImage?=?new?QImage(origin->width(),?origin->height(),?QImage::Format_ARGB32);?????????QRgb?*?line;?????????for(int?y?=?0;?y<newImage->height();?y++){??????????QRgb?*?line?=?(QRgb?*)origin->scanLine(y);?????????????for(int?x?=?0;?x<newImage->width();?x++){??????????????int?average?=?(qRed(line[x])?+?qGreen(line[x])?+?qRed(line[x]))/3;??????????????newImage->setPixel(x,y,?qRgb(average,?average,?average));??????????}?????????}?????????return?newImage;??}??
?
?
灰度
我們要學(xué)習(xí)的第一個(gè)技術(shù)就是將彩色圖轉(zhuǎn)換成灰度圖,我們首先要明白的一點(diǎn)就是,其實(shí)標(biāo)準(zhǔn)的灰度圖就是每個(gè)像素點(diǎn)的三個(gè)通道的值一樣或者近似,我們的策略就是將每個(gè)像素的每個(gè)通道的值都調(diào)成一樣,取R,G,B值為三者的算數(shù)平均數(shù)就可以了,比如原色是RGB(169,204,69), 那么最終的RGB就是(169+204+69)/3 = 147.
?
?
[cpp]?view plain?copy
QImage?*?MainWindow::greyScale(QImage?*?origin){??????QImage?*?newImage?=?new?QImage(origin->width(),?origin->height(),?QImage::Format_ARGB32);?????????QColor?oldColor;?????????for(int?x?=?0;?x<newImage->width();?x++){??????????for(int?y?=?0;?y<newImage->height();?y++){??????????????oldColor?=?QColor(origin->pixel(x,y));??????????????int?average?=?(oldColor.red()+oldColor.green()+oldColor.blue())/3;??????????????newImage->setPixel(x,y,qRgb(average,average,average));??????????}??????}?????????return?newImage;??}??
?
?
原始圖
?
灰度圖
?
亮度調(diào)節(jié)
就如之前我們提到的,白色用RGB(255,255,255)表示,黑色用RGB(0,0,0)表示,所以如果我們需要提高圖片的亮度(顏色接近白色),我們需要同時(shí)增加三個(gè)通道的數(shù)值,反之就是變暗。
?
在這里我們添加了一個(gè)函數(shù)參數(shù)來(lái)決定要提高多少亮度,如果參數(shù)是負(fù)數(shù)的話就是減少亮度了。在每個(gè)通道都加上delta值之后,需要做的就是讓它不要低于0且不要高于255.
原圖
加亮圖 Delta = 30
?
?
暖色調(diào)
當(dāng)我們說(shuō)一一幅暖色調(diào)的圖片的時(shí)候通常是因?yàn)檫@張圖色調(diào)偏黃。我們沒(méi)有黃色的通道,但是紅色和綠色混合起來(lái)就是黃色,所以我們?cè)黾舆@兩個(gè)通道值,然后藍(lán)色通道值不變就好了。
?
我們使用一個(gè)delta參數(shù)來(lái)決定增加紅色和綠色通道的值。一張暖色的圖片能夠給人一種復(fù)古效果,如果是有沙子的圖片,圖片將會(huì)更加生動(dòng)。
?
?
[cpp]?view plain?copy
QImage?*?MainWindow::warm(int?delta,?QImage?*?origin){??????QImage?*newImage?=?new?QImage(origin->width(),?origin->height(),?QImage::Format_ARGB32);?????????QColor?oldColor;??????int?r,g,b;?????????for(int?x=0;?x<newImage->width();?x++){??????????for(int?y=0;?y<newImage->height();?y++){??????????????oldColor?=?QColor(origin->pixel(x,y));?????????????????r?=?oldColor.red()?+?delta;??????????????g?=?oldColor.green()?+?delta;??????????????b?=?oldColor.blue();?????????????????//we?check?if?the?new?values?are?between?0?and?255??????????????r?=?qBound(0,?r,?255);??????????????g?=?qBound(0,?g,?255);?????????????????newImage->setPixel(x,y,?qRgb(r,g,b));??????????}??????}?????????return?newImage;??}??
?
?
原圖
暖色圖 Delta = 30
?
冷色調(diào)
如果說(shuō)暖色調(diào)的圖片偏黃色,那么冷色調(diào)的圖片應(yīng)該就是偏藍(lán)色了。在這個(gè)方法里面我們只增加藍(lán)色通道的值,紅色和綠色的值不變。
冷色調(diào)的圖片可以聯(lián)想到未來(lái),死亡或者,冷。
?
?
[cpp]?view plain?copy
QImage?*?MainWindow::cool(int?delta,?QImage?*?origin){??????QImage?*newImage?=?new?QImage(origin->width(),?origin->height(),?QImage::Format_ARGB32);?????????QColor?oldColor;??????int?r,g,b;?????????for(int?x=0;?x<newImage->width();?x++){??????????for(int?y=0;?y<newImage->height();?y++){??????????????oldColor?=?QColor(origin->pixel(x,y));?????????????????r?=?oldColor.red();??????????????g?=?oldColor.green();??????????????b?=?oldColor.blue()+delta;?????????????????//we?check?if?the?new?value?is?between?0?and?255??????????????b?=?qBound(0,?b,?255);?????????????????newImage->setPixel(x,y,?qRgb(r,g,b));??????????}??????}?????????return?newImage;??}??
?
?
原圖
冷色調(diào)圖 Delta = 30
?
飽和度
我們已經(jīng)說(shuō)了,顏色由三個(gè)通道組成:紅,綠,藍(lán),盡管如此,RGB不是唯一一個(gè)表示色彩的方式,在這里,我們使用HSL格式表示色彩 -?hue(色相), saturation(飽和度), lightness(明度)。
飽和的圖像擁有更加生動(dòng)的顏色,通常會(huì)比較好看,但是有一點(diǎn)要記住:不要濫用飽和度,因?yàn)楹苋菀壮霈F(xiàn)失真。
?
?
[cpp]?view plain?copy
QImage?*?MainWindow::saturation(int?delta,?QImage?*?origin){??????QImage?*?newImage?=?new?QImage(origin->width(),?origin->height(),?QImage::Format_ARGB32);?????????QColor?oldColor;??????QColor?newColor;??????int?h,s,l;?????????for(int?x=0;?x<newImage->width();?x++){??????????for(int?y=0;?y<newImage->height();?y++){??????????????oldColor?=?QColor(origin->pixel(x,y));?????????????????newColor?=?oldColor.toHsl();??????????????h?=?newColor.hue();??????????????s?=?newColor.saturation()+delta;??????????????l?=?newColor.lightness();?????????????????//we?check?if?the?new?value?is?between?0?and?255??????????????s?=?qBound(0,?s,?255);?????????????????newColor.setHsl(h,?s,?l);?????????????????newImage->setPixel(x,?y,?qRgb(newColor.red(),?newColor.green(),?newColor.blue()));??????????}??????}?????????return?newImage;??}??
?
?
?
原圖
?
?
飽和的圖片 Delta=30
?
模糊
這個(gè)效果相對(duì)于之前的有一點(diǎn)點(diǎn)復(fù)雜。我們會(huì)用到一個(gè)卷積濾波器,根據(jù)當(dāng)前像素的顏色和相鄰像素的顏色來(lái)獲得一個(gè)新的顏色。同時(shí)還有一個(gè)kernel的矩陣來(lái)決定計(jì)算中相鄰像素的影響程度。
?
原像素會(huì)在矩陣的中心,因此我們會(huì)使用基數(shù)行的行和列。我們不會(huì)修改邊緣的像素點(diǎn),因?yàn)槟切c(diǎn)沒(méi)有我們需要的相鄰像素點(diǎn),雖然我們也可以只使用有效的像素點(diǎn)。
?
舉了例子,讓我們來(lái)看看如何計(jì)算像素的RGB值。下面的三個(gè)舉證代表著當(dāng)前像素和鄰接像素的RGB值,最中間的是當(dāng)前像素。
R = 20 102 99
150 200 77?
170 210 105
G = 22 33 40
17 21 33
8 15 24
B = 88 70 55
90 72 59
85 69 50
?
Kenel =? 0 2 0
2?5 2
0 2 0
?
使用濾波器進(jìn)行計(jì)算:
r = ( (102*2) + (150*2) + (200*5) + (77*2) + (210*2) ) / (2+2+5+2+2) = 159
g = ( (33*2) + ( 17*2) + (21*5) + (33*2) + (15*2) ) / (2+2+5+2+2) = 23
b = ( (70*2) + (90*2) + (72*5) + (59*2) + (69*2) ) / (2+2+5+2+2) = 72
?
由原始的RGB(200, 21, 72)得到了RGB(159, 23, 72). ?發(fā)現(xiàn)最大的變化是紅色的通道,因?yàn)榧t色通道的值差距最大。
?
在修改肖像照片的時(shí)候通常會(huì)使用到模糊的技術(shù),它能后掩蓋住皮膚的瑕疵。
?
[cpp]?view plain?copy
QImage?*?MainWindow::blur(QImage?*?origin){??????QImage?*?newImage?=?new?QImage(*origin);?????????int?kernel?[5][5]=?{{0,0,1,0,0},??????????????????????????{0,1,3,1,0},??????????????????????????{1,3,7,3,1},??????????????????????????{0,1,3,1,0},??????????????????????????{0,0,1,0,0}};??????int?kernelSize?=?5;??????int?sumKernel?=?27;??????int?r,g,b;??????QColor?color;?????????for(int?x=kernelSize/2;?x<newImage->width()-(kernelSize/2);?x++){??????????for(int?y=kernelSize/2;?y<newImage->height()-(kernelSize/2);?y++){?????????????????r?=?0;??????????????g?=?0;??????????????b?=?0;?????????????????for(int?i?=?-kernelSize/2;?i<=?kernelSize/2;?i++){??????????????????for(int?j?=?-kernelSize/2;?j<=?kernelSize/2;?j++){??????????????????????color?=?QColor(origin->pixel(x+i,?y+j));??????????????????????r?+=?color.red()*kernel[kernelSize/2+i][kernelSize/2+j];??????????????????????g?+=?color.green()*kernel[kernelSize/2+i][kernelSize/2+j];??????????????????????b?+=?color.blue()*kernel[kernelSize/2+i][kernelSize/2+j];??????????????????}??????????????}?????????????????r?=?qBound(0,?r/sumKernel,?255);??????????????g?=?qBound(0,?g/sumKernel,?255);??????????????b?=?qBound(0,?b/sumKernel,?255);?????????????????newImage->setPixel(x,y,?qRgb(r,g,b));?????????????}??????}??????return?newImage;??}??
?
?
原圖
?
模糊圖
?
銳化
像模糊中一樣,銳化一張圖片也會(huì)使用一個(gè)卷積濾波器,但是kernel矩陣是不一樣的,相鄰像素對(duì)應(yīng)的值是負(fù)的。
銳化能夠處理模糊的照片,能夠提升細(xì)節(jié)。
?
[cpp]?view plain?copy
QImage?*?MainWindow::sharpen(QImage?*?origin){??????QImage?*?newImage?=?new?QImage(*?origin);?????????int?kernel?[3][3]=?{{0,-1,0},??????????????????????????{-1,5,-1},??????????????????????????{0,-1,0}};??????int?kernelSize?=?3;??????int?sumKernel?=?1;??????int?r,g,b;??????QColor?color;?????????for(int?x=kernelSize/2;?x<newImage->width()-(kernelSize/2);?x++){??????????for(int?y=kernelSize/2;?y<newImage->height()-(kernelSize/2);?y++){?????????????????r?=?0;??????????????g?=?0;??????????????b?=?0;?????????????????for(int?i?=?-kernelSize/2;?i<=?kernelSize/2;?i++){??????????????????for(int?j?=?-kernelSize/2;?j<=?kernelSize/2;?j++){??????????????????????color?=?QColor(origin->pixel(x+i,?y+j));??????????????????????r?+=?color.red()*kernel[kernelSize/2+i][kernelSize/2+j];??????????????????????g?+=?color.green()*kernel[kernelSize/2+i][kernelSize/2+j];??????????????????????b?+=?color.blue()*kernel[kernelSize/2+i][kernelSize/2+j];??????????????????}??????????????}?????????????????r?=?qBound(0,?r/sumKernel,?255);??????????????g?=?qBound(0,?g/sumKernel,?255);??????????????b?=?qBound(0,?b/sumKernel,?255);?????????????????newImage->setPixel(x,y,?qRgb(r,g,b));?????????????}??????}??????return?newImage;??}??
?
?
原圖
銳化圖
?
添加相框
繪制一個(gè)相框是非常見(jiàn)到那的,我們只需要把相框在原圖上面繪制就可以了。這里假設(shè)我們已經(jīng)有一個(gè)和圖片一樣大小的相框了,不一樣的話要resize到一樣大。
?
[cpp]?view plain?copy
QImage?*?MainWindow::drawFrame(QImage?*?origin){??????QImage?*?newImage?=?new?QImage(*?origin);??????QPainter?painter;?????????painter.begin(newImage);?????????painter.drawImage(0,0,?QImage(":images/frame.png"));?????????painter.end();?????????return?newImage;??}??
?
?
原圖
相框
添加相框之后
?
金屬效果
這個(gè)例子中我們會(huì)結(jié)合幾種技術(shù)來(lái)獲得一種效果。下面是處理的步驟:
1.調(diào)整圖像的亮度,獲得一個(gè)較暗的圖片。
2.將圖像轉(zhuǎn)成灰度。
3.將灰度圖繪制在金屬的紋理上,透明度50%。
?
[cpp]?view plain?copy
QImage?*?MainWindow::metal(QImage?*?origin){??????QImage?*?newImage?=?new?QImage(":images/metal.png");??????QImage?*?darkImage?=?brightness(-100,?origin);??????QImage?*?greyImage?=?greyScale(darkImage);??????QPainter?painter;?????????painter.begin(newImage);?????????painter.setOpacity(0.5);??????painter.drawImage(0,?0,?*?greyImage);?????????painter.end();?????????delete?greyImage;??????delete?darkImage;?????????return?newImage;??}??
?
?
?
?
原圖
?
金屬紋理
?
最終效果
?
模糊的邊框
最后再來(lái)學(xué)習(xí)一個(gè)融合的效果,這次我們想要做的是模糊圖片外延的部分,讓視線的焦點(diǎn)聚集在圖片的中間。
?
我們將會(huì)使用一張遮罩圖片,來(lái)決定需要模糊的部分,具體的操作步驟如下:
1.從原圖獲取一張完全模糊的圖片。
2.使用QPainter的一種融合模式,通過(guò)遮罩圖片截取出一個(gè)模糊的相框。點(diǎn)這里可以學(xué)習(xí)到更多的QPainter的融合模式。
3.在原圖上繪制出模糊的邊框。
?
[cpp]?view plain?copy
QImage?*?MainWindow::blurFrame(QImage?*?origin){??????QImage?*?newImage?=?new?QImage(*?origin);??????QImage?*?blurredImage?=?blur(newImage);??????QImage?*?mask?=?new?QImage(":images/mask.png");??????QPainter?painter;?????????//Using?the?composition?mode?SourceAtop?we?get?a?blurred?frame?stored?in?QImage?mask??????painter.begin(mask);?????????painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);??????painter.drawImage(0,?0,?*?blurredImage);?????????painter.end();?????????//With?our?new?frame?we?simply?draw?it?over?the?original?image??????painter.begin(newImage);?????????painter.setCompositionMode(QPainter::CompositionMode_SourceOver);??????painter.drawImage(0,?0,?*?mask);?????????painter.end();?????????delete?mask;??????delete?blurredImage;?????????return?newImage;??}??
?
?
原圖
?
遮罩
?
模糊的邊框
?
最終效果
?
手機(jī)Demo
你可以下載這個(gè)手機(jī)Demo的源碼,里面包含了文章中的源碼,在這個(gè)應(yīng)用中,包含了3張462*260的圖片。測(cè)試應(yīng)用的時(shí)候,你只要選擇其中一張并應(yīng)用下面的效果就可以了。
?
總結(jié)
這篇文章應(yīng)該可以成為你圖像處理的入門(mén),但是一切皆有可能。你可以修改這些方法,整合這些方法,使用其他的技術(shù)等等。想象力才是你唯一的限制。
總結(jié)
以上是生活随笔為你收集整理的Qimage像素级操作的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。