opencv基础60-用分水岭算法cv2.distanceTransform()实现图像分割与提取原理及示例

在图像处理的过程中,经常需要从图像中将前景对象作为目标图像分割或者提取出来。例如,在视频监控中,观测到的是固定背景下的视频内容,而我们对背景本身并无兴趣,感兴趣的是背景中出现的车辆、行人或者其他对象。我们希望将这些对象从视频中提取出来,而忽略那些没有对象进入背景的视频内容。

在前面的章节中,我们讨论了如何使用诸如图像形态学变换、阈值算法、图像金字塔、图像轮廓、边缘检测等方法对图像进行分割。本节介绍使用分水岭算法及 GrabCut 算法对图像进行分割及提取。

算法原理

冈萨雷斯在《数字图像处理》一书中,对分水岭算法进行了细致的分析与介绍。OpenCV的官网建议学习者阅读国立巴黎高等矿业学校图像处理实验室(The Image ProcessingLaboratory of MINES ParisTech)的 CMM(Centre for Mathematical Morphology)网站上关于分水岭算法的介绍和动画演示。

下面对分水岭算法的相关内容做简单的介绍。

任何一幅灰度图像,都可以被看作是地理学上的地形表面,灰度值高的区域可以被看成是山峰,灰度值低的区域可以被看成是山谷。如图 17-1 所示,其中左图是原始图像,右图是其对应的“地形表面”。

如果我们向每一个山谷中“灌注”不同颜色的水(这里采用了 OpenCV 官网的表述,冈萨 雷斯将灌注表述为在山谷中打洞,然后让水穿过洞以均匀的速率上升)。那么,随着水位不断 地升高,不同山谷的水就会汇集到一起。在这个过程中,为了防止不同山谷的水交汇,我们需 要在水流可能汇合的地方构建堤坝。该过程将图像分成两个不同的集合:集水盆地和分水岭线。我们构建的堤坝就是分水岭线,也即对原始图像的分割。这就是分水岭算法。

在这里插入图片描述
在图 17-2 中,左图是原始图像,右图是使用分水岭算法得到的图像分割结果。在 CMM 的网站上不仅提供了该示例图像,还提供了动画演示效果,有兴趣的读者可以去网站上看看。

在这里插入图片描述
由于噪声等因素的影响,采用上述基础分水岭算法经常会得到过度分割的结果。过度分割会将图像划分为一个个稠密的独立小块,让分割失去了意义。

图 17-3 展示了过度分割的图像。其中左图是电泳现象的图像,右图是过度分割的结果图像,可以看到过度分割现象非常严重。

在这里插入图片描述

为了改善图像分割效果,人们提出了基于掩模的改进的分水岭算法。改进的分水岭算法允许用户将他认为是同一个分割区域的部分标注出来(被标注的部分就称为掩模)。这样,分水岭算法在处理时,就会将标注的部分处理为同一个分割区域。大家可以尝试使用微软PowerPoint
中的“删除背景”功能,加深对此改进算法的理解。

在图 17-4 中,左图是原始图像,我们对其做了标注处理,其中被标注为深色的三个小色块表示:在使用掩模分水岭算法时,这些部分所包含的颜色都会被分割在同一个区域内。使用掩模分水岭算法得到的分割结果如图 17-4 中的右图所示。

在这里插入图片描述

采用改进的分水岭算法对图 17-5 中左侧的电泳图像进行掩模处理,得到右侧的分割结果。可以看出,分割结果得到明显的改进。

在这里插入图片描述

函数 cv2.watershed()介绍

在 OpenCV 中,可以使用函数 cv2.watershed()实现分水岭算法。在具体的实现过程中,还需要借助于形态学函数、距离变换函数 cv2.distanceTransform()、cv2.connectedComponents()来
完成图像分割。下面对分水岭算法中用到的函数进行简单的说明。

1. 形态学函数回顾

在使用分水岭算法对图像进行分割前,需要对图像进行简单的形态学处理。先回顾一下形态学里的基本操作。
(1)开运算
开运算是先腐蚀、后膨胀的操作,开运算能够去除图像内的噪声。例如,在图 17-6 中,先对左图进行腐蚀操作,会得到中间的图像,再对中间的图像进行膨胀操作,会得到右侧的图像。对左图进行开运算(先腐蚀、后膨胀)后,我们得到了右图。

通过观察可知,左图在经过开运算后变成右图以后,上面的毛刺(噪声信息)已经被去除了。

在这里插入图片描述
对图像进行开运算,能够去除图像内的噪声。在用分水岭算法处理图像前,要先使用开运算去除图像内的噪声,以避免噪声对图像分割可能造成的干扰。

(2)获取图像边界
通过形态学操作和减法运算能够获取图像的边界。

例如,在图 17-7 中,左图是原始图像,中间的图是对其进行腐蚀而得到的图像,对二者进行减法运算,就会得到右侧的图像。通过观察可知,右图是左图的边界。

在这里插入图片描述

示例:使用形态学变换,获取一幅图像的边界信息,并观察效果。

原图:

在这里插入图片描述

import cv2
import numpy as np
import matplotlib.pyplot as plt
o=cv2.imread("rice.png",cv2.IMREAD_UNCHANGED)
#构造一个5*5的结构元素
k=np.ones((5,5),np.uint8)
#腐蚀
e=cv2.erode(o,k)
#膨胀
b=cv2.subtract(o,e)plt.subplot(131)
plt.imshow(o)
plt.axis('off')
plt.subplot(132)
plt.imshow(e)
plt.axis('off')
plt.subplot(133)
plt.imshow(b)
plt.axis('off')
plt.show()

运行结果:

在这里插入图片描述
其中左图是原始图像,中间的图是对其进行腐
蚀而得到的图像,右图是原始图像减去腐蚀图像后得到的边界图像。可以看到,右图比较准确地显示出了左图内前景对象的边界信息。

通过以上分析可知,使用形态学操作和减法运算能够获取图像的边界信息。但是,形态学操作仅适用于比较简单的图像。如果图像内的前景对象存在连接的情况,使用形态学操作就无法准确获取各个子图像的边界了。

2. 距离变换函数distanceTransform

当图像内的各个子图没有连接时,可以直接使用形态学的腐蚀操作确定前景对象,但是如果图像内的子图连接在一起时,就很难确定前景对象了。此时,借助于距离变换函数
cv2.distanceTransform()可以方便地将前景对象提取出来。

距离变换函数 cv2.distanceTransform()计算二值图像内任意点到最近背景点的距离。一般情况下,该函数计算的是图像内非零值像素点到最近的零值像素点的距离,即计算二值图像中所有像素点距离其最近的值为 0 的像素点的距离。当然,如果像素点本身的值为 0,则这个距离也为 0。

距离变换函数 cv2.distanceTransform()的计算结果反映了各个像素与背景(值为0的像素点)
的距离关系。通常情况下:

  • 如果前景对象的中心(质心)距离值为 0 的像素点距离较远,会得到一个较大的值。
  • 如果前景对象的边缘距离值为 0 的像素点较近,会得到一个较小的值。

如果对上述计算结果进行阈值化,就可以得到图像内子图的中心、骨架等信息。距离变换函数 cv2.distanceTransform()可以用于计算对象的中心,还能细化轮廓、获取图像前景等,有多种功能。
距离变换函数 cv2.distanceTransform()的语法格式为:

dst=cv2.distanceTransform(src, distanceType, maskSize[, dstType]])

式中:

  • src 是 8 位单通道的二值图像。
  • distanceType 为距离类型参数,其具体值和含义如表 17-1 所示。

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

  • maskSize 为掩模的尺寸,其可能的值如表 17-2 所示。需要注意,当 distanceType =cv2.DIST_L1 或 cv2.DIST_C 时,maskSize 强制为 3(因为设置为 3 和设置为 5 及更大值没有什么区别)。

在这里插入图片描述

  • dstType 为目标图像的类型,默认值为 CV_32F。
  • dst 表示计算得到的目标图像,可以是 8 位或 32 位浮点数,尺寸和 src 相同。

示例:使用距离变换函数 cv2.distanceTransform(),计算一幅图像的确定前景,并观察效果。

import numpy as np
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('water_coins.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
ishow=img.copy()
#对图像阈值化处理,得到二值图像,黑色背景,白色前景,前景为我们要提取的目标,背景为0,前景为1
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
#定义一个3*3的卷积核,卷积核的作用是将前景与背景分离
kernel = np.ones((3,3),np.uint8)
#对二值图像进行开运算,去除噪声,开运算是先腐蚀再膨胀的过程,它可以去除小的干扰块,平滑较大的对象边界,同时并不明显改变其面积
opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 2)
#距离变换,得到每一个前景像素点到最近的背景像素点的距离,然后对其进行阈值化处理,得到的结果就是前景
dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5)
#对膨胀后的图像进行阈值化处理,得到前景,前景为我们要提取的目标,背景为0,前景为1,这样就得到了我们要提取的目标,但是目标并不连续
ret, fore = cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)plt.subplot(131)
plt.imshow(ishow)
plt.axis('off')
plt.subplot(132)
plt.imshow(dist_transform)
plt.axis('off')
plt.subplot(133)
plt.imshow(fore)
plt.axis('off')
plt.show()

结果如下:

在这里插入图片描述

  • 左图是原始图像。
  • 中间的是距离变换函数 cv2.distanceTransform()计算得到的距离图像。
  • 右图是对距离图像进行阈值化处理后的结果图像。

右图比较准确地显示出左图内的“确定前景”。这里的确定前景,通
常是指前景对象的中心。之所以认为这些点是确定前景,是因为它们距离背景点的距离足够远,都是距离大于足够大的固定阈值(0.7*dist_transform.max())的点。

3. 确定未知区域

使用形态学的膨胀操作能够将图像内的前景“膨胀放大”。当图像内的前景被放大后,背景就会被“压缩”,所以此时得到的背景信息一定小于实际背景的,不包含前景的“确定背景”。

以下为了方便说明将确定背景称为 B。距离变换函数cv2.distanceTransform()能够获取图像的“中心”,得到“确定前景”。为了方便说明,将确定前景称为 F。图像中有了确定前景 F 和确定背景 B,剩下区域的就是未知区域 UN 了。
这部分区域正是分水岭算法要进一步明确的区域。
针对一幅图像 O,通过以下关系能够得到未知区域 UN:

未知区域 UN = 图像 O - 确定背景 B - 确定前景 F

对上述表达式进行整理,可以得到:

未知区域 UN =(图像 O - 确定背景 B)- 确定前景 F

上式中的“图像 O - 确定背景 B”,可以通过对图像进行形态学的膨胀操作得到。

示例:标注一幅图像的确定前景、确定背景及未知区域。

import numpy as np
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('water_coins.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
ishow=img.copy()
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
kernel = np.ones((3,3),np.uint8)
opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 2)
#对图像进行膨胀操作,得到背景
bg = cv2.dilate(opening,kernel,iterations=3)dist = cv2.distanceTransform(opening,cv2.DIST_L2,5)
ret, fore = cv2.threshold(dist,0.7*dist.max(),255,0)
fore = np.uint8(fore)
un = cv2.subtract(bg,fore)
plt.subplot(221)
plt.imshow(ishow)
plt.axis('off')
plt.subplot(222)
plt.imshow(bg)
plt.axis('off')
plt.subplot(223)
plt.imshow(fore)
plt.axis('off')
plt.subplot(224)
plt.imshow(un)
plt.axis('off')
plt.show()

运行结果:

在这里插入图片描述

  • 左上角是原始图像 ishow。
  • 右上角是对图像 ishow 进行膨胀后得到的图像 bg,其背景图像是确定背景,前景图像是“原始图像-确定背景”。
  • 左下角是确定前景图像 fore。
  • 右下角图像中的小圆环就是未知区域图像 un,是由图像 bg 和图像 fore 相减得到的。也就是说,未知区域图像 un 来源于“原始图像-确定背景-确定前景”。

4. 函数connectedComponents

明确了确定前景后,就可以对确定前景图像进行标注了。在 OpenCV 中,可以使用函数
cv2.connectedComponents()进行标注。该函数会将背景标注为 0,将其他的对象使用从 1 开始的正整数标注。
函数 cv2.connectedComponents()的语法格式为:

retval, labels = cv2.connectedComponents( image )

式中:

  • image 为 8 位单通道的待标注图像。
  • retval 为返回的标注的数量。
  • labels 为标注的结果图像。

示例:使用函数 cv2.connectedComponents()标注一幅图像,并观察标注的效果。

import numpy as np
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('water_coins.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
ishow=img.copy()
ret, thresh = cv2.threshold(gray,0,255,
cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
kernel = np.ones((3,3),np.uint8)
opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 2)
sure_bg = cv2.dilate(opening,kernel,iterations=3)
dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5)
ret, fore = cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)
fore = np.uint8(fore)
ret, markers = cv2.connectedComponents(fore)
print(markers)
plt.subplot(131)
plt.imshow(ishow)
plt.axis('off')
plt.subplot(132)
plt.imshow(fore)
plt.axis('off')
plt.subplot(133)
plt.imshow(markers)
plt.axis('off')
print(ret)
plt.show()

运行结果:

  • 左图是原始图像 ishow。
  • 中间的是经过距离变换后得到的前景图像的中心点图像 fore。
  • 右图是对前景图像的中心点图像进行标注后的结果图像 markers。
    可以看到,前景图像的中心点被做了不同的标注在这里插入图片描述
    函数 cv2.connectedComponents()在标注图像时,会将背景标注为 0,将其他的对象用从 1
    开始的正整数标注。具体的对应关系为:
  • 数值 0 代表背景区域。
  • 从数值 1 开始的值,代表不同的前景区域。
    在分水岭算法中,标注值 0 代表未知区域。所以,我们要对函数 cv2.connectedComponents()标注的结果进行调整:将标注的结果都加上数值 1。经过上述处理后,在标注结果中:
  • 数值 1 代表背景区域。
  • 从数值 2 开始的值,代表不同的前景区域。

为了能够使用分水岭算法,还需要对原始图像内的未知区域进行标注,将已经计算出来的未知区域标注为 0 即可。
这里的关键代码为:

ret, markers = cv2.connectedComponents(fore)
markers = markers+1
markers[未知区域] = 0

示例:使用函数 cv2.connectedComponents()标注一幅图像,并对其进行修正,使未知区域被标注为 0,并观察标注的效果。

import numpy as np
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('water_coins.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
ishow=img.copy()
ret, thresh = cv2.threshold(gray,0,255,
cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
kernel = np.ones((3,3),np.uint8)
opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 2)
sure_bg = cv2.dilate(opening,kernel,iterations=3)
dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5)
ret, fore = cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)
fore = np.uint8(fore)
ret, markers1 = cv2.connectedComponents(fore)
foreAdv=fore.copy()
unknown = cv2.subtract(sure_bg,foreAdv)
ret, markers2 = cv2.connectedComponents(foreAdv)
markers2 = markers2+1
markers2[unknown==255] = 0
plt.subplot(121)
plt.imshow(markers1)
plt.axis('off')
plt.subplot(122)
plt.imshow(markers2)
plt.axis('off')
plt.show()

运行结果:

在这里插入图片描述

对比左右图可以看出,右图在前景图像的边缘(未知区域)进行了标注,使得每一个确定前景都有一个黑色的边缘,这个边缘是被标注的未知区域。

分水岭算法 函数cv2.watershed()

完成上述处理后,就可以使用分水岭算法对预处理结果图像进行分割了。在 OpenCV 中,实现分水岭算法的函数是 cv2.watershed(),其语法格式为:

markers = cv2.watershed( image, markers )

式中:

  • image 是输入图像,必须是 8 位三通道的图像。在对图像使用 cv2.watershed()函数处理之前,必须先用正数大致勾画出图像中的期望分割区域。每一个分割的区域会被标注为1、2、3 等。对于尚未确定的区域,需要将它们标注为 0。我们可以将标注区域理解为进行分水岭算法分割的“种子”区域。
  • markers 是 32 位单通道的标注结果,它应该和 image 具有相等大小。在 markers 中,每一个像素要么被设置为初期的“种子值”,要么被设置为“-1”表示边界。markers 可以省略。

使用分水岭算法进行图像分割时,基本的步骤为:

  1. 通过形态学开运算对原始图像 O 去噪。
  2. 通过腐蚀操作获取“确定背景 B”。需要注意,这里得到“原始图像-确定背景”即可。
  3. 利用距离变换函数 cv2.distanceTransform()对原始图像进行运算,并对其进行阈值处理,
    得到“确定前景 F”。
  4. 计算未知区域 UN(UN = O –B - F)。
  5. 利用函数 cv2.connectedComponents()对原始图像 O 进行标注。
  6. 对函数 cv2.connectedComponents()的标注结果进行修正。
  7. 使用分水岭函数完成对图像的分割。

代码示例:使用分水岭算法对一幅图像进行分割,并观察分割的效果。

import numpy as np
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('water_coins.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
ishow=img.copy()
ret, thresh = cv2.threshold(gray,0,255,
cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
kernel = np.ones((3,3),np.uint8)
opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 2)
sure_bg = cv2.dilate(opening,kernel,iterations=3)
dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5)
ret, sure_fg = cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg,sure_fg)
ret, markers = cv2.connectedComponents(sure_fg)
markers = markers+1
markers[unknown==255] = 0
markers = cv2.watershed(img,markers)
img[markers == -1] = [255,0,0]
plt.subplot(121)
plt.imshow(ishow)
plt.axis('off')
plt.subplot(122)
plt.imshow(img)
plt.axis('off')
plt.show()

运行效果:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/85371.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从安装 Seata 开始的分布式事务之旅 springboot集成seata

从安装 Seata 开始的分布式事务之旅 介绍什么是 Seata? 安装 Seata Server下载 Seata Server 发行版配置Seata解压文件配置Seata的yml文件把配置文件config.txt加载到nacos上修改config.txt文件加载到nacos上 启动Seata服务正常启动查看启动日志打开控制台页面 启动…

限流在不同场景的最佳实践

目录导读 限流在不同场景的最佳实践1. 前言2. 为什么要限流3. 有哪些限流场景3.1 限流场景分类3.2 限流与熔断降级之间的关系3.3 非业务限流3.4 业务限流 4. 有哪些限流算法4.1 计数器限流算法4.2 漏桶限流算法4.3 令牌桶限流算法4.4 滑动时间窗限流算法4.5 限流算法选型 5. 限…

NPM包的安装、更新、卸载

目录 1、下载安装全局包 2、解决全局安装包时的EACCES权限错误 2.1 重新安装NPM 2.2 手动更改npm的默认目录 3、更新从注册表下载的包 3.1 更新本地包 3.2 更新全局安装的软件包 3.3 确定哪些全局包需要更新 3.4 更新单个全局包 3.5 更新所有全局安装的软件包 4、在项…

VLAN监控及常见问题排查

局域网,我们通常称为LAN,是一种由基于同一地理位置的设备组成的网络,可实现它们之间的通信,局域网的虚拟对应物是虚拟局域网或 VLAN。VLAN 增强了 LAN,提供了进行更改的灵活性、更高的可扩展性和更好的安全性。 使用 …

使用 Gradio 构建生成式 AI 应用程序(一): 图片内容读取app

今天我们来学习DeepLearning.AI的在线课程:Building Generative AI Applications with Gradio,该课程主要讲述利用gradio来部署机器学习算法应用程序, 今天我们来学习第一课:Image captioning app,该课程主要讲述如何从图片中读取…

JavaScript 操作历史记录api怎样使用 JavaScript

JavaScript 操作历史记录api怎样使用 JavaScript History 是 window 对象中的一个 JavaScript 对象,它包含了关于浏览器会话历史的详细信息。你所访问过的 URL 列表将被像堆栈一样存储起来。浏览器上的返回和前进按钮使用的就是 history 的信息。 History 对象包含…

报错注入(主键重复)攻击原理

基本原理 利用数据表中主键不能重复的特点,通过构造重复的主键,使得数据库报错,并将报错结果返回到前端。 SQL说明函数 以pet数据表为例进行说明 rond(): 返回[0,1)区间内的任意浮点数。 count(): 返回每个组的列行数。 如&#xff0…

IDEA新建类时自动设置类注释信息,署名和日期

IDEA设置路径 File --> Settings --> Editor --> File and Code Templates --> Include --> File Header 官方模板 这里 ${USER} 会读取计算机的用户名 ${DATE}是日期 ${TIME}是时间 /*** Author ${USER}* Date ${DATE} ${TIME}* Version 1.0*/

简单易懂的Transformer学习笔记

1. 整体概述 2. Encoder 2.1 Embedding 2.2 位置编码 2.2.1 为什么需要位置编码 2.2.2 位置编码公式 2.2.3 为什么位置编码可行 2.3 注意力机制 2.3.1 基本注意力机制 2.3.2 在Trm中是如何操作的 2.3.3 多头注意力机制 2.4 残差网络 2.5 Batch Normal & Layer Narmal 2.…

智安网络|网络安全:危机下的创新与合作

随着信息技术的迅猛发展和互联网的普及,我们进入了一个高度网络化的社会。网络在提供便利和连接的同时,也带来了许多安全隐患和挑战。 一、网络安全的危险 **1.数据泄露和隐私侵犯:**网络上的个人和机构数据存在遭受泄露和盗取的风险&#…

clickhouse 删除操作

OLAP 数据库设计的宗旨在于分析适合一次插入多次查询的业务场景,市面上成熟的 AP 数据库在更新和删除操作上支持的均不是很好,当然 clickhouse 也不例外。但是不友好不代表不支持,本文主要介绍在 clickhouse 中如何实现数据的删除&#xff0c…

go语言的database/sql结合squirrel工具sql生成器完成数据库操作

database/sql database/sql是go语言内置数据库引擎,使用sql查询数据库,配置datasource后使用其数据库操作方法对数据库操作,如下: package mainimport ("database/sql""fmt"_ "github.com/Masterminds…

Cesium 1.107+ 自定义类支持 readyPromise

由于cesium 1.107 的图元(Primitive) 已经不支持 readyPromise。 但是个人感觉比较好用,于是用了一个插件来实现。 用法: // 定义图元并添加,和之前一样 const boxGreen new BoxPrimitive({color: "#ff0000" }) viewer.scene.primitives.add(boxGreen.primitive)/…

【Spring专题】Bean的声明周期流程图

前言 我向来不主张【通过源码】理解业务,因为每个人的能力有限,甚至可能会因为阅读错误导致出现理解上的偏差,所以我决定,还是先帮大家【开天眼】,先整体看看流程图,好知道,Spring在写源码的过…

一文讲述什么是数字孪生?

当前世界正处于百年未有之大变局,数字经济在各国已成为经济发展的重点。数字经济也是我国社会经济发展的必经之路。 近些年,大数据、人工智能、数字孪生等技术的发展促使技术与国内各产业进一步融合,从而推动了各产业在智能化、数字化等方面…

程序猿成长之路之密码学篇-分组密码加密模式及IV(偏移量)的详解

Cipher.getInstance("AES/ECB/PKCS5Padding"); Cipher cipher Cipher.getInstance("AES/CBC/PKCS5Padding"); 在进行加解密编程的时候应该有很多小伙伴接触过以上的语句,但是大伙儿在编码过程中是否了解过ECB/CBC的含义、区别以及PKCS5Padding…

10个AI绘图生成器让绘画更简单

AI不仅影响商业和医疗保健等行业,还在创意产业中发挥着越来越大的作用,开创了AI绘画生成器新时代。在绘画领域当然也是如此,与传统的绘画工具不同,AI人工智能时代的绘画工具是全自动的、智能的,甚至可以说是“傻瓜式”…

Three.js阴影

目录 Three.js入门 Three.js光源 Three.js阴影 Three.js纹理贴图 使用灯光后,场景中就会产生阴影。物体的背面确实在黑暗中,这称为核心阴影(core shadow)。我们缺少的是落下的阴影(drop shadow)&#…

【ultralytics仓库使用自己的数据集训练RT-DETR】

ultralytics仓库使用自己的数据集训练RT-DETR RT-DETR由百度开发,是一款尖端的端到端物体检测器(基于transformer架构),在提供实时性能的同时保持高精度。它利用视觉变换器(ViT)的力量,通过解耦…

水库大坝安全监测系统实施方案

一、方案概述 水库大坝作为特殊的建筑,其安全性质与房屋等建筑物完全不同,并且建造在地质构造复杂、岩土特性不均匀的地基上,目前对于大坝监测多采用人工巡查的方法,存在一定的系统误差,其工作性态和安全状况随时都在变…