OpenCV实战——基于GrabCut算法的图像分割
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
OpenCV实战——基于GrabCut算法的图像分割
1. GrabCut 算法
在 OpenCV 策略设计模式一节中我们已经了解了颜色信息如何用于将图像分割成与场景特定元素相对应的区域。每类对象通常具有独特的颜色通常可以通过识别相似颜色的区域来提取。OpenCV
提供了一种流行的图像分割算法—— GrabCut
算法的实现。GrabCut
是一种复杂且计算量大的算法但它通常会得到非常准确的结果。该算法特别适合提取图像中的前景对象例如将目标对象从一张图片剪切并粘贴到另一张图片中。
2. 图像分割实战
cv::grabCut
函数的使用方法非常简单只需要输入一个图像并将其中的一些像素标记为属于背景或前景。基于这些标记算法可以分割图像的前景/背景。
(1) 为输入图像指定部分前景/背景标签的一种方法是定义一个矩形其中包含前景对象
// 定义边界框
cv::Rect rectangle(290, 180, 170, 215);
以上代码定义了图像中的以下区域
在输入图像中该矩形之外的所有像素都将被标记为背景。
(2) 调用 cv::grabCut
函数需要定义两个矩阵其中包含算法构建的模型
// 分割结果
cv::Mat result;
// GrabCut 分割
cv::grabCut(
image, // 输入图像
result, // 分割结果
rectangle, // 包含前景的矩形
bgModel, fgModel, // 模型
5, // 迭代次数
cv::GC_INIT_WITH_RECT // 使用矩形
);
我们使用 cv::GC_INIT_WITH_RECT
标志作为函数的最后一个参数用于指定边界矩形模式。
(3) 输入/输出分割图像中的像素值可能属于以下四个值之一
cv::GC_BGD
肯定属于背景的像素的值(例如上图中矩形外的像素)cv::GC_FGD
肯定属于前景的像素的值cv::GC_PR_BGD
可能属于背景的像素值cv::GC_PR_FGD
可能属于前景的像素的值(例如上图中矩形内像素的初始值)
(4) 我们通过提取像素值等于 cv::GC_PR_FGD
的像素来获得分割的二值图像
// 获取标记为可能前景的像素
cv::compare(result, cv::GC_PR_FGD, result, cv::CMP_EQ);
// 创建白色图像
cv::Mat foreground(image.size(), CV_8UC3, cv::Scalar(255, 255, 255));
image.copyTo(foreground, result);
(5) 要提取所有前景像素即值等于 cv::GC_PR_FGD
或 cv::GC_FGD
可以使用位运算获取
result = result & 1;
。常量 cv::GC_PR_FGD
和 cv::GC_FGD
被定义为值 1
和 3
而 cv::GC_BGD
和 cv::GC_PR_BGD
被定义为 0
和 2
。
执行以上程序可以得到下图
在以上代码中GrabCut
算法能够通过指定包含感兴趣对象的矩形来提取前景对象。或者也可以将值 cv::GC_BGD
和 cv::GC_FGD
分配给输入图像的某些特定像素这些像素通过使用蒙版图像作为 cv::grabCut
函数的第二个参数提供然后指定 GC_INIT_WITH_MASK
作为输入模式标志。例如可以通过要求用户以交互方式标记图像中的一些元素来获得这些输入标签。也可以组合这两种输入模式。
使用以上输入信息GrabCut
算法按以下步骤创建背景/前景分割。首先将前景标签 (cv::GC_PR_FGD
) 暂时分配给所有未标记的像素。算法根据当前的分类将像素分为相似颜色的簇(即背景为 K
个簇前景为 K
个簇)接下来通过在前景和背景像素之间引入边界来确定背景/前景分割这是通过优化过程完成的该过程尝试将像素与相似标签进行连接并在强度相对均匀的区域中放置边界施加惩罚这一优化问题可以使用 Graph Cuts
算法解决此算法方法通过将问题表示为应用切割的连通图来找到问题的最佳解以获取最佳分割获得的分割为像素分配新的标签。
然后重复聚类过程并再次找到新的最佳分割因此GrabCut
算法是一个逐步改进分割结果的迭代过程。根据场景的复杂程度可以在更多或更少的迭代次数中得到一个较好的解。
因此用户可以指定要应用的迭代次数作为函数的参数。由算法维护的两个内部模型作为函数的参数传递(并返回)因此如果希望通过执行额外的迭代来改进分割结果可以再次使用上次运行的模型调用该函数。
3. 完整代码
完整代码 (extractObject.cpp
) 如下所示
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int main() {
// 读取输入图像
cv::Mat image = cv::imread("1.png");
if (!image.data) return 0;
cv::namedWindow("Original Image");
cv::imshow("Original Image", image);
// 定义边界框
cv::Rect rectangle(290, 180, 170, 215);
cv::Mat bgModel, fgModel;
// 分割结果
cv::Mat result;
// GrabCut 分割
cv::grabCut(
image, // 输入图像
result, // 分割结果
rectangle, // 包含前景的矩形
bgModel, fgModel, // 模型
5, // 迭代次数
cv::GC_INIT_WITH_RECT // 使用矩形
);
// 获取标记为可能前景的像素
cv::compare(result, cv::GC_PR_FGD, result, cv::CMP_EQ);
// 或者
// result = result & 1;
// 创建白色图像
cv::Mat foreground(image.size(), CV_8UC3, cv::Scalar(255, 255, 255));
image.copyTo(foreground, result);
// 在原图中绘制矩形框
cv::rectangle(image, rectangle, cv::Scalar(255, 255, 255), 1);
cv::namedWindow("Image with rectangle");
cv::imshow("Image with rectangle", image);
// 显示结果
cv::namedWindow("Foreground object");
cv::imshow("Foreground object", foreground);
cv::waitKey();
return 0;
}
相关链接
OpenCV实战1——OpenCV与图像处理基础
OpenCV实战2——OpenCV核心数据结构
OpenCV实战3——图像感兴趣区域
OpenCV实战4——像素操作
OpenCV实战5——图像运算详解
OpenCV实战6——OpenCV策略设计模式