图像分割之cvSnakeImage

用OpenCV3.14复现经典:手把手教你实现1988年的Snake主动轮廓分割算法 本文详细介绍了如何使用OpenCV3.14复现1988年提出的Snake主动轮廓分割算法,包括算法原理、OpenCV实现及优化技巧。通过能量最小化方法,该算法在医疗图像分割、交互式编辑等场景中仍具优势。文章还探讨了现代优化策略,如矩阵求逆加速和多尺度处理,使经典算法焕发新生。 阅读详情

基本概念

        OpenCV之前有个cvSnakeImage函数实现了Snakes活动轮廓模型,但是cvSnakeImage函数在opencv2中已经被去掉。由于没有无法得知其依据的论文,这里根据源代码简单介绍它的原理。遍历每个轮廓的点,计算该点邻域内每个点的能量E,将能量最小的点代替当前点。E= alpha * Econt + beta * Ecurv + gamma * Eimg。Econ表示轮廓点的距离信息,Ecurv表示轮廓点的曲率信息,Eimg表示像素或者像素梯度。
        用OpenCV3.x重新实现后,发现貌似只对二值化图像分割有作用。

示例演示

        用OpenCV3.x重新实现cvSnakeImage函数。完整工程代码。

void SnakeImage::segment(std::vector<cv::Point> &points)
{
    drawCurve(points);
	if (points.size() < 3)
		return;

	int i = 0, j = 0, k = 0;
	//win_ is a domain, in which  search minimization of energy for each point
	//neighbors is the number of points in win_
	int neighbors = win_.height * win_.width;
	//the center of win_
	int centerx = win_.width >> 1;
	int centery = win_.height >> 1;

	//energy terms of neighbors of every points
	float *Econt = new float[neighbors];
	float *Ecurv = new float[neighbors];
	float *Eimg = new float[neighbors];
	float *E = new float[neighbors];

	//for two order difference
	int tx = 0, ty = 0;

	//for calculating distance
	float averagedistance = 0, diffx = 0, diffy = 0;

	//for calculating moving
	int offsetx = 0, offsety = 0;

	//temporary variables
	float tmp = 0.0, energy = 0.0;

	//if SNAKE_GRAD
	cv::Mat dx, dy, src;
	src_.convertTo(src, CV_16SC1);
	Sobel(src, dx, src.depth(), 1, 0, 1);
	Sobel(src, dy, src.depth(), 0, 1, 1);

	// the number of moved points in each iteration
	int movecount = 0;

	bool converged = false;
	int iteration = 0;
	while (!converged)
	{
		movecount = 0; // the number of moved points
		iteration++;

		//the average of distance between points
		averagedistance = 0;
		for (i = 0; i < points.size(); i++)
		{
			if (i != 0)
			{
				diffx = points[i - 1].x - points[i].x;
				diffy = points[i - 1].y - points[i].y;
			}
			else //n-1->0
			{
				diffx = points[points.size() - 1].x - points[i].x;
				diffy = points[points.size() - 1].y - points[i].y;
			}
			averagedistance += std::sqrt(std::pow(diffx, 2) + std::pow(diffy, 2));
		}
		averagedistance /= points.size();

		//search each point
		for (i = 0; i < points.size(); i++)
		{
			//calculate Econt
			float maxEcont = 0;
			float minEcont = SNAKE_BIG;
			//calculate reasonable search boundaries 
			int left = std::min(points[i].x, centerx);
			int right = std::min(src_.cols - 1 - points[i].x, centerx);
			int upper = std::min(points[i].y, centery);
			int bottom = std::min(src_.rows - 1 - points[i].y, centery);
			for (j = -upper; j <= bottom; j++)
			{
				for (k = -left; k <= right; k++)
				{
					if (i == 0)
					{
						diffx = points[points.size() - 1].x - (points[i].x + k);
						diffy = points[points.size() - 1].y - (points[i].y + j);
					}
					else
					{
						diffx = points[i - 1].x - (points[i].x + k);
						diffy = points[i - 1].y - (points[i].y + j);
					}
					energy = std::abs(averagedistance - std::sqrt(std::pow(diffx, 2) + std::pow(diffy, 2)));
					Econt[(j + centery) * win_.width + k + centerx] = energy;
					maxEcont = std::max(maxEcont, energy);
					minEcont = std::min(minEcont, energy);
				}
			}
			//if maxEcont == minEcont, set Econt all to zero
			//else normalize Econt
			tmp = maxEcont - minEcont;
			tmp = (tmp == 0.0f) ? 0.0f : (1.0 / tmp);
			for (k = 0; k < neighbors; k++)
			{
				Econt[k] = (Econt[k] - minEcont) * tmp;
			}

			//calculate Ecurv
			float maxEcurv = 0.0f;
			float minEcurv = SNAKE_BIG;
			for (j = -upper; j <= bottom; j++)
			{
				for (k = -left; k <= right; k++)
				{
					if (i == 0)
					{
						tx = points[points.size() - 1].x - 2 * (points[i].x + k) + points[i + 1].x;
						ty = points[points.size() - 1].y - 2 * (points[i].y + j) + points[i + 1].y;
 					}
					else if (i == points.size() - 1)
					{
						tx = points[i - 1].x - 2 * (points[i].x + k) + points[0].x;
						ty = points[i - 1].y - 2 * (points[i].y + j) + points[0].y;
					}
					else
					{
						tx = points[i - 1].x - 2 * (points[i].x + k) + points[i + 1].x;
						ty = points[i - 1].y - 2 * (points[i].y + j) + points[i + 1].y;
					}
					energy = std::pow(tx, 2) + std::pow(ty, 2);
					Ecurv[(j + centery) * win_.width + k + centerx] = energy;
					maxEcurv = std::max(maxEcurv, energy);
					minEcurv = std::min(minEcurv, energy);
				}
			}
			// normalize as above
			tmp = maxEcurv - minEcurv;
			tmp = (tmp == 0.0f) ? 0.0f : (1.0 / tmp);
			for (k = 0; k < neighbors; k++)
			{
				Ecurv[k] = (Ecurv[k] - minEcurv) * tmp;
			}

			//calculate Eimg
			float maxEimg = 0.0f;
			float minEimg = SNAKE_BIG;
			for (j = -upper; j <= bottom; j++)
			{
				for (k = -left; k <= right; k++)
				{
					if (scheme_ == SNAKE_GRAD)
					{
						energy = std::pow(dx.at<short>(points[i].y + j, points[i].x + k), 2);
						energy += std::pow(dy.at<short>(points[i].y + j, points[i].x + k), 2);
						Eimg[(j + centery) * win_.width + k + centerx] = energy;
					}
					else
					{
						energy = src_.at<uchar>(points[i].y + j, points[i].x + k)  ;
						Eimg[(j + centery) * win_.width + k + centerx] = energy;
					}
					maxEimg = std::max(maxEimg, energy);
					minEimg = std::min(minEimg, energy);
				}
			}
			// normalize as above 
			tmp = (maxEimg - minEimg);
 			tmp = (tmp == 0.0f) ? 0.0f : (1.0 / tmp);
			for (k = 0; k < neighbors; k++)
			{
				Eimg[k] = (minEimg - Eimg[k]) * tmp;
			}

			//calculate energy of neighbors
			for (k = 0; k < neighbors; k++)
			{
				E[k] = alpha_ * Econt[k] + beta_ * Ecurv[k] + gamma_ * Eimg[k];
			}
			//find minimum energy of neighbors
			float Emin = SNAKE_BIG;
			for (j = -upper; j <= bottom; j++)
			{
				for (k = -left; k <= right; k++)
				{
					if (E[(j + centery) * win_.width + k + centerx] < Emin)
					{
						Emin = E[(j + centery) * win_.width + k + centerx];
						offsetx = k;
						offsety = j;
					}
				}
			}
			//if moved
			if (offsetx || offsety)
			{
				points[i].x += offsetx;
				points[i].y += offsety;
				movecount++;
			}
		} // for each point

		//judge convergence
		converged = (movecount == 0);
		if ((termcriteria_.type & CV_TERMCRIT_ITER) && (iteration >= termcriteria_.maxCount))
			converged = 1;
		if ((termcriteria_.type & CV_TERMCRIT_EPS) && (movecount <= termcriteria_.epsilon))
			converged = 1;

		drawCurve(points);
	} //for each iteration

	//free resource
	delete[] Econt;
	delete[] Ecurv;
	delete[] Eimg;
	delete[] E;   
}

运行结果

相机
在这里插入图片描述
圆
在这里插入图片描述

别再只调OpenCV函数了!深入理解Snake主动轮廓模型的三大能量项(线、边、末端) 本文深入解析Snake主动轮廓模型的三大能量项(线、边、末端),揭示其算法原理及OpenCV实现中的关键调参技巧。通过物理隐喻和数学公式,详细阐述能量函数如何控制轮廓演化,并提供医学影像分割的实战参数配置与动态调参策略,帮助开发者超越基础API调用,掌握模型核心机制。 阅读详情

相关推荐

基于Opencv3的活动轮廓模型--CV模型

见博客http://blog.youkuaiyun.com/dingkeyanlail/article/details/78818386

主动轮廓线模型Snake模型简介&openCV中cvSnakeImage()函数代码分析

主动轮廓线模型又称为Snake模型,自Kass于1987年提出以来,已广泛应用于数字图像分析和计算机视觉领域。由于Snake模型具有良好的提取和跟踪特定区域内目标轮廓的能力,因此非常适合于医学图像如CT和MR图像的处理,以获得特定器官及组织的轮廓。简单的来讲,Snake模型就是一条可变形的参数曲线及相应的能量函数,以最小化能量目标函数为目标,控制参数曲线变形,具有最小能量的闭合曲线就是目标轮廓。 Snake模型具有一些经典方法所无法比拟的优点:图像数据、初始估计、目标轮廓及基于知识的约束统一于一个过程中;经

天下对手教会少林武僧 2664

opencv3.0移植cvSnakeImage

最近做一个项目,用到了opencv3.0,又需要用cvSnakeImage函数,但是opencv3.0中删除了这个函数,于是花了几天的时间把这个函数移植过来了,经测试可用,本代码中附带一个可用的SnakePlus类,可直接使用。

Snake Contour 蛇形轮廓分割算法总结 以及 CVPR2020 Deep Snake for Real-Time Instance Segmentation

图线分割的基本概念以及算法 图像分割指的是根据灰度、颜色、纹理和形状等特征把图像划分成若干互不交迭的区域,并使这些特征在同一区域内呈现出相似性,而在不同区域间呈现出明显的差异性。 1. 基于阈值的分割方法 阈值法的基本思想是基于图像的灰度特征来计算一个或多个灰度阈值,并将图像中每个像素的灰度值与阈值相比较,最后将像素根据比较结果分到合适的类别中。因此,该类方法最为关键的一步就是按照某个准则函数来求解最佳灰度阈值。 2. 基于边缘的分割方法 边缘是两个不同区域的边界线上连续的像素点的集合,是图像局部特征不连

weixin_43120238的博客 2371

蛇形分割算法+python

蛇形分割算法+python,能对图片进行较好的分割,压缩内含有示例图

ABB机器人ModbusTCP通信实战:如何高效传输Float数据到PLC(附完整代码)

本文详细介绍了ABB机器人通过ModbusTCP协议高效传输Float数据到PLC的实战方法,包括底层逻辑解析、RAPID程序架构设计、PLC服务器配置及现场调试技巧。特别针对浮点数据传输中的字节顺序问题提供了解决方案,并附有完整代码示例,助力工程师快速实现工业自动化设备间的高精度数据交互。

weixin_30836759的博客 482

主动轮廓线模型Snake模型简介&openCV中cvSnakeImage()函数代码分析

主动轮廓线模型又称为Snake模型,自Kass于1987年提出以来,已广泛应用于数字图像分析和计算机视觉领域。由于Snake模型具有良好的提取和跟踪特定区域内目标轮廓的能力,因此非常适合于医学图像如CT和MR图像的处理,以获得特定器官及组织的轮廓。简单的来讲,Snake模型就是一条可变形的参数曲线及相应的能量函数,以最小化能量目标函数为目标,控制参数曲线变形,具有最小能量的闭合曲线就是目标轮廓。 ...

蚂蚁搬家 1万+

AT32F403A ADC通道管理与转换模式实战解析

本文深入解析AT32F403A微控制器的ADC模块,重点阐述其通道管理机制与四种转换模式(序列模式、抢占自动转换模式、反复模式、分割模式)的实战应用。通过详细的配置步骤与代码示例,帮助开发者高效实现模拟数字转换,优化数据采集性能与系统响应。

sky77的博客 279

图像处理之其他杂项(四)之cvSnakeImage()函数代码升级,从C接口到C++接口:snakeImage()

cvSnakeImage()函数代码升级,从C接口到C++接口:snakeImage() 1.snake算法存在于老版本的opencv中,采用的是C接口,函数为cvSnakeImage(),新版本opencv2中取消了这个函数,现对于网上存在的老代码进行升级为C++接口,适合opencv2中使用,对于算法的具体实现原理未具体明白,只是在原有代码的基础上按葫芦画瓢,对于相应代码、函数进行了替换

coming_is_winter的博客 906

别再乱选离散化方法了!从零到一用Matlab对比差分、双线性Tustin和预矫正的实战效果

本文深入探讨了Matlab中四种主流离散化方法(差分、双线性Tustin和预矫正)的实战效果对比,帮助工程师在数字控制系统设计中做出明智选择。通过详细的理论分析和Matlab代码示例,展示了不同方法在稳定性、频率响应和计算复杂度上的差异,并提供了针对不同场景的工程选用指南。

weixin_28737603的博客 141

13-单片机模块化程序: 单片机AT指令配置模块程序模板(非阻塞版)

资料源码:https://gitee.com/yang456/OpenProgrammingModuleForMCU.git 点击加入群聊【单片机,物联网,上位机】: 说明1:知识从未如此性感。 烂程序员关心的是代码,好程序员关心的是数据结构和它们之间的关系! 说明2:学的是思想,而非程序!此代码思路适用于所有的单片机。 说明3:学会以后,下面的代码可能会跟你一辈子! 说明4:这一系列文章是为大幅度裁剪本人博客文章!使博客文章更有条理。便于推其它教程! 拷贝这两个文件到自己的工程 ..

杨奉武的博客 1280

Snake分割

Active Contours 被用作物体边界精确定位上,opencv 给出了一个实现,首先给出物体大致边界的外包围曲线作为初始曲线,然后通过迭代方式寻找邻域最小值,直到能量函数收敛或达到迭代次数停止。alpha, beta, gamma 为积分项对应的系数,可以是一个固定值或者关于参数 p 的函数,coeffUsage 表示参数类型(固定值或者参数 p 的函数),6)给定初始曲线,在初始曲线的一个邻域内搜索最小能量值,并使用该最小能量值曲线作为新的搜索曲线,直到达到收敛条件为止;2)将曲线 C 分解为。

luofeiju的专栏 864

Debezium报错处理系列之二十八: Expiring 1 record(s) for test-ts-0:120001 ms has passed since batch creation

Debezium报错处理系列之二十八: Expiring 1 record(s) for test-ts-0:120001 ms has passed since batch creation

zhengzaifeidelushang的博客 3290

openCV中cvSnakeImage()函数代码分析

/*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. // // By downloading, copying, inst

lbd2008的专栏 9010

MATLAB基础应用精讲-【图像处理】GNN图神经网络(附python代码实现)

图神经网络由于其在处理非欧空间数据和复杂特征方面的优势,受到广泛关注并应用于推荐系统、知识图谱、交通道路分析等场景。图结构可描述复杂关系,如基因结构、通信网络、交通路线、社交网络等。图计算挖掘结构信息,但无法学习节点特征。神经网络在欧氏空间数据上表现优越,但无法直接应用于非欧氏空间的图数据。图神经网络结合图计算和神经网络优势,处理非欧氏空间数据及其复杂特征,应用于网络链接预测、推荐系统和交通道路分析等场景。实际应用中,图数据规模庞大,如百亿级节点、千亿级边,存储开销超过10TB。

qq_36130719的博客 1139

uni-app - 刘海屏(Iphone X)底部横杠(——)白色区域块(安全距离处更改颜色)解决方案

场景:因为要做无tabbar页面,所以在配置文件中取消底部导航栏。取消后,iPhoneX(刘海屏)底部就出现如下图所示白色安全区域问题:这个问题,是因为uni-app默认会为iPhoneX(刘海屏)留出安全距离,这个安全距离的背景色就是白色。所以,如果你将页面颜色设置为其他颜色,就会与默认白色冲突。有2个不同的解决方案(原生占位),虽然原理是一样,但使用的场景不同,请根据您的需求来采纳。

🏆 前端领域优质创作者 1万+

OpenCV3中实现Snake算法踩坑实录:从原理推导到代码调试的完整避坑指南

本文详细解析了OpenCV3中实现Snake算法的完整流程,从主动轮廓模型的能量方程推导到代码调试的实战技巧。通过分析内部能量参数设置、图像能量归一化处理以及五对角矩阵优化等关键环节,帮助开发者避开常见陷阱,提升算法在医学影像和工业检测中的精确度与效率。

weixin_33725126的博客 324

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

图像处理之图像分割(一)之活动轮廓模型:Snake算法简单梳理

图像处理之图像分割之Snake算法简单梳理   Snake算法,应该也可以翻译成蛇形算法,或者是包含曲折前进的意思。具体函数背景原理介绍参考:zouxy09,http://blog.youkuaiyun.com/zouxy09/article/details/8712287,图像分割之(五)活动轮廓模型之Snake模型简介,这里还是就几个自己思考的重点拓展一下。     1. 这

coming_is_winter的博客 1万+

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

【OpenCV 基础知识 15】蛇形处理算法

函数 cvSnakeImage 更新 snake 是为了最小化 snake 的整个能量,其中能量是依赖于轮廓形状的内部能量(轮廓越光滑,内部能量越小)以及依赖于能量场的外部能量之和,外部能量通常在哪些局部能量极值点中达到最小值(这些局部能量极值点与图像梯度表示的图像边缘相对应)。如果在迭代中去掉的点数目小于 criteria.epsilon 或者函数达到了最大的迭代次数 criteria.max_iter ,则终止函数。

记录日常技术经验 1052

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

opencv主动轮廓模型

前言 主动轮廓线模型,又叫做Snake模型。它对噪声和对比度不敏感,能够将矿堆从具有复杂车辙等噪声的地面背景中提取出来。Snake的主要原理是在我们提供一个原始轮廓以后,对其定义能量函数,使轮廓沿着能量降低的方向靠近,当能量函数达到最小的时候,我们就认为原始轮廓已经收敛到矿堆的真实轮廓。Snake的能量函数分为内部能量函数和外部能量函数,其中内部能量控制轮廓的平滑和连续。外部能量控制着收敛。具体公...

我的博客 3644

cvsnakeimage 的例子

opencv例子里没有提供cvsnakeimage的使用方法,在此整理一个例子,可以形象的看看snake算法的结果,大致做法是:首先设定域值分割,把基本的轮廓找出来,见图中蓝色轮廓线,再将轮廓点传入cvSnakeImage函数,计算出绿色的snake轮廓线。其中参数alpha代表点相互靠拢的权值(0-1.0),beta表示弯曲能量(越小越容易弯曲)(0-1.0),gamma表示整体能量(0

7992

【主动轮廓模型(一)】《Snakes: Active Contour Models》算法原理与OpenCV实现

1 概述 主动轮廓模型(也称Active Contour Model、Snake)是Kass等人在1988年提出的,该算法将图像分割问题转换为求解能量泛函最小值的问题。主要思路是通过构造能量泛函,经过算法迭代,轮廓曲线由初始位置逐渐向使能量函数最小(或局部极小)的图像边缘逼近,最终分割出目标。 2 算法原理 首先需要人为地在图像上给出初始轮廓曲线,确切的说是一组用于控制曲线形状的控制点:v(s)=[x(s),y(s)]s∈[0,1]v(s)=[x(s),y(s)] s\in[0,1]v(s)=[x(s),y

CCC的专栏 1万+

[转]-Snake模型&&openCV代码

Snake模型&&openCV代码 出处:http://blog.youkuaiyun.com/hongxingabc/article/details/51606520        主动轮廓线模型又称为Snake模型,由Kass于1987年提出,它对于在噪声和对比度不敏感,能将目标从复杂背景中分割出来,并能有效的跟踪目标的形变和非刚体的复杂运动而被广泛用于图像分割和物体跟踪等图像处理领域。已广泛

lizhi403的博客 1022
上一篇: 图像处理之高斯滤波的几种实现方式
下一篇: 泛函分析之变分法
暮-成-雪
博客等级 码龄12年 5328粉丝 319原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值