OpenCV:GrabCut 交互式前景分割小案例

文章讲述了GrabCut算法如何通过用户交互指定区域,结合GMM模型进行图像分割,以及一个Python类如何封装这一功能以实现图像的交互式抠图。
摘要由CSDN通过智能技术生成

GrabCut 获取前景物体
通过交互的方式获得前景物体.

  • 用户指定前景的大体区域, 剩下的为背景区域.
  • 用户可以明确指定某些地方为前景或背景.
  • GrabCut采用分段迭代的方法分析前景物体, 形成模型树.
  • 最后根据权重决定某个像素是前景还是背景.

GrabCut算法主要基于以下知识:

  • k均值聚类
  • 高斯混合模型建模(GMM)
  • max flow/min cut

这里介绍一些GrabCut算法的实现步骤:

  1. 在图片中定义(一个或者多个)包含物体的矩形。
  2. 矩形外的区域被自动认为是背景 。
  3. 对于用户定义的矩形区域,可用背景中的数据来区分它里面的前景和背景区域。
  4. 用高斯混合模型(GMM)来对背景和前景建模,并将未定义的像素标记为可能的前景或者背景。
  5. 图像中的每一个像素都被看做通过虚拟边与周围像素相连接,而每条边都有一个属于前景或者背景的概率,这是基于它与周边像素颜色上的相似性。
  6. 每一个像素(即算法中的节点)会与一个前景或背景节点连接。
  7. 在节点完成连接后(可能与背景或前景连接),若节点之间的边属于不同终端(即一个节点属于前景,另一个节点属于背景),则会切断他们之间的边,这就能将图像各部分分割出来。下图能很好的说明该算法:

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

img——待分割的源图像,必须是8位3通道,在处理的过程中不会被修改

mask——掩码图像,如果使用掩码进行初始化,那么mask保存初始化掩码信息;在执行分割的时候,也可以将用户交互所设定的前景与背景保存到mask中,然后再传入grabCut函数;在处理结束之后,mask中会保存结果。mask只能取以下四种值:
        GCD_BGD(=0),背景;
        GCD_FGD(=1),前景;
        GCD_PR_BGD(=2),可能的背景;
        GCD_PR_FGD(=3),可能的前景。

  • rect——用于限定需要进行分割的图像范围,只有该矩形窗口内的图像部分才被处理;
  • bgdModel——背景模型,如果为None,函数内部会自动创建一个bgdModel;bgdModel必须是单通道浮点型图像,且行数只能为1,列数只能为13x5;
  • fgdModel——前景模型,如果为None,函数内部会自动创建一个fgdModel;fgdModel必须是单通道浮点型图像,且行数只能为1,列数只能为13x5;
  • iterCount——迭代次数,必须大于0;
  • mode——用于指示grabCut函数进行什么操作,可选的值有:

        GC_INIT_WITH_RECT(=0),用矩形窗初始化GrabCut;
        GC_INIT_WITH_MASK(=1),用掩码图像初始化GrabCut;
        GC_EVAL(=2),执行分割。

# 面向对象:把grabCut进行交互式抠图的功能封装成一个类
import cv2
import numpy as np

class App:
    
    def __init__(self, image):
        self.image = image
        self.img = cv2.imread(self.image)
        self.img2 = self.img.copy()
        self.start_x = 0
        self.start_y = 0
        # 是否需要绘制矩形的标志
        self.rect_flag = False

        self.rect = (0, 0, 0, 0)
        self.mask = np.zeros(shape=self.img.shape[:2], dtype=np.uint8)
        # 输出
        self.output = np.zeros(shape=self.img.shape[:2], dtype=np.uint8)

    def on_mouse(self,event, x, y, flags, param):
        # 按下左键开始框选前景区
        if event == cv2.EVENT_LBUTTONDOWN:
            # 记录起始坐标
            self.start_x = x
            self.start_y = y
            self.rect_flag = True
        elif event == cv2.EVENT_LBUTTONUP:
            self.rect_flag = False
            # 记录用户矩形的大小
            self.rect = (min(self.start_x, x), min(self.start_y, y), abs(self.start_x - x), abs(self.start_y - y))

            cv2.rectangle(self.img, (self.start_x, self.start_y), (x, y), (0, 0, 255), 2)
        elif event == cv2.EVENT_MOUSEMOVE and self.rect_flag:
            # 画矩形
            self.img = self.img2.copy()
            cv2.rectangle(self.img, (self.start_x, self.start_y), (x, y), (0, 255, 0), 2)

    # 窗口 回调函数 图片
    def run(self):
        
        cv2.namedWindow('img')
        # 绑定鼠标事件
        cv2.setMouseCallback('img', self.on_mouse)
        while True:
            cv2.imshow('img', self.img)
            cv2.imshow('output', self.output)

            key = cv2.waitKey(1)
            if key == 27:    # 按Esc退出
                break
            elif key == ord('c'):
                # 进行切图
                cv2.grabCut(self.img2, self.mask, self.rect, None, None, 5, mode = cv2.GC_INIT_WITH_RECT)
                # 此时mask已经被修改
                # 把前景或者可能是前景的位置设置成255
                mask2 = np.where((self.mask == 1) | (self.mask == 3), 255, 0).astype(np.uint8)
                # 使用与运算抠图
                self.output = cv2.bitwise_and(self.img2, self.img2, mask=mask2)
            elif key == ord('x'):   # 按下x键时,获取更大范围想要的前景
                self.mask[200 : 300, 350: 450] = 1
                cv2.grabCut(self.img2, self.mask, self.rect, None, None, 5, mode = cv2.GC_INIT_WITH_RECT)
                mask2 = np.where((self.mask == 1) | (self.mask == 3), 255, 0).astype(np.uint8)
                self.output = cv2.bitwise_and(self.img2, self.img2, mask=mask2)

        cv2.destroyAllWindows()


app = App('images/lena.png')
app.run()

 

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
cv::grabCutOpenCV中的一个图像分割算法,用于将图像分割前景和背景。它基于图割算法,通过迭代优化来实现分割效果。具体用法如下: ```cpp void cv::grabCut(cv::InputArray img, // 输入图像 cv::InputOutputArray mask, // 输入输出掩码 cv::Rect rect, // 包含前景的矩形 cv::InputOutputArray bgdModel, // 背景模型 cv::InputOutputArray fgdModel, // 前景模型 int iterCount, // 迭代次数 int mode // 模式 ) ``` 其中,参数说明如下: - img:输入图像。 - mask:输入输出掩码,用于指定前景和背景的区域。 - rect:包含前景的矩形,用于初始化分割。 - bgdModel:背景模型,用于存储背景像素的统计信息。 - fgdModel:前景模型,用于存储前景像素的统计信息。 - iterCount:迭代次数,用于优化分割结果。 - mode:模式,用于指定算法的初始化方式。 具体的使用方法可以参考以下示例代码: ```cpp #include <opencv2/opencv.hpp> int main() { cv::Mat image = cv::imread("input.jpg"); cv::Rect rect(50, 50, 200, 200); cv::Mat mask(image.size(), CV_8UC1, cv::GC_BGD); cv::Mat bgdModel, fgdModel; cv::grabCut(image, mask, rect, bgdModel, fgdModel, 5, cv::GC_INIT_WITH_RECT); cv::Mat result; cv::compare(mask, cv::GC_PR_FGD, result, cv::CMP_EQ); cv::imshow("Result", result); cv::waitKey(0); return 0; } ``` 这段代码将会对输入图像进行分割,将前景部分标记为白色,背景部分标记为黑色,并显示分割结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值