- 操作系统:ubuntu22.04
- OpenCV版本:OpenCV4.9
- IDE:Visual Studio Code
- 编程语言:C++11
功能描述
GrabCut 算法是一种用于图像分割的技术,由 Carsten Rother、Vladimir Kolmogorov 和 Andrew Blake 在 2004 年 SIGGRAPH 会议的论文《GrabCut: 交互式前景提取使用迭代图割》中提出。
GrabCut 的主要概念是提供一个交互式的工具,让用户能够轻松地从图像中提取前景对象。用户只需标记图像的某些区域为确定的前景、确定的背景或不确定区域,算法会迭代地细化这些边界,使用图割方法求解全局优化问题,找到最佳的分割方案。
以下是 GrabCut 算法的基本工作流程:
- 初始化:用户通过简单的画笔工具标记图像的部分区域为确定的前景、确定的背景和不确定区域。
- 图构建:构建一个图,其中节点代表像素,边连接相邻像素。边的权重基于颜色相似性和空间邻近性。
- 迭代优化:
- 软分割:初始阶段,每个像素被赋予属于前景或背景的概率。
- 图割:应用图割技术来细化分割,通过迭代优化过程调整前景和背景的概率。
- 贝叶斯分类:使用贝叶斯分类器来更新像素的概率分布,考虑颜色模型和邻域信息。
- 迭代终止:当分割结果收敛或达到预设的迭代次数时,算法停止。
最终,GrabCut 能够生成高质量的图像分割,即使是在前景和背景之间有复杂边缘或相似颜色的情况下也能很好地工作。该算法广泛应用于图像编辑软件中,如 Adobe Photoshop,为用户提供了一个快速而有效的工具来分离图像的前景部分。
函数grabCut()
OpenCV中的函数grabCut()实现了GrabCut 算法。
函数原型
void cv::grabCut
(
InputArray img,
InputOutputArray mask,
Rect rect,
InputOutputArray bgdModel,
InputOutputArray fgdModel,
int iterCount,
int mode = GC_EVAL
)
参数
- 参数img 输入的8位、3通道彩色图像.
- 参数mask 这是一个输入/输出的8位单通道掩码图像, 在函数调用开始时,如果模式设置为GC_INIT_WITH_RECT,这个掩码会被初始化。掩码的元素可以是GrabCut算法定义的几种类型之一,分别代表不同的分割状态:确定的背景 (cv::GC_BGD)、确定的前景 (cv::GC_FGD)、可能的背景 (cv::GC_PR_BGD) 和可能的前景 (cv::GC_PR_FGD)。
- 参数rect 这是一个ROI(Region Of Interest,感兴趣区域)矩形,它包含了你想要分割的对象。在GC_INIT_WITH_RECT模式下,这个矩形以外的像素将被标记为“明显的背景”。如果使用此模式,你需要提供一个矩形来粗略地包围目标物体。
- 参数bgdModel 临时数组,用于背景模型,在处理同一张图像时,你不应该修改这些数组。
- 参数fgdModel 临时数组,用于前景模型,在处理同一张图像时,你不应该修改这些数组
iterCount : 这是你希望算法执行的迭代次数。更多的迭代通常意味着更准确的分割结果,但也会增加计算时间。注意,你可以在后续的调用中使用GC_INIT_WITH_MASK或GC_EVAL模式来进一步细化结果。 - 参数mode 这是操作模式,可以是GrabCut算法定义的几种模式之一,包括:
- GC_INIT_WITH_RECT: 使用矩形初始化掩码。
- GC_INIT_WITH_MASK: 使用掩码初始化。
- GC_EVAL: 评估模式,用于在没有改变掩码的情况下评估分割质量。
- GC_EVAL_FREEZE_MODEL: 类似于GC_EVAL,但在这种模式下,模型不会被更新。
代码示例
#include <iostream>
#include <opencv2/opencv.hpp>
int main( int argc, char** argv )
{
// 加载图像
cv::Mat img = cv::imread( "/media/dingxin/data/study/OpenCV/sources/images/fruit_small.jpg", cv::IMREAD_COLOR );
if ( !img.data )
{
std::cout << "Could not open or find the image" << std::endl;
return -1;
}
cv::Mat src = img.clone();
// 创建掩码,与图像同样大小,初始化为GC_INIT_WITH_RECT
cv::Mat mask( img.size(), CV_8UC1, cv::Scalar::all( 0 ) );
// cv::Rect rect( 267, 463, 200, 250 ); // 前景的初始矩形区域
cv::Rect rect( 105, 52, 270, 470 ); // 前景的初始矩形区域
mask( rect ) = cv::GC_PR_FGD; // 可能的前景区域
// 创建临时矩阵,用于存储分割过程中的中间结果
cv::Mat bgdModel, fgdModel;
// 执行GrabCut算法
cv::grabCut( img, mask, rect, bgdModel, fgdModel, 5, cv::GC_INIT_WITH_MASK );
// 将掩码转换为只包含前景和背景的二值图像
mask = ( mask == cv::GC_FGD ) + ( mask == cv::GC_PR_FGD );
// 使用掩码显示分割后的前景
img.setTo( cv::Scalar(), mask == 0 );
cv::namedWindow( "original image", cv::WINDOW_NORMAL );
cv::imshow( "original image", src );
// 显示结果
cv::namedWindow( "GrabCut Result", cv::WINDOW_NORMAL );
cv::imshow( "GrabCut Result", img );
cv::waitKey( 0 );
return 0;
}