Table of contents
6.1.1 Defining foreground and background
6.1.4 Algorithm
implementation
Github code address: GitHub - Qinong/OpenCV
Chapter 6 Image Segmentation
Opencv provides a commonly used image segmentation algorithm Grabcut. The Grabcut algorithm is relatively complex and requires a lot of calculations, but it has high accuracy.
6.1 Grabcut implementation
6.1.1 Defining foreground and background
The usage of the cv::grabCut function is very simple. You only need to mark the input image as "belongs to the background" or "belongs to the front". Based on this local mark, the algorithm will calculate the foreground/background dividing line for the entire image.
You can specify local foreground/background labels of the input image by defining a rectangle.
// 定义一个带边框的矩形
// 矩形外部的像素会被标记为背景
cv::Rect rectangle(100,120,650,350);
6.1.2 cv::grabCut()
How the GrabCut algorithm works is: Reference article
Accepts an input image with either (1) a bounding box we want to segment or (2) a mask i.e. approximate segmentation involving the position of the object specified in the image
Repeat these steps:
Step 1: Estimate the color distribution of foreground and background via Gaussian Mixture Model (GMM)
Step 2: Construct a Markov random field on the pixel labels (i.e., foreground vs. background)
Step 3: Apply graph cut optimization for final segmentation
GrabCut is an improved version of Graph Cut, an iterative Graph Cut. The GrabCut algorithm in OpenCV is implemented based on the article ""GrabCut" - Interactive Foreground Extraction using Iterated Graph Cuts". This algorithm utilizes the texture (color) information and boundary (contrast) information in the image, and can obtain better segmentation results with only a small amount of user interaction. Unlike Graph cut, which specifies two vertices, grabcut only needs to specify a rough border that can frame the target to complete a good segmentation.
Although neural network segmentation has taken over the mainstream, in many cases such intensive training is not required, so GrabCut is also one of the options.
void grabCut( InputArray img,
InputOutputArray mask,
Rect rect,
InputOutputArray bgdModel,
InputOutputArray fgdModel,
int iterCount,
int mode = GC_EVAL );
- img: input image
- mask: Get the mask matrix, whose values are the following four
cv::GC_BGD == 0//Indicates background
cv::GC_FGD == 1//Indicates the foreground
cv::GC_PR_BGD == 2//Indicates it may be background
cv::GC_PR_FGD == 3//Indicates that it may be the foreground
- rect: the specified matrix containing the target object
- bdgModel: background model. If it is null, a bgdModel will be automatically created inside the function; bgdModel must be a single-channel floating point (CV_32FC1) image, and the number of rows can only be 1 and the number of columns can only be 13*5
- fgdModel: Foreground model, if it is null, a fgdModel will be automatically created inside the function; fgdModel must be a single-channel floating point (CV_32FC1) image, and the number of rows can only be 1 and the number of columns can only be 13x5; (bgdModel, fgdModel can be in Used under cv::GC_INIT_WITH_MASK, you can continue to iterate based on the information they saved based on previous iterations)
- iterCount: Specifies the number of iterations
- mode: There are three values available
cv::GC_INIT_WITH_RECT//Initialize grabCut with matrix
cv::GC_INIT_WITH_MASK//Initialize grabCut with mask
cv::GC_EVAL//Perform splitting
6.1.3 cv::compare()
cv:: compare () is mainly used to compare pixels between two images and output the comparison results. The specific usage is as follows:
bool cv::compare(cv::InputArray src1, // 输入数组1
cv::InputArray src2, // 输入数组2
cv::OutputArray dst, // 输出数组
int cmpop // 比较操作子,见注释
- cmpop: comparison operator
cv::CMP_EQ src==src1
cv::CMP_GT src>src1
cv::CMP_GE src>=src1
cv::CMP_LT src<src1
cv::CMP_LE src<=src1
cv::CMP_NE src!=src1
6.1.4 Algorithm implementation
int main()
{
cv::Mat image= cv::imread("Ferrar_F8.png");
if (!image.data)
return 0;
cv::namedWindow("Image");
cv::imshow("Image",image);
// 定义边框矩形
cv::Rect rectangle(100,120,650,350);
// 定义前景、背景和分割结果
cv::Mat bgModel,fgModel,result;
// GrabCut分割
cv::grabCut(image,
result,
rectangle,
bgModel,
fgModel,
5,
cv::GC_INIT_WITH_RECT); // use rectangle
// 标记可能属于前景的区域
cv::compare(result,cv::GC_PR_FGD,result,cv::CMP_EQ);
// or:
// 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(200,0,200),4);
cv::namedWindow("Rectangle");
cv::imshow("Rectangle",image);
cv::namedWindow("Foreground");
cv::imshow("Foreground",foreground);
cv::waitKey();
return 0;
}