美文网首页OpenCV
手写区域分裂合并算法

手写区域分裂合并算法

作者: SIENTIST | 来源:发表于2018-09-09 17:00 被阅读0次

    区域分裂合并法:区域分裂合并法是一种图像分割算法。 它与区域生长法略有相似之处,但无需预先指定种子点,而是按某种一致性准则分裂或者合并区域。
    分裂合并法对分割复杂的场景图像比较有效。

    算法的思想并不复杂,总的来说,就是先把图像分成4块,若这其中的一块符合分裂条件,那么这一块又分裂成4块,就这样一直分裂。分裂到一定数量时,以每块为中心,检查相邻的各块,满足一定条件,就合并。
    如此循环往复进行分裂和合并的操作。
    最后合并小区,即把一些小块图像的合并到旁边的大块里。

    运算流程:可以先进行分裂运算,然后再进行合并运算;也可以分裂和合并运算同时进行,经过连续的分裂和合并,最后得到图像的精确分割效果。

    具体实现时,分裂合并算法是基于四叉树数据表示方式进行。


    该算法难点在于分裂与合并的准则并不好判断,分裂的准则又称为均匀性测试准则,用于判断该块图像是否需要分裂。
    最初使用每块图像区域中极大与极小灰度值之差是否在允许的偏差范围来作为均匀性测试准则。 后来均匀性测试准则又被不断的发展。目前,统计检验,比如均方误差最小、F检测等都是最常用的均匀性测试准则。

    介绍一下均方误差最小准则:
    先计算平均值:C是区域 Ω 中N个点的平均值,f(x,y)可以理解为区域 Ω 中点(x,y)的灰度值。


    有了平均值后计算均方差:

    当然还要设定一个阈值,判断均方差大于一定值时就进行分裂。
    ————————————————————————————————

    选用合适的均匀性测试准则对于提高图像分割质量十分重要,当均匀性测试准则选择不当时,很容易会引起“方块效应”。

    我感觉这个算法思想很棒,不用像区域生长一样还要选个“种子点”,而且不是特别复杂,就花一天的时间自己手写代码实现了一下。
    但是发现自己写的程序效果并不尽如人意,总结了一下原因,一个是分裂的准则不好选取,另一个是只凭借灰度值来判断条件太少,如果能加上纹理之类的属性就好了。
    而且我这个程序合并图像块的时间长了点,还需继续优化。感觉还是得上聚类、K-means之类的机器学习算法,甚至深度学习才是终极的图像分割之道。
    ————————————————————————————————
    自己的代码效果:


    原图 图像分裂的效果 分裂后合并的效果

    —————— ————————— —————


    原图 图像分裂的效果 分裂后合并的效果

    分裂的效果还是不错的,但是可能合并程序中的涂色控制与合并准则有点问题,合并的效果并不是特别好


    原图
    分裂后

    我看到网上有人使用递归来进行分裂,这种操作我不太会…… 于是就用了2个vector容器,2个vector轮流操作,一个vector把元素压入另一个vector,同时删除自己的元素,如此循环。

    代码如下:
    my_Image_Process.h 文件

    #ifndef __my_Image_Process_h__
    #define __my_Image_Process_h__
    
    #include<opencv2\opencv.hpp>
    #include<opencv2\imgproc\imgproc.hpp>
    #include<opencv2\highgui/highgui.hpp>
    #include<opencv2\core\core.hpp>
    #include<iostream>
    #include<fstream>
    #include<string>
    #include<cmath>
    
    
    class My_Image_Processing
    {
    public:
        My_Image_Processing(Mat inputImg); //构造函数
        My_Image_Processing(My_Image_Processing &imageProcessing); //复制构造函数
        ~My_Image_Processing(); //析构函数
        Mat get_Split_Merge();
        
    
    private:
        int finshSplitFlag = 0;
        Mat srcImg;
        Mat drawImg;
        Mat grayImg;
        Mat drawResultImg;
    
        vector<Rect> ouRectVector, jiRectVector;
    
        bool decideSplit(Mat srcImg);
        bool decideMerge(Mat Img_1, Mat Img_2);
    
        void ouToJiSplit(); //偶数矩形vector分裂后移入奇数矩形vector
        void jiToOuSplit(); //奇数矩形vector分裂后移入偶数矩形vector
    
        bool twoRectNeighbor(Rect midRect,Rect nearRect); //判断两个矩形是否为相邻的
    
        void mergeImg(vector<Rect> rectVector);
        Mat drawRect(Mat Img, Rect rect, int color);
    
    };
    
    #endif 
    

    ————————————————————————
    my_Image_Process.cpp 文件

    #include"my_Image_Process.h"
    
    using namespace std;
    using namespace cv;
    
    
    My_Image_Processing::My_Image_Processing(Mat inputImg) //构造函数
    {
        srcImg = inputImg;
    }
    
    My_Image_Processing::My_Image_Processing(My_Image_Processing &imageProcessing) //复制构造函数
    {
        srcImg = imageProcessing.srcImg;
    }
    
    My_Image_Processing::~My_Image_Processing() //析构函数
    {
    
    }
    
    void My_Image_Processing::ouToJiSplit() //偶数矩形vector分裂后移入奇数矩形vector
    {
        int width, height;
        int x, y;
        int splitFlag = 0;
    
        for (int i = 0; i < ouRectVector.size(); i++)
        {
            //只有满足分裂准则,并且面积>=8时才进行分裂
            if (decideSplit(grayImg(ouRectVector[i])) && ouRectVector[i].area() >= 6) //8
            {
                x = ouRectVector[i].x;
                y = ouRectVector[i].y;
                width = ouRectVector[i].width;
                height = ouRectVector[i].height;
    
                jiRectVector.push_back(Rect(x + 0, y + 0, width / 2, height / 2));
                jiRectVector.push_back(Rect(x + width / 2, y + 0, width - width / 2, height / 2));
                jiRectVector.push_back(Rect(x + 0, y + height / 2, width / 2, height - height / 2));
                jiRectVector.push_back(Rect(x + width / 2, y + height / 2, width - width / 2, height - height / 2));
    
                //画分割线
                for (int i = 0; i < width; i++)
                    drawImg.at<uchar>(y + height / 2, x + i) = 255;
                for (int i = 0; i < height; i++)
                    drawImg.at<uchar>(y + i, x + width / 2) = 255;
    
                splitFlag = 1;
            }
            else
            {
                jiRectVector.push_back(ouRectVector[i]);
            }
        }
    
        if (!splitFlag) //如果一次都没有进行分裂,则说明到了结束分裂的时候了
            finshSplitFlag = 1;
    
        //ouRectVector到jiRectVector转换完毕后清空ouRectVector
        while (!ouRectVector.empty())
            ouRectVector.pop_back();
    
    
    }
    
    void My_Image_Processing::jiToOuSplit() //奇数矩形vector分裂后移入偶数矩形vector
    {
        int width, height;
        int x, y;
        int splitFlag = 0;
    
        for (int i = 0; i < jiRectVector.size(); i++)
        {
            //只有满足分裂准则,并且面积>=8时才进行分裂
            if (decideSplit(grayImg(jiRectVector[i])) && jiRectVector[i].area() >= 6) //8
            {
                x = jiRectVector[i].x;
                y = jiRectVector[i].y;
                width = jiRectVector[i].width;
                height = jiRectVector[i].height;
    
                ouRectVector.push_back(Rect(x + 0, y + 0, width / 2, height / 2));
                ouRectVector.push_back(Rect(x + width / 2, y + 0, width - width / 2, height / 2));
                ouRectVector.push_back(Rect(x + 0, y + height / 2, width / 2, height - height / 2));
                ouRectVector.push_back(Rect(x + width / 2, y + height / 2, width - width / 2, height - height / 2));
    
                //画分割线
                for (int i = 0; i < width; i++)
                    drawImg.at<uchar>(y + height / 2, x + i) = 255;
                for (int i = 0; i < height; i++)
                    drawImg.at<uchar>(y + i, x + width / 2) = 255;
    
                splitFlag = 1;
            }
            else
            {
                ouRectVector.push_back(jiRectVector[i]);
            }
        }
    
        if (!splitFlag) //如果一次都没有进行分裂,则说明到了结束分裂的时候了
            finshSplitFlag = 1;
    
        //jiRectVector到ouRectVector转换完毕后清空jiRectVector
        while (!jiRectVector.empty()) //注意不能使用 for(i=0;i<jiRectVector.size();i++) jiRectVector.pop_back(); 的方法,因为size()会不断减小
            jiRectVector.pop_back();
    }
    
    Mat My_Image_Processing::get_Split_Merge()
    {
        Mat outImg;
    
        cvtColor(srcImg, grayImg, CV_BGR2GRAY);
        grayImg.copyTo(drawImg);
    
        imshow("gray image", grayImg);
    
        int Count = 0;
    
        ouRectVector.push_back(Rect(0, 0, grayImg.cols, grayImg.rows)); //先将总图像送入ouRectVector
    
        while (!finshSplitFlag /* Count <= 10*/)
        {
            if (Count % 2 == 0) //若Count为偶数,则从 ouMatVector 分裂图像,送入 jiMatVector,同时ouMatVector清空栈
            {
                ouToJiSplit(); //偶到奇的分裂
            }
            else
            {
                jiToOuSplit(); //奇到偶的分裂
            }
    
            Count++;
        }
    
    
        imshow("draw image", drawImg);
    
        cout << "分裂次数为" << Count << endl;
    
        if (Count % 2 == 1)
        {
            cout << "偶到奇  " << endl;
            cout << "偶的size为" << ouRectVector.size() << endl;
            cout << "奇的size为" << jiRectVector.size() << endl;
    
            //  for (int i = 0; i < jiRectVector.size(); i++)
            //      imshow(to_string(i), grayImg(jiRectVector[i]));
        }
        else
        {
            cout << "奇到偶  " << endl;
            cout << "偶的size为" << ouRectVector.size() << endl;
            cout << "奇的size为" << jiRectVector.size() << endl;
    
            //  for (int i = 0; i < ouRectVector.size(); i++)
            //      imshow(to_string(i), grayImg(ouRectVector[i]));
        }
    
        drawImg.copyTo(drawResultImg);
    
        for (int i = 0; i < drawResultImg.rows; i++)
            for (int j = 0; j < drawResultImg.cols; j++)
                drawResultImg.at<uchar>(i, j) = 1;
    
        if (Count % 2 == 0)
            mergeImg(ouRectVector);
        else
            mergeImg(jiRectVector);
    
        imshow("result", drawResultImg);
    
        return outImg;
    }
    
    //在图像中为矩形填充颜色
    Mat My_Image_Processing::drawRect(Mat Img, Rect rect, int color)
    {
        for (int i = rect.y; i <= rect.y + rect.height - 1; i++)
        for (int j = rect.x; j <= rect.x + rect.width - 1; j++)
            Img.at<uchar>(i, j) = color;
    
        return Img;
    }
    
    void My_Image_Processing::mergeImg(vector<Rect> rectVector)
    {
        int finshMergeFlag = 0;
        int mergeCount = 0;
    
        int color = 5;
    
    
        while (rectVector.size()>2) //若已经合并到了一定数量  
        {
            Rect nowRect = rectVector.front(); //得到rectVector中的第一个元素,其下标为0  rectVector.front()
    
            if (drawResultImg.at<uchar>(nowRect.tl().y, nowRect.tl().x) == 1) //若该矩形仍是原色
                color = (color + 30) < 255 ? (color + 30) : 255;
            else
                color = drawResultImg.at<uchar>(nowRect.y, nowRect.x); //若该矩阵不为原色,取该矩阵的颜色
    
    
            drawResultImg = drawRect(drawResultImg, nowRect, color);
    
            if (rectVector.size() >= 2)
            {
                for (int i = 1; i < rectVector.size(); i++) //从第二个元素开始遍历
                {
                    if (twoRectNeighbor(nowRect, rectVector[i]) && decideMerge(grayImg(nowRect), grayImg(rectVector[i])))
                    {
    
                        if (drawResultImg.at<uchar>(rectVector[i].tl().y, rectVector[i].tl().x) != 1)
                            color = drawResultImg.at<uchar>(rectVector[i].y, rectVector[i].x);
    
                        drawResultImg = drawRect(drawResultImg, rectVector[i], color);
    
                    }
                }
            }
    
            rectVector.erase(rectVector.begin()); //删掉遍历后的第一个元素
            mergeCount++;
        }
        cout << "总次数为: " << mergeCount << endl;
        cout << "剩余的数量为" << rectVector.size() << endl;
    
    
    
    }
    
    
    bool My_Image_Processing::decideSplit(Mat srcImg)
    {
        int i = 0, j = 0;
        int maxValue = 0, minValue = 1000;
        bool flag;
    
        for (i = 0; i < srcImg.rows; i++)
        {
            for (j = 0; j < srcImg.cols; j++)
            {
                if (srcImg.at<uchar>(i, j)>maxValue) //取出最大值
                    maxValue = srcImg.at<uchar>(i, j);
    
                if (srcImg.at<uchar>(i, j) < minValue) //取出最小值
                    minValue = srcImg.at<uchar>(i, j);
            }
        }
    
        if (maxValue - minValue>40)  //30
            flag = 1;
        else
            flag = 0;
    
        return flag;
    }
    
    bool My_Image_Processing::decideMerge(Mat Img_1, Mat Img_2)
    {
        int i = 0, j = 0;
        int maxValue = 0, minValue = 1000;
        int Sum_1 = 0, Sum_2 = 0;
        int count_1 = 0, count_2 = 0;
        bool flag;
    
        for (i = 0; i < Img_1.rows; i++)
        {
            for (j = 0; j < Img_1.cols; j++)
            {
                Sum_1 = Sum_1 + Img_1.at<uchar>(i, j);
                count_1++;
            }
        }
        Sum_1 /= count_1; //计算Img_1的平均值
    
        for (i = 0; i < Img_2.rows; i++)
        {
            for (j = 0; j < Img_2.cols; j++)
            {
                Sum_2 = Sum_2 + Img_2.at<uchar>(i, j);
                count_2++;
            }
        }
        Sum_2 /= count_2; //计算Img_2的平均值
    
        if (abs(Sum_1 - Sum_2)<5) //若平均值之差小于20,满足条件,可以合并
            flag = true;
        else
            flag = false;
    
        return flag;
    }
    
    bool My_Image_Processing::twoRectNeighbor(Rect midRect, Rect nearRect) //判断两个矩形是否为相邻的
    {
        bool flag = false;
    
        Rect bigMidRect = midRect;
    
        //将矩形四条边都扩大1
        bigMidRect.x -= 1;
        bigMidRect.y -= 1;
        bigMidRect.width += 2;
        bigMidRect.height += 2;
    
        for (int i = nearRect.tl().x; i < nearRect.br().x; i++)
        {
            if (bigMidRect.contains(Point(i, nearRect.tl().y)) || bigMidRect.contains(Point(i, nearRect.br().y)))
            {
                return true;
            }
        }
    
        for (int i = nearRect.tl().y; i < nearRect.br().y; i++)
        {
            if (bigMidRect.contains(Point(nearRect.tl().x, i)) || bigMidRect.contains(Point(nearRect.br().x, i)))
            {
                return true;
            }
        }
    
    
        return false;
    }
    

    相关文章

      网友评论

        本文标题:手写区域分裂合并算法

        本文链接:https://www.haomeiwen.com/subject/jlxdgftx.html