【opencv】示例-grabcut.cpp 使用OpenCV库的GrabCut算法进行图像分割

41f08706f303066b2ff6f1dc27fbfc16.png

left mouse button - set rectangle

33bae4d5ad6bf1e6d3d10211a597a22c.png

bd1a7f237d6a0e78aeb8b84c86f86330.png

SHIFT+left mouse button - set GC_FGD pixels

CTRL+left mouse button - set GC_BGD pixels

这段代码是一个使用OpenCV库的GrabCut算法进行图像分割的C++程序。它允许用户通过交互式方式选择图像中的一个区域,并利用GrabCut算法尝试将其分割出来。代码中包含用户操作指南、颜色定义、事件处理以及GrabCut算法的迭代过程,实现了用户通过鼠标交互和按键控制来迭代分割效果。程序逻辑结构清晰,采用面向对象的方式封装了GrabCut分割相关的操作。

// 引入OpenCV库中处理图像编解码的相关头文件
#include "opencv2/imgcodecs.hpp"
// 引入OpenCV库中高级GUI的相关头文件
#include "opencv2/highgui.hpp"
// 引入OpenCV库中图像处理的相关头文件
#include "opencv2/imgproc.hpp"// 引入输入输出流的库文件
#include <iostream>// 使用标准命名空间std,免去std::前缀
using namespace std;
// 使用OpenCV命名空间cv,免去cv::前缀
using namespace cv;// 定义静态辅助函数,用于显示程序帮助信息
static void help(char** argv)
{cout << "\nThis program demonstrates GrabCut segmentation -- select an object in a region\n""and then grabcut will attempt to segment it out.\n""Call:\n"<< argv[0] << " <image_name>\n" // 显示如何调用程序"\nSelect a rectangular area around the object you want to segment\n"// 以下是热键操作说明"\nHot keys: \n""\tESC - quit the program\n" // 按ESC键退出程序"\tr - restore the original image\n" // 按'r'键还原到原始图像"\tn - next iteration\n" // 按'n'键执行下一迭代步骤"\n"// 以下是鼠标操作说明"\tleft mouse button - set rectangle\n" // 左键点击设置矩形区域"\n""\tCTRL+left mouse button - set GC_BGD pixels\n" // 按住CTRL键同时左键点击设置背景像素"\tSHIFT+left mouse button - set GC_FGD pixels\n" // 按住SHIFT键同时左键点击设置前景像素"\n""\tCTRL+right mouse button - set GC_PR_BGD pixels\n" // 按住CTRL键同时右键点击设置可能的背景像素"\tSHIFT+right mouse button - set GC_PR_FGD pixels\n" << endl; // 按住SHIFT键同时右键点击设置可能的前景像素
}// 定义一些标量常量表示不同的颜色
const Scalar RED = Scalar(0,0,255);
const Scalar PINK = Scalar(230,130,255);
const Scalar BLUE = Scalar(255,0,0);
const Scalar LIGHTBLUE = Scalar(255,255,160);
const Scalar GREEN = Scalar(0,255,0);// 定义背景键和前景键的标志,用于鼠标操作事件中
const int BGD_KEY = EVENT_FLAG_CTRLKEY;
const int FGD_KEY = EVENT_FLAG_SHIFTKEY;// 声明静态函数,用于从复合掩模图中提取二值掩模图
static void getBinMask( const Mat& comMask, Mat& binMask )
{// 确保comMask非空且类型为CV_8UC1,否则抛出错误if( comMask.empty() || comMask.type()!=CV_8UC1 )CV_Error( Error::StsBadArg, "comMask is empty or has incorrect type (not CV_8UC1)" );// 如果binMask为空或大小与comMask不一致,则重新创建与comMask大小一致的binMaskif( binMask.empty() || binMask.rows!=comMask.rows || binMask.cols!=comMask.cols )binMask.create( comMask.size(), CV_8UC1 );// 将comMask中的最低位复制到binMask中binMask = comMask & 1;
}// 声明GrabCut应用类,用于实现GrabCut算法的交互操作
// 定义用于GrabCut算法的应用类
class GCApplication
{
public:// 定义选择状态的枚举类型enum{ NOT_SET = 0, IN_PROCESS = 1, SET = 2 };// 定义圆的半径static const int radius = 2;// 定义圆的线条厚度,-1表示填充圆static const int thickness = -1;// 成员函数声明void reset();  // 重置函数void setImageAndWinName( const Mat& _image, const string& _winName );  // 设置图像和窗口名void showImage() const;  // 展示图像void mouseClick( int event, int x, int y, int flags, void* param );  // 鼠标点击事件处理函数int nextIter();  // 迭代处理函数int getIterCount() const { return iterCount; }  // 获取迭代次数的函数private:// 私有成员函数和变量声明void setRectInMask();  // 设置矩形到掩模中void setLblsInMask( int flags, Point p, bool isPr );  // 设置标签到掩模中的函数const string* winName;  // 窗口名称const Mat* image;  // 图像引用Mat mask;  // 掩模矩阵Mat bgdModel, fgdModel;  // 背景和前景模型uchar rectState, lblsState, prLblsState;  // 矩形和标签状态变量bool isInitialized;  // 是否已经初始化的标志Rect rect;  // 矩形区域vector<Point> fgdPxls, bgdPxls, prFgdPxls, prBgdPxls;  // 记录前景和背景像素位置的向量int iterCount;  // 迭代计数
};// 成员函数定义
// 重置GCApplication
void GCApplication::reset()
{if( !mask.empty() )mask.setTo(Scalar::all(GC_BGD));  // 如果掩模非空,设置掩模的所有值为GC_BGD// 清除前景、背景像素位置的记录bgdPxls.clear(); fgdPxls.clear();prBgdPxls.clear();  prFgdPxls.clear();// 重置相关的状态标志和迭代计数isInitialized = false;rectState = NOT_SET;lblsState = NOT_SET;prLblsState = NOT_SET;iterCount = 0;
}// 设置图像和窗口名
void GCApplication::setImageAndWinName( const Mat& _image, const string& _winName  )
{if( _image.empty() || _winName.empty() )return;  // 如果图像或窗口名为空,直接返回image = &_image;  // 设置图像引用winName = &_winName;  // 设置窗口名mask.create( image->size(), CV_8UC1);  // 创建与图像相同大小的掩模矩阵reset();  // 重置GCApplication
}// 展示图像
void GCApplication::showImage() const
{if( image->empty() || winName->empty() )return;  // 如果图像为空或窗口名为空,直接返回Mat res;Mat binMask;image->copyTo( res );  // 复制图像到resif( isInitialized ){getBinMask( mask, binMask);  // 如果已经初始化,获取二值掩模Mat black (binMask.rows, binMask.cols, CV_8UC3, cv::Scalar(0,0,0));black.setTo(Scalar::all(255), binMask);  // 设置黑色图像的掩模区域为白色addWeighted(black, 0.5, res, 0.5, 0.0, res);  // 将黑色图像与原图混合}vector<Point>::const_iterator it;// 绘制背景和前景像素位置for( it = bgdPxls.begin(); it != bgdPxls.end(); ++it )circle( res, *it, radius, BLUE, thickness );for( it = fgdPxls.begin(); it != fgdPxls.end(); ++it )circle( res, *it, radius, RED, thickness );for( it = prBgdPxls.begin(); it != prBgdPxls.end(); ++it )circle( res, *it, radius, LIGHTBLUE, thickness );for( it = prFgdPxls.begin(); it != prFgdPxls.end(); ++it )circle( res, *it, radius, PINK, thickness );// 绘制矩形框if( rectState == IN_PROCESS || rectState == SET )rectangle( res, Point( rect.x, rect.y ), Point(rect.x + rect.width, rect.y + rect.height ), GREEN, 2);imshow( *winName, res );  // 显示图像
}   // GCApplication类的成员函数:设置矩形框到掩模中
void GCApplication::setRectInMask()
{CV_Assert( !mask.empty() ); // 确认掩模不为空mask.setTo( GC_BGD ); // 将掩模的全部像素值设置为背景// 修正矩形坐标,确保矩形框不会超出图像范围rect.x = max(0, rect.x);rect.y = max(0, rect.y);rect.width = min(rect.width, image->cols-rect.x);rect.height = min(rect.height, image->rows-rect.y);// 将矩形框内的像素值设置为可能的前景(mask(rect)).setTo( Scalar(GC_PR_FGD) );
}// GCApplication类的成员函数:根据用户输入,在掩模上设置前景或背景标签
void GCApplication::setLblsInMask( int flags, Point p, bool isPr )
{vector<Point> *bpxls, *fpxls; // 指向背景和前景像素点的向量uchar bvalue, fvalue; // 背景和前景的像素值// 根据是否是概率图(isPr)设置不同的指针和像素值if( !isPr ){bpxls = &bgdPxls;fpxls = &fgdPxls;bvalue = GC_BGD;fvalue = GC_FGD;}else{bpxls = &prBgdPxls;fpxls = &prFgdPxls;bvalue = GC_PR_BGD;fvalue = GC_PR_FGD;}// flags中对应的按键标志位被设置,更新背景像素点向量,并在掩模上画圆if( flags & BGD_KEY ){bpxls->push_back(p);circle( mask, p, radius, bvalue, thickness );}// flags中对应的按键标志位被设置,更新前景像素点向量,并在掩模上画圆if( flags & FGD_KEY ){fpxls->push_back(p);circle( mask, p, radius, fvalue, thickness );}
}// GCApplication类的成员函数:处理鼠标点击事件
void GCApplication::mouseClick( int event, int x, int y, int flags, void* )
{switch( event ){// 左键按下事件:设置矩形或者GC_BGD/GC_FGD像素标签case EVENT_LBUTTONDOWN: {bool isb = (flags & BGD_KEY) != 0,isf = (flags & FGD_KEY) != 0;if( rectState == NOT_SET && !isb && !isf ){rectState = IN_PROCESS; // 开始绘制矩形框rect = Rect( x, y, 1, 1 ); // 初始化矩形框大小}if ( (isb || isf) && rectState == SET )lblsState = IN_PROCESS; // 开始设置标签状态}break;// 右键按下事件:设置GC_PR_BGD/GC_PR_FGD像素标签case EVENT_RBUTTONDOWN: {bool isb = (flags & BGD_KEY) != 0,isf = (flags & FGD_KEY) != 0;if ( (isb || isf) && rectState == SET )prLblsState = IN_PROCESS; // 开始设置概率标签状态}break;// 左键释放事件:完成矩形绘制或者设置像素标签case EVENT_LBUTTONUP:if( rectState == IN_PROCESS ){// 如果起点和终点一样,则不设置矩形框if(rect.x == x || rect.y == y){rectState = NOT_SET;}else{rect = Rect( Point(rect.x, rect.y), Point(x,y) );rectState = SET; // 设置矩形框状态为已设置setRectInMask(); // 在掩模上绘制矩形框CV_Assert( bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty() );}showImage(); // 显示图像}if( lblsState == IN_PROCESS ){setLblsInMask(flags, Point(x,y), false); // 设置像素标签lblsState = SET; // 设置标签状态为已设置nextIter(); // 进行一次迭代showImage(); // 显示图像}else{// 如果矩形框状态已设置,进行一次迭代后显示图像if(rectState == SET){nextIter();showImage();}}break;// 右键释放事件:完成概率标签的设置case EVENT_RBUTTONUP:if( prLblsState == IN_PROCESS ){setLblsInMask(flags, Point(x,y), true); // 设置概率标签prLblsState = SET; // 设置概率标签状态为已设置}// 如果矩形框状态已设置,进行一次迭代后显示图像if(rectState == SET){nextIter();showImage();}break;// 鼠标移动事件:更新矩形框大小或者继续设置像素标签case EVENT_MOUSEMOVE:if( rectState == IN_PROCESS ){rect = Rect( Point(rect.x, rect.y), Point(x,y) );CV_Assert( bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty() );showImage(); // 显示图像}else if( lblsState == IN_PROCESS ){setLblsInMask(flags, Point(x,y), false); // 设置像素标签showImage(); // 显示图像}else if( prLblsState == IN_PROCESS ){setLblsInMask(flags, Point(x,y), true); // 设置概率标签showImage(); // 显示图像}break;}
}// 主函数入口
int main( int argc, char** argv )
{// 解析命令行参数cv::CommandLineParser parser(argc, argv, "{@input| messi5.jpg |}");// 调用help函数显示帮助信息help(argv);// 获取图片文件名string filename = parser.get<string>("@input");// 如果文件名为空,输出错误信息并返回1if( filename.empty() ){cout << "\nDurn, empty filename" << endl;return 1;}// 读取图片Mat image = imread(samples::findFile(filename), IMREAD_COLOR);// 如果读取失败,输出错误信息并返回1if( image.empty() ){cout << "\n Durn, couldn't read image filename " << filename << endl;return 1;}// 创建窗口const string winName = "image";namedWindow( winName, WINDOW_AUTOSIZE );// 设置鼠标回调函数setMouseCallback( winName, on_mouse, 0 );// 设置gcapp对象的图片和窗口名称gcapp.setImageAndWinName( image, winName );// 显示图片gcapp.showImage();// 无限循环,等待用户按键for (;;){// 等待按键事件char c = (char)waitKey(0);switch (c){case '\x1b': // 按Esc键退出cout << "Exiting ..." << endl;goto exit_main;case 'r': // 按'r'键重置cout << endl;gcapp.reset(); // 调用重置函数gcapp.showImage(); // 显示图像break;case 'n': // 按'n'键进行下一次迭代int iterCount = gcapp.getIterCount(); // 获取当前迭代次数cout << "<" << iterCount << "... ";int newIterCount = gcapp.nextIter(); // 调用下一个迭代if (newIterCount > iterCount){gcapp.showImage(); // 显示新的图像cout << iterCount << ">" << endl;}elsecout << "rect must be determined>" << endl; // 提示需要先确定矩形框break;}}exit_main:// 销毁窗口并退出destroyWindow( winName );return 0;
}

4396662620bffcce3c85a06b8f6b8519.png

vector<Point>::const_iterator it;// 绘制背景和前景像素位置for (it = bgdPxls.begin(); it != bgdPxls.end(); ++it)circle(res, *it, radius, BLUE, thickness);for (it = fgdPxls.begin(); it != fgdPxls.end(); ++it)circle(res, *it, radius, RED, thickness);for (it = prBgdPxls.begin(); it != prBgdPxls.end(); ++it)circle(res, *it, radius, LIGHTBLUE, thickness);for (it = prFgdPxls.begin(); it != prFgdPxls.end(); ++it)circle(res, *it, radius, PINK, thickness);

f7e7b43a3df3c230bdc740bf725eeb2f.png

de922bc6f830506138ace52857ca324d.png

grabCut(*image, mask, rect, bgdModel, fgdModel, 1);

70dc097b43f56b2b7747b1df365b07ad.png

// 如果标签已经设置或概率标签已经设置,用mask初始化grabCutif (lblsState == SET || prLblsState == SET)grabCut(*image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_MASK);else// 否则,用rect初始化grabCutgrabCut(*image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_RECT);

a69d4dac7b64bbaebfd262eea01fcebe.png

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

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

相关文章

ruoyi-nbcio-plus基于vue3的flowable的自定义业务显示历史信息组件的升级修改

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://122.227.135.243:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a…

云LIS系统源码,ASP.NET区域LIS系统源码,实验室信息系统

云LIS系统源码&#xff0c;ASP.NET区域LIS系统源码&#xff0c;实验室信息系统 LIS技术架构&#xff1a;ASP.NET CORE 3.1 MVC SQLserver Redis等 开发语言&#xff1a;C# 6.0、JavaScript 前端框架&#xff1a;JQuery、EasyUI、Bootstrap 后端框架&#xff1a;MVC、S…

大数据架构之关系型数据仓库——解读大数据架构(二)

文章目录 前言什么是关系型数仓对数仓的错误认识与使用自上而下的方法关系型数仓的优点关系型数仓的缺点数据加载加载数据的频率如何确定变更数据 关系型数仓会消失吗总结 前言 本文对关系型数据仓库&#xff08;RDW&#xff09;进行了简要的介绍说明&#xff0c;包括什么是关…

python--递归算法篇

1、给定一个包含n1个整数的数组nums&#xff0c;其数字在1到n之间&#xff08;包含1和n&#xff09;&#xff0c; 可知至少存在一个重复的整数&#xff0c;假设只有一个重复的整数&#xff0c;请找出这个重复的数 def repeat(ls:list) -> list:#把个数超过1的数&#xff0c…

使用geneHapR进行基因单倍型分析(以vcf文件为例)

前记 在群体基因组学研究中&#xff0c;我们常常需要知道一些位点的变异情况&#xff0c;以便于根据对应的表型信息估算这些位点的效应&#xff0c;同时了解这些位点在不同亚群之间的变化情况。这个时候我们就需要进行单倍型分析(Haplotype Analysis)&#xff0c;单倍型分析是研…

Spring Boot | SpringBoot 对 SpringMVC的 “整合支持“

目录: SpringMVC 的 “整合支持” ( 引入"Web依赖启动器"&#xff0c;几乎可以在无任何额外的配置的情况下进行"Web开发")1.SpringMVC "自动配置" 介绍 ( 引入Web依赖启动器"后&#xff0c;SpringBoot会自动进行一些“自动配置”&#xff0…

Linux下mysql的彻底卸载

Linux下mysql的彻底卸载 1、查看mysql的安装情况2、删除上图安装的软件3、都删除成功之后&#xff0c;查找相关的mysql的文件4、删除全部文件5、再次执行命令 1、查看mysql的安装情况 rpm -qa | grep -i mysql2、删除上图安装的软件 rpm -ev mysql-community-libs-5.7.27-1.e…

蓝桥杯 前一晚总结 模板 新手版

《准备实足&#xff0c;冲冲冲 省一》https://www.yuque.com/lenyan-svokd/hi7hp2/hfka297matrtsxy2?singleDoc# 《准备实足&#xff0c;冲冲冲 省一》 #include<bits/stdc.h> // 包含标准库头文件using namespace std; using ll long long; // 定义 long long 数据类…

【opencv】示例-opencv_version.cpp 输出OpenCV的版本和构建配置的示例

#include <opencv2/core/utility.hpp> // 引入OpenCV核心工具库 #include <iostream> // 引入标准输入输出流库// 定义一个包含命令行参数的字符串 static const std::string keys "{ b build | | print complete build info }" // 定义参数b&#xff…

【数据结构】06图

图 1. 定义1.1 无向图和有向图1.2 度、入度和出度1.3 图的若干定义1.4 几种特殊的图 2. 图的存储2.1 邻接矩阵-顺序存储&#xff08;数组&#xff09;2.2 邻接表-顺序存储链式存储&#xff08;数组链表&#xff09;2.3 十字链表-适用于有向图2.4 邻接多重表-适用于无向图 3. 图…

设计模式代码实战-建造者模式

1、问题描述 小明家新开了一家自行车工厂&#xff0c;用于使用自行车配件&#xff08;车架 frame 和车轮 tires &#xff09;进行组装定制不同的自行车&#xff0c;包括山地车和公路车。 山地车使用的是Aluminum Frame&#xff08;铝制车架&#xff09;和 Knobby Tires&#x…

【随笔】Git 高级篇 -- 管理多分支 git rebase(二十二)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

PyTorch环境配置问题

为什么深度学习都是用英伟达的显卡&#xff1f; 首先我们需要了解什么是CUDA&#xff1f; CUDA&#xff08;Compute Unified Device Architecture&#xff09;&#xff0c;是显卡厂商 NVIDIA 推出的运算平台。 CUDA就类似于编程语言&#xff0c;开发者和显卡通过CUDA进行交流…

Android网络抓包--Charles

一、Android抓包方式 对Https降级进行抓包&#xff0c;降级成Http使用抓包工具对Https进行抓包 二、常用的抓包工具 wireshark&#xff1a;侧重于TCP、UDP传输层&#xff0c;HTTP/HTTPS也能抓包&#xff0c;但不能解密HTTPS报文。比较复杂fiddler&#xff1a;支持HTTP/HTTPS…

【SpringBoot】mybatis-plus实现增删改查

mapper继承BaseMapper service 继承ServiceImpl 使用方法新增 save,updateById新增和修改方法返回boolean值,或者使用saveOrUpdate方法有id执行修改操作,没有id 执行新增操作 案例 Service public class UserService extends ServiceImpl<UserMapper,User> {// Au…

用于大规模数据集(大于1TB)的并行运算的MapReduce是怎么实现的?

MapReduce 是一种编程模型&#xff0c;用于处理和生成大数据集。MapReduce 分为两个阶段&#xff1a;Map 阶段和 Reduce 阶段。 Map 阶段&#xff1a;在这个阶段&#xff0c;输入数据被拆分成不同的数据块&#xff0c;这些数据块被分发到各个 Map 任务上。每个 Map 任务对输入的…

Golang | Leetcode Golang题解之第24题两两交换链表中的节点

题目&#xff1a; 题解&#xff1a; func swapPairs(head *ListNode) *ListNode {dummyHead : &ListNode{0, head}temp : dummyHeadfor temp.Next ! nil && temp.Next.Next ! nil {node1 : temp.Nextnode2 : temp.Next.Nexttemp.Next node2node1.Next node2.Nex…

数据结构(算法)

总结&#xff0c;建议看EXCEL的《算法》页签&#xff0c;不然感觉有点乱 备注原理/步骤时间复杂度空间复杂度串的应用模式匹配简单/暴力O(mn) KMP  O(mn) 树的应用树哈夫曼树1、带权路径长度WPL 2、外部排序-最佳归并树1、哈夫曼树的度&#xff0c;只有0和m&#xff08;m叉…

Linux上下载部署zentao v15.5及具体的使用

1.先查询一下Linux的操作系统的位数&#xff0c;确保下载的文件位数与os的一致 [rootlocalhost xiaoming]# uname -m x86_64 [rootlocalhost xiaoming]# getconf LONG_BIT 64 2.下载zentao的Linux压缩包 wget https://www.zentao.net/dl/zentao/15.5/ZenTaoPMS.15.5.zbox…

【opencv】示例-inpaint.cpp 图像修复是通过填充损坏图像部分从而修复这些损坏的过程...

原始图像 这段代码展示了一个使用OpenCV库进行图像修复的例子。它首先包含了处理图像编码、解码、显示、处理和照片处理所必要的OpenCV模块的头文件。然后利用cv和std命名空间下的类和方法。通过定义一个鼠标回调函数onMouse来处理图像上的绘图操作&#xff0c;并通过主函数mai…