Сегментация изображения - разделение и слияние (Quadtrees) - PullRequest
7 голосов
/ 13 августа 2011

Существует ли реализация метода разделения и слияния сегментации изображений? Любой совет будет высоко ценится.

Ответы [ 3 ]

14 голосов
/ 18 августа 2011

Что такое сегментация?

Сегментация означает разделение вашего изображения на несколько связанных областей. По сути, вы можете выполнить сегментацию с двумя определениями региона: вы можете определить регион как группу связанных одинаковых пикселей или набор связанных пикселей, окруженных разрывами (ребрами). Разделение и объединение использует первый подход.

Математически говоря: если все ваше изображение представлено набором пикселей (называемых R), то вы хотели бы получить подмножества, такие как

  1. Сегментация завершена, поэтому все субрегионы суммируются до целого R. Объединение всех регионов имеет вид R 1 UR 2 U ... UR n = R.
  2. R i подключен.
  3. Регионы различны. R я и колпачок, R J = & пусто; учитывая, что я & J; 1026 *
  4. Регионы имеют схожие свойства. Это может быть выражено функцией, называемой критерием однородности (P). Он должен давать TRUE для членов данного региона и FALSE для всех других регионов.
  5. Соседние регионы не могут быть объединены. Для всех регионов P (R i U R j ) = FALSE, учитывая, что i & ne; j.

Что такое алгоритм разделения и слияния?

Итак, сначала мы должны выбрать критерий однородности. Критерий однородности может быть глобальным (в зависимости от всего региона) или локальным (в зависимости от небольшого окна региона и, если это верно для всех окон, чем для региона). Простым примером может служить отклонение от среднего значения, которое должно быть меньше порогового значения. & gt; p i & isin; R i : | p i -μ | & le; f * σ.

Алгоритм разделения и слияния состоит из двух фаз: разделения и фазы слияния. В фазе разделения мы рекурсивно разделяем области на четыре субрегиона (начиная с всего изображения как одной области), пока наш критерий однородности не будет соблюден во всех субрегионах. Легко видеть, что 1-4 условия сегментации выполнены. Переходим к шагу слияния, чтобы выполнить условие 5 th .

На этапе объединения мы проверяем, что P (R i U R j ) = TRUE для каждых двух соседних областей, и объединяем эти две области. Мы повторяем этот шаг до тех пор, пока больше не понадобятся изменения. Теперь мы выполнили все условия, наше изображение было сегментировано на субрегионы.

псевдокод

Вот псевдокод для алгоритма разделения и объединения:

  1. Init: у нас есть только один большой регион (все изображение).
  2. Разделить: Если P (R i ) = TRUE, переходите к следующему шагу. В противном случае подразделите R i на четыре субрегиона и выполните для них шаг 2.
  3. Объединить: Если R i и R j являются соседями и P (R i UR j ) = TRUE, объединить две области, затем повторите шаг 3. Если таких областей нет, мы закончили.
9 голосов
/ 06 февраля 2013

Это моя реализация.Я не гуру c ++ / opencv, поэтому, если кто-то найдет способ оптимизировать этот скрипт, добавьте комментарии, пожалуйста!

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <iostream>

using namespace cv;
using namespace std;

Mat img;
Size size;

struct region {
    // tree data structure
    vector<region> childs;
    bool validity; // TODO: have a method for clear the data structure and remove regions with false validity

    // tree for split&merge procedure
    Rect roi;
    Mat m;
    Scalar label;
    Mat mask; // for debug. don't use in real cases because it is computationally too heavy.
};

//----------------------------------------------------------------------------------------------------------------------- merging
bool mergeTwoRegion(region& parent, const Mat& src, region& r1, region& r2,  bool (*predicate)(const Mat&)) {
    if(r1.childs.size()==0 && r2.childs.size()==0) {

        Rect roi1 = r1.roi;
        Rect roi2 = r2.roi;
        Rect roi12 = roi1 | roi2;
        if(predicate( src(roi12) )) {
            r1.roi = roi12;

            // recompute mask
            r1.mask = Mat::zeros(size, CV_8U);
            rectangle(r1.mask, r1.roi, 1, CV_FILLED);

            r2.validity = false;
            return true;
        }
    }
    return false;
}

void merge(const Mat& src, region& r, bool (*predicate)(const Mat&)) {
    // check for adjiacent regions. if predicate is true, then  merge.
    // the problem is to check for adjiacent regions.. one way can be:
    // check merging for rows. if neither rows can be merged.. check for cols.

    bool row1=false, row2=false, col1=false, col2=false;

    if(r.childs.size()<1) return;

    // try with the row
    row1 = mergeTwoRegion(r, src, r.childs[0], r.childs[1], predicate);
    row2 = mergeTwoRegion(r, src, r.childs[2], r.childs[3], predicate);

    if( !(row1 | row2) ) {
        // try with column
        col1 = mergeTwoRegion(r, src, r.childs[0], r.childs[2], predicate);
        col2 = mergeTwoRegion(r, src, r.childs[1], r.childs[3], predicate);
    } 

    for(int i=0; i<r.childs.size(); i++) {
        if(r.childs[i].childs.size()>0) 
            merge(src, r.childs[i], predicate);
    }
}

//----------------------------------------------------------------------------------------------------------------------- quadtree splitting
region split(const Mat& src, Rect roi, bool (*predicate)(const Mat&)) {
    vector<region> childs;
    region r;

    r.roi = roi;
    r.m = src;
    r.mask = Mat::zeros(size, CV_8U);
    rectangle(r.mask, r.roi, 1, CV_FILLED);
    r.validity = true;

    bool b = predicate(src);
    if(b) {
        Scalar mean, s;
        meanStdDev(src, mean, s);
        r.label = mean;
    } else {
        int w = src.cols/2;
        int h = src.rows/2;
        region r1 = split(src(Rect(0,0, w,h)), Rect(roi.x, roi.y, w,h), predicate);
        region r2 = split(src(Rect(w,0, w,h)), Rect(roi.x+w, roi.y, w,h), predicate);
        region r3 = split(src(Rect(0,h, w,h)), Rect(roi.x, roi.y+h, w,h), predicate);
        region r4 = split(src(Rect(w,h, w,h)), Rect(roi.x+w, roi.y+h, w,h), predicate);
        r.childs.push_back( r1 );
        r.childs.push_back( r2 );
        r.childs.push_back( r3 );
        r.childs.push_back( r4 );
    }
    //merge(img, r, predicate);
    return r;
}

//----------------------------------------------------------------------------------------------------------------------- tree traversing utility
void print_region(region r) {
    if(r.validity==true && r.childs.size()==0) {
        cout << r.mask << " at " << r.roi.x << "-" << r.roi.y << endl;
        cout << r.childs.size() << endl;
        cout << "---" << endl;
    }
    for(int i=0; i<r.childs.size(); i++) {
        print_region(r.childs[i]);
    }
}

void draw_rect(Mat& imgRect, region r) {
    if(r.validity==true && r.childs.size()==0) 
        rectangle(imgRect, r.roi, 50, .1);
    for(int i=0; i<r.childs.size(); i++) {
        draw_rect(imgRect, r.childs[i]);
    }
}

void draw_region(Mat& img, region r) {
    if(r.validity==true && r.childs.size()==0) 
        rectangle(img, r.roi, r.label, CV_FILLED);
    for(int i=0; i<r.childs.size(); i++) {
        draw_region(img, r.childs[i]);
    }
}

//----------------------------------------------------------------------------------------------------------------------- split&merge test predicates
bool predicateStdZero(const Mat& src) {
    Scalar stddev, mean;
    meanStdDev(src, mean, stddev);
    return stddev[0]==0;
}
bool predicateStd5(const Mat& src) {
    Scalar stddev, mean;
    meanStdDev(src, mean, stddev);
    return (stddev[0]<=5.8) || (src.rows*src.cols<=25);
}

//----------------------------------------------------------------------------------------------------------------------- main
int main( int /*argc*/, char** /*argv*/ )
{
    img = (Mat_<uchar>(4,4) << 0,0,1,1,
                               1,1,1,1, 
                               3,3,3,3,
                               3,4,4,3);

    cout << img << endl;
    size = img.size();

    region r;
    r = split(img, Rect(0,0,img.cols,img.rows), &predicateStdZero);
    merge(img, r, &predicateStdZero);
    cout << "------- print" << endl;
    print_region(r);

    cout << "-----------------------" << endl;

    img = imread("lena.jpg", 0);

    // round (down) to the nearest power of 2 .. quadtree dimension is a pow of 2.
    int exponent = log(min(img.cols, img.rows)) / log (2);
    int s = pow(2.0, (double)exponent);
    Rect square = Rect(0,0, s,s);
    img = img(square).clone();

    namedWindow("original", CV_WINDOW_AUTOSIZE);
    imshow( "original", img );

    cout << "now try to split.." << endl;
    r = split(img, Rect(0,0,img.cols,img.rows), predicateStd5);

    cout << "splitted" << endl;
    Mat imgRect = img.clone();
    draw_rect(imgRect, r);
    namedWindow("split", CV_WINDOW_AUTOSIZE);
    imshow( "split", imgRect );
    imwrite("split.jpg", imgRect);

    merge(img, r, &predicateStd5);
    Mat imgMerge = img.clone();
    draw_rect(imgMerge, r);
    namedWindow("merge", CV_WINDOW_AUTOSIZE);
    imshow( "merge", imgMerge );
    imwrite( "merge.jpg", imgMerge );

    Mat imgSegmented = img.clone();
    draw_region(imgSegmented, r);
    namedWindow("segmented", CV_WINDOW_AUTOSIZE);
    imshow( "segmented", imgSegmented );
    imwrite( "segmented.jpg", imgSegmented );

    while( true )
    {
        char c = (char)waitKey(10);
        if( c == 27 ) { break; }
    }

    return 0;
}
3 голосов
/ 20 ноября 2011

алгоритм сегментации с разделением-слиянием был реализован здесь: http://www.uni -koblenz.de / ~ lb / lb_downloads

...