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中图像分割和图像修复技术的入门指南,从基本原理到代码实现,简要覆盖这些技术的关键方面。
opencv logo

一、图像分割

图像分割是图像处理中的一项重要技术,它涉及将图像划分为多个部分或区域,以便更容易地分析和处理。

1.1 漫水填充法

1.1.1 漫水填充法原理

漫水填充 (Flood Fill)算法基于区域生长的概念。它从图像中的一个点(种子点)开始,然后向所有与该点相连的、颜色/强度相似的区域扩展。

1.1.2 漫水填充法实现步骤

  1. 选择一个种子点。
  2. 检查相邻像素是否属于同一区域(基于颜色/强度相似度)。
  3. 如果相邻像素符合条件,则包括它,并继续向外扩展。
  4. 重复此过程直到无法扩展。

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()

Flood Filled Image
cv2.floodFill 函数用于实现漫水填充算法,即填充一个连通区域的颜色或图案。这个函数非常适合于图像分割、对象检测和图像编辑等任务。下面是对 cv2.floodFill 函数参数和功能的简要介绍:

def floodFill(image, mask, seedPoint, newVal, loDiff=None, upDiff=None, flags=None)

  1. image: 输入/输出图像。这是一个单通道或三通道的8位或浮点图像。除非设置了 FLOODFILL_MASK_ONLY 标志,否则此函数会修改输入图像。

  2. mask: 操作掩码,应该是一个单通道8位图像,比输入图像宽2个像素,高2个像素。这是一个输入和输出参数,因此在使用前需要初始化。漫水填充不能穿过掩码中非零的像素。例如,边缘检测器的输出可以作为掩码,以阻止填充穿过边缘。

  3. seedPoint: 开始点。这是漫水填充开始的像素位置。

  4. newVal: 重新绘制域像素的新值。

  5. loDiffupDiff: 分别代表最大下限和上限的亮度/颜色差异。这些参数决定了填充颜色与周围像素颜色的最大允许差异。

  6. rect: 可选的输出参数,函数设置为重新绘制域的最小边界矩形。

  7. flags: 操作标志。前8位包含连接值,4代表只考虑四个最近邻像素(那些共享边的像素),8代表将考虑八个最近邻像素(那些共享角的像素)。接下来的8位(8-16位)包含用来填充掩码的值(默认值为1)。例如,4 | (255 << 8) 将考虑4个最近邻居,并用255的值填充掩码。

这个函数通过比较像素与其邻居或种子点的颜色/亮度差异来确定哪些像素属于同一连接组件,并将这些像素填充为新的颜色或值。在实际应用中,需要根据具体的图像和需求调整参数,以达到最佳的填充效果。

1.2 分水岭法

1.2.1 分水岭法原理

分水岭(Watershed)算法模拟地理学中的水流原理。在图像中,任何灰度值可以看作高度,算法模拟雨水流入低洼地区,形成不同的“湖泊”,每个湖泊代表图像的一个分割区域。

1.2.2 分水岭法实现步骤

  1. 对图像应用边缘检测,例如使用Canny算法。
  2. 应用距离变换。
  3. 应用分水岭算法分割图像。

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()

Segmented Image
cv2.watershed 用于基于标记的图像分割的函数,实现了一种变体的分水岭算法。这个函数的关键功能是将图像分割成多个区域,这些区域基于提供的标记来确定。

def watershed(image, markers)

  1. image: 输入参数。这是一个8位的3通道图像,即一般的彩色图像。这个图像是要应用分水岭算法进行分割的图像。

  2. 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法实现步骤

  1. 用户定义前景和背景区域。
  2. 算法初始化并迭代更新每个像素的标签(前景或背景)。
  3. 使用GMM(高斯混合模型)对颜色分布建模。
  4. 利用图割算法优化像素标签。

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()

GrabCut
cv2.grabCut 实现了 GrabCut 算法,这是一种用于图像分割的迭代算法。GrabCut 算法可以有效地从图像中分离前景(目标对象)和背景。

def grabCut(img, mask, rect, bgdModel, fgdModel, iterCount, mode=None)

  1. img: 输入参数。这是一个8位的3通道图像,即一般的彩色图像。这个图像是要应用 GrabCut 算法的对象。

  2. mask: 输入/输出参数。这是一个8位单通道的掩码图像。掩码的元素可以是 GrabCutClasses 中的一个,表示像素的不同分类(如明确的背景、可能的背景、明确的前景、可能的前景)。当 mode 设置为 GC_INIT_WITH_RECT 时,函数会初始化这个掩码。

  3. rect: 输入参数,表示包含分割对象的感兴趣区域(ROI)。当 mode == GC_INIT_WITH_RECT 时使用此参数。ROI 外的像素被标记为“明确的背景”。

  4. bgdModel: 输入/输出参数。这是用于背景模型的临时数组。处理同一图像时不应修改它。

  5. fgdModel: 输入/输出参数。这是用于前景模型的临时数组。处理同一图像时不应修改它。

  6. iterCount: 输入参数,表示算法应该执行的迭代次数。

  7. 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法实现步骤

  1. 在特征空间(例如颜色空间)中选择一个窗口。
  2. 计算窗口内所有点的均值。
  3. 移动窗口到均值位置。
  4. 重复步骤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()

Mean-Shift Segmentation
cv2.pyrMeanShiftFiltering 用于执行图像分割的 Mean-Shift 算法的函数。这个函数主要用于平滑图像的颜色梯度,同时保留边缘信息,从而对图像进行“平面化”处理。以下是对 cv2.pyrMeanShiftFiltering 函数的参数和功能的简要介绍:

def pyrMeanShiftFiltering(src, sp, sr, dst=None, maxLevel=None, termcrit=None)

  1. src: 输入参数,源图像。这是一个8位的3通道图像,即一般的彩色图像。

  2. sp: 输入参数,空间窗口半径。这个参数决定了像素邻域的大小。在此邻域内进行Mean-Shift迭代。

  3. sr: 输入参数,颜色窗口半径。这个参数决定了颜色空间中像素邻域的大小,用于在颜色空间内聚类。

  4. dst: 输出参数,目标图像。这是处理后的图像,格式和大小与源图像相同。

  5. maxLevel: 可选输入参数,金字塔的最大层级。当这个值大于0时,算法首先在金字塔的最小层上执行,然后结果被传播到更大的层上。

  6. termcrit: 可选输入参数,终止条件。它决定了Mean-Shift迭代何时停止。

cv2.pyrMeanShiftFiltering 函数的主要功能是对图像进行分割和平滑处理,同时保留边缘。在空间和颜色空间中,函数对每个像素进行迭代,直到满足终止条件。这个过程可以帮助去除图像的细节和噪声,同时保留主要的结构信息。这种方法在图像分割和对象识别等应用中非常有用。在实际使用时,spsr 参数需要根据具体的应用场景和图像内容进行调整。

二、图像修复

在图像处理领域,图像修复是一项至关重要的技术,它涉及修复图像中的损坏部分或去除不需要的对象。

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()

Restored Image Telea and Navier-Stokes
cv2.inpaint 用于修复图像中的选定区域的函数。它基于图像的邻域信息来重建图像的损坏或不希望的部分。这个函数特别适合用于去除扫描照片中的灰尘和划痕,或从静态图像或视频中去除不需要的对象。

def inpaint(src, inpaintMask, inpaintRadius, flags, dst=None)

  1. src: 输入参数,源图像。这应该是一个8位、16位无符号或32位浮点的单通道或三通道图像。

  2. inpaintMask: 输入参数,修复掩模。这是一个8位单通道图像,非零像素指示需要修复的区域。

  3. inpaintRadius: 输入参数,圆形邻域的半径。每个待修复点考虑的邻域大小由此半径决定。

  4. flags: 输入参数,修复方法。可以是以下两种之一:

    • INPAINT_NS: 基于 Navier-Stokes 的方法。
    • INPAINT_TELEA: Alexandru Telea 提出的方法。
  5. dst: 输出参数,与源图像大小和类型相同的输出图像。

cv2.inpaint 函数通过考虑待修复区域边界附近的像素来重建选定的图像区域。该方法可用于修复小的图像区域,例如去除图像中的小斑点或遮挡物。它通过估算损坏区域周围的颜色和强度分布,以合理的方式填充这些区域,从而达到修复的效果。

在实际应用中,需要根据具体问题选择合适的修复半径和方法。例如,较小的半径适用于小面积的修复,而较大的半径可能更适合广泛区域的修复。同样,两种不同的方法(INPAINT_NS 和 INPAINT_TELEA)在不同类型的图像和损坏情况下可能会有不同的效果。

2.2 修补算法

2.2.1 修补算法原理

修补(Patching)算法通过选取图像中未损坏的区域并用其覆盖损坏部分来进行修复。这种方法对于去除图像中的小对象或缺陷特别有效。

2.2.2 修补算法实现步骤

  1. 选择一个图像的未损坏区域作为补丁。
  2. 选择需要修复的区域。
  3. 将补丁应用到损坏的区域。

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()

Restored Image

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()

Restored Image


总结

在本篇博客中,我们探讨了OpenCV中几种常用的图像分割方法:漫水填充法、分水岭法、GrabCut法和Mean-Shift法,以及图像修复技术:Telea方法和Navier-Stokes方法,还有修补算法。每种方法都有其独特的原理和适用场景。漫水填充法和分水岭法适用于基于区域的分割,GrabCut法适合交互式前景提取,而Mean-Shift法适用于基于密度的聚类分割。在图像修复方面,Telea方法和Navier-Stokes方法提供了强大的工具,用于修复图像中的损坏区域。

通过本篇博客,我们能够获得对OpenCV图像分割和修复技术的基本理解,并能够开始在自己的项目中应用这些技术。图像处理是一个不断发展的领域,随着技术的进步,将有更多的方法和技术被开发出来。因此,持续学习和实践对于保持在该领域的领先地位至关重要。

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

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

相关文章

多级缓存快速上手

哈喽~大家好&#xff0c;这篇来看看多级缓存。 &#x1f947;个人主页&#xff1a;个人主页​​​​​ &#x1f948; 系列专栏&#xff1a;【微服务】 &#x1f949;与这篇相关的文章&#xff1a; JAVA进程和线程JAVA进程和线程-CSDN博客Http…

不做机器视觉工程师,转行,转岗的建议与想法

正所谓外行看热闹&#xff0c;内行看门道。提前咨询前辈们&#xff0c;多问问&#xff0c;多看看。要做就做&#xff0c;一定要提前做好防范。 无论你是要转行或者是转岗&#xff0c;看你有没有本钱和试错成本 有些人&#xff0c;家庭好&#xff0c;可以一直去试错和从头再来。…

学习.NET验证模块FluentValidation的基本用法(续1:其它常见用法)

FluentValidation模块支持链式验证方法调用&#xff0c;也就是说&#xff0c;除了 RuleFor(r > r.UserName).NotEmpty()调用方式之外&#xff0c;还可以将对单个属性的多种验证函数以链式调用方式串接起来&#xff0c;比如UserName属性不能为空&#xff0c;长度在5~10之间&a…

浅谈 Guava 中的 ImmutableMap.of 方法的坑

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《EffectiveJava》独家解析》专栏作者。 热门文章推荐&…

国自然项目基金撰写的隐藏技巧、范例分析及提交前的自我审查

目录 一、基金项目申请要求、重点及项目介绍 二、基金的撰写技巧 三、基金撰写的隐藏技巧 四、范例分析及提交前的自我审查 更多应用 基金项目申请需要进行跨学科的技术融合&#xff0c;申请人需要与不同领域结合&#xff0c;形成多学科交叉的研究。基金项目申请在新时期更…

由红黑树引出的HashMap扩容机制的思考

红黑树是什么&#xff1f; 三大特点&#xff1a; 根节点是黑色&#xff0c;叶节点是不存储数据的黑色空节点 任何相邻的两个节点不能同时为红色 任意节点到其可到达的节点间包含相同数量的黑色节点 联想&#xff1a;Java HashMap底层红黑树原理 HashMap基于哈希表Map接口实…

WordPress站点屏蔽过滤垃圾评论教程(Akismet反垃圾评论插件)

前段时间我的WordPress站点经常收到垃圾评论的轰炸&#xff0c;严重时一天会收到几十条垃圾评论。我这个小破站一没啥流量&#xff0c;二又不盈利&#xff0c;实在是不太理解为啥有人要这么执着地浪费资源在上面。 Akismet反垃圾评论插件 其实用了 Akismet 反垃圾评论插件后&a…

ARCore:在Android上构建令人惊叹的增强现实体验

ARCore&#xff1a;在Android上构建令人惊叹的增强现实体验 一、 AR 介绍1.1 AR技术简介1.2 AR技术原理1.3 AR技术应用领域 二、Google的增强现实平台ARCore2.1 ARCore简介2.2 ARCore API介绍2.3 ARCore API使用示例 三、总结 一、 AR 介绍 增强现实 Augmented Reality&#x…

An issue was found when checking AAR metadata

一、报错信息 An issue was found when checking AAR metadata:1. Dependency androidx.activity:activity:1.8.0 requires libraries and applications that depend on it to compile against version 34 or later of the Android APIs.:app is currently compiled against …

关于elementui和ant design vue无法禁止浏览器自动填充问题

以and design vue 为例&#xff1a; 图标用来显隐账号密码 html&#xff1a; <a-form-model-item label"账号密码:" prop"password"><a-input v-if"passwordTab" ref"passwordInput" v-model"form.password" typ…

详解最长公共子序列问题(三种方法)

这里&#xff0c;为了更方便地解释&#xff0c;我以洛谷上的一道典型题目为例&#xff0c;为大家讲解处理最长公共子序列问题的几种常见方法。这道题目中规定了两个子序列的长度相等&#xff0c;如果遇到不等的情况&#xff0c;也只需要对长度稍作修改即可&#xff0c;算法思想…

qs-一个序列化和反序列化的JavaScript库

起因 一个业务场景中&#xff0c;最终得到一串字符"status[0]value1&status[1]value2" 通过解析&#xff0c;理应得到一个数组&#xff0c;却得到一个对象 于是展开问题排查 最终发现是qs.parse 这个地方出了问题 排查结果 qs解析这种带下标的字符串时&#xff…

【Java 进阶篇】Redis 命令操作:轻松掌握基本操作

Redis是一款高性能的键值对存储系统&#xff0c;以其快速、灵活的特性而备受开发者推崇。本文将详细介绍Redis的基本命令操作&#xff0c;包括键值操作、数据查询、事务处理等方面&#xff0c;帮助初学者更好地理解和使用Redis。 基本命令 1. 键值操作 1.1 SET&#xff1a;设…

spark shuffle 剖析

ShuffleExchangeExec private lazy val writeMetrics SQLShuffleWriteMetricsReporter.createShuffleWriteMetrics(sparkContext)private[sql] lazy val readMetrics SQLShuffleReadMetricsReporter.createShuffleReadMetrics(sparkContext)用在了两个地方&#xff0c;承接的是…

【Flink】Standalone运行模式

独立模式是独立运行的&#xff0c;不依赖任何外部的资源管理平台&#xff1b;当然独立也是有代价的&#xff1a;如果资源不足&#xff0c;或者出现故障&#xff0c;没有自动扩展或重分配资源的保证&#xff0c;必须手动处理。所以独立模式一般只用在开发测试或作业非常少的场景…

Ps:参考线

参考线 Guides用于帮助精确地定位图像或元素&#xff0c;显示为浮动在图像上的非打印线&#xff0c;可以移动或移除&#xff0c;还可以临时锁定。 Ps 中的参考线可分为三大类&#xff1a;画布参考线、画板参考线和智能参考线。 可在“首选项/参考线、网格和切片”中设置参考线的…

深信服防火墙路由模式开局部署-手把手教学(小白篇)

PS&#xff1a;深信服的设备只有400能够通过console连接&#xff0c;一般用户是无法连接的&#xff0c;所以大家不要妄想着从Console连接设备了&#xff0c;开局就通过MANAGE进入Web就可以 接通电源后&#xff0c;开机拿一根网线&#xff0c;一端连接防火墙的MANAGE口&#xf…

【C++】泛型编程 ⑭ ( 类模板示例 - 数组类模板 | 容器思想 | 自定义类可拷贝 - 深拷贝与浅拷贝 | 自定义类可打印 - 左移运算符重载 )

文章目录 一、容器思想1、自定义类可拷贝 - 深拷贝与浅拷贝2、自定义类可拷贝 - 代码示例3、自定义类可打印 - 左移运算符重载 二、代码示例1、Array.h 头文件2、Array.cpp 代码文件3、Test.cpp 主函数代码文件4、执行结果 一、容器思想 1、自定义类可拷贝 - 深拷贝与浅拷贝 上…

大模型生态新篇章:以AI Agent为引,助企业创新应用落地

文 | 智能相对论 作者 | 沈浪 以聊天机器人、虚拟助手、智能客服等为代表的对话式人工智能 (Conversational AI Agents ) 在具体服务场景中的应用已经十分普遍。今年以来&#xff0c;随着大模型技术的爆发与加持&#xff0c;对话式AI被市场赋予了更高的期望。 “所有行业都值…

微服务实战系列之Feign

前言 不知不觉&#xff0c;“微服务实战系列”已完成了六篇&#xff0c;每篇都聚焦一个主题&#xff0c;目的是便于各位盆友能够快速、全面地接收和消化。 博主从服务注册到服务监控&#xff0c;从服务路由到服务安全&#xff0c;从身份认证到加密技术均有涉猎。凡此均有关微服…