图像分割 - 拆分和合并(四叉树)

时间:2011-08-13 11:34:29

标签: c# computer-vision image-segmentation

是否有图像分割的拆分和合并方法的实现? 任何建议都会非常感激。

3 个答案:

答案 0 :(得分:14)

有哪些细分?

分割意味着将图像划分为多个连接区域。基本上,您可以使用两个区域定义进行分割:您可以将区域定义为一组连接的相似像素,或者将一组连接的像素定义为不连续(边缘)。拆分和合并使用第一种方法。

从数学上讲:如果你的整个图像由像素集(称为R)表示,那么你想获得的子集如

  1. 分割完成,因此所有子区域总和为整个R.所有区域的联合是R 1 UR 2 U ... UR n < / sub> = R。
  2. R i 已连接。
  3. 地区不同。 R i ∩R j =∅假设i≠j
  4. 区域具有相似的属性。这可以通过称为同质性标准(P)的函数来表示。对于给定区域的成员,它应该为TRUE,对于所有其他区域,它应该为FALSE。
  5. 无法合并邻居区域。对于所有区域P(R i U R j )= FALSE,假设i≠j。
  6. 什么是拆分和合并算法?

    首先,我们必须选择同质性标准。均匀性标准可以是全局的(取决于整个区域)或局部的(取决于区域的小窗口,如果对于所有窗口都是如此,则对于该区域是真的)。一个简单的例子可能是偏离平均值应小于阈值。 ∀p i ∈R i :| p i -μ|≤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 )=是,合并两个区域,而不是重复步骤3.如果没有这样的区域,我们就完成了。

答案 1 :(得分:9)

这是我的实施。我不是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;
}

答案 2 :(得分:3)

这里实现了拆分合并分段算法:http://www.uni-koblenz.de/~lb/lb_downloads