基于边缘的主动轮廓模型——从零到一用python实现snake

article/2025/10/16 8:09:55

从零到一实现snake算法

  • 1、Snake算法原理
  • 2、基于曲线演化的实现方法
    • 2.1演化方程推导
    • 2.2离散化过程
    • 2.3 代码实现
  • 3、基于水平集的实现方法
  • 4、讨论与分析
  • 源码地址[snake](https://github.com/woshimami/snake)

1、Snake算法原理

Kass等人1最早于1988年提出了主动轮廓模型,该方法通过构造能量函数,从而将图像分割转化为求解能量泛函极值的问题,其核心思想在于,作者认为和之前的分割方法相比,在不同的图像解读(image interpretation)任务中,表观或者浅层次(low level)的图像判读任务应该需要一些深层次(high level)的图像信息,并论文[1]中进行了举例分析,有兴趣的同学可以自行翻阅论文查看,这也和主动轮廓模型的两大特点不谋而合:全局性和可拓展性。全局性也就是说在图像处理任务进行中,不仅要关注局部区域或者边缘信息,同样也要关注图像或曲线的整体信息如曲线整体的长度和曲率等。再者针对不同的图像特点,能够设计或者添加个性化的分割策略,接下来我将根据公式对此进一步分析。
对于图像 I ( x , y ) , C ( s ) = C ( x ( s ) , y ( s ) ) I(x,y),C(s)=C(x(s),y(s)) I(x,y),C(s)=C(x(s),y(s))为演化曲线,定义snake模型的能量函数为: E s n a k e = E i n t + E e x t E_{snake}= E_{int}+E_{ext} Esnake=Eint+Eext 这里我们区分内部能量和外部能量的依据就是看它是否和演化曲线本身的特性相关。比如说下式中,定义内部能量项的两部分内容分别代表的就是曲线的长度和曲率: E i n t = ∫ 0 1 1 2 ∗ ( α ( s ) ∣ c s ( s ) ∣ 2 + β ( s ) ∣ c s s ( s ) ∣ 2 ) d s E_{int} =\int_0^1 \frac{1}{2}* {(\alpha(s)|c_s(s)|^2+\beta(s)|c_{ss}(s)|^2)} \,{\rm d}s Eint=0121(α(s)cs(s)2+β(s)css(s)2)ds 其中公式中的两项内容分别表示的就是曲线的长度项和曲线的光滑程度, α ( s ) \alpha(s) α(s) β ( s ) \beta(s) β(s)可以根据曲线上某一点的特征进行个性化处理,但是大多数情况下我们一般定义它们为常数,用于调节曲线长度项和光滑程度占比。
而外部能量项通常由两部分组成构成: E e x t = E i m g + E c o n s E_{ext} = E_{img}+E_{cons} Eext=Eimg+Econs E i m g E_{img} Eimg通常表示和图像相关的外部能量项目,主要包含如下三种信息,
(1)

  1. 图像的像素信息(Line functional): E l i n e = ∫ 0 1 I ( x ( s ) , y ( s ) ) d s E_{line}=\int_0^1{I(x(s),y(s))}\,{\rm d}s Eline=01I(x(s),y(s))ds
  2. 图像的梯度信息(edge functional)(注意负号): E e d g e = ∫ 0 1 − ∣ ∇ I ( x ( s ) , y ( s ) ) d s E_{edge}=\int_0^1-|\nabla{I(x(s),y(s))}\,{\rm d}s Eedge=01I(x(s),y(s))ds
  3. 或者曲线周围小范围空间的梯度信息(scale space): E l i n e = ∫ 0 1 − g δ ∗ ∇ 2 I ( x ( s ) , y ( s ) ) d s E_{line}=\int_0^1-{g_{\delta}*\nabla^2I(x(s),y(s))}\,{\rm d}s Eline=01gδ2I(x(s),y(s))ds在实际使用中图像的梯度信息 E e d g e E_{edge} Eedge使用的较为广泛,以及后面的实现过程中,我们也会用 E e d g e E_{edge} Eedge作为图像力,负号的作用就在于,在边缘梯度较大的情况下,整体的能量泛函越小,曲线也就更加趋向于演化到图像的边缘位置。

最后还有一个限制项 E c o n E_{con} Econ,这一项就是对曲线演化添加一定的限制作用,比如限制整体曲线到某一点或者某一区域 R R R的距离: E c o n = ∫ 0 1 ∣ ∣ C ( x ( s ) , y ( s ) ) − R ∣ ∣ d s E_con = \int_0^1{||C(x(s),y(s))-R||}\,{\rm d}s Econ=01C(x(s),y(s))Rds具体的情况大家可以根据图片特点或者需求灵活定义。

2、基于曲线演化的实现方法

好了,终于到了激动人心的实现环节了,大家是不是跟我一样激动的直搓手,哈哈哈哈哈哈哈
开始之前我先说明两点内容:1)本次实现过程基于较为简单基础的能量泛函,大家可以先实现体验一边,然后再触类旁通,举一反三;2)以方面需要对一些数学知识进行回顾分析,另一方面希望大家仔细品一品从连续到离散的变化过程。

2.1演化方程推导

首先我们定义能量函数: E s n a k e = ∫ 0 1 1 2 ∗ ( α ∣ c s ( s ) ∣ 2 + β ∣ c s s ( s ) ∣ 2 − ∣ ∇ I ( x ( s ) , y ( s ) ) ∣ 2 ) d s E_{snake} =\int_0^1 \frac{1}{2}* {(\alpha|c_s(s)|^2+\beta|c_{ss}(s)|^2 -|\nabla{I(x(s),y(s))}|^2)}\,{\rm d}s Esnake=0121(αcs(s)2+βcss(s)2I(x(s),y(s))2)ds,当能量函数最小时,就可以获得 C ( x ( s ) , y ( s ) ) C(x(s),y(s)) C(x(s),y(s))的曲线方程,本质上这也是一个求能量泛函极值的问题,直接根据欧拉-拉格朗日方程(变分原理),当能量泛函取极值时,应满足: − α c ′ ′ ( s ) + β c ′ ′ ′ ′ ( s s ) + ∇ E e x t = 0 -\alpha c^{''}(s)+\beta c^{''''}(ss)+\nabla E_{ext} = 0 αc(s)+βc(ss)+Eext=0 可是就算推到这种程度我们以然没办法直接求出曲线 c ( x ( s ) , y ( s ) ) c(x(s),y(s)) c(x(s),y(s)),尤其是我们希望公式能够以偏微分方程的形式出现,这样我们就可以利用计算机进行编程序迭代计算了,于是我们在曲线中引入了一个辅组变量 t t t,则解可以表示为 c ( t , s ) c(t,s) c(t,s),随时间变化的解 c ( t , s ) c(t,s) c(t,s)应该使得能量函数逐渐变小(推导过程后续会补充),从而可以得出: ∂ c ( s ) ∂ t = α c ′ ′ ( s ) − β c ′ ′ ′ ′ ( s s ) − ∇ E e x t \frac{\partial c(s)}{\partial t} = \alpha c^{''}(s)-\beta c^{''''}(ss)-\nabla E_{ext} tc(s)=αc(s)βc(ss)Eext

2.2离散化过程

接下来就是整个离散化过程了,首先,我们先明确一下概念,上式中的 s s s表示什么意思,因为我们看到的图片都是离散的点,所以我们刚开定义初始化的snake曲线时,实际上定义的也就是一系列离散的点的集合,而曲线的演化过程实际上也就是这些固定数目的点的演化过程,离散化后,我们用 i i i表示 s s s,那假设初始化的snake曲线有 N N N个点,那 i ∈ [ 0 , N − 1 ] i\in [0,N-1] i[0,N1],且 x [ N ] = x [ 0 ] , x [ N + 1 ] = x [ 2 ] . . . . x[N] = x[0],x[N+1]=x[2].... x[N]=x[0],x[N+1]=x[2]....,那么对于曲线上任一点 c ( x ( i ) , y ( i ) ) c(x(i),y(i)) c(x(i),y(i)) x ′ ′ [ i ] = x [ i − 1 ] − 2 x [ i ] + x [ i + 1 ] x^{''}[i] = x[i-1]-2x[i]+x[i+1] x[i]=x[i1]2x[i]+x[i+1] x ′ ′ ′ ′ [ i ] = x [ i − 2 ] − 4 x [ i − 1 ] + 6 x [ i ] − 4 x [ i + 1 ] + x [ i + 2 ] x^{''''}[i] = x[i-2]-4x[i-1]+6x[i]-4x[i+1]+x[i+2] x[i]=x[i2]4x[i1]+6x[i]4x[i+1]+x[i+2]对y也是同理,假设图像 x x x方向和 y y y方向的梯度为 G ( x , y ) G(x,y) G(x,y)(均为正值),那外部力则分别为 G x [ x , y ] , G y [ x , y ] G_{x}[x,y],G_{y}[x,y] Gx[x,y],Gy[x,y],假定每次迭代后,曲线变化较小,我们用 G t − 1 ( x , y ) G_{t-1}(x,y) Gt1(x,y)代替 G t ( x , y ) G_{t}(x,y) Gt(x,y),那么对任一点 c ( x ( i ) , y ( i ) ) c(x(i),y(i)) c(x(i),y(i))就可以推导出离散后的公式为:
∂ x t [ i ] ∂ t = α ( x t [ i − 1 ] − 2 x t [ i ] + x t [ i + 1 ] ) + β ( − x t [ i − 2 ] + 4 x t [ i − 1 ] − 6 x t [ i ] + 4 x t [ i + 1 ] − x t [ i + 2 ] ) + G x ( x t [ i ] , y t [ i ] ) \frac{\partial x_t[i] }{\partial t} = α(x_t[i-1]-2xt[i]+x_t[i+1]) + β(-x_t[i-2]+4x_t[i-1]-6xt[i]+4x_t[i+1]-x_t[i+2]) + G_x(x_t[i],y_t[i]) txt[i]=α(xt[i1]2xt[i]+xt[i+1])+β(xt[i2]+4xt[i1]6xt[i]+4xt[i+1]xt[i+2])+Gx(xt[i],yt[i]) λ \lambda λ表示步长,则可以进一步表示为: x t [ i ] − x t − 1 [ i ] λ = α ( x t [ i − 1 ] − 2 x t [ i ] + x t [ i + 1 ] ) + β ( − x t [ i − 2 ] + 4 x t [ i − 1 ] − 6 x t [ i ] + 4 x t [ i + 1 ] − x t [ i + 2 ] ) + G x ( x t − 1 [ i ] , y t − 1 [ i ] ) \frac{x_t[i] - x_{t-1}[i] }{\lambda} = α(x_t[i-1]-2xt[i]+x_t[i+1]) + β(-x_t[i-2]+4x_t[i-1]-6xt[i]+4x_t[i+1]-x_t[i+2]) + G_x(x_{t-1}[i],y_{t-1}[i]) λxt[i]xt1[i]=α(xt[i1]2xt[i]+xt[i+1])+β(xt[i2]+4xt[i1]6xt[i]+4xt[i+1]xt[i+2])+Gx(xt1[i],yt1[i])同理, y t [ i ] − y t − 1 [ i ] λ = α ( y t [ i − 1 ] − 2 y t [ i ] + y t [ i + 1 ] ) + β ( − y t [ i − 2 ] + 4 y t [ i − 1 ] − 6 y t [ i ] + 4 y t [ i + 1 ] − y t [ i + 2 ] ) + G y ( y t − 1 [ i ] , y t − 1 [ i ] ) \frac{y_t[i] - y_{t-1}[i] }{\lambda} = α(y_t[i-1]-2yt[i]+y_t[i+1]) + β(-y_t[i-2]+4y_t[i-1]-6yt[i]+4y_t[i+1]-y_t[i+2]) + G_y(y_{t-1}[i],y_{t-1}[i]) λyt[i]yt1[i]=α(yt[i1]2yt[i]+yt[i+1])+β(yt[i2]+4yt[i1]6yt[i]+4yt[i+1]yt[i+2])+Gy(yt1[i],yt1[i]) ,如果一条曲线上有 N N N个点,那就对 x x x y y y分别列出 N N N个式子,如果用矩阵表示的话,那就是: X t − X t − 1 λ = A X t + G x ( x t − 1 , y t − 1 ) \frac{X_{t}-X_{t-1}}{\lambda} = AX_t + G_x(x_{t-1},y_{t-1}) λXtXt1=AXt+Gx(xt1,yt1)矩阵A则为: ( − 2 α + 6 β α + 4 β − β 0 0 0 ⋯ − β α + 4 β α + 4 β − 2 α + 6 β α + 4 β − β 0 0 ⋯ 0 − β − β α + 4 β − 2 α + 6 β α + 4 β − β 0 ⋯ 0 0 0 − β α + 4 β − 2 α + 6 β α + 4 β − β ⋯ 0 0 0 0 − β α + 4 β − 2 α + 6 β α + 4 β ⋯ 0 0 ⋮ ⋮ ⋮ ⋱ ⋮ − β 0 0 0 0 0 ⋯ − 2 α + 6 β α + 4 β α + 4 β − β 0 0 0 0 ⋯ α + 4 β − 2 α + 6 β ) \begin{pmatrix} -2\alpha+6\beta & \alpha+4\beta & -\beta & 0 & 0& 0 & \cdots & -\beta & \alpha+4\beta & \\ \alpha+4\beta & -2\alpha+6\beta & \alpha+4\beta & -\beta & 0& 0 & \cdots & 0 & -\beta & \\ -\beta & \alpha+4\beta & -2\alpha+6\beta & \alpha+4\beta & -\beta & 0 & \cdots & 0 & 0 & \\ 0 & -\beta & \alpha+4\beta & -2\alpha+6\beta & \alpha+4\beta & -\beta & \cdots & 0 & 0 & \\ 0 & 0 & -\beta & \alpha+4\beta & -2\alpha+6\beta & \alpha+4\beta & \cdots & 0 & 0 & \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ -\beta & 0 &0 & 0 & 0& 0 & \cdots & -2\alpha+6\beta & \alpha+4\beta & \\ \alpha+4\beta & -\beta &0 & 0 & 0& 0 & \cdots & \alpha+4\beta & -2\alpha+6\beta & \\ \end{pmatrix} 2α+6βα+4ββ00βα+4βα+4β2α+6βα+4ββ00ββα+4β2α+6βα+4ββ000βα+4β2α+6βα+4β0000βα+4β2α+6β00000βα+4β00β00002α+6βα+4βα+4ββ000α+4β2α+6β,求解矩阵即可得出: X t = ( 1 − λ A ) − 1 X t − 1 + λ G x ( X t − 1 , Y t − 1 ) X_t = (1-\lambda A)^{-1}{X_{t-1}+\lambda G_x(X_{t-1},Y_{t-1})} Xt=(1λA)1Xt1+λGx(Xt1,Yt1) Y t = ( 1 − λ A ) − 1 Y t − 1 + λ G y ( X t − 1 , Y t − 1 ) Y_t = (1-\lambda A)^{-1}{Y_{t-1}+\lambda G_y(X_{t-1},Y_{t-1})} Yt=(1λA)1Yt1+λGy(Xt1,Yt1)至此,需要的核心演化公式到手。

2.3 代码实现

看完上面的离散化过程,是不是大家现在都胸有成竹,跃跃欲试,蠢蠢欲动啦,哈哈,接下来就让我们一起开始愉快的coding吧。
声明使用的依赖库主要有:

import numpy as np
import cv2
import matplotlib.pyplot as plt
from pylab import*
import skimage.filters as filt

代码实现总共分为如下几个步骤:

  1. 读入图片:
    随手用windows画图工具画了个圆(简约派):
    size:189*256
    然后读入图片,注意
Image = cv2.imread('xxx.bmp',1)
image = cv2.cvtColor(Image,cv2.COLOR_BGR2GRAY)
img = np.array(image,dtype = np.float)
plt.imshow(img,cmap = 'gray') # 读入灰度图
print(shape(img))
  1. 定义初始化snake曲线,注意 注意 注意,此处我们需要首先明确x,y分别表示矩阵的列和行,同时也就意味着绘图的时候x,y分别表示x-y坐标系的横坐标轴和纵坐标轴呀,一定要品明白哈,不然会出大问题的哈(说多了都是泪)
t = np.linspace(0,2*np.pi,60,endpoint = True)
y = 100+30*np.cos(t)
x = 100+30*np.sin(t)
  1. 然后定义关键参数,部分参数值参考原论文:
alpha = 0.001
beta  = 0.4
gamma = 100 # 迭代步长
sigma = 20  # 滤波用的高斯分布参数
iterations = 300 # 迭代次数
  1. 计算矩阵,有没有发现整个矩阵只由参数控制,而和图片无关:
N = np.size(x)
a = gamma*(2*alpha+6*beta)+1
b = gamma*(-alpha-4*beta)
c = gamma*betap = np.zeros((N,N),dtype=np.float)
p[0] = np.c_[a,b,c,np.zeros((1,N-5)),c,b]
print(p[0].shape)
for i in range(N):p[i] = np.roll(p[0],i)
p = np.linalg.inv(p)
  1. 计算外部力,因为手绘图像噪声较小,很难直接通过梯度产生外部力,因此先对图像进行高斯滤波处理,注意此处的卷积核和高斯分布参数都选择的较大,大家可以品一品为什么要这样做:
smoothed = cv2.GaussianBlur((img-img.min()) / (img.max()-img.min()),(89,89),sigma)
giy,gix  = np.gradient(smoothed)
gmi = (gix**2+giy**2)**0.5
gmi = (gmi - gmi.min()) / (gmi.max() - gmi.min())
Iy,Ix = np.gradient(gmi)
  1. 定义函数,确保演化曲线不超出图像边界,并返回整数形位置信息:
def fmax(x,y):x[x < 0] = 0y[y < 0] = 0x[x > img.shape[1]-1] = img.shape[1]-1y[y > img.shape[0]-1] = img.shape[0]-1return y.round().astype(int),x.round().astype(int)
  1. 迭代计算,并显示图像:
plt.plot(y,x,'.')
for i in range(iterations):fex = Ix[fmax(x,y)]fey = Iy[fmax(x,y)]x = np.dot(p,x+gamma*fex)y = np.dot(p,y+gamma*fey)if i%10 ==0:plt.plot(x,y,'.')
plt.show()

计算结果,撒花,撒花,撒花!
曲线演化过程

3、基于水平集的实现方法

本来这一段打算拖一拖的,但是看到评论区有哥们儿问到了,我就仔细找了找文献,看看怎么给处理下,结果发现自己给自己挖了个大坑,对于原始的snake算法来讲,使用level set并不是一个很可行的方案或者说需要对snake算法做一些改变,那么为什么将level set方法直接引用到snake模型上不合适呢?
1)首先,大家需要明白常见的主动轮廓模型的常见处理流程:1、构建能量泛函; 2、极小化能量泛函(欧拉-拉格朗日方程+梯度下降法)获得用于演化的偏微分方程; 3、离散化偏微分方程; 4、代码实现与调试,一般都是从步骤1或者步骤2中引入level set,可以参见我的另一篇博客(水平集方法引入主动轮廓模型算法中的两种方法);2)但是,对于原始的snake算法来讲,只存在从步骤1即从能量函数中引入水平集函数的可能性,不过因为那样可能处理起来太麻烦,所以后来Caselles 等人就在1993年直接对原始snake算法就行了调整,提出了改进的几何活动轮廓模型,不仅引入了水平集方法还摆脱了原始snake模型对参数的依赖问题。
因此,总的来说,将level set方法引入snake是理论上的可行性的,只不过比较麻烦,这个博主最近时间比较紧张,等过一段时间闲了再想办法给实现下吧(又立flag…)。

4、讨论与分析

接下来就让我们愉快的讨(tu)论(cao)下snake算法吧! 有一说一,不得不承认,Kass等人1当初将图像分割问题转化为求解能量泛函极值的问题确实很有想象力,这个控制领域的李雅普诺夫函数简直有异曲同工之妙之妙,很好奇当时作者哪来的这么大的脑洞哈。

  1. 先说优点,因为能量函数的构造方式,就决定了这种方法具有很好的扩展性,这也是主动轮廓模型能够针对具体应用问题不断改进的重要原因;
  2. 但是缺点也很明显:1)snake模型太过于依赖参数,针对不同的图像图像都需要找出"合适的参数"才能完美的分割轮廓,大家可以尝试在代码中调试参数值就会有所体会,甚至减少初始演化点的个数都会导致演化失败,如下两图分别是相同参数,初始时设定60个演化点和30个演化点的演化结果区别,初始60个演化点初始30个演化点,在不改变图像及参数的情况下,仅仅因为演化点个数减少,演化结果就迥然不同,这就凸显了snake模型对参数的依赖性以及算法较差的鲁棒性,这显然不能符合实际应用需求。因此后来Caselles等人2提出了几何活动轮廓模型对snake进行了改进:1)曲线能够自动地根据图像特征如梯度而不是依赖人工设定参数演化到目标边缘;2)引入了水平集方法,用面的演化代替了点的演化,并且使得图像能够处理拓扑结构的变化。

源码地址snake

[1] (KASS,M. snake Active contour models[J]. Proc.first Intl Conf.computer Vision, 1988, 4.)
[2] (Caselles V . Geometric models for active contours[C]// International Conference on Image Processing. IEEE Computer Society, 1995.)

http://chatgpt.dhexx.cn/article/25hDVkFM.shtml

相关文章

主动轮廓模型snake

原理概述 snake模型将图像分割问题转换为求解能量泛函最小值的问题。主要思路是构造能量函数进行迭代后&#xff0c;轮廓曲线由初始位置逐渐向使能量函数最小&#xff08;局部极小&#xff09;的图像边缘逼近&#xff0c;最终分割出目标。 曲线理论 假设一条光滑封闭曲线 C …

腾讯电脑管家,vs安装文件报成木马,还能信吗?

今天在公司安装vs2013&#xff0c;安装过程中腾讯公司的产品“电脑管家”提示有新版本&#xff0c;没有犹豫的点了升级&#xff0c;完成后直接在管家主界面上点了“全面体检”按钮&#xff0c;这一点不要紧&#xff0c;报告有一个木马&#xff0c;看紧看一下“详情”&#xff0…

计算机windows8黑屏怎么办,Win8电脑开机黑屏只有鼠标光标怎么解决

有些win8系统用户在开机的时候&#xff0c;遇到了黑屏的情况&#xff0c; 整个屏幕上面只有一个闪烁的鼠标光标&#xff0c;导致无法进入到系统桌面&#xff0c;遇到这样的情况该怎么解决呢&#xff1f;现在给大家分享一下Win8电脑开机黑屏只有鼠标光标的具体解决方法吧。 Win8…

解决ValueError: Cannot run multiple SparkContexts at once; existing SparkContext

一、问题描述 创建sparkcontext和SparkSession&#xff0c;连接spark集群时报错&#xff0c;如题ValueError: Cannot run multiple SparkContexts at once; existing SparkContext。 from pyspark.sql import SparkSession from pyspark.sql import functions as F from pysp…

1、Qt线程(二):继承QThread,重写run

一、功能说明 1、通过继承QThread&#xff0c;重写run的方式实现多线程 2、点击“开始”按钮启动子线程&#xff0c;同时通过信号槽的方式给子线程发送“开始”字符串&#xff1b; 3、子线程每隔1秒向主线程发送累加数&#xff1b; 4、点击"停止"按钮&#xff0c…

诡异的RunOnce病毒启动项和神奇的URL Protocol

整理磁盘发现之前有个有趣的流氓招数忘记分享了,每次看到新鲜的东东都感慨黑暗势力的层出不穷的招数,比某些安全厂商是不是自相残杀好多了.电脑日常使用过程中我们经常输入开头为http ftp,点击诸如ed2k的链接,每个链接的背后都会执行相应的功能.如http 通过iexplore.exe,ed2k通…

如何创建水晶报表

开发工具与关键技术&#xff1a;VS与MVC 作者&#xff1a;刘华叶 撰写时间&#xff1a;2019年4月29日 MVC是软件工程中的一种软件架构模式&#xff0c;涉及到的知识点也是相当广泛&#xff0c;而我们在做项目的过程中&#xff0c;总会遇到要制作一些报表&#xff0c;水晶报表就…

java 水晶报表教程_水晶报表 (Crystal Reports 2008)的配置

概要&#xff1a; Crystal Reports(水晶报表)是一款商务智能(BI)软件&#xff0c;主要用于设计及产生报表。水晶报表是业内最专业、功能最强的报表系统&#xff0c;它除了强大的报表功能外。最大的优势是实现了与绝大多数流行开发工具的集成和接口。在VS.Net平台做过报表开发的…

水晶报表教程:手把手教你制作基本报表

ASP.NET水晶报表的学习 这篇文章教你如何在.Net Web应用中使用水晶报表&#xff0c;也可以让你在学习过程中少走一些弯路。为了得到最好的效果&#xff0c;读者最好需要有一些基础的Asp.Net访问数据库的知识以及使用VS.Net的开发经验。 简介 水晶报表可以由很多的方法得到&a…

水晶报表基本使用方法

开发工具与关键技术&#xff1a;VS/MVC 作者&#xff1a;何桂朋 撰写时间&#xff1a;2019年4月22日 Crystal Reports&#xff08;水晶报表&#xff09;是一款商务智能&#xff08;BI&#xff09;软件&#xff0c;主要用于设计及产生报表。水晶报表是业内最专业、功能最强的报…

php try catch 不处理,如何解决php try catch不起作用的问题

如何解决php try catch不起作用的问题 发布时间&#xff1a;2020-09-22 09:59:14 来源&#xff1a;亿速云 阅读&#xff1a;103 作者&#xff1a;小新 这篇文章主要介绍了如何解决php try catch不起作用的问题&#xff0c;具有一定借鉴价值&#xff0c;需要的朋友可以参考下。希…

try catch执行过程分析

本篇文章带大家聊聊try catch的执行过程&#xff0c;有时候在开发的过程中&#xff0c;try代码里如果出现异常&#xff0c;catch后的步骤还会继续执行吗&#xff1f;以及finally的使用。 下面来分析一下几种使用场景&#xff1a; 场景一&#xff1a; try代码块中出现异常后&a…

面试官问我 ,try catch 应该在for循环里面还是外面?

前言 有个老哥昨天被面试官欺负了&#xff0c;但是是被这个问题&#xff08;标题&#xff09;欺负的&#xff1f; 其实是个比较基础的问题&#xff0c;只要有了解过&#xff0c;叙述是非常简单OK的。 只要有初学者觉得有疑惑&#xff0c;那么我相信不止是他一个。 所以&#…

try catch异常捕获

这次我们介绍try catch异常捕获。下面是try catch的语法。 thy catch 的作用就是异常捕获&#xff0c;在一些会报错的地方时的时候才会用到。 例如这个代码&#xff0c;当我们在控制器时输入的不是数字而是其他的文字或者其他符号呢&#xff1f;所以这里程序就会报错&#xff0…

try catch 的作用

不加try catch package com.web; public class Test25 {public static void main(String[] args) {int i 1 / 0;System.out.println("i " i);System.out.println("aaaa");//直接不运行了} }加try catch package com.web;public class Test25 {public …

kotlin try catch使用方法

5 kotlin之 try catch kotlin的try catch比Java更简洁面料采用,使用更方便 val value "10a"var out: Int? null//错误处理try {out Integer.parseInt(value)} catch (e: NumberFormatException) {println("NumberFormatException")println(e.message)…

try catch的作用

try catch的作用&#xff1a;当程序发生错误时&#xff0c;能够保证程序继续执行下去。 用一个简单例子说明&#xff1a; 1:无try catch public static void main(String[] args) { int i; i 2/0; System.out.println(i); System.out.println(1111111111); } 运行结果&…

try catch finally

结论&#xff1a; 1、不管有木有出现异常&#xff0c;finally块中代码都会执行&#xff1b; 2、当try和catch中有return时&#xff0c;finally仍然会执行&#xff1b; 3、finally是在return后面的表达式运算后执行的&#xff08;此时并没有返回运算后的值&#xff0c;而是先把要…

try catch 嵌套

实践测试 在单元测试中写入以下方法: testMain()主方法, out()里面嵌套了两层try catch 异常代码写在内层try中 示例一&#xff1a; Testpublic void testMain(){out();log.info("后续处理业务");}public void out(){//外层trytry {System.out.println("外层输…

try和catch的用法

try 和 catch 是用于处理异常的语句&#xff0c;它们构成了一种异常处理机制。在一个 try 语句中&#xff0c;程序执行一段代码&#xff0c;如果发生了异常&#xff0c;则会被捕获&#xff0c;并转到相应的 catch 语句中进行处理。 例如&#xff0c;在下面的代码中&#xff0c…