您的位置:首页 > 财经 > 产业 > 国外设计网站pinterest极速版_企业网站建设问题_seo技术分享博客_网络推广专员岗位职责

国外设计网站pinterest极速版_企业网站建设问题_seo技术分享博客_网络推广专员岗位职责

2025/5/4 18:49:40 来源:https://blog.csdn.net/qq_53832058/article/details/146019242  浏览:    关键词:国外设计网站pinterest极速版_企业网站建设问题_seo技术分享博客_网络推广专员岗位职责
国外设计网站pinterest极速版_企业网站建设问题_seo技术分享博客_网络推广专员岗位职责

1. GrabCut 算法简介

GrabCut 是一种基于图割(Graph Cut)的交互式图像分割算法。它只需要用户提供少量的前景和背景信息(通常是一个包含前景的矩形框),就能通过迭代优化,实现高质量的前景提取。

核心思想:

  • 构建图模型: 将图像像素视为图的节点,相邻像素之间通过边连接。边的权重反映了像素之间的相似度(颜色、梯度等)。
  • 用户交互: 用户通过矩形框指定一个包含前景的区域。矩形框外的区域被认为是确定的背景(GC_BGD),框内的区域被认为是可能的前景(GC_PR_FGD)。
  • 高斯混合模型(GMM): 分别为前景和背景构建颜色分布的高斯混合模型。每个像素根据其颜色属于前景或背景的概率被标记。
  • 迭代优化: 通过最小化能量函数(考虑像素颜色与 GMM 的匹配度以及相邻像素标签的平滑性),不断更新像素的标签(前景/背景)和 GMM 参数,直到收敛。

2. 代码逐步解析

我们来详细分析你提供的 Python 代码,理解每一步的作用。

import cv2
import numpy as np# 定义一个应用类,用于图像处理和鼠标交互
class app:# 初始化类变量,用于标记是否正在绘制矩形,以及矩形的相关信息flag_rect=Falserect=(0,0,0,0)startx= 0starty= 0# 鼠标回调函数,处理鼠标事件def onmouse(self, event, x, y, flags, param):# 当鼠标左键按下时,开始记录起始位置if event == cv2.EVENT_LBUTTONDOWN:self.flag_rect= Trueself.startx= xself.starty= yprint("down")# 当鼠标左键抬起时,绘制矩形并记录矩形信息elif event == cv2.EVENT_LBUTTONUP:self.img2=self.img.copy()self.flag_rect= Falsecv2.rectangle(self.img2, (self.startx, self.starty), (x, y), (0, 0, 255), 2)self.rect=(min(self.startx,x), min(self.starty,y), abs(self.startx-x), abs(self.starty-y)) print("up")# 当鼠标移动且左键按下时,动态绘制矩形elif event == cv2.EVENT_MOUSEMOVE and (self.flag_rect & cv2.EVENT_FLAG_LBUTTON):self.img2=self.img.copy()cv2.rectangle(self.img2, (self.startx, self.starty), (x, y), (0, 255, 0), 2)print("move")# 主程序运行函数def run(self):print("Hello World")# 创建窗口并设置鼠标回调函数cv2.namedWindow("input")cv2.setMouseCallback("input", self.onmouse)# 读取图像并创建副本及掩码self.img=cv2.imread("./grabc/lena.png")self.img2=self.img.copy()self.mask= np.zeros(self.img.shape[:2], dtype=np.uint8)self.output=np.zeros(self.img.shape, dtype=np.uint8)# 主循环,用于不断显示图像和处理键盘输入while True:cv2.imshow("input",self.img2)cv2.imshow("output",self.output)key = cv2.waitKey(1)if key == 27:breakif key == ord("g"):# 初始化模型参数bgdModel= np.zeros((1, 65), np.float64)fgdModel= np.zeros((1, 65), np.float64)# 使用GrabCut算法提取前景cv2.grabCut(self.img, self.mask, self.rect, bgdModel, fgdModel, 1, cv2.GC_INIT_WITH_RECT)mask2= np.where((self.mask==1)|(self.mask==3), 255, 0).astype("uint8")self.output=cv2.bitwise_and(self.img, self.img, mask=mask2)# 如果作为主程序运行,则创建类实例并运行
if __name__ == "__main__":app().run()
  • 导入库:

    import cv2
    import numpy as np
    

    导入 OpenCV(cv2)和 NumPy(np)库。OpenCV 用于图像处理,NumPy 用于数值计算(特别是数组操作)。

  • 定义 app 类:

    class app:# ... (类的成员和方法)
    

    创建一个名为 app 的类,用于封装图像处理和用户交互的逻辑。

    • 类变量初始化:

      flag_rect=False
      rect=(0,0,0,0)
      startx= 0
      starty= 0
      
      • flag_rect: 布尔值,用于标记是否正在绘制矩形。
      • rect: 一个元组 (x, y, width, height),用于存储矩形的左上角坐标、宽度和高度。
      • startx, starty: 记录矩形绘制的起始点坐标。
    • onmouse 方法(鼠标回调函数):

      def onmouse(self, event, x, y, flags, param):# ... (鼠标事件处理逻辑)
      

      这个方法是 OpenCV 鼠标回调函数的实现。当鼠标在窗口中发生事件(按下、抬起、移动等)时,OpenCV 会自动调用这个函数。

      • event: 鼠标事件类型(例如 cv2.EVENT_LBUTTONDOWNcv2.EVENT_LBUTTONUPcv2.EVENT_MOUSEMOVE)。
      • x, y: 鼠标指针的当前坐标。
      • flags: 鼠标事件的附加标志(例如 cv2.EVENT_FLAG_LBUTTON 表示左键是否按下)。
      • param: 用户自定义的参数(这里没有用到)。

      鼠标事件处理逻辑:

      1. 鼠标左键按下 (cv2.EVENT_LBUTTONDOWN):

        if event == cv2.EVENT_LBUTTONDOWN:self.flag_rect= Trueself.startx= xself.starty= yprint("down")
        
        • flag_rect 设置为 True,表示开始绘制矩形。
        • 记录起始点的 xy 坐标。
      2. 鼠标左键抬起 (cv2.EVENT_LBUTTONUP):

        elif event == cv2.EVENT_LBUTTONUP:self.img2=self.img.copy()self.flag_rect= Falsecv2.rectangle(self.img2, (self.startx, self.starty), (x, y), (0, 0, 255), 2)self.rect=(min(self.startx,x), min(self.starty,y), abs(self.startx-x), abs(self.starty-y)) print("up")
        
        • 将原始图像 self.img 复制到 self.img2,以便在 self.img2 上绘制矩形而不影响原始图像。
        • flag_rect 设置为 False,表示矩形绘制结束。
        • 使用 cv2.rectangleself.img2 上绘制一个红色的矩形((0, 0, 255) 表示 BGR 颜色)。
        • 计算并存储矩形的 (x, y, width, height) 信息到 self.rect
      3. 鼠标移动且左键按下 (cv2.EVENT_MOUSEMOVEcv2.EVENT_FLAG_LBUTTON):

        elif event == cv2.EVENT_MOUSEMOVE and (self.flag_rect & cv2.EVENT_FLAG_LBUTTON):self.img2=self.img.copy()cv2.rectangle(self.img2, (self.startx, self.starty), (x, y), (0, 255, 0), 2)print("move")
        
        • 同样复制原始图像到 self.img2
        • self.img2 上绘制一个绿色的矩形,实现动态绘制效果。
    • run 方法(主程序运行函数):

      def run(self):# ... (主程序逻辑)
      

      这是程序的主要执行部分。

      1. 打印欢迎信息:

        print("Hello World")
        
      2. 创建窗口并设置鼠标回调:

        cv2.namedWindow("input")
        cv2.setMouseCallback("input", self.onmouse)
        
        • cv2.namedWindow("input"): 创建一个名为 “input” 的窗口,用于显示图像。
        • cv2.setMouseCallback("input", self.onmouse): 将 self.onmouse 函数设置为 “input” 窗口的鼠标回调函数。
      3. 读取图像、创建副本和掩码:

        self.img=cv2.imread("./grabc/lena.png")
        self.img2=self.img.copy()
        self.mask= np.zeros(self.img.shape[:2], dtype=np.uint8)
        self.output=np.zeros(self.img.shape, dtype=np.uint8)
        
        • self.img = cv2.imread("./grabc/lena.png"): 读取图像文件(你需要将 "./grabc/lena.png" 替换为你的图像路径)。
        • self.img2 = self.img.copy(): 创建图像的副本,用于显示和绘制。
        • self.mask = np.zeros(self.img.shape[:2], dtype=np.uint8): 创建一个与图像大小相同的全零掩码数组(self.mask)。这个掩码将用于 GrabCut 算法。
        • self.img.shape[:2] 保证了 mask 与 img 的尺寸一致
        • self.output = np.zeros(self.img.shape, dtype=np.uint8): 创建一个与图像大小相同的全零数组,用于存储 GrabCut 的输出结果。
      4. 主循环:

        while True:cv2.imshow("input",self.img2)cv2.imshow("output",self.output)key = cv2.waitKey(1)if key == 27:breakif key == ord("g"):# ... (GrabCut 算法调用)
        
        • cv2.imshow("input", self.img2): 在 “input” 窗口中显示 self.img2(包含绘制的矩形)。

        • cv2.imshow("output", self.output): 在 “output” 窗口中显示 self.output(GrabCut 的结果)。

        • key = cv2.waitKey(1): 等待键盘输入,等待时间为 1 毫秒。返回值是按键的 ASCII 码。

        • if key == 27: 如果按下 Esc 键(ASCII 码为 27),退出循环。

        • if key == ord("g"): 如果按下 ‘g’ 键,执行 GrabCut 算法。

          GrabCut 算法调用:

          bgdModel= np.zeros((1, 65), np.float64)
          fgdModel= np.zeros((1, 65), np.float64)
          cv2.grabCut(self.img, self.mask, self.rect, bgdModel, fgdModel, 1, cv2.GC_INIT_WITH_RECT)
          mask2= np.where((self.mask==1)|(self.mask==3), 255, 0).astype("uint8")
          self.output=cv2.bitwise_and(self.img, self.img, mask=mask2)
          
          • bgdModel = np.zeros((1, 65), np.float64): 创建一个用于存储背景模型参数的数组。
          • fgdModel = np.zeros((1, 65), np.float64): 创建一个用于存储前景模型参数的数组。
          • cv2.grabCut(...): 调用 GrabCut 算法。
            • self.img: 输入图像。
            • self.mask: 输入/输出掩码。初始时,矩形框外的区域会被自动标记为 cv2.GC_BGD(确定的背景),框内区域为 cv2.GC_PR_FGD(可能的前景)。GrabCut 算法会更新这个掩码。
            • self.rect: 包含前景的矩形框。
            • bgdModel: 背景模型。
            • fgdModel: 前景模型。
            • 1: 迭代次数。
            • cv2.GC_INIT_WITH_RECT: 初始化模式,表示使用矩形框初始化。
          • mask2 = np.where((self.mask == 1) | (self.mask == 3), 255, 0).astype("uint8"): 根据 GrabCut 更新后的掩码 self.mask 创建一个二值掩码 mask2
            • self.mask == 1self.mask == 3 表示像素被标记为确定的背景或可能的前景。
            • np.where(...): 将满足条件的像素设置为 255(白色),不满足条件的设置为 0(黑色)。
            • .astype("uint8"): 将掩码的数据类型转换为 uint8
          • self.output = cv2.bitwise_and(self.img, self.img, mask=mask2): 使用 mask2 作为掩码,对原始图像 self.img 进行按位与操作,提取前景。
  • 运行 app

    if __name__ == "__main__":app().run()
    

    如果这个脚本是主程序(而不是被导入的模块),则创建一个 app 类的实例并调用其 run 方法,启动程序。

3. 运行和使用

  1. 准备图像: 准备一张你想要分割的图像,例如 "lena.png"
  2. 运行代码: 运行这段 Python 代码。
  3. 绘制矩形: 在弹出的 “input” 窗口中,用鼠标左键拖动绘制一个矩形,将你想要提取的前景包含在矩形内。
  4. 执行 GrabCut: 按下键盘上的 ‘g’ 键。
  5. 查看结果: “output” 窗口中会显示分割后的前景图像。
  6. 退出: 按下 Esc 键退出程序。

4. 改进和扩展

  • 多次迭代: 你可以通过增加 cv2.grabCut 函数中的迭代次数(例如 510)来获得更精细的分割结果。
  • 使用掩码初始化: 除了使用矩形框,你还可以手动绘制掩码来更精确地指定前景和背景区域。这需要将 cv2.GC_INIT_WITH_RECT 改为 cv2.GC_INIT_WITH_MASK,并在 self.mask 中设置相应的像素值:
    • cv2.GC_BGD (0): 确定的背景
    • cv2.GC_FGD (1): 确定的前景
    • cv2.GC_PR_BGD (2): 可能的背景
    • cv2.GC_PR_FGD (3): 可能的前景
  • 交互式修正: 你可以添加更多的鼠标交互,允许用户在 GrabCut 结果的基础上进行手动修正(例如,用画笔标记错误分割的区域)。
  • 参数调整
    • gamma: gamma 值越小, 算法会倾向于将更多的像素标记为前景。
    • lambda: lambda 值控制了平滑项的权重, lambda 值越大, 分割边界越平滑。

希望这个详细的教程对你有帮助!

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com