圖像融合:拉普拉斯金字塔融合算法

Jacob的全景圖像融合算法系列
OpenCV 尺度不變特征檢測(cè):SIFT休溶、SURF咖祭、BRISK渡冻、ORB
OpenCV 匹配興趣點(diǎn):SIFT辣辫、SURF 和二值描述子
OpenCV 估算圖像的投影關(guān)系:基礎(chǔ)矩陣和RANSAC
OpenCV 單應(yīng)矩陣應(yīng)用:全景圖像融合原理
圖像融合:拉普拉斯金字塔融合算法

繼圖像拼接的課程設(shè)計(jì)之后簿废,對(duì)這方面依舊十分感興趣。很巧合的是络它,數(shù)圖老師表示剛好手上有這么一個(gè)項(xiàng)目族檬,要用到這方面的知識(shí),可以讓我去作為畢業(yè)設(shè)計(jì)化戳。雖然距離畢業(yè)還遠(yuǎn)单料,不過(guò)如果能選到一個(gè)感興趣并且有一定深度的題目還是很好的埋凯。這些天在看論文的時(shí)候,了解到這么一個(gè)強(qiáng)大的算法扫尖,打算寫篇文記錄下心得吧白对。

一些圖像融合算法

圖像拼接主要可以分為兩個(gè)步驟:圖像配準(zhǔn)圖像融合。其中圖像配準(zhǔn)的目的是將圖一場(chǎng)景中不同視角的圖像投影到同一平面并進(jìn)行對(duì)準(zhǔn)换怖。比如我之前這篇博客中使用SIFT特征檢測(cè)和單應(yīng)矩陣的目的甩恼,就是進(jìn)行圖像配準(zhǔn)。

圖像配準(zhǔn)

經(jīng)過(guò)圖像配準(zhǔn)之后沉颂,就需要進(jìn)行圖像融合条摸。而圖像融合的目的就是使兩幅圖像的重疊區(qū)域過(guò)渡自然且平滑。在上圖中铸屉,可以看到明顯的邊界钉蒲,這對(duì)拼接來(lái)說(shuō)是無(wú)法接受的。這主要是因?yàn)橥獠苛炼鹊淖兓ㄌ炜诊h過(guò)了一朵萌萌的云彩彻坛?)以及曝光時(shí)相機(jī)參數(shù)不一致導(dǎo)致的顷啼。要消除或緩和這種現(xiàn)象,就需要進(jìn)行圖像融合昌屉。

主流的圖像融合算法有:

1)加權(quán)平均法钙蒙。這個(gè)很好理解,即簡(jiǎn)單的使用加權(quán)的方式從左邊過(guò)渡到右邊间驮。這種方法效果一般躬厌,但算法實(shí)現(xiàn)極其簡(jiǎn)單,速度快蜻牢。課設(shè)時(shí)我用的就是這個(gè)方法烤咧。

2)羽化算法 。這種方法過(guò)渡會(huì)比加權(quán)平均法自然抢呆,但會(huì)造成不好的模糊效果煮嫌。

3)拉普拉斯金字塔融合。有的地方也稱為多分辨率融合算法抱虐。這種方法是將圖像建立一個(gè)拉普拉斯金字塔昌阿,其中金字塔的每一層都包含了圖像不同的頻段。分開不同頻段進(jìn)行融合效果出奇的好恳邀。這也是本文主要介紹的方法懦冰。

高斯金字塔、拉普拉斯金字塔

之前在寫SIFT相關(guān)博客的時(shí)候說(shuō)到過(guò)高斯金字塔谣沸。圖像金字塔的意思無(wú)非就是對(duì)原圖進(jìn)行下采樣刷钢,然后塞到一個(gè)C++的Vector或者其他什么語(yǔ)言中的數(shù)組里。在可視化的時(shí)候乳附,最大的圖像放在最下面内地,最小的圖像放在最上面伴澄,所以稱為圖像金字塔。

圖像金字塔

而高斯金字塔的每一層的構(gòu)建步驟分為兩步:首先對(duì)下一層的圖像進(jìn)行高斯模糊阱缓。這個(gè)步驟相信讀者都了解非凌,是圖像處理中最基本的概念。然后刪除模糊后的圖像的偶數(shù)行和列荆针,就得到了當(dāng)前層的圖像了敞嗡。不斷進(jìn)行這個(gè)步驟,最終就得到了高斯金字塔航背。

拉普拉斯金字塔的構(gòu)造需要用到高斯金字塔喉悴。拉普拉斯金字塔第i層的數(shù)學(xué)定義如下


每一層的定義

意思是拉普拉斯金字塔每一層的圖像為同一層高斯金字塔的圖像減去上一層的圖像進(jìn)行上采樣并高斯模糊的結(jié)果。說(shuō)的有點(diǎn)繞沃粗,可以看網(wǎng)上的這幅圖進(jìn)行理解粥惧。


高斯金字塔與拉普拉斯金字塔的關(guān)系

算法原理

1)首先建立兩幅圖像高斯金字塔键畴,然后建立一定層數(shù)的拉普拉斯金字塔最盅。拉普拉斯金字塔的層數(shù)越高,融合效果越好起惕。層數(shù)N作為一個(gè)參數(shù)涡贱。

2)傳入一個(gè)mask掩膜,代表了融合的位置惹想。比如說(shuō)想在兩圖的中間進(jìn)行融合问词,那么掩膜圖像的左半為255,右半為0嘀粱,反過(guò)來(lái)是一樣的激挪。根據(jù)這個(gè)mask建立一個(gè)高斯金字塔,用于后續(xù)融合锋叨,層數(shù)為N+1垄分。

3)根據(jù)mask將兩幅圖像的拉普拉斯金字塔的圖像進(jìn)行相加,mask為權(quán)值娃磺。相加的結(jié)果即為一個(gè)新的金字塔薄湿。同時(shí),兩幅圖像的高斯金字塔的N+1層也進(jìn)行這個(gè)操作偷卧,記這個(gè)圖像為IMG1豺瘤。

4)根據(jù)這個(gè)新的金字塔重建出最終的圖像,重建的過(guò)程跟一般的拉普拉斯金字塔一樣听诸。首先對(duì)IMG1上采樣坐求,然后跟新金字塔的頂層相加,得到IMG2晌梨。IMG2進(jìn)行上采樣后跟下一層相加桥嗤,得到IMG3赛糟。重復(fù)這個(gè)過(guò)程,最終得到的結(jié)果就是拉普拉斯金字塔融合算法的結(jié)果砸逊。

因?yàn)閙ask建立金字塔的過(guò)程中使用了高斯模糊璧南,所以融合的邊緣是比較平滑的。

算法實(shí)現(xiàn)

類實(shí)現(xiàn)主要參考其他博客

/**************************************************************
 * Created by 楊幫杰 on 1/1/2019
 * Right to use this code in any way you want without
 * warranty, support or any guarantee of it working
 * E-mail: yangbangjie1998@qq.com
 * Association: SCAU 華南農(nóng)業(yè)大學(xué)
 **************************************************************/
#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>

using namespace std;
using namespace cv;

#define IMG1_PATH "/home/jacob/圖片/blend1.jpg"
#define IMG2_PATH "/home/jacob/圖片/blend2.jpg"

/**
 * @brief The LaplacianBlending class
 * @private leftImg & rightImg 用于拼接的左圖和右圖
 * @private blendMask 用于融合的掩膜师逸,值為加權(quán)平均的系數(shù)
 * @ref https://blog.csdn.net/abcjennifer/article/details/7628655
 */
class LaplacianBlending {
private:
    Mat leftImg;
    Mat rightImg;
    Mat blendMask;

    //Laplacian Pyramids
    vector<Mat> leftLapPyr, rightLapPyr, resultLapPyr;
    Mat leftHighestLevel, rightHighestLevel, resultHighestLevel;
    //mask為三通道方便矩陣相乘
    vector<Mat> maskGaussianPyramid;

    int levels;

    void buildPyramids()
    {
        buildLaplacianPyramid(leftImg, leftLapPyr, leftHighestLevel);
        buildLaplacianPyramid(rightImg, rightLapPyr, rightHighestLevel);
        buildGaussianPyramid();
    }

    void buildGaussianPyramid()
    {
        //金字塔內(nèi)容為每一層的掩模
        assert(leftLapPyr.size()>0);

        maskGaussianPyramid.clear();
        Mat currentImg;
        cvtColor(blendMask, currentImg, CV_GRAY2BGR);
        //保存mask金字塔的每一層圖像
        maskGaussianPyramid.push_back(currentImg); //0-level

        currentImg = blendMask;
        for (int l = 1; l<levels + 1; l++) {
            Mat _down;
            if (leftLapPyr.size() > l)
                pyrDown(currentImg, _down, leftLapPyr[l].size());
            else
                pyrDown(currentImg, _down, leftHighestLevel.size()); //lowest level

            Mat down;
            cvtColor(_down, down, CV_GRAY2BGR);
            //add color blend mask into mask Pyramid
            maskGaussianPyramid.push_back(down);
            string winName = to_string(l);
            imshow(winName,down);
            currentImg = _down;
        }
    }

    void buildLaplacianPyramid(const Mat& img, vector<Mat>& lapPyr, Mat& HighestLevel)
    {
        lapPyr.clear();
        Mat currentImg = img;
        for (int l = 0; l<levels; l++) {
            Mat down, up;
            pyrDown(currentImg, down);
            pyrUp(down, up, currentImg.size());
            Mat lap = currentImg - up;
            lapPyr.push_back(lap);
            currentImg = down;
        }
        currentImg.copyTo(HighestLevel);
    }

    Mat reconstructImgFromLapPyramid()
    {
        //將左右laplacian圖像拼成的resultLapPyr金字塔中每一層
        //從上到下插值放大并與殘差相加司倚,即得blend圖像結(jié)果
        Mat currentImg = resultHighestLevel;
        for (int l = levels - 1; l >= 0; l--)
        {
            Mat up;
            pyrUp(currentImg, up, resultLapPyr[l].size());
            currentImg = up + resultLapPyr[l];
        }
        return currentImg;
    }

    void blendLapPyrs()
    {
        //獲得每層金字塔中直接用左右兩圖Laplacian變換拼成的圖像resultLapPyr
        resultHighestLevel = leftHighestLevel.mul(maskGaussianPyramid.back()) +
            rightHighestLevel.mul(Scalar(1.0, 1.0, 1.0) - maskGaussianPyramid.back());
        for (int l = 0; l<levels; l++)
        {
            Mat A = leftLapPyr[l].mul(maskGaussianPyramid[l]);
            Mat antiMask = Scalar(1.0, 1.0, 1.0) - maskGaussianPyramid[l];
            Mat B = rightLapPyr[l].mul(antiMask);
            Mat blendedLevel = A + B;

            resultLapPyr.push_back(blendedLevel);
        }
    }

public:
    LaplacianBlending(const Mat& _left, const Mat& _right, const Mat& _blendMask, int _levels) ://construct function, used in LaplacianBlending lb(l,r,m,4);
        leftImg(_left), rightImg(_right), blendMask(_blendMask), levels(_levels)
    {
        assert(_left.size() == _right.size());
        assert(_left.size() == _blendMask.size());
        //創(chuàng)建拉普拉斯金字塔和高斯金字塔
        buildPyramids();
        //每層金字塔圖像合并為一個(gè)
        blendLapPyrs();
    };

    Mat blend()
    {
        //重建拉普拉斯金字塔
        return reconstructImgFromLapPyramid();
    }
};

Mat LaplacianBlend(const Mat &left, const Mat &right, const Mat &mask)
{
    LaplacianBlending laplaceBlend(left, right, mask, 10);
    return laplaceBlend.blend();
}

int main() {
    Mat leftImg = imread(IMG1_PATH);
    Mat rightImg = imread(IMG2_PATH);

    int hight = leftImg.rows;
    int width = leftImg.cols;

    Mat leftImg32f, rightImg32f;
    leftImg.convertTo(leftImg32f, CV_32F);
    rightImg.convertTo(rightImg32f, CV_32F);

    //創(chuàng)建用于混合的掩膜,這里在中間進(jìn)行混合
    Mat mask = Mat::zeros(hight, width, CV_32FC1);
    mask(Range::all(), Range(0, mask.cols * 0.5)) = 1.0;

    Mat blendImg = LaplacianBlend(leftImg32f, rightImg32f, mask);

    blendImg.convertTo(blendImg, CV_8UC3);

    imshow("left", leftImg);
    imshow("right", rightImg);
    imshow("blended", blendImg);

    waitKey(0);
    return 0;
}

左圖
右圖
融合結(jié)果

后記

可以看到篓像,融合結(jié)果簡(jiǎn)直驚艷动知。話說(shuō)寫完這篇博客的時(shí)候已經(jīng)是2019的元旦了,如果你能看到這里员辩,就祝你新年快樂(lè)吧~~

Reference:
圖像融合之拉普拉斯融合
圖像拉普拉斯金字塔融合(Laplacian Pyramid Blending)
【OpenCV入門教程之十三】OpenCV圖像金字塔:高斯金字塔盒粮、拉普拉斯金字塔與圖片尺寸縮放

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市奠滑,隨后出現(xiàn)的幾起案子丹皱,更是在濱河造成了極大的恐慌,老刑警劉巖宋税,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摊崭,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡杰赛,警方通過(guò)查閱死者的電腦和手機(jī)呢簸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)乏屯,“玉大人根时,你說(shuō)我怎么就攤上這事〕皆危” “怎么了蛤迎?”我有些...
    開封第一講書人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)伞芹。 經(jīng)常有香客問(wèn)我忘苛,道長(zhǎng),這世上最難降的妖魔是什么唱较? 我笑而不...
    開封第一講書人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任扎唾,我火速辦了婚禮,結(jié)果婚禮上南缓,老公的妹妹穿的比我還像新娘胸遇。我一直安慰自己,他們只是感情好汉形,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開白布纸镊。 她就那樣靜靜地躺著倍阐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪逗威。 梳的紋絲不亂的頭發(fā)上峰搪,一...
    開封第一講書人閱讀 51,258評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音凯旭,去河邊找鬼概耻。 笑死,一個(gè)胖子當(dāng)著我的面吹牛罐呼,可吹牛的內(nèi)容都是我干的鞠柄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼嫉柴,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼厌杜!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起计螺,我...
    開封第一講書人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤夯尽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后危尿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體呐萌,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡馁痴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年谊娇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片罗晕。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡济欢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出小渊,到底是詐尸還是另有隱情法褥,我是刑警寧澤,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布酬屉,位于F島的核電站半等,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏呐萨。R本人自食惡果不足惜杀饵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望谬擦。 院中可真熱鬧切距,春花似錦、人聲如沸惨远。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至葡幸,卻和暖如春最筒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蔚叨。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工是钥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人缅叠。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓悄泥,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親肤粱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子弹囚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容