【OpenCV-Python】教程:3-16 利用Grabcut交互式前景提取

news2024/11/15 10:18:59

OpenCV Python Grabcut分割

【目标】

  • Grabcut 算法
  • 创建一个交互程序

【理论】

从用户角度是如何工作的呢?用户在需要的目标上初始绘制一个矩形,前景目标必须完全在矩形内部,算法迭代的去分割然后得到更好的效果,但是有些情况下,分割效果不是很好,例如:会将部分前景标记为背景,反之亦然。这个时候,就需要用户做一些交互,告诉分割结果,哪些是前景哪些是背景,这样下次迭代就会得到更好的效果。

背景发生了哪些动作?

  • 所有在矩形外的部分被标记为背景,在矩形框里面的都是未知。相似的所有用户给定的前景和背景都是 硬标签
  • 计算机不会初始化标签,主要取决于用户给定的数据,标记前景和背景像素。
  • GMM(混合高斯模型)被用户前景和背景
  • 根据给定的数据,GMM学习和创造一些新的像素分布,位置像素会被标记为可能前景或背景,取决于其他硬标签像素(有点类似聚类)
  • 图通过像素分布创建,图中的节点是像素,增加两个节点,一个是源节点,一个是汇节点,每个前景像素与源节点连接,每个背景像素与汇节点连接。
  • 连接像素直接边的权重主要根据像素属于前景或背景的概率而定,像素之间的权重根据边缘信息和像素相似度而定。如果像素颜色有很大的不同,那么他们之间的边缘权重会小一些。
  • mincut算法用于分割图,它以最小代价函数将图分成两个分离的源节点和汇节点。代价函数是所有边权重之和。cut之后,所有连接到源节点的像素变成了前景,连接到汇节点的像素变成了背景。
  • 这个过程持续到分类收敛。

在这里插入图片描述

【代码】

在这里插入图片描述

import numpy as np 
import cv2 

import sys 


COLOR_BLUE = [255, 0, 0]      # 矩形框颜色
COLOR_RED  = [0, 0, 255]      # 可能背景绘制颜色
COLOR_GREEN = [0, 255, 0]     # 可能前景绘制颜色
COLOR_BLACK = [0, 0, 0]       # 背景绘制颜色
COLOR_WHITE = [255, 255, 255] # 前景绘制颜色

DRAW_BG = {'color': COLOR_BLACK, 'val': 0}    # 背景,标记为0
DRAW_FG = {'color': COLOR_WHITE, 'val': 1}    # 前景,标记为1
DRAW_PR_BG = {'color': COLOR_RED, 'val': 2}    # 可能背景,标记为2
DRAW_PR_FG = {'color': COLOR_GREEN, 'val': 3}  # 可能前景,标记为3


class GrabCutApp():
    """
    GrabCutApp 利用grabcut对图像进行前景提取

    USAGE:
        python grabcut.py <image_name>

    README FIRST:
        two windows will show up, one for input and one for output 
      
        at first, in input window, right mouse buttom draw a rectangle around the object with blue. 
        then, press 'n' to segment the object (once of a few times). if the effect is not so good, 
        please prees key below what you want, the key concept is below:
      
        key '0' - select area of sure background
        key '1' - select area of sure foreground
        key '2' - select area of probable background
        key '3' - select area of probable foreground
      
        key 'r' - reset the setup
        key 'n' - segment the object
        key 's' - save the segmented image to "image_name_grabcut.png"
        key 'q' - quit
        key esc - quit
    """

    # 初始化
    def __init__(self, imagename: str) -> None:
        self.img = cv2.imread(imagename)
        if self.img is None:
            print('图像读取失败')
            sys.exit(0)
          
        self.rect = (0, 0, 1, 1) # 矩形框初始化
        self.drawing = False
        self.rectangle = False # 是否开始绘制矩形框
        self.rect_over = False # 判断矩形是否结束
        self.rect_or_mask = 100 # 矩形框或者mask的种类
        self.value = DRAW_FG
        self.thickness = 3
        self.radius = 5
      
      
    # 鼠标回调
    def onmouse(self, event, x, y, flags, param) -> None:
        # 自定义鼠标回调函数
        if event == cv2.EVENT_RBUTTONDOWN:
            self.rectangle = True
            self.ix, self.iy = x, y
        elif event == cv2.EVENT_MOUSEMOVE:
            if self.rectangle == True:
                self.img = self.img2.copy()
                cv2.rectangle(self.img, (self.ix, self.iy), (x, y), 
                            COLOR_BLUE, self.thickness)
                self.rect = (min(self.ix, x), min(self.iy, y), 
                            abs(self.ix - x), abs(self.iy - y))
                self.rect_or_mask = 0
        elif event == cv2.EVENT_RBUTTONUP:
            self.rectangle = False
            self.rect_over = True
            cv.rectangle(self.img, (self.ix, self.iy), (x, y), 
                        COLOR_BLUE, self.thickness)
            self.rect = (min(self.ix, x), min(self.iy, y), 
                        abs(self.ix - x), abs(self.iy - y))
            self.rect_or_mask = 0
            print(" Now press the key 'n' a few times until no further change \n")

        # 交互操作
        if event == cv2.EVENT_LBUTTONDOWN:
            if self.rect_over == False:
                print("draw object first \n")
            else:
                self.drawing = True
                cv2.circle(self.img, (x, y), self.radius, self.value['color'], -1)
                cv2.circle(self.mask, (x, y), self.radius, self.value['val'], -1)
        elif event == cv2.EVENT_MOUSEMOVE:
            if self.drawing == True:
                cv2.circle(self.img, (x, y), self.radius, self.value['color'], -1)
                cv2.circle(self.mask, (x, y), self.radius, self.value['val'], -1)
        elif event == cv2.EVENT_LBUTTONUP:
            if self.drawing == True:
                self.drawing = False
                cv2.circle(self.img, (x, y), self.radius, self.value['color'], -1)
                cv2.circle(self.mask, (x, y), self.radius, self.value['val'], -1)

    def run(self):
        # 拷贝
        self.img2 = self.img.copy()
        # 初始化一个mask图像
        self.mask = np.zeros(self.img.shape[:2], dtype=np.uint8)
        self.output = np.zeros(self.img.shape, np.uint8)

        cv2.namedWindow('output')
        cv2.namedWindow('input')
        cv2.setMouseCallback('input', self.onmouse)
        cv2.moveWindow('input', self.img.shape[1] + 10, 0)

        print('draw a rectangle around the object use right mouse button to draw')

        while(1):
            cv2.imshow('output', self.output)
            cv2.imshow('input', self.img)
            k = cv2.waitKey(1)
          
            # 
            if k == 27 or k == ord('q'): # esc or 'q' to quit 
                break
            elif k == ord('0'): # BG drawing
                print("mark background regions with left mouse buttom \n")
                self.value = DRAW_BG
            elif k == ord('1'): # FG drawing
                print("mark foreground regions with left mouse buttom \n")
                self.value = DRAW_FG
            elif k == ord('2'): # PR_BG drawing
                self.value = DRAW_PR_BG
            elif k == ord('3'): # PR_FG drawing
                self.value = DRAW_PR_FG
            elif k == ord('s'): # save image
                bar = np.zeros((self.img.shape[0], 5, 3), np.uint8)
                res = np.hstack((self.img2, bar, self.img, bar, self.output))
                cv2.imwrite('grabcut_output_result.png', res)
                print('result saved as grabcut_output_result.png\n')
            elif k == ord('r'): # restore to original status
                print('reset all settings ...\n')
                self.rect = (0, 0, 1, 1)
                self.drawing = False
                self.rectangle = False 
                self.rect_or_mask = 100
                self.rect_over = False
                self.value = DRAW_FG
                self.img = self.img2.copy()

                self.mask = np.zeros(self.img.shape[:2], dtype=np.uint8)
                self.output = np.zeros(self.img.shape, np.uint8)
            elif k == ord('n'):
                print("for finer touchups, mark foreground and background after pressing keys 0-3")
                try:
                    bgdmodel = np.zeros((1, 65), np.float64)
                    fgdmodel = np.zeros((1, 65), np.float64)
                    if (self.rect_or_mask == 0): # original rectangle
                        cv2.grabCut(self.img2, self.mask, self.rect, bgdmodel, 
                                    fgdmodel, 1, cv2.GC_INIT_WITH_RECT)
                        self.rect_or_mask = 1
                    elif (self.rect_or_mask == 1): # grabcut after rectangle
                        cv2.grabCut(self.img2, self.mask, self.rect, bgdmodel, 
                                    fgdmodel, 5, cv2.GC_INIT_WITH_MASK)
                except:
                    import traceback
                    traceback.print_exc()
            mask2 = np.where((self.mask==1)+ (self.mask==3), 255, 0).astype('uint8')
            cv2.imshow('mask2', mask2)
            self.output = cv2.bitwise_and(self.img2, self.img2, mask=mask2)
      
        cv2.destroyAllWindows()


if __name__ == '__main__':
    app = GrabCutApp('assets/messi5.jpg')
    print(app.__doc__)
    app.run()

【接口】

cv.grabCut(	img, mask, rect, bgdModel, fgdModel, iterCount[, mode]	) ->	mask, bgdModel, fgdModel

执行 grabcut 算法

  • img: 输入8位3通道图像
  • mask: 输入输出的8位单通道图像,用矩形初始化
  • rect: ROI矩形,在矩形外面的部分被认为是背景,只有当 mode = GC_INIT_WITH_RECT 时才有效
    bgdModel: 存储背景模型的参数,处理同一个图像时,不要修改该模型
    fgdModel: 存储前景模型的参数,处理同一个图像时,不要修改该模型
  • iterCount: 迭代次数
  • mode: 不同的模式 GrabCutModes
  • GrabCutModes

在这里插入图片描述

【参考】

  1. OpenCV 官方文档
  2. "GrabCut": interactive foreground extraction using iterated graph cuts
  3. GrabCut image segmentation algorithm.

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

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

相关文章

Java9-17新特性解读,了解少于3个你可能脱节了

前言 Java8出来这么多年后&#xff0c;已经成为企业最成熟稳定的版本&#xff0c;相信绝大部分公司用的还是这个版本&#xff0c;但是一眨眼今年Java19都出来了&#xff0c;相信很多Java工程师忙于学习工作对新特性没什么了解&#xff0c;有的话也仅限于某一块。 本篇就是博主对…

【论文笔记】Radatron: Accurate Detection Using Multi-Resolution Cascaded MIMO Radar

文章及补充材料链接&#xff1a;https://jguan.page/Radatron/ 理解本文需要一些电磁波与雷达信号处理相关的理论知识。 1. 引言 雷达的角度分辨率通常很低。雷达的角度分辨率与雷达的天线孔径成反比&#xff0c;因此毫米波雷达要达到激光雷达的角度分辨率&#xff0c;需要3000…

黑盒测试用例设计 - 因果图法

说明 因果图法是一种适合于描述对多种输入条件组合的测试方法根据输入条件的组合、约束关系和输出条件的因果关系&#xff0c;分析输入条件的各种组合情况&#xff0c;从而设计测试用例的方法它适合于检查程序输入条件涉及的各种组合情况 使用步骤 第一步&#xff1a;根据功…

60岁首席工程师被SpaceX边缘化,主管:我怕他退休或死了

金磊 发自 凹非寺量子位 | 公众号 QbitAI在SpaceX&#xff0c;连首席工程师竟也能遭遇职场边缘化。故事的主人公叫做John Johnson&#xff08;姑且叫他“逊哥”&#xff09;&#xff0c;他在2018年加入SpaceX。当时的逊哥已经58岁&#xff0c;但在光学工程领域可谓颇有建树。刚…

Java---数据库---MyBatisPlus

MyBatisPlus1&#xff0c;MyBatisPlus入门案例与简介1.1 入门案例步骤1:创建数据库及表步骤2:创建SpringBoot工程步骤3:勾选配置使用技术步骤4:pom.xml补全依赖步骤5:添加MP的相关配置信息步骤6:根据数据库表创建实体类步骤7:创建Dao接口步骤8:编写引导类步骤9:编写测试类1.2 M…

Java项目:ssm图书馆管理系统

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 功能介绍 基于ssm的图书馆管理系统.主要功能包括&#xff1a;图书查询、图书管理、图书编辑、读者管理、图书的借阅与归还以及借还日志记录等。 用户分为…

ArrayList源码阅读笔记

1. 基础知识 1.1 概念 ArrayList是可以动态增长和缩减的索引序列&#xff0c;它是基于数组实现的List类。该类封装了一个动态再分配的Object[]数组&#xff0c;每个对象都有一个capacity属性&#xff0c;表示它们所封装的Object[]数组长度&#xff0c;当向ArrayList中添加元素…

DAMOYOLO windows 单卡训练

最近达摩院放出了目前最能打的yolo算法&#xff0c;时间和精度都得到了提升 目前代码已经开源&#xff1a; 代码地址&#xff1a;GitHub - tinyvision/DAMO-YOLO: DAMO-YOLO: a fast and accurate object detection method with some new techs, including NAS backbones, effi…

详细的科技特长生路径和成长规划

2021年&#xff0c;教育部印发了《关于进一步加强和改进普通高等学校艺术类专业考试招生工作的指导意见》&#xff0c;在其中规范了艺术类专业的招生政策&#xff0c;包括艺术类考试和高水平艺术团考试&#xff0c;其中明确自2024年起&#xff0c;高校高水平艺术团不再从高校招…

今年双十二值得买的数码好物推荐!双十二数码产品抢购攻略

时间过得真快&#xff0c;一年一度的双十二年终最后的大促即将正式拉开序幕。此刻&#xff0c;不知道大家制定好购物计划了吗&#xff1f;如果你想入手数码好物&#xff0c;那么不妨看一下我刚刚整理的这份清单&#xff0c;涵盖了手机、平板、耳机等多个品类&#xff0c;相信总…

Centos Linux 7 查看网卡

Centos Linux 7 查看网卡 一. 二.

使用 Learner Lab - 使用 CloudWatch 进行排错,搭配 API Gateway 与 Lambda

使用 Learner Lab - 使用 CloudWatch 进行排错&#xff0c;搭配 API Gateway 与 Lambda AWS Academy Learner Lab 是提供一个帐号让学生可以自行使用 AWS 的服务&#xff0c;让学生可以在 100 USD的金额下&#xff0c;自行练习所要使用的 AWS 服务&#xff0c;如何进入 Learne…

设计模型之单例设计

前言 单例模式&#xff08;Singleton Pattern&#xff09;是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类&#xff0c;该类负责创建自己的对象&#xff0c;同时确保只有单个对象被…

3. Exchange 交换机的使用

二八佳人体似酥&#xff0c;腰间仗剑斩愚夫。虽然不见人头落&#xff0c;暗里教君骨髓枯。 在上一节中&#xff0c;我们创建了一个工作队列。我们假设的是工作队列背后&#xff0c;每个任务都恰好交付给一个消 费者(工作进程)。在这一部分中&#xff0c;我们将做一些完全不同的…

孪生神经网络

孪生神经网络 孪生神经网络&#xff08;Siamese network&#xff09;主要用途是比较两图片的相似程度&#xff0c;其核心思想就是权值共享。 卷积神将网络是通过卷积运算提取图像的特征进行训练的&#xff0c;如果想比较两个图像的相似程度&#xff0c;也要对两个图像分别进行…

毛里智慧小学宿舍楼工程量清单编制

目 录 摘 要 I 第1章 前言 1 第2章 招标控制价编制 3 2.1招标控制价 3 2.2建设项目招标控制价汇总表 4 2.3单项工程招标控制价汇总表 5 2.4单项工程招标控制价汇总表 14 2.5分部分项工程和单价措施项目清单与计价表 24 2.6总价措施项目清单与计价表 27 2.7综合单价分析表 28 2.…

C语言学习之路(基础篇)—— 文件操作(上)

说明&#xff1a;该篇博客是博主一字一码编写的&#xff0c;实属不易&#xff0c;请尊重原创&#xff0c;谢谢大家&#xff01; 概述 1) 磁盘文件和设备文件 磁盘文件 指一组相关数据的有序集合,通常存储在外部介质(如磁盘)上&#xff0c;使用时才调入内存。 设备文件 在操作…

亚马逊云科技re:Invent:Serverless是所有构想的核心

12月2日&#xff0c;2022亚马逊云科技re:Invent全球大会上&#xff0c;Amazon.com副总裁兼首席技术官Werner Vogels博士向开发者们展示了另一种可能。在一系列Serverless工具的帮助下&#xff0c;一些代码可以少写&#xff0c;因为未来你可能再也不需要写它们了。这恐怕是自云原…

包装类-Wrapper

包装类的分类 针对八种基本数据类型相应的引用类型-包装类有了类的特点&#xff0c;就可以调用对应的类中的方法 装箱和拆箱 Java是一种面向对象的编程语言&#xff0c;学习Java时就被明确灌输了一个概念&#xff1a;OOP&#xff0c;即面向对象编程。一切皆对象。但是基本…

[附源码]JAVA毕业设计框架的电脑测评系统(系统+LW)

[附源码]JAVA毕业设计框架的电脑测评系统&#xff08;系统LW&#xff09; 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技…