OpenCV快速入门:图像分析——图像分割和图像修复
文章目录
- 前言
- 一、图像分割
- 1.1 漫水填充法
- 1.1.1 漫水填充法原理
- 1.1.2 漫水填充法实现步骤
- 1.1.3 代码实现
- 1.2 分水岭法
- 1.2.1 分水岭法原理
- 1.2.2 分水岭法实现步骤
- 1.2.3 代码实现
- 1.3 GrabCut法
- 1.3.1 GrabCut法原理
- 1.3.2 GrabCut法实现步骤
- 1.3.3 代码实现
- 1.4 Mean-Shift法
- 1.4.1 Mean-Shift法原理
- 1.4.2 Mean-Shift法实现步骤
- 1.4.3 代码实现
- 二、图像修复
- 2.1 图像修复原理
- 2.1.1 Telea方法
- 2.1.2 Navier-Stokes方法
- 2.1.3 代码实现
- 2.2 修补算法
- 2.2.1 修补算法原理
- 2.2.2 修补算法实现步骤
- 2.2.3 OpenCV代码实现
- 2.2.3.1 方形补丁修补
- 2.2.3.2 圆形补丁修补
- 总结
前言
OpenCV(Open Source Computer Vision Library)作为一个开源的计算机视觉和机器学习软件库,提供了丰富的图像处理功能,使得图像分析变得更加高效和易于实现。本篇博客旨在提供一个关于OpenCV中图像分割和图像修复技术的入门指南,从基本原理到代码实现,简要覆盖这些技术的关键方面。

一、图像分割
图像分割是图像处理中的一项重要技术,它涉及将图像划分为多个部分或区域,以便更容易地分析和处理。
1.1 漫水填充法
1.1.1 漫水填充法原理
漫水填充 (Flood Fill)算法基于区域生长的概念。它从图像中的一个点(种子点)开始,然后向所有与该点相连的、颜色/强度相似的区域扩展。
1.1.2 漫水填充法实现步骤
- 选择一个种子点。
- 检查相邻像素是否属于同一区域(基于颜色/强度相似度)。
- 如果相邻像素符合条件,则包括它,并继续向外扩展。
- 重复此过程直到无法扩展。
1.1.3 代码实现
import cv2
import numpy as np# 读取图像
image = cv2.imread('tulips.jpg')
# 创建一个与图像大小相同的掩码(mask),并初始化为全0
mask = np.zeros((image.shape[0] + 2, image.shape[1] + 2), dtype=np.uint8)# 定义填充的起始点
start_point1 = (100, 100) # 开始填充的坐标
start_point2 = (420, 200) # 开始填充的坐标
# 定义填充颜色
fill_color = (0, 0, 0) # 黑色
# 定义颜色容差范围
tolerance = (160, 160, 160, 160) # 上下左右的容差# floodFill函数的参数
flood_fill_flags = 4
flood_fill_flags |= 255 << 8
flood_fill_flags |= cv2.FLOODFILL_FIXED_RANGE# 调用floodFill函数
image_fill = image.copy()
cv2.floodFill(image_fill, mask, start_point1, fill_color, tolerance, tolerance, flood_fill_flags)
cv2.floodFill(image_fill, mask, start_point2, fill_color, tolerance, tolerance, flood_fill_flags)# 显示结果
mask_img = cv2.merge([mask,mask,mask])
# 获取图像的高度和宽度
height, width ,channel= image.shape
# 创建一个新的图像,高度和宽度各增加一
new_height = height + 2
new_width = width + 2
image_origin = np.zeros((new_height, new_width, channel), dtype=np.uint8)
# 将原始图像复制到新图像中
image_origin[:height, :width] = imageimage_flood = np.zeros((new_height, new_width, channel), dtype=np.uint8)
# 将原始图像复制到新图像中
image_flood[:height, :width] = image_fillcv2.imshow('Flood Filled Image', cv2.hconcat([image_origin,image_flood, mask_img]))
cv2.waitKey(0)
cv2.destroyAllWindows()

cv2.floodFill 函数用于实现漫水填充算法,即填充一个连通区域的颜色或图案。这个函数非常适合于图像分割、对象检测和图像编辑等任务。下面是对 cv2.floodFill 函数参数和功能的简要介绍:
def floodFill(image, mask, seedPoint, newVal, loDiff=None, upDiff=None, flags=None)
image: 输入/输出图像。这是一个单通道或三通道的8位或浮点图像。除非设置了
FLOODFILL_MASK_ONLY标志,否则此函数会修改输入图像。mask: 操作掩码,应该是一个单通道8位图像,比输入图像宽2个像素,高2个像素。这是一个输入和输出参数,因此在使用前需要初始化。漫水填充不能穿过掩码中非零的像素。例如,边缘检测器的输出可以作为掩码,以阻止填充穿过边缘。
seedPoint: 开始点。这是漫水填充开始的像素位置。
newVal: 重新绘制域像素的新值。
loDiff 和 upDiff: 分别代表最大下限和上限的亮度/颜色差异。这些参数决定了填充颜色与周围像素颜色的最大允许差异。
rect: 可选的输出参数,函数设置为重新绘制域的最小边界矩形。
flags: 操作标志。前8位包含连接值,4代表只考虑四个最近邻像素(那些共享边的像素),8代表将考虑八个最近邻像素(那些共享角的像素)。接下来的8位(8-16位)包含用来填充掩码的值(默认值为1)。例如,4 | (255 << 8) 将考虑4个最近邻居,并用255的值填充掩码。
这个函数通过比较像素与其邻居或种子点的颜色/亮度差异来确定哪些像素属于同一连接组件,并将这些像素填充为新的颜色或值。在实际应用中,需要根据具体的图像和需求调整参数,以达到最佳的填充效果。
1.2 分水岭法
1.2.1 分水岭法原理
分水岭(Watershed)算法模拟地理学中的水流原理。在图像中,任何灰度值可以看作高度,算法模拟雨水流入低洼地区,形成不同的“湖泊”,每个湖泊代表图像的一个分割区域。
1.2.2 分水岭法实现步骤
- 对图像应用边缘检测,例如使用Canny算法。
- 应用距离变换。
- 应用分水岭算法分割图像。
1.2.3 代码实现
import cv2
import numpy as np# 读取图像
image = cv2.imread('tulips.jpg')
# 转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 应用阈值化来标记前景区域
_, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)# 去除噪声
kernel = np.ones((7, 7), np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)# 确定背景区域
sure_bg = cv2.dilate(opening, kernel, iterations=6)
# 确定前景区域
dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)
_, sure_fg = cv2.threshold(dist_transform, 0.8 * dist_transform.max(), 255, 0)# 找到未知区域
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg, sure_fg)# 标记标签
_, markers = cv2.connectedComponents(sure_fg)# 增加1以确保背景不是0,而是1
markers = markers + 1# 标记未知区域为0
markers[unknown == 255] = 0# 应用分水岭
cv2.watershed(image, markers)
image[markers == -1] = [255, 255, 255]# 遍历所有的标记
for marker in np.unique(markers):if marker == 0 or marker == -1:# 忽略背景和边界continue# 创建一个掩码,使得当前标记区域为白色,其他区域为黑色mask = np.zeros(gray.shape, dtype=np.uint8)mask[markers == marker] = 255# 应用掩码到原始图像segmented_image = cv2.bitwise_and(image, image, mask=mask)# 显示提取的区域cv2.imshow(f'Segmented area {marker}', segmented_image)# 显示结果
cv2.imshow('Segmented Image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

cv2.watershed 用于基于标记的图像分割的函数,实现了一种变体的分水岭算法。这个函数的关键功能是将图像分割成多个区域,这些区域基于提供的标记来确定。
def watershed(image, markers)
image: 输入参数。这是一个8位的3通道图像,即一般的彩色图像。这个图像是要应用分水岭算法进行分割的图像。
markers: 输入/输出参数。这是一个32位单通道图像,代表标记。它的大小应该与
image相同。在输入时,markers图像中应该有正值(>0)标记出预期分割区域的大致轮廓。每个区域由一个或多个连接组件表示,像素值为1、2、3等。函数处理后,markers中的每个像素将被设置为其所属“种子”组件的值,或在区域边界处被设置为-1。
- 正值(>0): 表示不同的对象或区域。
- 零值(0): 表示这些像素的归属尚未确定,需要算法来定义。
- 负值(-1): 在函数输出中,这表示不同区域之间的边界。
cv2.watershed 函数的目的是根据提供的标记,将图像分割成不同的区域。这对于图像分割、对象识别和计算机视觉应用特别有用。在实际应用中,通常需要先对图像进行预处理(如边缘检测、阈值化等),然后生成合适的标记图像,最后应用这个函数进行分割。
1.3 GrabCut法
1.3.1 GrabCut法原理
GrabCut是一种基于图论的图像分割方法,使用用户定义的前景和背景区域来初始化分割。算法通过迭代方式优化每个像素属于前景或背景的概率。
1.3.2 GrabCut法实现步骤
- 用户定义前景和背景区域。
- 算法初始化并迭代更新每个像素的标签(前景或背景)。
- 使用GMM(高斯混合模型)对颜色分布建模。
- 利用图割算法优化像素标签。
1.3.3 代码实现
import cv2
import numpy as np# 读取图像
image = cv2.imread('tulips.jpg')# 定义前景和背景模型
mask = np.zeros(image.shape[:2], np.uint8)
bgdModel = np.zeros((1, 65), np.float64)
fgdModel = np.zeros((1, 65), np.float64)# 定义矩形(用户定义的前景区域)
rect = (100, 50, 200, 300)# 应用GrabCut
cv2.grabCut(image, mask, rect, bgdModel, fgdModel, 1, cv2.GC_INIT_WITH_RECT)# 提取前景和可能的前景区域
mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
image = image * mask2[:, :, np.newaxis]# 显示结果
cv2.imshow('GrabCut Image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

cv2.grabCut 实现了 GrabCut 算法,这是一种用于图像分割的迭代算法。GrabCut 算法可以有效地从图像中分离前景(目标对象)和背景。
def grabCut(img, mask, rect, bgdModel, fgdModel, iterCount, mode=None)
img: 输入参数。这是一个8位的3通道图像,即一般的彩色图像。这个图像是要应用 GrabCut 算法的对象。
mask: 输入/输出参数。这是一个8位单通道的掩码图像。掩码的元素可以是
GrabCutClasses中的一个,表示像素的不同分类(如明确的背景、可能的背景、明确的前景、可能的前景)。当mode设置为GC_INIT_WITH_RECT时,函数会初始化这个掩码。rect: 输入参数,表示包含分割对象的感兴趣区域(ROI)。当
mode == GC_INIT_WITH_RECT时使用此参数。ROI 外的像素被标记为“明确的背景”。bgdModel: 输入/输出参数。这是用于背景模型的临时数组。处理同一图像时不应修改它。
fgdModel: 输入/输出参数。这是用于前景模型的临时数组。处理同一图像时不应修改它。
iterCount: 输入参数,表示算法应该执行的迭代次数。
mode: 输入参数,操作模式,可以是
GrabCutModes中的一个。常见模式包括GC_INIT_WITH_RECT(使用矩形初始化)和GC_INIT_WITH_MASK(使用掩码初始化)。
cv2.grabCut 函数的主要用途是通过迭代方式将图像中的目标对象与背景分离。这在图像编辑、计算机视觉和对象识别等领域非常有用。函数的实现方式是先用一个矩形粗略地标记出感兴趣的对象,然后算法迭代地改进前景和背景的分割。
1.4 Mean-Shift法
1.4.1 Mean-Shift法原理
Mean-Shift算法是一种基于特征空间分析的非参数密度估计技术。在图像分割的上下文中,它通常用于基于颜色的聚类。
1.4.2 Mean-Shift法实现步骤
- 在特征空间(例如颜色空间)中选择一个窗口。
- 计算窗口内所有点的均值。
- 移动窗口到均值位置。
- 重复步骤2和3,直到收敛。
1.4.3 代码实现
import cv2# 读取图像
image = cv2.imread('tulips.jpg')# 转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 应用Canny边缘检测
edges = cv2.Canny(gray, 50, 150)# Mean-Shift 参数
spatial_radius = 20 # 空间窗口大小
color_radius = 40 # 颜色窗口大小
max_pyramid_level = 2 # 金字塔层数# 应用Mean-Shift算法
result = cv2.pyrMeanShiftFiltering(image, spatial_radius, color_radius, max_pyramid_level)
# 转换为灰度图
gray = cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)
# 应用Canny边缘检测
result_edges = cv2.Canny(gray, 50, 150)# 显示结果
img_edges = cv2.merge([edges,edges,edges])
img_result_edges = cv2.merge([result_edges,result_edges,result_edges])
cv2.imshow('Mean-Shift Segmentation', cv2.vconcat([cv2.hconcat([image, img_edges]),cv2.hconcat([result,img_result_edges])
]))
cv2.waitKey(0)
cv2.destroyAllWindows()

cv2.pyrMeanShiftFiltering 用于执行图像分割的 Mean-Shift 算法的函数。这个函数主要用于平滑图像的颜色梯度,同时保留边缘信息,从而对图像进行“平面化”处理。以下是对 cv2.pyrMeanShiftFiltering 函数的参数和功能的简要介绍:
def pyrMeanShiftFiltering(src, sp, sr, dst=None, maxLevel=None, termcrit=None)
src: 输入参数,源图像。这是一个8位的3通道图像,即一般的彩色图像。
sp: 输入参数,空间窗口半径。这个参数决定了像素邻域的大小。在此邻域内进行Mean-Shift迭代。
sr: 输入参数,颜色窗口半径。这个参数决定了颜色空间中像素邻域的大小,用于在颜色空间内聚类。
dst: 输出参数,目标图像。这是处理后的图像,格式和大小与源图像相同。
maxLevel: 可选输入参数,金字塔的最大层级。当这个值大于0时,算法首先在金字塔的最小层上执行,然后结果被传播到更大的层上。
termcrit: 可选输入参数,终止条件。它决定了Mean-Shift迭代何时停止。
cv2.pyrMeanShiftFiltering 函数的主要功能是对图像进行分割和平滑处理,同时保留边缘。在空间和颜色空间中,函数对每个像素进行迭代,直到满足终止条件。这个过程可以帮助去除图像的细节和噪声,同时保留主要的结构信息。这种方法在图像分割和对象识别等应用中非常有用。在实际使用时,sp 和 sr 参数需要根据具体的应用场景和图像内容进行调整。
二、图像修复
在图像处理领域,图像修复是一项至关重要的技术,它涉及修复图像中的损坏部分或去除不需要的对象。
2.1 图像修复原理
图像修复 (Inpainting)是一种用于修复图像中损坏区域的技术。它通过分析周围的像素来重建缺失或损坏的部分。OpenCV提供了两种主要的Inpainting技术:Telea方法和Navier-Stokes方法。
2.1.1 Telea方法
Telea方法是一种基于快速行进算法的图像修复技术。这种方法的核心思想是从损坏区域的边缘开始,逐步向内部填充,直到整个区域被修复完毕。
原理
Telea方法的关键在于它如何选择用于填充缺失区域的像素值。它考虑到了边缘周围像素的信息,并基于这些信息来估计缺失像素的最佳值。具体而言,该方法依赖于周围像素的几何距离和光强差异来计算修复值。
应用
Telea方法非常适合于修复小到中等大小的损坏区域。由于其基于边缘信息进行填充,因此在修复裂缝或小孔等缺陷时表现尤为出色。
2.1.2 Navier-Stokes方法
Navier-Stokes方法是另一种流行的图像修复技术。它基于流体动力学中的Navier-Stokes方程,适用于重建更大区域的图像。
原理
这种方法将图像修复问题视为一个流体动力学问题,其中图像的每个像素都被视为流体粒子。它利用Navier-Stokes方程来模拟流体粒子的运动,从而估算缺失区域的像素值。
应用
Navier-Stokes方法特别适用于大面积损坏的修复,例如修复破损的古老照片或艺术作品中的大片缺失部分。该方法能够有效地重建图像的结构和纹理信息,使修复后的区域与周围环境融合得更自然。
2.1.3 代码实现
import cv2
import numpy as npdef inpaint_image(image, mask, inpaint_radius=3):restored_telea = cv2.inpaint(image, mask, inpaint_radius, cv2.INPAINT_TELEA)restored_ns = cv2.inpaint(image, mask, inpaint_radius, cv2.INPAINT_NS)return restored_telea, restored_ns# 加载图像
image = cv2.imread('tulips.jpg')# 创建第一个掩模,用于模拟损坏的区域
mask1 = np.zeros(image.shape[:2], np.uint8)
mask1[40:60, 50:550] = 255 # 水平损坏区域
mask1[60:260, 300:320] = 255 # 垂直损坏区域
mask1[200:220, 100:300] = 255 # 水平损坏区域# 创建第二个掩模,用于模拟损坏的区域
mask2 = np.zeros(image.shape[:2], np.uint8)
cv2.circle(mask2, (395, 215), 80, 255, -1) # 圆形损坏区域# 应用掩模,模拟损坏
damaged_image1 = image.copy()
damaged_image1[mask1 == 255] = [0, 0, 0]
damaged_image2 = image.copy()
damaged_image2[mask2 == 255] = [0, 0, 0]# 使用封装的函数进行修复
restored_telea1, restored_ns1 = inpaint_image(damaged_image1, mask1)
restored_telea2, restored_ns2 = inpaint_image(damaged_image2, mask2)# 显示结果
horizontal_stack1 = np.hstack((damaged_image1, restored_telea1, restored_ns1))
horizontal_stack2 = np.hstack((damaged_image2, restored_telea2, restored_ns2))
vertical_stack = np.vstack((horizontal_stack1, horizontal_stack2))
cv2.imshow('Restored Image Telea and Navier-Stokes', vertical_stack)
cv2.waitKey(0)
cv2.destroyAllWindows()

cv2.inpaint 用于修复图像中的选定区域的函数。它基于图像的邻域信息来重建图像的损坏或不希望的部分。这个函数特别适合用于去除扫描照片中的灰尘和划痕,或从静态图像或视频中去除不需要的对象。
def inpaint(src, inpaintMask, inpaintRadius, flags, dst=None)
src: 输入参数,源图像。这应该是一个8位、16位无符号或32位浮点的单通道或三通道图像。
inpaintMask: 输入参数,修复掩模。这是一个8位单通道图像,非零像素指示需要修复的区域。
inpaintRadius: 输入参数,圆形邻域的半径。每个待修复点考虑的邻域大小由此半径决定。
flags: 输入参数,修复方法。可以是以下两种之一:
- INPAINT_NS: 基于 Navier-Stokes 的方法。
- INPAINT_TELEA: Alexandru Telea 提出的方法。
dst: 输出参数,与源图像大小和类型相同的输出图像。
cv2.inpaint 函数通过考虑待修复区域边界附近的像素来重建选定的图像区域。该方法可用于修复小的图像区域,例如去除图像中的小斑点或遮挡物。它通过估算损坏区域周围的颜色和强度分布,以合理的方式填充这些区域,从而达到修复的效果。
在实际应用中,需要根据具体问题选择合适的修复半径和方法。例如,较小的半径适用于小面积的修复,而较大的半径可能更适合广泛区域的修复。同样,两种不同的方法(INPAINT_NS 和 INPAINT_TELEA)在不同类型的图像和损坏情况下可能会有不同的效果。
2.2 修补算法
2.2.1 修补算法原理
修补(Patching)算法通过选取图像中未损坏的区域并用其覆盖损坏部分来进行修复。这种方法对于去除图像中的小对象或缺陷特别有效。
2.2.2 修补算法实现步骤
- 选择一个图像的未损坏区域作为补丁。
- 选择需要修复的区域。
- 将补丁应用到损坏的区域。
2.2.3 OpenCV代码实现
2.2.3.1 方形补丁修补
import cv2
import numpy as np# 加载图像
image = cv2.imread('tulips.jpg')# 创建掩模,用于模拟损坏的区域
mask2 = np.zeros(image.shape[:2], np.uint8)
cv2.circle(mask2, (395, 215), 80, 255, -1) # 圆形损坏区域# 应用掩模,模拟损坏
damaged_image = image.copy()
damaged_image[mask2 == 255] = [0, 0, 0]# 选择一个补丁区域
# 注意:这里的坐标(x1, y1, x2, y2)需要根据您的图像进行调整
x1, y1, x2, y2 = 20, 20, 140, 150 # 补丁区域的坐标
patch = image[y1:y2, x1:x2]# 确定损坏区域的坐标
# 这里我们使用与圆形损坏区域相同的坐标
dx1, dy1, dx2, dy2 = 395 - 80, 215 - 80, 395 + 80, 215 + 80# 确保补丁和损坏区域的大小相同
patch_resized = cv2.resize(patch, (dx2 - dx1, dy2 - dy1))# 应用补丁
restored_image = damaged_image.copy()
restored_image[dy1:dy2, dx1:dx2] = patch_resized# 显示修复后的图像
cv2.imshow('Restored Image', cv2.hconcat([image, damaged_image,restored_image]))
cv2.waitKey(0)
cv2.destroyAllWindows()

2.2.3.2 圆形补丁修补
import cv2
import numpy as np# 加载图像
image = cv2.imread('tulips.jpg')# 创建掩模,用于模拟损坏的区域
mask2 = np.zeros(image.shape[:2], np.uint8)
cv2.circle(mask2, (395, 215), 80, 255, -1) # 圆形损坏区域# 应用掩模,模拟损坏
damaged_image = image.copy()
damaged_image[mask2 == 255] = [0, 0, 0]# 选择一个圆形补丁区域
px, py, radius = 78, 80, 70 # 原始补丁的半径
patch_mask = np.zeros(image.shape[:2], np.uint8)
cv2.circle(patch_mask, (px, py), radius, 255, -1)# 创建原始补丁图像
original_patch = np.zeros_like(image)
original_patch[patch_mask == 255] = image[patch_mask == 255]# 放大补丁到新半径
new_radius = 80
scale_factor = new_radius / radius
resized_patch = cv2.resize(original_patch, (0, 0), fx=scale_factor, fy=scale_factor)# 重新创建圆形掩模以裁剪放大的补丁
resized_mask = np.zeros(resized_patch.shape[:2], np.uint8)
cv2.circle(resized_mask, (int(px*scale_factor), int(py*scale_factor)), new_radius, 255, -1)
final_patch = np.zeros_like(resized_patch)
final_patch[resized_mask == 255] = resized_patch[resized_mask == 255]restored_image = damaged_image.copy()
# 应用放大的圆形补丁
for i in range(-new_radius, new_radius):for j in range(-new_radius, new_radius):if i**2 + j**2 <= new_radius**2:restored_image[215 + i, 395 + j] = final_patch[int(py * scale_factor) + i, int(px * scale_factor) + j]# 显示修复后的图像
cv2.imshow('Restored Image', cv2.hconcat([image, damaged_image,restored_image]))
cv2.waitKey(0)
cv2.destroyAllWindows()

总结
在本篇博客中,我们探讨了OpenCV中几种常用的图像分割方法:漫水填充法、分水岭法、GrabCut法和Mean-Shift法,以及图像修复技术:Telea方法和Navier-Stokes方法,还有修补算法。每种方法都有其独特的原理和适用场景。漫水填充法和分水岭法适用于基于区域的分割,GrabCut法适合交互式前景提取,而Mean-Shift法适用于基于密度的聚类分割。在图像修复方面,Telea方法和Navier-Stokes方法提供了强大的工具,用于修复图像中的损坏区域。
通过本篇博客,我们能够获得对OpenCV图像分割和修复技术的基本理解,并能够开始在自己的项目中应用这些技术。图像处理是一个不断发展的领域,随着技术的进步,将有更多的方法和技术被开发出来。因此,持续学习和实践对于保持在该领域的领先地位至关重要。
相关文章:
OpenCV快速入门:图像分析——图像分割和图像修复
文章目录 前言一、图像分割1.1 漫水填充法1.1.1 漫水填充法原理1.1.2 漫水填充法实现步骤1.1.3 代码实现 1.2 分水岭法1.2.1 分水岭法原理1.2.2 分水岭法实现步骤1.2.3 代码实现 1.3 GrabCut法1.3.1 GrabCut法原理1.3.2 GrabCut法实现步骤1.3.3 代码实现 1.4 Mean-Shift法1.4.1…...
BART - 磁共振重建库 linux系统安装 MATLAB 使用
本文主要介绍如何在linux系统中安装伯克利大学的磁共振重建库BART 和在matlab中的配置使用。 安装必要的库 (linux 命令行) $ sudo apt-get install make gcc libfftw3-dev liblapacke-dev libpng-dev libopenblas-dev 下载编译BART 文件 (官网链接:BART Toolbox) 命令行下…...
在linux下在官网的nginxtar包 的安装方式并配置全局变量
在Linux系统中,Nginx是一种流行的Web服务器和反向代理服务器,它可以提供高性能的静态内容服务和动态内容服务。本文将详细介绍如何在Linux系统中安装Nginx,并配置全局变量。 安装Nginx 以下是在Linux系统中安装Nginx的步骤: 1.…...
Java中数据库查询方法MapListProcessor的应用
1.供应链系统的销售合同捉过了两个金额一样的,同一个项目 2.合同号也一样,oaid不一样,但是从OA前台只有一个 3.一个是建云的一个是泛微的 4.做下过滤,如果同一个合同编号,在泛微里面有的,建云的就不获取了 …...
一键换肤功能
使用css3的css变量来实现 示例: 代码: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name&qu…...
Oracle SQL 注入上的 Django GIS 函数和聚合漏洞 (CVE-2020-9402)
漏洞描述 Django 于2020年3 月4日发布了一个安全更新,修复了 GIS 函数和聚合中的 SQL 注入漏洞。 参考链接: Django security releases issued: 3.0.4, 2.2.11, and 1.11.29 | Weblog | Django 该漏洞要求开发者使用 JSONField/HStoreField;此外&…...
MYSQL 排序和分组怎么做?
文章目录 前言MySQL 排序语法在命令提示符中使用 ORDER BY 子句在PHP脚本中使用 ORDER BY 子句 MySQL 分组GROUP BY 语法实例演示使用 WITH ROLLUP 后言 前言 hello world欢迎来到前端的新世界 😜当前文章系列专栏:Mysql 🐱👓博…...
ESP32 ESP-IDF5.1 在Visual Studio Code中自定义分区表与调整Flash大小
好记心不如烂笔头 使用ESP-IDF开发ESP32的时候,要是同时用到蓝牙和WIFI的话,很多时候会提示Flash不够, 我是照着这样解决的,存档记录 来源 : zaixingxing2539 大佬的 ESP32 ESP-IDF5.0 在VSCODE中自定义分区表 用Visual Studio Code自定义分区表 # ESP-IDF Partition Table…...
【Amazon】通过代理连接的方式导入 AWS EKS集群至KubeSphere主容器平台
文章目录 一、设置主集群方式一:使用 Web 控制台方式二:使用 Kubectl命令 二、在主集群中设置代理服务地址方式一:使用 Web 控制台方式二:使用 Kubectl命令 三、登录控制台验证四、准备成员集群方式一:使用 Web 控制台…...
网络爬虫(Python:Selenium、Scrapy框架;爬虫与反爬虫笔记)
网络爬虫(Python:Selenium、Scrapy框架;爬虫与反爬虫笔记) SeleniumWebDriver 对象提供的相关方法定位元素ActionChains的基本使用selenium显示等待和隐式等待显示等待隐式等待 Scrapy(异步网络爬虫框架)Sc…...
一个简易计算器实现(c语言)
该程序使用c语言实现了一个简易的计算器,该计算器具有以下功能: 1、设计功能选择界面,实现菜单选择、数据输入和输出的功能; 2、至少自定义五个子函数分别完成加、减、乘、除以及求模运算功能; 3、考虑代码的健壮性和…...
JVM——垃圾回收算法(垃圾回收算法评价标准,四种垃圾回收算法)
目录 1.垃圾回收算法发展简介2.垃圾回收算法的评价标准1.吞吐量2.最大暂停时间3.堆使用效率 3.垃圾回收算法01-标记清除算法垃圾回收算法-标记清除算法的优缺点 4.垃圾回收算法02-复制算法垃圾回收算法-复制算法的优缺点 5.垃圾回收算法03-标记整理算法标记整理算法的优缺点 6.…...
【虚拟机】在VM中安装 CentOS 7
1.2.创建虚拟机 Centos7是比较常用的一个Linux发行版本,在国内的使用比例还是比较高的。 大家首先要下载一个Centos7的iso文件,我在资料中给大家准备了一个mini的版本,体积不到1G,推荐大家使用: 我们在VMware《主页》…...
Qt 信号与槽简介
Qt是一个跨平台的C应用程序开发框架,它提供了丰富的功能和工具来帮助开发者构建高质量、高性能的GUI应用程序。在Qt中,信号与槽(Signal and Slot)机制是一种用于处理事件的重要特性。 信号(Signal)…...
ruoyi-plus-vue docker 部署
本文以 ruoyi-vue-plus 5.x docker 部署为基础 安装虚拟机 部署文档 安装docker 安装docker 安装docker-compose 配置idea环境 上传 /doicker 文件夹 到服务器;赋值 777权限 chmod -R 777 /docker idea构建 jar 包 利用 idea 构建镜像; 创建基础服务 docker…...
springboot使用redis缓存乱码(key或者 value 乱码)一招解决
如果查看redis中的值是这样 创建一个配置类就可以解决 package com.deka.config;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; i…...
rk3588配置uac功能,android13使能uac及adb的复合设备
最近,因新增需求需要在现有产品上增加UAC的功能,查阅并学习相关知识后,在rk3588 SOC硬件平台搭载android13系统平台上成功配置了uac及uac&adb的复合设备。基于开源共享精神希望给大家提供些参考。 1.技术可行性预研 (1&#…...
对未来新能源车测试工具的看法
汽车行业正在经历变革的说法算是比较轻描淡写的了,还记得我1983年加入这个行业时,行业聚焦点是引入发动机管理系统。当时还是以家庭掀背车为主的时代,发动机分析仪的体积像衣柜一样大,还没出现“CAN”通信协议。现在经常听到我的导…...
案例说法:智能网联车背后的安全隐患
随着汽车智能化、网联化的发展,汽车数据处理能力日益增强,未经授权对个人信息和重要数据采集、利用等数据安全问题逐步凸显。对车辆网络攻击、网络侵入等网络安全问题恐将危及个人生命安全、合法权益,甚至危害公共利益、国家安全,…...
基于C#实现双端队列
话说有很多数据结构都在玩组合拳,比如说:块状链表,块状数组,当然还有本篇的双端队列,是的,它就是栈和队列的组合体。 一、概念 我们知道普通队列是限制级的一端进,另一端出的 FIFO 形式&#…...
wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...
力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...
