SLIC 超像素分割(C++)

摘要:

SLIC:simple linear iterative clustering,簡(jiǎn)單的線性迭代聚類,它使用k-means聚類來(lái)生成超像素,實(shí)現(xiàn)起來(lái)簡(jiǎn)單悼粮,運(yùn)行效率還比較高掀淘,超像素對(duì)邊界的保持效果也比較好,具體的與其他方法的對(duì)比結(jié)果在下方論文中有詳細(xì)記錄耘纱。

論文地址:https://infoscience.epfl.ch/record/177415/files/Superpixel_PAMI2011-2.pdf

1.原理

SLIC本質(zhì)上說(shuō)只需要一個(gè)聚類數(shù)k敬肚,這個(gè)聚類數(shù)k也就是超像素的個(gè)數(shù),希望把圖片分割成多少個(gè)超像素就設(shè)多少個(gè)聚類中心束析,在初始化時(shí)把聚類中心等間隔的放到圖像上艳馒,設(shè)圖像像素?cái)?shù)為N,則在每個(gè)邊長(zhǎng)為\sqrt{N/k}的方格內(nèi)放置一個(gè)初始聚類中心员寇, 為了避免把噪聲像素和邊緣當(dāng)作聚類中心弄慰,會(huì)把該中心移動(dòng)到周圍3*3鄰域內(nèi)梯度最小的地方。
在用k-means做聚類時(shí)蝶锋,采用的特征是Lab顏色空間的三個(gè)值加上坐標(biāo)x陆爽,y,一共5個(gè)維度扳缕,x_{i.feature} = [L,a,b,x,y]^T,另外與常規(guī)的k均值不同的地方是慌闭,這里計(jì)算距離時(shí)只考慮聚類中心周圍2\sqrt{N/k} * 2\sqrt{N/k}的鄰域别威,因此計(jì)算量和一般的k均值聚類相比要小很多。搜索空間對(duì)比如下圖驴剔,S = \sqrt{N/k}

在這里插入圖片描述

聚類后計(jì)算每一類的平均特征省古,并把聚類中心的特征更新為平均特征。如果迭代超過(guò)10次或者前后兩次聚類的差小于閾值則結(jié)束仔拟,否則繼續(xù)聚類衫樊,更新聚類中心........。算法流程如下:

在這里插入圖片描述

2. 實(shí)現(xiàn)

2.1 初始化聚類中心

原文中按聚類數(shù)k來(lái)等間隔地初始化聚類中心利花,假設(shè)樣本總數(shù)為N科侈,聚類數(shù)為k,則每隔N/k個(gè)樣本放置一個(gè)聚類中心炒事。在圖片上等間隔地放置k個(gè)初始聚類中心臀栈,也就是把圖片等分成邊長(zhǎng)為\sqrt{N/k}的格子,在格子的固定位置放置初始聚類中心挠乳。另外為了避免初始的聚類中心落在物體邊緣上权薯,還要對(duì)每一個(gè)聚類中心都進(jìn)行一下微調(diào),具體就是計(jì)算初始聚類中心點(diǎn)周圍鄰域的梯度睡扬,把中心點(diǎn)移到梯度最小的點(diǎn)上盟蚣。
這里實(shí)現(xiàn)的時(shí)候以初始超像素的邊長(zhǎng)len作為參數(shù)會(huì)比較直觀,可以很簡(jiǎn)單的控制生成的超像素的大小卖怜,k和len的關(guān)系是len=\sqrt{N/k}屎开。
注:圖片坐標(biāo)系以左上角為原點(diǎn),水平向右為x軸正方向马靠,水平向下為y軸正方向(與opencv一致)奄抽,在訪問(wèn)圖片數(shù)據(jù)矩陣時(shí),一般用行和列的方式來(lái)描述甩鳄,也就是row對(duì)應(yīng)y逞度,colum對(duì)應(yīng)x。

  • 等間隔放置聚類中心,
int initilizeCenters(cv::Mat &imageLAB, std::vector<center> &centers, int len)
{
    if (imageLAB.empty())
    {
        std::cout << "In itilizeCenters:     image is empty!\n";
        return -1;
    }

    uchar *ptr = NULL;
    center cent;
    int num = 0;
    for (int i = 0; i < imageLAB.rows; i += len)
    {
        cent.y = i + len / 2;
        if (cent.y >= imageLAB.rows) continue;
        ptr = imageLAB.ptr<uchar>(cent.y);
        for (int j = 0; j < imageLAB.cols; j += len)
        {
            cent.x = j + len / 2;
            if ((cent.x >= imageLAB.cols)) continue;
            cent.L = *(ptr + cent.x * 3);
            cent.A = *(ptr + cent.x * 3 + 1);
            cent.B = *(ptr + cent.x * 3 + 2);
            cent.label = ++num;
            centers.push_back(cent);
        }
    }
    return 0;
}
  • 把聚類中心移到到周圍8鄰域里梯度最小的地方,梯度用Sobel計(jì)算(不希望聚類中心落在邊緣上所以才調(diào)整的)
int fituneCenter(cv::Mat &imageLAB, cv::Mat &sobelGradient, std::vector<center> &centers)
{
    if (sobelGradient.empty()) return -1;

    center cent;
    double *sobPtr = sobelGradient.ptr<double>(0);
    uchar *imgPtr = imageLAB.ptr<uchar>(0);
    int w = sobelGradient.cols;
    for (int ck = 0; ck < centers.size(); ck++)
    {
        cent = centers[ck];
        if (cent.x - 1 < 0 || cent.x + 1 >= sobelGradient.cols || cent.y - 1 < 0 || cent.y + 1 >= sobelGradient.rows)
        {
            continue;
        }//end if
        double minGradient = 9999999;
        int tempx = 0, tempy = 0;
        for (int m = -1; m < 2; m++)
        {
            sobPtr = sobelGradient.ptr<double>(cent.y + m);
            for (int n = -1; n < 2; n++)
            {
                double gradient = pow(*(sobPtr + (cent.x + n) * 3), 2)
                    + pow(*(sobPtr + (cent.x + n) * 3 + 1), 2)
                    + pow(*(sobPtr + (cent.x + n) * 3 + 2), 2);
                if (gradient < minGradient)
                {
                    minGradient = gradient;
                    tempy = m;//row
                    tempx = n;//column
                }//end if
            }
        }
        cent.x += tempx;
        cent.y += tempy;
        imgPtr = imageLAB.ptr<uchar>(cent.y);
        centers[ck].x = cent.x;
        centers[ck].y = cent.y;
        centers[ck].L = *(imgPtr + cent.x * 3);
        centers[ck].A = *(imgPtr + cent.x * 3 + 1);
        centers[ck].B = *(imgPtr + cent.x * 3 + 2);

    }//end for
    return 0;
}

2.2 聚類

對(duì)每一個(gè)聚類中心center_k妙啃,計(jì)算它到周圍2len*2len區(qū)域的所有點(diǎn)的距離档泽,如果新計(jì)算的距離比原來(lái)的距離更小,則把該點(diǎn)歸入center_k這一類揖赴。
注意聚類的本質(zhì)就是“物以類聚”茁瘦,判斷樣本點(diǎn)和聚類中心的“近似”程度,可以從兩個(gè)方面來(lái)考察储笑,一種是距離測(cè)度:距離越近越相似甜熔,另一種是相似性測(cè)度,例如角度相似系數(shù)突倍,相關(guān)系數(shù)腔稀,指數(shù)相似系數(shù)等盆昙。
距離的計(jì)算方式有很多種,比如:

  • 歐拉距離d(\vec{x},\vec{y}) = ||\vec{x}-\vec{y}||_2^2
  • 城區(qū)距離d(\vec{x},\vec{y}) = |\vec{x}-\vec{y}|
  • 切比雪夫距離d(\vec{x},\vec{y}) = max|x_i-y_i|,i表示維度
  • 馬氏距離(Mahalanobis):d(\vec{x},\vec{y}) = (\vec{x}-\vec{y})^TV^{-1}(\vec{x}-\vec{y}),V表示樣本總體的協(xié)方差矩陣焊虏,V= \frac{1}{n-1}\Sigma_{i=1}^{n}(\vec{x_i - \bar{\vec{x}}})(\vec{x_i - \bar{\vec{x}}})^T,n為樣本數(shù)淡喜, \vec{x_i}為第i個(gè)樣本(列向量), \bar{\vec{x}}為樣本均值诵闭,它有一點(diǎn)比較好的性質(zhì)就是與量綱無(wú)關(guān)(另外它還對(duì)坐標(biāo)尺度炼团、旋轉(zhuǎn)、平移保持不變疏尿,從統(tǒng)計(jì)意義上去除了分量間的相關(guān)性)瘟芝,在這里分割超像素時(shí),Lab顏色空間的距離往往會(huì)比空間距離大很多褥琐,用歐式距離時(shí)需要加一個(gè)權(quán)重參數(shù)來(lái)調(diào)節(jié)顏色距離和空間距離的比例锌俱。

要是以后有時(shí)間的話可以考慮一下都試一下這些距離聚類的效果。這里采用的是歐式距離敌呈,而且因?yàn)長(zhǎng)ab顏色空間和圖像xy坐標(biāo)空間量綱不同贸宏,需要調(diào)整顏色空間距離和xy坐標(biāo)距離的權(quán)重,論文中用下面的方式來(lái)計(jì)算距離
\begin{aligned} d_{c}&=\sqrt{\left(l_{j}-l_{i}\right)^{2}+\left(a_{j}-a_{i}\right)^{2}+\left(b_{j}-b_{i}\right)^{2}} \\ d_{s} &=\sqrt{\left(x_{j}-x_{i}\right)^{2}+\left(y_{j}-y_{i}\right)^{2}}\\D&=\sqrt{d_{c}^{2}+\left(\frac{d_{s}}{S}\right)^{2} m^{2}} \end{aligned}
但是實(shí)際上在做超像素分割時(shí)我們更關(guān)心超像素的大小磕洪,而不是有多少個(gè)吭练,雖然尺寸S和聚類數(shù)k有明確的對(duì)應(yīng)關(guān)系,但是把k當(dāng)輸入?yún)?shù)不如直接用尺寸S來(lái)得直接析显,另外ds的權(quán)重用\frac{m^2}{S^2}實(shí)際用起來(lái)有點(diǎn)麻煩鲫咽,因?yàn)閱为?dú)修改m或者s都會(huì)被另外一個(gè)參數(shù)調(diào)制,所以D的計(jì)算我改成了下面這樣\begin{aligned}D=\sqrt{d_{c}^{2}+md_{s}^{2} }\end{aligned}

int clustering(const cv::Mat &imageLAB, cv::Mat &DisMask, cv::Mat &labelMask,
    std::vector<center> &centers, int len, int m)
{
    if (imageLAB.empty())
    {
        std::cout << "clustering :the input image is empty!\n";
        return -1;
    }

    double *disPtr = NULL;//disMask type: 64FC1
    double *labelPtr = NULL;//labelMask type: 64FC1
    const uchar *imgPtr = NULL;//imageLAB type: 8UC3

    //disc = std::sqrt(pow(L - cL, 2)+pow(A - cA, 2)+pow(B - cB,2))
    //diss = std::sqrt(pow(x-cx,2) + pow(y-cy,2));
    //dis = sqrt(disc^2 + (diss/len)^2 * m^2)
    double dis = 0, disc = 0, diss = 0;
    //cluster center's cx, cy,cL,cA,cB;
    int cx, cy, cL, cA, cB, clabel;
    //imageLAB's  x, y, L,A,B
    int x, y, L, A, B;

    //注:這里的圖像坐標(biāo)以左上角為原點(diǎn)叫榕,水平向右為x正方向,水平向下為y正方向浑侥,與opencv保持一致
    //      從矩陣行列角度看姊舵,i表示行晰绎,j表示列,即(i,j) = (y,x)
    for (int ck = 0; ck < centers.size(); ++ck)
    {
        cx = centers[ck].x;
        cy = centers[ck].y;
        cL = centers[ck].L;
        cA = centers[ck].A;
        cB = centers[ck].B;
        clabel = centers[ck].label;

        for (int i = cy - len; i < cy + len; i++)
        {
            if (i < 0 | i >= imageLAB.rows) continue;
            //pointer point to the ith row
            imgPtr = imageLAB.ptr<uchar>(i);
            disPtr = DisMask.ptr<double>(i);
            labelPtr = labelMask.ptr<double>(i);
            for (int j = cx - len; j < cx + len; j++)
            {
                if (j < 0 | j >= imageLAB.cols) continue;
                L = *(imgPtr + j * 3);
                A = *(imgPtr + j * 3 + 1);
                B = *(imgPtr + j * 3 + 2);

                disc = std::sqrt(pow(L - cL, 2) + pow(A - cA, 2) + pow(B - cB, 2));
                diss = std::sqrt(pow(j - cx, 2) + pow(i - cy, 2));
                dis = sqrt(pow(disc, 2) + m * pow(diss, 2));

                if (dis < *(disPtr + j))
                {
                    *(disPtr + j) = dis;
                    *(labelPtr + j) = clabel;
                }//end if
            }//end for
        }
    }//end for (int ck = 0; ck < centers.size(); ++ck)


    return 0;
}

2.3 更新聚類中心

對(duì)每一個(gè)聚類中心center_k括丁,把所有屬于這一類的點(diǎn)的特征加起來(lái)求平均荞下,把這個(gè)平均特征賦給center_k。

int updateCenter(cv::Mat &imageLAB, cv::Mat &labelMask, std::vector<center> &centers, int len)
{
    double *labelPtr = NULL;//labelMask type: 64FC1
    const uchar *imgPtr = NULL;//imageLAB type: 8UC3
    int cx, cy;

    for (int ck = 0; ck < centers.size(); ++ck)
    {
        double sumx = 0, sumy = 0, sumL = 0, sumA = 0, sumB = 0, sumNum = 0;
        cx = centers[ck].x;
        cy = centers[ck].y;
        for (int i = cy - len; i < cy + len; i++)
        {
            if (i < 0 | i >= imageLAB.rows) continue;
            //pointer point to the ith row
            imgPtr = imageLAB.ptr<uchar>(i);
            labelPtr = labelMask.ptr<double>(i);
            for (int j = cx - len; j < cx + len; j++)
            {
                if (j < 0 | j >= imageLAB.cols) continue;

                if (*(labelPtr + j) == centers[ck].label)
                {
                    sumL += *(imgPtr + j * 3);
                    sumA += *(imgPtr + j * 3 + 1);
                    sumB += *(imgPtr + j * 3 + 2);
                    sumx += j;
                    sumy += i;
                    sumNum += 1;
                }//end if
            }
        }
        //update center
        if (sumNum == 0) sumNum = 0.000000001;
        centers[ck].x = sumx / sumNum;
        centers[ck].y = sumy / sumNum;
        centers[ck].L = sumL / sumNum;
        centers[ck].A = sumA / sumNum;
        centers[ck].B = sumB / sumNum;

    }//end for

    return 0;
}

2.4 顯示超像素分割結(jié)果

方式一:把屬于同一類的點(diǎn)的特征都替換成平均特征;

方式二:畫出聚類邊界;

3. 實(shí)測(cè)效果

  • 左側(cè)為原圖史飞,中間為在原圖上畫出超像素邊界效果圖尖昏,右側(cè)為超像素圖像效果


    bassball.png
zebra.png
building.png
bus.png
football.png
giraffe.png
plane.png
skiing.png

4. 完整源碼

//
//created by Mr. Peng. 2021\08\31
//

#include "opencv.hpp"

struct center
{
    int x;//column
    int y;//row
    int L;
    int A;
    int B;
    int label;
};


/////////////////////////////////////////////////////
//input parameters:
//imageLAB:    the source image in Lab color space
//DisMask:       it save the shortest distance to the nearest center
//labelMask:   it save every pixel's label  
//centers:       clustering center
//len:         the super pixls will be initialize to len*len
//m:           a parameter witch adjust the weights of the spacial and color space distance 
//
//output:
////////////////////////////////////////////////////
int clustering(const cv::Mat &imageLAB, cv::Mat &DisMask, cv::Mat &labelMask,
    std::vector<center> &centers, int len, int m)
{
    if (imageLAB.empty())
    {
        std::cout << "clustering :the input image is empty!\n";
        return -1;
    }

    double *disPtr = NULL;//disMask type: 64FC1
    double *labelPtr = NULL;//labelMask type: 64FC1
    const uchar *imgPtr = NULL;//imageLAB type: 8UC3

    //disc = std::sqrt(pow(L - cL, 2)+pow(A - cA, 2)+pow(B - cB,2))
    //diss = std::sqrt(pow(x-cx,2) + pow(y-cy,2));
    //dis = sqrt(disc^2 + (diss/len)^2 * m^2)
    double dis = 0, disc = 0, diss = 0;
    //cluster center's cx, cy,cL,cA,cB;
    int cx, cy, cL, cA, cB, clabel;
    //imageLAB's  x, y, L,A,B
    int x, y, L, A, B;

    //注:這里的圖像坐標(biāo)以左上角為原點(diǎn),水平向右為x正方向,水平向下為y正方向构资,與opencv保持一致
    //      從矩陣行列角度看抽诉,i表示行,j表示列吐绵,即(i,j) = (y,x)
    for (int ck = 0; ck < centers.size(); ++ck)
    {
        cx = centers[ck].x;
        cy = centers[ck].y;
        cL = centers[ck].L;
        cA = centers[ck].A;
        cB = centers[ck].B;
        clabel = centers[ck].label;

        for (int i = cy - len; i < cy + len; i++)
        {
            if (i < 0 | i >= imageLAB.rows) continue;
            //pointer point to the ith row
            imgPtr = imageLAB.ptr<uchar>(i);
            disPtr = DisMask.ptr<double>(i);
            labelPtr = labelMask.ptr<double>(i);
            for (int j = cx - len; j < cx + len; j++)
            {
                if (j < 0 | j >= imageLAB.cols) continue;
                L = *(imgPtr + j * 3);
                A = *(imgPtr + j * 3 + 1);
                B = *(imgPtr + j * 3 + 2);

                disc = std::sqrt(pow(L - cL, 2) + pow(A - cA, 2) + pow(B - cB, 2));
                diss = std::sqrt(pow(j - cx, 2) + pow(i - cy, 2));
                dis = sqrt(pow(disc, 2) + m * pow(diss, 2));

                if (dis < *(disPtr + j))
                {
                    *(disPtr + j) = dis;
                    *(labelPtr + j) = clabel;
                }//end if
            }//end for
        }
    }//end for (int ck = 0; ck < centers.size(); ++ck)


    return 0;
}


/////////////////////////////////////////////////////
//input parameters:
//imageLAB:    the source image in Lab color space
//labelMask:    it save every pixel's label
//centers:       clustering center
//len:         the super pixls will be initialize to len*len
//
//output:
////////////////////////////////////////////////////
int updateCenter(cv::Mat &imageLAB, cv::Mat &labelMask, std::vector<center> &centers, int len)
{
    double *labelPtr = NULL;//labelMask type: 64FC1
    const uchar *imgPtr = NULL;//imageLAB type: 8UC3
    int cx, cy;

    for (int ck = 0; ck < centers.size(); ++ck)
    {
        double sumx = 0, sumy = 0, sumL = 0, sumA = 0, sumB = 0, sumNum = 0;
        cx = centers[ck].x;
        cy = centers[ck].y;
        for (int i = cy - len; i < cy + len; i++)
        {
            if (i < 0 | i >= imageLAB.rows) continue;
            //pointer point to the ith row
            imgPtr = imageLAB.ptr<uchar>(i);
            labelPtr = labelMask.ptr<double>(i);
            for (int j = cx - len; j < cx + len; j++)
            {
                if (j < 0 | j >= imageLAB.cols) continue;

                if (*(labelPtr + j) == centers[ck].label)
                {
                    sumL += *(imgPtr + j * 3);
                    sumA += *(imgPtr + j * 3 + 1);
                    sumB += *(imgPtr + j * 3 + 2);
                    sumx += j;
                    sumy += i;
                    sumNum += 1;
                }//end if
            }
        }
        //update center
        if (sumNum == 0) sumNum = 0.000000001;
        centers[ck].x = sumx / sumNum;
        centers[ck].y = sumy / sumNum;
        centers[ck].L = sumL / sumNum;
        centers[ck].A = sumA / sumNum;
        centers[ck].B = sumB / sumNum;

    }//end for

    return 0;
}


int showSLICResult(const cv::Mat &image, cv::Mat &labelMask, std::vector<center> &centers, int len)
{
    cv::Mat dst = image.clone();
    cv::cvtColor(dst, dst, cv::COLOR_BGR2Lab);
    double *labelPtr = NULL;//labelMask type: 32FC1
    uchar *imgPtr = NULL;//image type: 8UC3

    int cx, cy;
    double sumx = 0, sumy = 0, sumL = 0, sumA = 0, sumB = 0, sumNum = 0.00000001;
    for (int ck = 0; ck < centers.size(); ++ck)
    {
        cx = centers[ck].x;
        cy = centers[ck].y;

        for (int i = cy - len; i < cy + len; i++)
        {
            if (i < 0 | i >= image.rows) continue;
            //pointer point to the ith row
            imgPtr = dst.ptr<uchar>(i);
            labelPtr = labelMask.ptr<double>(i);
            for (int j = cx - len; j < cx + len; j++)
            {
                if (j < 0 | j >= image.cols) continue;

                if (*(labelPtr + j) == centers[ck].label)
                {
                    *(imgPtr + j * 3) = centers[ck].L;
                    *(imgPtr + j * 3 + 1) = centers[ck].A;
                    *(imgPtr + j * 3 + 2) = centers[ck].B;
                }//end if
            }
        }
    }//end for

    cv::cvtColor(dst, dst, cv::COLOR_Lab2BGR);
    cv::namedWindow("showSLIC", 0);
    cv::imshow("showSLIC", dst);
    cv::waitKey(1);

    return 0;
}


int showSLICResult2(const cv::Mat &image, cv::Mat &labelMask, std::vector<center> &centers, int len)
{
    cv::Mat dst = image.clone();
    //cv::cvtColor(dst, dst, cv::COLOR_Lab2BGR);
    double *labelPtr = NULL;//labelMask type: 32FC1
    double *labelPtr_nextRow = NULL;//labelMask type: 32FC1
    uchar *imgPtr = NULL;//image type: 8UC3

    for (int i = 0; i < labelMask.rows - 1; i++)
    {
        labelPtr = labelMask.ptr<double>(i);
        imgPtr = dst.ptr<uchar>(i);
        for (int j = 0; j < labelMask.cols - 1; j++)
        {
            //if left pixel's label is different from the right's 
            if (*(labelPtr + j) != *(labelPtr + j + 1))
            {
                *(imgPtr + 3 * j) = 0;
                *(imgPtr + 3 * j + 1) = 0;
                *(imgPtr + 3 * j + 2) = 0;
            }

            //if the upper pixel's label is different from the bottom's 
            labelPtr_nextRow = labelMask.ptr<double>(i + 1);
            if (*(labelPtr_nextRow + j) != *(labelPtr + j))
            {
                *(imgPtr + 3 * j) = 0;
                *(imgPtr + 3 * j + 1) = 0;
                *(imgPtr + 3 * j + 2) = 0;
            }
        }
    }

    //show center
    for (int ck = 0; ck < centers.size(); ck++)
    {
        imgPtr = dst.ptr<uchar>(centers[ck].y);
        *(imgPtr + centers[ck].x * 3) = 100;
        *(imgPtr + centers[ck].x * 3 + 1) = 100;
        *(imgPtr + centers[ck].x * 3 + 1) = 10;
    }

    cv::namedWindow("showSLIC2", 0);
    cv::imshow("showSLIC2", dst);
    cv::waitKey(1);
    return 0;
}


int initilizeCenters(cv::Mat &imageLAB, std::vector<center> &centers, int len)
{
    if (imageLAB.empty())
    {
        std::cout << "In itilizeCenters:     image is empty!\n";
        return -1;
    }

    uchar *ptr = NULL;
    center cent;
    int num = 0;
    for (int i = 0; i < imageLAB.rows; i += len)
    {
        cent.y = i + len / 2;
        if (cent.y >= imageLAB.rows) continue;
        ptr = imageLAB.ptr<uchar>(cent.y);
        for (int j = 0; j < imageLAB.cols; j += len)
        {
            cent.x = j + len / 2;
            if ((cent.x >= imageLAB.cols)) continue;
            cent.L = *(ptr + cent.x * 3);
            cent.A = *(ptr + cent.x * 3 + 1);
            cent.B = *(ptr + cent.x * 3 + 2);
            cent.label = ++num;
            centers.push_back(cent);
        }
    }
    return 0;
}


//if the center locates in the edges, fitune it's location.
int fituneCenter(cv::Mat &imageLAB, cv::Mat &sobelGradient, std::vector<center> &centers)
{
    if (sobelGradient.empty()) return -1;

    center cent;
    double *sobPtr = sobelGradient.ptr<double>(0);
    uchar *imgPtr = imageLAB.ptr<uchar>(0);
    int w = sobelGradient.cols;
    for (int ck = 0; ck < centers.size(); ck++)
    {
        cent = centers[ck];
        if (cent.x - 1 < 0 || cent.x + 1 >= sobelGradient.cols || cent.y - 1 < 0 || cent.y + 1 >= sobelGradient.rows)
        {
            continue;
        }//end if
        double minGradient = 9999999;
        int tempx = 0, tempy = 0;
        for (int m = -1; m < 2; m++)
        {
            sobPtr = sobelGradient.ptr<double>(cent.y + m);
            for (int n = -1; n < 2; n++)
            {
                double gradient = pow(*(sobPtr + (cent.x + n) * 3), 2)
                    + pow(*(sobPtr + (cent.x + n) * 3 + 1), 2)
                    + pow(*(sobPtr + (cent.x + n) * 3 + 2), 2);
                if (gradient < minGradient)
                {
                    minGradient = gradient;
                    tempy = m;//row
                    tempx = n;//column
                }//end if
            }
        }
        cent.x += tempx;
        cent.y += tempy;
        imgPtr = imageLAB.ptr<uchar>(cent.y);
        centers[ck].x = cent.x;
        centers[ck].y = cent.y;
        centers[ck].L = *(imgPtr + cent.x * 3);
        centers[ck].A = *(imgPtr + cent.x * 3 + 1);
        centers[ck].B = *(imgPtr + cent.x * 3 + 2);

    }//end for
    return 0;
}


/////////////////////////////////////////////////////
//input parameters:
//image:    the source image in RGB color space
//resultLabel:     it save every pixel's label
//len:         the super pixls will be initialize to len*len
//m:           a parameter witch adjust the weights of diss 
//output:
////////////////////////////////////////////////////
int SLIC(cv::Mat &image, cv::Mat &resultLabel, std::vector<center> &centers, int len, int m)
{
    if (image.empty())
    {
        std::cout << "in SLIC the input image is empty!\n";
        return -1;

    }

    int MAXDIS = 999999;
    int height, width;
    height = image.rows;
    width = image.cols;

    //convert color
    cv::Mat imageLAB;
    cv::cvtColor(image, imageLAB, cv::COLOR_BGR2Lab);

    //get sobel gradient map
    cv::Mat sobelImagex, sobelImagey, sobelGradient;
    cv::Sobel(imageLAB, sobelImagex, CV_64F, 0, 1, 3);
    cv::Sobel(imageLAB, sobelImagey, CV_64F, 1, 0, 3);
    cv::addWeighted(sobelImagex, 0.5, sobelImagey, 0.5, 0, sobelGradient);//sobel output image type is CV_64F

    //initiate
    //std::vector<center> centers;
    //disMask save the distance of the pixels to center;
    cv::Mat disMask ;
    //labelMask save the label of the pixels
    cv::Mat labelMask = cv::Mat::zeros(cv::Size(width, height), CV_64FC1);

    //initialize centers,  get centers
    initilizeCenters(imageLAB, centers, len);
    //if the center locates in the edges, fitune it's location
    fituneCenter(imageLAB, sobelGradient, centers);

    //update cluster 10 times 
    for (int time = 0; time < 10; time++)
    {
        //clustering
        disMask = cv::Mat(height, width, CV_64FC1, cv::Scalar(MAXDIS));
        clustering(imageLAB, disMask, labelMask, centers, len, m);
        //update
        updateCenter(imageLAB, labelMask, centers, len);
        //fituneCenter(imageLAB, sobelGradient, centers);
    }

    resultLabel = labelMask;

    return 0;
}


int SLIC_Demo()
{
    
    std::string imagePath = "K:\\deepImage\\plane.jpg";
    cv::Mat image = cv::imread(imagePath);
    cv::Mat labelMask;//save every pixel's label
    cv::Mat dst;//save the shortest distance to the nearest centers
    std::vector<center> centers;//clustering centers

    int len = 25;//the scale of the superpixel ,len*len
    int m = 10;//a parameter witch adjust the weights of spacial distance and the color space distance
    SLIC(image, labelMask, centers, len, m);

    cv::namedWindow("image", 1);
    cv::imshow("image", image);
    showSLICResult(image, labelMask, centers, len);
    showSLICResult2(image, labelMask, centers, len);
    
    cv::waitKey(0);
    return 0;
}


int main()
{
    SLIC_Demo();
    return 0;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末迹淌,一起剝皮案震驚了整個(gè)濱河市河绽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌唉窃,老刑警劉巖耙饰,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異纹份,居然都是意外死亡苟跪,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門蔓涧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)件已,“玉大人,你說(shuō)我怎么就攤上這事蠢笋〔ζ耄” “怎么了?”我有些...
    開封第一講書人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵昨寞,是天一觀的道長(zhǎng)瞻惋。 經(jīng)常有香客問(wèn)我,道長(zhǎng)援岩,這世上最難降的妖魔是什么歼狼? 我笑而不...
    開封第一講書人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮享怀,結(jié)果婚禮上羽峰,老公的妹妹穿的比我還像新娘。我一直安慰自己添瓷,他們只是感情好梅屉,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鳞贷,像睡著了一般坯汤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上搀愧,一...
    開封第一講書人閱讀 51,231評(píng)論 1 299
  • 那天惰聂,我揣著相機(jī)與錄音,去河邊找鬼咱筛。 笑死搓幌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的迅箩。 我是一名探鬼主播溉愁,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼饲趋!你這毒婦竟也來(lái)了拐揭?” 一聲冷哼從身側(cè)響起罢缸,我...
    開封第一講書人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎投队,沒(méi)想到半個(gè)月后疚沐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體昔穴,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了悠砚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悼吱。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡乐导,死狀恐怖肯骇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情碟案,我是刑警寧澤愿险,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站价说,受9級(jí)特大地震影響辆亏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鳖目,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一扮叨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧领迈,春花似錦彻磁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至尘喝,卻和暖如春磁浇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瞧省。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工扯夭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鳍贾,地道東北人鞍匾。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像骑科,于是被迫代替她去往敵國(guó)和親橡淑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354

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

  • 姓名:袁卓成咆爽;學(xué)號(hào):20021210612梁棠; 學(xué)院:電子工程學(xué)院 轉(zhuǎn)自https://blog.csdn.net/...
    普羅米修斯的貓閱讀 2,096評(píng)論 0 0
  • SLIC超像素分割詳解(一) 超像素概念是2003年Xiaofeng Ren提出和發(fā)展起來(lái)的圖像分割技術(shù)置森,是指具有...
    壹米玖坤閱讀 18,847評(píng)論 0 4
  • 在計(jì)算機(jī)視覺(jué)領(lǐng)域里,圖像分割(Segmentation)是指將數(shù)字圖像分割成一些列像素集合的過(guò)程符糊。這些集合也被成為...
    雨幻逐光閱讀 1,975評(píng)論 0 0
  • SLIC(simple linear iterativeclustering) ,即簡(jiǎn)單線性迭代聚類凫海。將彩色圖像轉(zhuǎn)...
    zerowl閱讀 1,429評(píng)論 0 0
  • 論文地址 原文 基本原理 算法通過(guò)將具有相似紋理男娄、顏色行贪、亮度等特征的相鄰像素構(gòu)成的有一定視覺(jué)意義的不規(guī)則像素塊,從...
    Potato_D閱讀 3,751評(píng)論 0 0