opencv图像分割之Grabcut

Grabcut

原理部分请大家自行百度学习,本人不擅长。

  • 基于交互式界面由用户选择前景区域;
  • 定义一个单通道的输出掩码,0为背景,1为前景,2为可能的背景,3为可能的前景;
  • grabCut抠图;将输出结果与可能的前景作比较得到可能的前景;
  • 定义三通道的结果图像;
  • 从原图中拷贝可能的前景到结果图像;

在这里插入图片描述

相关api

函数api

grabCut( InputArray img, 
        InputOutputArray mask, 
        Rect rect,
        InputOutputArray bgdModel, 
        InputOutputArray fgdModel,
        int iterCount, 
        int mode = GC_EVAL );

参数介绍

  • img:输入原图像;
  • mask:8位单通道掩码。如果函数设置mode= GC_INIT_WITH_RECT,则该掩码由函数来初始化。它的元素值是GrabCutClasses中的一个。;
  • rect:包含待分割目标的ROI区域。在该区域之外的像素被标记为"obvious background(背景)"。这个参数仅在mode==GC_INIT_WITH_RECT的时候使用;
  • bgModel:输出背景图像;
  • fgModel:输出前景图像;
  • iterCount:迭代次数;
  • mode: 默认GC_EVAL:必须是枚举类型GrabCutModes中的一种
GC_INIT_WITH_RECT该函数使用提供的矩形初始化状态和掩码。之后,它将运行算法的iterCount迭代
GC_INIT_WITH_MASK该函数使用提供的掩码初始化状态。 请注意,可以合并GC_INIT_WITH_RECTGC_INIT_WITH_MASK。 然后,使用GC_BGD自动初始化ROI之外的所有像素。
GC_EVAL该值表示算法应恢复
GC_EVAL_FREEZE_MODEL该值表示该算法应仅使用固定模型运行grabCut算法(单次迭代)

代码

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>

using namespace  std;
using namespace cv;

#define PIC_PATH "/work/opencv_pic/"
#define PIC_NAME "flower.jpeg"

Rect rect;   //roi区域
bool init =false;   //扣图 显示标志位
Mat src; 
Mat mask,bgModel,fgModel;  //掩码矩阵
void on_mouse_callback(int event,int x,int y,int flags,void *param);
void show_img(void);
void set_roi(void);
string win_name = "src pic";
int numRun =0;
void run_grabcut(void);
int main(void)
{
    //获取完整的图片路径及名称
    string pic = string(PIC_PATH)+string(PIC_NAME);

    //打印图片路径
    cout << "pic path is :"<<pic<<endl;

    //读取图片
    src = imread(pic);

    //判断图片是否存在
    if(src.empty())
    {
        cout<<"pic is not exist!!!!"<<endl;
        return -1;
    }

    //显示图片

    namedWindow(win_name,WINDOW_AUTOSIZE);
    imshow(win_name,src);

    //初始化mask
    mask.create(src.size(),CV_8UC1);   //创建mask 大小与原图一致 单通道8位
    mask.setTo(Scalar::all(GC_BGD));   //先全部设置为背景

    //鼠标事件回调函数
    setMouseCallback(win_name,on_mouse_callback);

    while(true)
    {
        //检测键值
        char c = (char)waitKey(0);
        if(c == 'n')   //检测到n进行一次扣图 可以根据按键次数迭代扣图次数
        {
            run_grabcut();   //扣图算法
            numRun ++;
            show_img();   //显示图像
            printf("current ite: %d\n",numRun);  //显示当前迭代次数
        }
        if(c == 27)   //检测到esc键 退出程序运行
        {
            break;
        }
    }

    destroyAllWindows();
    return 0;
}
void show_img(void)
{
    Mat result,bin_mask;
    bin_mask.create(mask.size(),CV_8UC1);
    bin_mask = mask & 1;
    if(init)
    {
        src.copyTo(result,bin_mask);    //获取掩码覆盖区域
    }else
    {
        src.copyTo(result);
    }

    rectangle(result,rect,Scalar(255,0,0),2);
    imshow(win_name,result);
}
void set_roi(void)
{
    /*
        GC_FGD = 1  前景
        GC_BGD = 0  背景
        GC_PF_FGD = 3  可能前景
        GC_PF_BGD = 2  可能背景
    */
    mask.setTo(GC_BGD);   //mask全部设置为纯背景
    rect.x = max(0,rect.x);
    rect.y = max(0,rect.y);
    rect.width = min(rect.width,src.cols-rect.x);
    rect.height = min(rect.height,src.rows-rect.y);

    //将矩形框选定区域设置为疑似前景 因为里面包含要扣取的对象
    mask(rect).setTo(Scalar(GC_PR_FGD));
}
void on_mouse_callback(int event,int x,int y,int flags,void *param)
{
    switch (event)
    {
        case EVENT_LBUTTONDOWN:
            rect.x = x;
            rect.y = y;
            rect.width = 1;
            rect.height = 1;
            init = false;
            numRun = 0;
        break;
        case EVENT_MOUSEMOVE:
            if(flags & EVENT_FLAG_LBUTTON)
            {
                rect = Rect(Point(rect.x,rect.y),Point(x,y));
                show_img();
            }
        break;
        case EVENT_LBUTTONUP:
            if(rect.width>1 && rect.height>1)
            {
                set_roi();   //将选定的区域设置为感兴趣区域
                show_img();
                init = true;
            }
        break;
        default:
            break;
    }
}

void run_grabcut(void)
{
    //检测范围太小的话 直接退出
    if(rect.width<2 || rect.height <2)
    {
        return ;
    }
    if(init)
    {
        //获取mask
        grabCut(src,mask,rect,bgModel,fgModel,1);
    }else
    {
        //只初始化 不获取
        grabCut(src,mask,rect,bgModel,fgModel,1,GC_INIT_WITH_RECT);
    }
}

效果

原图
在这里插入图片描述
扣取对象
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值