图像分割-区域生长算法的实现

采用区域生长法作为图像分割方法,它的基本原理是将相同特征的像素点归为一类。并且这些特征在针对具体应用的实现中可以是灰度值、像素梯度等(同时作为比较的对象,即可以选择最初的种子,也可以动态选择邻域的中心)。
作为区域增长的起点(种子)的选择同样重要,根据图像的复杂情况,可以选择多个点作为初始值,当有多个种子,在区域增长时,需要考虑相同特征像素点的合并问题。因此最终分割对象的数量要小于等于种子数量。

优缺点分析:

  • 优点:思想相对简单,可以分割出封闭的区域,在复杂环境下有较好的分割效果;
  • 缺点:对噪声敏感,会造成空洞(一般需要进行平滑操作),时间和空间复杂度较大

区域分割算法实现的一般步骤:

  1. 选择合适方法和数量的种子;
  2. 确定邻域内(8邻域或4邻域)不同像素点特征的计算和比较方法;
  3. 确定生长停止的条件

本文主要为理解区域分割法的核心思想,采用人工选择的种子。以灰度值作为像素点的特征;特征比较方式是与最开始的种子进行比较(+/-5);将邻域内且边界内相同特征的像素点push到STL的stack中(也可以用queue吧,没有比较过);生长的停止条件为遍历完容器内所有符合要求的点。

使用OpenCV实现如下:

RegionGrowup.h

#include<opencv2/opencv.hpp>
#include<vector>
class RegionGrowup
{
public:
	RegionGrowup(cv::Mat src); //构造函数
	~RegionGrowup() {}		   //析构函数

	void SetInitPoints(std::vector<cv::Point2i> points);
	cv::Mat computI();

private:
	int _height, _width;
	int _channels;
	cv::Mat _src;
	std::vector<cv::Point2i> PointShift2D;
	std::vector<cv::Point2i> InitPoints2D; //使用单个种子点进行测试
	cv::RNG rng;
};

RegionGrowup.cpp

#include "Func.h"
#include <stack>
#include <ctime>
RegionGrowup::RegionGrowup(cv::Mat src) : _src(src)
{
    this->_height = src.rows;
    this->_width = src.cols;
    this->_channels = src.channels();

    PointShift2D.push_back(cv::Point2i(1, 0));
    PointShift2D.push_back(cv::Point2i(-1, 0));
    PointShift2D.push_back(cv::Point2i(0, -1));
    PointShift2D.push_back(cv::Point2i(0, 1));
    PointShift2D.push_back(cv::Point2i(-1, -1));
    PointShift2D.push_back(cv::Point2i(-1, 1));
    PointShift2D.push_back(cv::Point2i(1, -1));
    PointShift2D.push_back(cv::Point2i(1, 1));
}

void RegionGrowup::SetInitPoints(std::vector<cv::Point2i> points)
{
    this->InitPoints2D = points;
}

cv::Mat RegionGrowup::computI()
{
    cv::Mat dst = cv::Mat(_height, _width, CV_8UC3, cv::Scalar(0, 0, 0));
    //此处再对_src进行判断
    if (!_src.data)
        return dst;

    //将原图转换为灰度图
    if (_channels != 1)
    {
        cv::cvtColor(_src, _src, cv::COLOR_RGB2GRAY);
    }
    //首先处理背景
    cv::Point2i backPoint(5, 5);
    uchar backValue = _src.at<uchar>(backPoint);

    //获得单个种子点的灰度值
    int j = 0;
    for (auto index : InitPoints2D)
    {
        uchar SeedValue = _src.at<uchar>(index);
        if (SeedValue >= backValue - 20 && SeedValue <= backValue + 20)
            continue;
        //构建栈用于保存和种子点处于同一区域的点
        std::stack<cv::Point2i> pointStack;
        pointStack.push(index);

        int temp = j * 30;
        int r = rng.uniform(temp, 255 + temp) - temp;
        int g = rng.uniform(temp, 255 + temp) - temp;
        int b = rng.uniform(temp, 255 + temp) - temp;
        //判断种子点的8邻域(4邻域)范围的点
        while (!pointStack.empty())
        {
            cv::Point2i top = pointStack.top();
            pointStack.pop();
            for (int i = 0; i < 8; ++i)
            {
                //获得一个方向上的点(相对于当前点)
                cv::Point2i p = top + PointShift2D[i];
                //需要对边界外边的灰度值进行判断
                if ((p.x < 0 || p.x > _width) || (p.y < 0 || p.y > _height))
                    continue;
                //获得该点的灰度值
                uchar pvalue = _src.at<uchar>(p);
                uchar topvalue = _src.at<uchar>(top);
                //std::cout << (int)pvalue << std::endl;
                //当该点的灰度值在SeedValue+-5范围内时,可以将该点
                //和InitPoint归类为同一类
                if ((pvalue > topvalue - 2 && pvalue < topvalue + 2) && (dst.at<cv::Vec3b>(p) == cv::Vec3b(0, 0, 0)))
                {
                    //同时将这个点push到stack中
                    cv::Vec3b color(r, g, b); //根据j的值计算颜色
                    dst.at<cv::Vec3b>(p) = color;
                    pointStack.push(p);
                }
            }
        }
        ++j;
    }
    return dst;
}

测试函数main.cpp

#include "Func.h"
#include<random>
void PrintImage(cv::Mat src,std::vector<cv::Point2i>pots);
std::vector<cv::Point2i>GetRandomPoint(int nrows,int ncols,int n);

int main(int argc, char **argv)
{
	cv::Mat src = cv::imread("../0004ResizeBlur.jpg", 0);
	if (!src.data)
	{
		std::cout << "fail to load image" << std::endl;
		return -1;
	}
	cv::imshow("1", src);
	
	RegionGrowup rg(src);
    //获取随机点
	std::vector<cv::Point2i>pots=GetRandomPoint(src.rows,src.cols,50);

	//PrintImage(src,pots);
	rg.SetInitPoints(pots); //这边以一个点作为测试
	cv::Mat dst = rg.computI();
	if (!dst.data)
	{
		std::cout << "compute error" << std::endl;
		return -1;
	}
	cv::imshow("2", dst);
	cv::waitKey(0);
	cv::destroyAllWindows();
	return 0;
}

void PrintImage(cv::Mat src,std::vector<cv::Point2i>pots)
{
	cv::cvtColor(src,src,cv::COLOR_GRAY2RGB);
	for(auto index:pots)
	{
		cv::circle(src,index,2,cv::Scalar(0,0,255),-1);
	}
	cv::imshow("srcPoint",src);
}

std::vector<cv::Point2i>GetRandomPoint(int nrows,int ncols,int n)
{
    std::vector<cv::Point2i>res;
    std::default_random_engine generator;
    std::normal_distribution<double>distuibutionx(ncols/2,65);
    std::normal_distribution<double>distuibutiony(nrows/2,65);
    for(int i=0;i<n;++i)
    {
        cv::Point2i temp;
        temp.y = static_cast<int>(distuibutiony(generator));
        temp.x = static_cast<int>(distuibutionx(generator));
        if(temp.x>=0&&temp.x<=ncols&&temp.y>=0&&temp.y<=nrows) res.push_back(temp);
    }
    return res;
}

结果显示:

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值