图像分割之水平集(Level Set)分割

article/2024/12/26 11:38:31

几何活动轮廓模型——水平集分割:Active Contours Without Edges

水平集方法

        水平集是跟踪轮廓和表面运动的一种数字化方法,它不直接对轮廓进行操作,而是将轮廓设置成一个高维函数的零水平集。这个高维函数叫做水平集函数。然后对该水平集函数进行微分,通过从输出中提取零水平集来得到运动的轮廓。使用水平集的主要优点是可以对任意复杂的结构进行模式化和拓扑变换。
        水平集(Level Set)方法是由Sethian和Osher于1988年提出。简单来说,水平集方法把低维的一些计算上升到更高一维,把N维的描述看成是N+1维的一个水平。比如,一个二维平面的圆,如x2+y2=1,可以看成三维中二元函数f(x,y) = x2+y2的1的水平集。如下图
在这里插入图片描述
专业描述:水平集方法将平面闭合曲线隐含地表达为连续函数曲面

几何活动轮廓模型

        这里具体介绍2001年的文献《Active Contours Without Edges》,即Chan-Vese模型。作者提出了基于Mumford–Shah分割技术和水平集方法的活动轮廓模型,该模型不依赖边缘函数,即图像梯度。能量函数有fitting项和一些正则项组成。
在这里插入图片描述
作者把上面关于曲线C的能量函数写成关于水平集函数ϕ的能量函数,然后求解。
在这里插入图片描述
其中Heaviside函数和Delata函数,如下
在这里插入图片描述
最后得到水平集函数的迭代公式:
在这里插入图片描述
在这里插入图片描述

示例演示

        下面基于OpenCV实现该算法。完整工程代码。
头文件

/**********************************************************************Copyright (c) Mr.Bin. All rights reserved.
For more information visit: http://blog.csdn.net/webzhuce**********************************************************************/
/**
* @brief   This is a filter that implements  Tony F. Chan. Active 
*          Contours Without Edges[J].2001
*/
#pragma once
#include "opencv2/opencv.hpp"class LevelSet
{
public:LevelSet(const cv::Mat &src);//initialize level setvoid initializePhi(cv::Point2f center, float radius);void evolving();	private:int iterationnum_ = 100;float lambda1_ = 1.0f;	 //weight for c1 fitting termfloat lambda2_ = 1.0f;   //weight for c2 fitting termfloat mu_ = 0.1 * 255 * 255;	//μ: weight for length termfloat nu_ = 0.0;  //ν: weight for area term, default value 0 float timestep_ = 0.1; //time step: δt//ε: parameter for computing smooth Heaviside and dirac functionfloat epsilon_ = 1.0;	float c1_;	//average(u0) inside level setfloat c2_;	//average(u0) outside level setcv::Mat phi_;		//level set: φcv::Mat dirac_;		//dirac level set: δ(φ)cv::Mat heaviside_;	//heaviside:Н(φ)cv::Mat curv_;		cv::Mat src_;cv::Mat image_; //for showingvoid gradient(const cv::Mat &src, cv::Mat &gradx, cv::Mat &grady);//Dirac functionvoid dirac();//Heaviside functionvoid heaviside();void calculateCurvature();//calculate c1 and c2void calculatC();//show evolvingvoid showEvolving();
};

源文件

/**********************************************************************Copyright (c) Mr.Bin. All rights reserved.
For more information visit: http://blog.csdn.net/webzhuce**********************************************************************/
#include "levelset.h"using namespace cv;
using namespace std;LevelSet::LevelSet(const cv::Mat &src)
{if (src.channels() == 3){cv::cvtColor(src, src_, CV_BGR2GRAY);src.copyTo(image_);}else{src.copyTo(src_);cv::cvtColor(src, image_, CV_GRAY2BGR);}src_.convertTo(src_, CV_32FC1);phi_ = Mat::zeros(src_.size(), CV_32FC1);dirac_ = Mat::zeros(src_.size(), CV_32FC1);heaviside_ = Mat::zeros(src_.size(), CV_32FC1);
}void LevelSet::initializePhi(cv::Point2f center, float radius)
{const float c = 2.0f;float value = 0.0;for (int i = 0; i < src_.rows; i++){for (int j = 0; j < src_.cols; j++){value = -sqrt(pow((j - center.x), 2) + pow((i - center.y), 2)) + radius;if (abs(value) < 0.1) //phi_.at<float>(i, j) = 0;else if (value >=0.1) //insidephi_.at<float>(i, j) = c;elsephi_.at<float>(i, j) = -c;}}
}void LevelSet::gradient(const cv::Mat &src, cv::Mat &gradx, cv::Mat &grady)
{if (src.rows < 2 || src.cols < 2)return;src.copyTo(gradx);src.copyTo(grady);for (int i = 0; i < src.rows; i++){for (int j = 0; j < src.cols; j++){if (j == 0)gradx.at<float>(i, j) = src.at<float>(i, j + 1) - src.at<float>(i, j);else if (j == src.cols - 1)gradx.at<float>(i, j) = src.at<float>(i, j) - src.at<float>(i, j - 1);elsegradx.at<float>(i, j) = (src.at<float>(i, j + 1) - src.at<float>(i, j - 1)) / 2.0;}}for (int j = 0; j < src.cols; j++){for (int i = 0; i < src.rows; i++){if (i == 0)grady.at<float>(i, j) = src.at<float>(i + 1, j) - src.at<float>(i, j);else if (i == src.rows - 1)grady.at<float>(i, j) = src.at<float>(i, j) - src.at<float>(i - 1, j);elsegrady.at<float>(i, j) = (src.at<float>(i + 1, j) - src.at<float>(i - 1, j)) / 2.0;}}}void LevelSet::dirac()
{const float k1 = epsilon_ / CV_PI;const float k2 = epsilon_*epsilon_;for (int i = 0; i < src_.rows; i++){for (int j = 0; j < src_.cols; j++){dirac_.at<float>(i, j) = k1 / (k2 + pow(phi_.at<float>(i, j),2));}}
}void LevelSet::heaviside()
{const float k3 = 2 / CV_PI;for (int i = 0; i < src_.rows; i++){for (int j = 0; j < src_.cols; j++){heaviside_.at<float>(i, j) = 0.5 * (1 + k3 * atan(phi_.at<float>(i, j) / epsilon_));}}
}void LevelSet::calculateCurvature()
{Mat dx, dy;gradient(src_, dx, dy);Mat norm;pow(dx.mul(dx) + dy.mul(dy), 0.5, norm);Mat dxx, dxy, dyx, dyy;gradient(dx / norm, dxx, dxy);gradient(dy / norm, dyx, dyy);curv_ = dxx + dyy;
}void LevelSet::calculatC()
{c1_ = 0.0f;c2_ = 0.0f;float sum1 = 0.0f;float h1 = 0.0f;float sum2 = 0.0f;float h2 = 0.0f;int outsidenum = 0;float value = 0.0f;float h = 0.0f;for (int i = 0; i < src_.rows; i++){for (int j = 0; j < src_.cols; j++){value = src_.at<float>(i, j);h = heaviside_.at<float>(i, j);h1 += h;sum1 = h *  value;h2 += (1 - h);sum2 += (1 - h) * value;}}c1_ = sum1 / (h1 + 1e-10);c2_ = sum2 / (h2 + 1e-10);
}void LevelSet::showEvolving()
{Mat image = image_.clone();Mat mask = phi_ >= 0;cv::dilate(mask, mask, Mat(), Point(-1, -1), 3);cv::erode(mask, mask, Mat(), Point(-1, -1), 3);vector<vector<Point> > contours;findContours(mask, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);drawContours(image, contours, -1, CV_RGB(0, 255,0), 2);imshow("Evolving", image);waitKey(0);
}void LevelSet::evolving()
{showEvolving();//iterationfor (int i = 0; i < iterationnum_; i++){heaviside();dirac();calculateCurvature();calculatC();//update phifor (int i = 0; i < src_.rows; i++){for (int j = 0; j < src_.cols; j++){float curv = curv_.at<float>(i, j);float dirac = dirac_.at<float>(i, j);float u0 = src_.at<float>(i, j);;float lengthTerm = mu_* dirac * curv;float areamterm = nu_ * dirac;float fittingterm = dirac * (-lambda1_ * pow(u0 - c1_, 2) + lambda2_ * pow(u0 - c2_, 2));float term = lengthTerm + areamterm + fittingterm;phi_.at<float>(i, j) = phi_.at<float>(i, j) + timestep_ * term;}}//just for showingshowEvolving();}
}

运行结果

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

参考资料

  • Level set method: Explanation
  • 计算机视觉之图像分割——水平集方法_ACWE2001
  • 水平集图像分割序列——CV模型

http://chatgpt.dhexx.cn/article/Tv48op5x.shtml

相关文章

BGP选路——本地优先级选路(+BGP路由水平分割机制)

目录 一、本地优先级特性 二、配置命令&#xff1a; 三、图解&#xff1a; 四、BGP路由水平分割机制 BGP路由13条选路顺序&#xff1a; 权重——本地优先级——本地始发——最短AS-PATH——起源属性——MED属性——EBGP路由优于IBGP——八——九——十——十一——十二——…

html文字段落分割,HTML设置水平分割线_html/css_WEB-ITnose

在Web中使用水平分割线可以分割不同的文字段落或者其它网页组件&#xff0c;轻松地修饰了段落排版&#xff0c;使之更美观。当然&#xff0c;水平分割线还可以更加明显地突出某一段重要的文字&#xff0c;使之更加醒目。 使用标签可以轻松地设置一条水平线。方法如下&#xff1…

初步了解BGP-2【update-source、水平分割、同步概念】

初步了解BGP-2【update-source、水平分割、同步概念】 目录 初步了解BGP-2【update-source、水平分割、同步概念】Update-sourceIBGP 水平分割IBGP与IGP的同步 Update-source 由于BGP无法像IGP那样自动发现邻居&#xff0c;而BGP的邻居需要手动指定邻居&#xff0c;一般情况下…

html分割线颜色怎么在css中写,html水平分割线 html 分割线颜色怎么变浅

深入理解es6和es6标准入门哪本好 Dubbo的分布式系统架构实战需要哪些步骤完成 HTML如何添加水平分割线: HTML提供了修饰段落的水平分割线&#xff0c;在很多的网页布局中都可以轻松使用&#xff0c;而不需要另外作图。水平分割线的标签是单标签&#xff1a; 默认情况下只占一行…

水平集分割

基于距离正则的水平集分割MATLAB代码&#xff0c;无需初始化 % This Matlab code demonstrates an edge-based active contour model as an application of % the Distance Regularized Level Set Evolution (DRLSE) formulation in the following paper: % % C. Li, C. X…

图像分割 - 水平集算法

水平集介绍 水平集分为三种&#xff1a; 1 . 基于图像边缘灰度梯度信息 &#xff0c;适用于边缘强的图像分割 2 . 基于区域特征 &#xff0c;利用区域信息引导曲线慢慢靠近 &#xff0c;比如分割曲线区域的内外灰度均值&#xff0c;分割曲线内部区域面积&#xff08;例如 Ch…

IBGP水平分割

IBGP水平分割规则 IBGP水平分割用于在IBGP对等体之间进行路由传递时&#xff0c;无法像EBGP对等体那样一来AS-Path属性进行防止环路的问题&#xff0c;因为AS-Path属性在AS内进行传递时是不会发生改变的。 下图便是极有可能出现IBGP对等体环路的场景&#xff1a; R1将10.1.1.…

垂直分割和水平分割

2019独角兽企业重金招聘Python工程师标准>>> 1&#xff0c;水平分割&#xff1a; 例&#xff1a;QQ的登录表。假设QQ的用户有100亿&#xff0c;如果只有一张表&#xff0c;每个用户登录的时候数据库都要从这100亿中查找&#xff0c;会很慢很慢。如果将这一张表分成1…

RIP的水平分割及触发更新(超详细,小白基础实验)

RIP的水平分割及触发更新 希望有需要的小伙伴可以参考参考&#xff0c;写的不好&#xff0c;请多包涵&#xff01; 基本概念&#xff1a; 1&#xff1a;水平分割&#xff08;Split Horizon&#xff09;指的是RIP从某个接口接收到的路由信息&#xff0c;不会从该接口再发给邻居…

分库分表的垂直分割与水平分割

1、垂直分库 根据业务耦合性&#xff0c;将关联度低的不同表存储在不同的数据库。做法与大系统拆分为多个小系统类似&#xff0c;按业务分类进行独立划分。与“微服务治理”的做法相似&#xff0c;每个微服务使用单独的一个系统。如图&#xff1a; 2、垂直分表 基于数据表中的…

一起聊聊 dB、dB、dBm、dBi 吧!

点击上方“小麦大叔”&#xff0c;选择“置顶/星标公众号” 福利干货&#xff0c;第一时间送达 dB应该是无线通信中最基本、最习以为常的一个概念了。我们常说“传播损耗是xx dB”、“发射功率是xx dBm”、“天线增益是xx dBi”……有时候&#xff0c;这些长得很像的dBx们可能被…

单位意义:dB、dBm与dBw、dBμ与dBV、dBi与dBd、dBFS

dB单位概念一直是以前比较模糊的地方&#xff0c;机缘下&#xff0c;就整体的把一些相关的dB单位的文献统一看了一些&#xff0c;下面就简单的解释一下这些基本单位的意义和基本换算。 dB 简单解释下dB产生的由来&#xff0c;dB是decibel的缩写,意即十分之一贝尔(bel)&#xf…

分贝dB、dBm、dBw

文章目录 【1. 物理意义】1.1 功率增益1.2 幅值增益 【2. 3dB】【3. dBm、dBw】 【1. 物理意义】 分贝&#xff08;decibel&#xff0c;/dɛsɪ.bɛl/&#xff09;是量度两个相同单位之数量比例的计量单位&#xff0c;常用dB表示。 1.1 功率增益 A ( P ) ( d B ) 10 l g ( P…

一分钟读懂dB、dBm、dBw的区别

dB应该是无线通信中最基本、最习以为常的一个概念了。我们常说“传播损耗是xx dB”、“发射功率是xx dBm”、“天线增益是xx dBi”…… 有时&#xff0c;这些长得很像的dBx们可能被弄混&#xff0c;甚至造成计算失误。它们究竟有什么区别呢&#xff1f; 这事不得不先从dB说起。…

EMC常见术语-dB、dBm、dBw以及如何计算

1. 手把手教&#xff1a;如何计算dB、dBm、dBw…… dB应该是无线通信中最基本、最习以为常的一个概念了。我们常说“传播损耗是xx dB”、“发射功率是xx dBm”、“天线增益是xx dBi”…… 有时&#xff0c;这些长得很像的dBx们可能被弄混&#xff0c;甚至造成计算失误。它们究…

dB dBm dBW 的关系与换算

前言 这些都叫“分贝数”&#xff0c;表示“相对”的思想。 “dB” 字段可看作 “相对于”&#xff1a; dBdBm (dBmW)&#xff1a;相对于 1 mW 是多少dBW&#xff1a;相对于 1 W 是多少 文中采用方括号 [ ] 表示采用基本功率定义的分贝数 一、定义 1. dB 定义&#xff1a…

dBm和dB(纯计数单位)

分贝毫瓦&#xff08;dBm&#xff09; 分贝毫瓦(dBm&#xff0c;全写为“decibel relative to one milliwatt”)为一个指代功率的绝对值&#xff0c;而不同于dB只是一个相对值。 任意功率P(mW)与xdBm换算的公式如下&#xff1a; 以及 例如&#xff0c;1毫瓦(1 mW)换算成分贝毫…

DDL语言(添加、修改、删除)

数据库意义&#xff1a;数据存储&#xff0c;数据管理 DML语言&#xff1a;数据操作语言&#xff08;insert、update、delete&#xff09; 添加&#xff08;insert&#xff09; 语法&#xff1a; insert into 表名(字段1,字段2,字段3,...) values(值1),(值2),(值3),(...) 例&a…

使用数据库DDL语言创建数据库和基本表?(SQL Server 2014)

摘要&#xff1a;微信搜索【三桥君】 检索&#xff1a;《数据库系统原理》课程实验报告——实验一 建立数据库和基本表结构 说明&#xff1a;本实验是在SQL Server 2014版本数据库下操作完成的。 本实验通过举例创建一个数据库、一张有定义的表、以及添加数据到该表的实验过程&…

实验1 SQL的DDL语言和单表查询

第1关&#xff1a;创建供应商表S(SNO,SNAME,STATUS,CITY) 任务描述 创建供应商表S(SNO,SNAME,STATUS,CITY) 相关知识 供应商表S由供应商代码&#xff08;SNO&#xff09;、供应商姓名&#xff08;SNAME&#xff09;、供应商状态&#xff08;STATUS&#xff09;、供应商所在城市…