当前位置: 首页 > article >正文

【python】OpenCV—WaterShed Algorithm(2)

在这里插入图片描述

文章目录

  • 1、功能描述
  • 2、代码实现
  • 3、效果展示
  • 4、完整代码
  • 5、参考

1、功能描述

基于 opencv python,调用分水岭算法 demo

2、代码实现

导入必要的包

from skimage.feature import peak_local_max
from skimage.morphology import watershed
from scipy import ndimage
import numpy as np
import argparse
import imutils
import cv2

构造参数解析并解析参数

ap = argparse.ArgumentParser()
# ap.add_argument("-i", "--image", default="HFOUG.jpg", help="path to input image")
ap.add_argument("-i", "--image", default="1.jpg", help="path to input image")
args = vars(ap.parse_args())

加载图像并执行金字塔均值偏移滤波以辅助阈值化步骤,

将图像转换为灰度,然后应用 OTSU 阈值 二值化

image = cv2.imread(args["image"])
cv2.imshow("Input", image)

shifted = cv2.pyrMeanShiftFiltering(image, 21, 51)
cv2.imshow("Shifted", shifted)

# 将图像转换为灰度,然后应用大津阈值
gray = cv2.cvtColor(shifted, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv2.imshow("Thresh", thresh)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

计算从每个二进制图像中的像素到最近的零像素的精确欧氏距离,然后找出这个距离图中的峰值

D = ndimage.distance_transform_edt(thresh)

# 可视化距离函数
D_show = cv2.normalize(D, None, 0, 1, cv2.NORM_MINMAX)
# print(np.max(D_show))
cv2.imshow("D_show", D_show)

在这里插入图片描述

以坐标列表(indices=True)或布尔掩码(indices=False)的形式查找图像中的峰值。峰值是 2 * min_distance + 1 区域内的局部最大值。

即峰值之间至少相隔min_distance)。

此处我们将确保峰值之间至少有20像素的距离。

localMax = peak_local_max(D, indices=False, min_distance=20, labels=thresh)
# 可视化localMax
temp = localMax.astype(np.uint8)
cv2.imshow("localMax", temp * 255)

在这里插入图片描述

使用8-连通性对局部峰值进行连接成分分析,然后应用分水岭算法

scipy.ndimage.label(input, structure=None, output=None)
  • input :待标记的数组对象。输入中的任何非零值都被视为待标记对象,零值被视为背景。

  • structure:定义要素连接的结构化元素。对于二维数组。默认是四连通, 此处选择8连通

markers = ndimage.label(localMax, structure=np.ones((3, 3)))[0]  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 可视化markers
temp_markers = markers.astype(np.uint8)
cv2.imshow("temp_markers", temp_markers * 20)

在这里插入图片描述
由于分水岭算法假设我们的标记代表距离图中的局部最小值(即山谷),因此我们取 D 的负值。

labels = watershed(-D, markers, mask=thresh)
print("[INFO] {} unique segments found".format(len(np.unique(labels)) - 1))

output

[INFO] 10 unique segments found

循环遍历分水岭算法返回的标签

for label in np.unique(labels):
    # 0表示背景,忽略它
    if label == 0:
        continue
    # 否则,为标签区域分配内存并将其绘制在掩码上
    mask = np.zeros(gray.shape, dtype="uint8")
    mask[labels == label] = 255
    # 在mask中检测轮廓并抓取最大的一个
    cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,
                            cv2.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    c = max(cnts, key=cv2.contourArea)
    # 在物体周围画一个圆
    ((x, y), r) = cv2.minEnclosingCircle(c)
    cv2.circle(image, (int(x), int(y)), int(r), (0, 255, 0), 2)
    cv2.putText(image, "#{}".format(label), (int(x) - 10, int(y)),
                cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
# 显示输出图像
cv2.imshow("Output", image)
cv2.waitKey(0)

在这里插入图片描述

十个标记,发现三不见了,debug 分析,3 和 2 重复了,改下代码跳过 2 即可显示 3

for label in np.unique(labels):
    if label == 2:
        continue

在这里插入图片描述

3、效果展示

输入图片

在这里插入图片描述

输出结果

在这里插入图片描述

输入图片

在这里插入图片描述

白色背景,二值化的时候注意下前景背景要反过来,或者

thresh = cv2.bitwise_not(thresh)

在这里插入图片描述

在这里插入图片描述

输入图片

在这里插入图片描述

在这里插入图片描述

输入图片

在这里插入图片描述

在这里插入图片描述

输入图片

在这里插入图片描述

在这里插入图片描述

输入图片

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

可以观察到边界还是没有那么完美的

4、完整代码

# 打开一个新文件,将其命名为 watershed.py ,然后插入以下代码:

# 导入必要的包
from skimage.feature import peak_local_max
from skimage.morphology import watershed
from scipy import ndimage
import numpy as np
import argparse
import imutils
import cv2

# 构造参数解析并解析参数
ap = argparse.ArgumentParser()
# ap.add_argument("-i", "--image", default="HFOUG.jpg", help="path to input image")
ap.add_argument("-i", "--image", default="3.jpg", help="path to input image")
args = vars(ap.parse_args())

# 加载图像并执行金字塔均值偏移滤波以辅助阈值化步骤
image = cv2.imread(args["image"])
cv2.imshow("Input", image)

shifted = cv2.pyrMeanShiftFiltering(image, 21, 51)
cv2.imshow("Shifted", shifted)

# 将图像转换为灰度,然后应用大津阈值
gray = cv2.cvtColor(shifted, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# thresh = cv2.bitwise_not(thresh)  # 白色背景时可以开启

cv2.imshow("Thresh", thresh)

# 计算从每个二进制图像中的像素到最近的零像素的精确欧氏距离,然后找出这个距离图中的峰值
D = ndimage.distance_transform_edt(thresh)

# 可视化距离函数
D_show = cv2.normalize(D, None, 0, 1, cv2.NORM_MINMAX)
# print(np.max(D_show))
cv2.imshow("D_show", D_show)


# 以坐标列表(indices=True)或布尔掩码(indices=False)的形式查找图像中的峰值。峰值是2 * min_distance + 1区域内的局部最大值。
# (即峰值之间至少相隔min_distance)。此处我们将确保峰值之间至少有20像素的距离。
localMax = peak_local_max(D, indices=False, min_distance=20, labels=thresh)
# 可视化localMax
temp = localMax.astype(np.uint8)
cv2.imshow("localMax", temp * 255)
# 使用8-连通性对局部峰值进行连接成分分析,然后应用分水岭算法
# scipy.ndimage.label(input, structure=None, output=None)
# input :待标记的数组对象。输入中的任何非零值都被视为待标记对象,零值被视为背景。
# structure:定义要素连接的结构化元素。对于二维数组。默认是四连通, 此处选择8连通
#
markers = ndimage.label(localMax, structure=np.ones((3, 3)))[0]  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 可视化markers
temp_markers = markers.astype(np.uint8)
cv2.imshow("temp_markers", temp_markers * 20)

# 由于分水岭算法假设我们的标记代表距离图中的局部最小值(即山谷),因此我们取 D 的负值。
labels = watershed(-D, markers, mask=thresh)
print("[INFO] {} unique segments found".format(len(np.unique(labels)) - 1))

# 循环遍历分水岭算法返回的标签
for label in np.unique(labels):
    # 0表示背景,忽略它
    if label == 0:
        continue
    # 否则,为标签区域分配内存并将其绘制在掩码上
    mask = np.zeros(gray.shape, dtype="uint8")
    mask[labels == label] = 255
    # 在mask中检测轮廓并抓取最大的一个
    cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,
                            cv2.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    c = max(cnts, key=cv2.contourArea)
    # 在物体周围画一个圆
    ((x, y), r) = cv2.minEnclosingCircle(c)
    cv2.circle(image, (int(x), int(y)), int(r), (0, 255, 0), 2)
    cv2.putText(image, "#{}".format(label), (int(x) - 10, int(y)),
                cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
# 显示输出图像
cv2.imshow("Output", image)
cv2.imwrite(f"result_{args['image']}", image)
cv2.waitKey(0)

绘制轮廓而不是圆形的画,改下最后绘制的代码即可

# 打开一个新文件,将其命名为 watershed.py ,然后插入以下代码:

# 导入必要的包
from skimage.feature import peak_local_max
from skimage.morphology import watershed
from scipy import ndimage
import numpy as np
import argparse
import imutils
import cv2
import random as rng

# 构造参数解析并解析参数
ap = argparse.ArgumentParser()
# ap.add_argument("-i", "--image", default="HFOUG.jpg", help="path to input image")
ap.add_argument("-i", "--image", default="1.jpg", help="path to input image")
args = vars(ap.parse_args())

# 加载图像并执行金字塔均值偏移滤波以辅助阈值化步骤
image = cv2.imread(args["image"])
cv2.imshow("Input", image)

shifted = cv2.pyrMeanShiftFiltering(image, 21, 51)
cv2.imshow("Shifted", shifted)

# 将图像转换为灰度,然后应用大津阈值
gray = cv2.cvtColor(shifted, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# thresh = cv2.bitwise_not(thresh)  # 白色背景时可以开启

cv2.imshow("Thresh", thresh)

# 计算从每个二进制图像中的像素到最近的零像素的精确欧氏距离,然后找出这个距离图中的峰值
D = ndimage.distance_transform_edt(thresh)

# 可视化距离函数
D_show = cv2.normalize(D, None, 0, 1, cv2.NORM_MINMAX)
# print(np.max(D_show))
cv2.imshow("D_show", D_show)


# 以坐标列表(indices=True)或布尔掩码(indices=False)的形式查找图像中的峰值。峰值是2 * min_distance + 1区域内的局部最大值。
# (即峰值之间至少相隔min_distance)。此处我们将确保峰值之间至少有20像素的距离。
localMax = peak_local_max(D, indices=False, min_distance=20, labels=thresh)
# 可视化localMax
temp = localMax.astype(np.uint8)
cv2.imshow("localMax", temp * 255)
# 使用8-连通性对局部峰值进行连接成分分析,然后应用分水岭算法
# scipy.ndimage.label(input, structure=None, output=None)
# input :待标记的数组对象。输入中的任何非零值都被视为待标记对象,零值被视为背景。
# structure:定义要素连接的结构化元素。对于二维数组。默认是四连通, 此处选择8连通
#
markers = ndimage.label(localMax, structure=np.ones((3, 3)))[0]  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 可视化markers
temp_markers = markers.astype(np.uint8)
cv2.imshow("temp_markers", temp_markers * 20)

# 由于分水岭算法假设我们的标记代表距离图中的局部最小值(即山谷),因此我们取 D 的负值。
labels = watershed(-D, markers, mask=thresh)
print("[INFO] {} unique segments found".format(len(np.unique(labels)) - 1))

# 循环遍历分水岭算法返回的标签
for label in np.unique(labels):
    # 0表示背景,忽略它
    if label == 0:
        continue
    # 否则,为标签区域分配内存并将其绘制在掩码上
    mask = np.zeros(gray.shape, dtype="uint8")
    mask[labels == label] = 255
    # 在mask中检测轮廓并抓取最大的一个
    cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,
                            cv2.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)

    color = (rng.randint(0, 256), rng.randint(0, 256), rng.randint(0, 256))
    cv2.drawContours(image, cnts, -1, color, 3)  # 轮廓标记从1开始

# 显示输出图像
cv2.imshow("Output", image)
cv2.imwrite(f"result_{args['image']}", image)
cv2.waitKey(0)

在这里插入图片描述

5、参考

  • https://pyimagesearch.com/2015/11/02/watershed-opencv/
  • https://gael-varoquaux.info/scipy-lecture-notes/packages/scikit-image/auto_examples/plot_segmentations.html
  • OpenCV分水岭分割算法2
  • 【python】OpenCV—WaterShed Algorithm

http://www.kler.cn/a/388486.html

相关文章:

  • lv_ffmpeg学习及播放rtsp
  • 谷歌Gemini与Anthropic Claude对比测试引发争议:AI竞赛暗流涌动
  • 细说STM32F407单片机轮询方式读写SPI FLASH W25Q16BV
  • 全面掌握 AutoGluon:从入门到生产环境的实践指南
  • 详细介绍如何使用rapidjson读取json文件
  • SpringBoot3-第六篇(整合NoSQL)
  • Knowledge Graph-Enhanced Large Language Models via Path Selection
  • 海康Android面试题及参考答案
  • PSINS工具箱,MATLAB例程,仅以速度为观测量的SINS/GNSS组合导航(滤波方式为EKF)
  • jmeter常用配置元件介绍总结之分布式压测
  • Python | Leetcode Python题解之第557题反转字符串中的单词III
  • 团结引擎中直接出鸿蒙包hap app
  • 2024 年(第 7 届)“泰迪杯”数据分析技能赛B 题 特殊医学用途配方食品数据分析 完整代码 结果 可视化分享
  • Windows 结合 Docker 下使用 Django+Celery+Pool
  • [翻译]ANSI X9.24-3-2017
  • AI 刷题实践选题:精选真题功能的深度剖析与学习实践| 豆包MarsCode AI刷题
  • “双十一”电商狂欢进行时,在AI的加持下看网易云信IM、RTC如何助力商家!
  • UE5.4 PCG 执行后生成函数
  • Word大珩助手:超大数字怎么读?35位数字?69位数字?
  • 嵌入式学习-网络高级-Day01
  • 【青牛科技】应用方案 | D75xx-150mA三端稳压器
  • 如何在 HFSS 3D 布局中创建 cutout 子设计
  • goroutine 介绍
  • 我爸瘫痪,我们三兄弟每月出2500让嫂子伺候,得知我工资10000,嫂子打电话:你多给1000行吗?...
  • lua脚本调用 c/c++中的接口
  • Spring Shell——快速构建终端应用,自定义终端命令