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

发布于 2024-11-29 04:27:33 字数 35 浏览 0 评论 0原文

图像分割的分割和合并方法有实现吗? 任何建议将不胜感激。

Is there an implementation for the split and merge method of image segmentation?
any advice would be much appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

旧竹 2024-12-06 04:27:33

分割是什么?

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

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

  1. 分割完成,因此所有子区域总和为整个 R。所有区域的并集为 R1 U R2 U ... U Rn = R。R
  2. i 已连接。
  3. 地区不同。 Ri∩Rj=∅ 假设 i≠j
  4. 区域具有相似的属性。这可以用称为同质性准则 (P) 的函数来表达。对于给定区域的成员,它应该给出 TRUE,对于所有其他区域,它应该给出 FALSE。
  5. 相邻区域不能合并。对于所有区域,假设 i≠j,P(Ri U Rj)=FALSE。

分割和合并算法是关于什么的?

所以首先,我们必须选择同质性标准。同质性标准可以是全局的(取决于整个区域)或局部的(取决于该区域的一个小窗口,并且如果它对于所有窗口都成立,那么对于该区域也成立)。一个简单的例子是与平均值的偏差应该小于阈值。 ∀pi∈Ri: |pi-μ|≤f*σ。

拆分和合并算法有两个阶段:拆分和合并阶段。
在分割阶段,我们递归地将区域分割为四个子区域(从整个图像作为一个区域开始),直到所有子区域都满足我们的同质性标准。很容易看出满足1-4个分割条件。我们继续合并步骤以满足第五个条件。

在合并步骤中,我们检查每两个相邻区域的 P(Ri U Rj)=TRUE,然后合并这两个区域。我们重复此步骤,直到不需要进行更多更改。现在我们满足了所有条件,我们将图像分割成子区域。

伪代码

这是一个分割和合并算法的伪代码:

  1. Init:我们只有一个大区域(整个图像)。
  2. 拆分:如果 P(Ri)=TRUE,则继续下一步。否则将 Ri 细分为四个子区域并对它们执行步骤 2。
  3. 合并:如果 Ri 和 Rj 是邻居且 P(Ri U Rj) = TRUE,则合并两个区域,然后重复步骤 3。如果没有这样的区域,我们就完成了。

What segmentation is about?

Segmentation means division of your image into several connected regions. Basically, you could do segmentation with two definitions of region: you could define a region as a group of connected similar pixels, or a set of connected pixels surrounded by discontinuities (edges). Split and merge uses the first approach.

Mathematically speaking: if your whole image is represented by the set of pixels (called R), than you would like to get subsets such as

  1. Segmentation is complete, so all subregions sum up to the whole R. Union of all regions is R1 U R2 U ... U Rn = R.
  2. Ri is connected.
  3. Regions are distinct. Ri∩Rj=∅ given that i≠j
  4. Regions have similar properties. This could be expressed by a function called the homogenity criterion (P). It should give TRUE for the members of the given region, and FALSE for all the other regions.
  5. Neighbor regions cannot been merged. For all regions P(Ri U Rj)=FALSE given that i≠j.

What split and merge algorithm is about?

So first, we have to choose a homogenity criterion. A homogenity criterion could be global (depending on the whole region) or local (depending on a small window of the region, and if it's true for all windows, than it's true for the region). A simple example could be the deviation from average should be smaller than a threshold. ∀pi∈Ri: |pi-μ|≤f*σ.

The split and merge algorithm have two phases: the split, and the merge phase.
In the split phase we recursively split regions into four subregions (starting with the whole image as one region) until our homogenity criterion is met in all subregions. It's easy to see that the 1-4 conditions of segmentation are met. We proceed to merge step in order to satisfy the 5th condition.

In the merge step we check that P(Ri U Rj)=TRUE for each two neighbor regions, and merge the two regions. We repeat this step until no more changes are necessary. Now we met all the conditions, we had our image segmented into subregions.

Pseudocode

Here is a pseudocode to split and merge algorithm:

  1. Init: we have only one big region (the whole image).
  2. Split: If P(Ri)=TRUE proceed to next step. Otherwise subdivide Ri to four subregions and perform step 2 on them.
  3. Merge: If Ri and Rj are neighbors and P(Ri U Rj) = TRUE, merge the two regions, than repeat step 3. If there are no such regions we are finished.
爱殇璃 2024-12-06 04:27:33

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

This is my implementation. I am not a c++ / opencv guru so if someone find out some way to optimize this script add comments please!

#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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文