opencv学习之路(31)、GrabCut & FloodFill图像分割
一、GrabCut
1、利用Rect做分割
#include "opencv2/opencv.hpp" using namespace cv; void main() { Mat src = imread("E://bird.jpg"); Rect rect(84, 84, 406, 318);//左上坐标(X,Y)和长宽 Mat result, bg, fg; grabCut(src, result, rect, bg, fg, 1, GC_INIT_WITH_RECT); imshow("grab", result); /*threshold(result, result, 2, 255, CV_THRESH_BINARY); imshow("threshold", result);*/ compare(result, GC_PR_FGD, result, CMP_EQ);//result和GC_PR_FGD对应像素相等时,目标图像该像素值置为255 imshow("result",result); Mat foreground(src.size(), CV_8UC3, Scalar(255, 255, 255)); src.copyTo(foreground, result);//copyTo有两种形式,此形式表示result为mask imshow("foreground", foreground); waitKey(0); }
grab并非是全黑图像,对其使用二值化后能看到低像素值的情况
2、利用mask做分割
#include "opencv2/opencv.hpp" using namespace cv; void main() { Mat src = imread("E://bird.jpg"); //Rect rect(84, 84, 406, 318); Rect rect; Mat bgModel, fgModel; Mat result(src.size(), CV_8U, Scalar(0)); Mat ROI(result(Rect(84, 84, 406, 318))); ROI.setTo(GC_PR_FGD);//ROI设置为可能是前景 grabCut(src, result, rect, bgModel, fgModel, 1, GC_INIT_WITH_MASK); //threshold(result, result, 2, 255, CV_THRESH_BINARY); imshow("grab", result); compare(result, GC_PR_FGD, result, CMP_EQ); //result = result&1; imshow("result", result); Mat foreground(src.size(), CV_8UC3, Scalar(255, 255, 255)); src.copyTo(foreground, result); imshow("foreground", foreground); waitKey(0); }
二、漫水填充算法——floodFill
#include "opencv2/opencv.hpp" using namespace cv; void main() { Mat src = imread("E://bird.jpg"); imshow("src", src); Rect rect; //原图,种子点,新颜色,重绘区域的最小边界矩形,负差,正差 floodFill(src, Point(20,20), Scalar(255, 0, 0), &rect, Scalar(10, 10, 10), Scalar(10, 10, 10)); imshow("result", src); waitKey(0); }
三、综合应用(代码来自浅墨大神)
1 #include "opencv2/opencv.hpp" 2 #include <iostream> 3 using namespace std; 4 using namespace cv; 5 6 Mat g_srcImage, g_dstImage, g_grayImage, g_maskImage;//定义原始图、目标图、灰度图、掩模图 7 int g_nFillMode = 1;//漫水填充的模式 8 int g_nLowDifference = 20, g_nUpDifference = 20;//负差最大值、正差最大值 9 int g_nConnectivity = 4;//表示floodFill函数标识符低八位的连通值 10 int g_bIsColor = true;//是否为彩色图的标识符布尔值 11 bool g_bUseMask = false;//是否显示掩膜窗口的布尔值 12 int g_nNewMaskVal = 255;//新的重新绘制的像素值 13 14 static void ShowHelpText() 15 { 16 //输出一些帮助信息 17 printf("\n\n\n\t欢迎来到漫水填充示例程序~\n\n"); 18 printf("\n\n\t按键操作说明: \n\n" 19 "\t\t鼠标点击图中区域- 进行漫水填充操作\n" 20 "\t\t键盘按键【ESC】- 退出程序\n" 21 "\t\t键盘按键【1】- 切换彩色图/灰度图模式\n" 22 "\t\t键盘按键【2】- 显示/隐藏掩膜窗口\n" 23 "\t\t键盘按键【3】- 恢复原始图像\n" 24 "\t\t键盘按键【4】- 使用空范围的漫水填充\n" 25 "\t\t键盘按键【5】- 使用渐变、固定范围的漫水填充\n" 26 "\t\t键盘按键【6】- 使用渐变、浮动范围的漫水填充\n" 27 "\t\t键盘按键【7】- 操作标志符的低八位使用4位的连接模式\n" 28 "\t\t键盘按键【8】- 操作标志符的低八位使用8位的连接模式\n" 29 "\n\n\t\t\t\t\t\t\t\t by浅墨\n\n\n" 30 ); 31 } 32 33 //鼠标消息onMouse回调函数 34 static void onMouse(int event, int x, int y, int, void*) 35 { 36 // 若鼠标左键没有按下,便返回 37 if (event != CV_EVENT_LBUTTONDOWN) 38 return; 39 //-------------------【<1>调用floodFill函数之前的参数准备部分】--------------- 40 Point seed = Point(x, y); 41 int LowDifference = (g_nFillMode == 0) ? 0 : g_nLowDifference;//空范围的漫水填充,此值设为0,否则设为全局的g_nLowDifference 42 int UpDifference = g_nFillMode == 0 ? 0 : g_nUpDifference;//空范围的漫水填充,此值设为0,否则设为全局的g_nUpDifference 43 int flags = g_nConnectivity + (g_nNewMaskVal << 8) + 44 (g_nFillMode == 1 ? CV_FLOODFILL_FIXED_RANGE : 0);//标识符的0~7位为g_nConnectivity,8~15位为g_nNewMaskVal左移8位的值,16~23位为CV_FLOODFILL_FIXED_RANGE或者0。 45 46 //随机生成bgr值 47 int b = (unsigned)theRNG() & 255;//随机返回一个0~255之间的值 48 int g = (unsigned)theRNG() & 255;//随机返回一个0~255之间的值 49 int r = (unsigned)theRNG() & 255;//随机返回一个0~255之间的值 50 Rect ccomp;//定义重绘区域的最小边界矩形区域 51 52 Scalar newVal = g_bIsColor ? Scalar(b, g, r) : Scalar(r*0.299 + g*0.587 + b*0.114);//在重绘区域像素的新值,若是彩色图模式,取Scalar(b, g, r);若是灰度图模式,取Scalar(r*0.299 + g*0.587 + b*0.114) 53 54 Mat dst = g_bIsColor ? g_dstImage : g_grayImage;//目标图的赋值 55 int area; 56 57 //--------------------【<2>正式调用floodFill函数】----------------------------- 58 if (g_bUseMask) 59 { 60 threshold(g_maskImage, g_maskImage, 1, 128, CV_THRESH_BINARY); 61 area = floodFill(dst, g_maskImage, seed, newVal, &ccomp, Scalar(LowDifference, LowDifference, LowDifference), 62 Scalar(UpDifference, UpDifference, UpDifference), flags); 63 imshow("mask", g_maskImage); 64 } 65 else 66 { 67 area = floodFill(dst, seed, newVal, &ccomp, Scalar(LowDifference, LowDifference, LowDifference), 68 Scalar(UpDifference, UpDifference, UpDifference), flags); 69 } 70 71 imshow("效果图", dst); 72 cout << area << " 个像素被重绘\n"; 73 } 74 75 void main() 76 { 77 system("color 2F");//改变console字体颜色 78 g_srcImage = imread("E://lena.jpg", 1);//载入原图 79 if (!g_srcImage.data) { printf("Oh,no,读取图片image0错误~! \n"); return; } 80 ShowHelpText(); 81 82 g_srcImage.copyTo(g_dstImage);//拷贝源图到目标图 83 //cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);//转换三通道的image0到灰度图 84 g_maskImage.create(g_srcImage.rows + 2, g_srcImage.cols + 2, CV_8UC1);//利用image0的尺寸来初始化掩膜mask 85 86 namedWindow("效果图", CV_WINDOW_NORMAL); 87 //创建Trackbar 88 createTrackbar("负差最大值", "效果图", &g_nLowDifference, 255, 0); 89 createTrackbar("正差最大值", "效果图", &g_nUpDifference, 255, 0); 90 //鼠标回调函数 91 setMouseCallback("效果图", onMouse, 0); 92 93 //循环轮询按键 94 while (1) 95 { 96 //先显示效果图 97 imshow("效果图", g_bIsColor ? g_dstImage : g_grayImage); 98 //获取键盘按键 99 int c = waitKey(0); 100 //判断ESC是否按下,若按下便退出 101 if ((c & 255) == 27) 102 { 103 cout << "程序退出...........\n"; 104 break; 105 } 106 107 //根据按键的不同,进行各种操作 108 switch ((char)c) 109 { 110 case '1': //如果键盘“1”被按下,效果图在在灰度图,彩色图之间互换 111 if (g_bIsColor)//若原来为彩色,转为灰度图,并且将掩膜mask所有元素设置为0 112 { 113 cout << "键盘“1”被按下,切换彩色/灰度模式,当前操作为将【彩色模式】切换为【灰度模式】\n"; 114 cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY); 115 g_maskImage = Scalar::all(0); //将mask所有元素设置为0 116 g_bIsColor = false; //将标识符置为false,表示当前图像不为彩色,而是灰度 117 } 118 else//若原来为灰度图,便将原来的彩图image0再次拷贝给image,并且将掩膜mask所有元素设置为0 119 { 120 cout << "键盘“1”被按下,切换彩色/灰度模式,当前操作为将【彩色模式】切换为【灰度模式】\n"; 121 g_srcImage.copyTo(g_dstImage); 122 g_maskImage = Scalar::all(0); 123 g_bIsColor = true;//将标识符置为true,表示当前图像模式为彩色 124 } 125 break; 126 case '2'://显示/隐藏掩膜窗口 127 if (g_bUseMask) 128 { 129 destroyWindow("mask"); 130 g_bUseMask = false; 131 } 132 else 133 { 134 namedWindow("mask", 0); 135 g_maskImage = Scalar::all(0); 136 imshow("mask", g_maskImage); 137 g_bUseMask = true; 138 } 139 break; 140 case '3'://恢复原始图像 141 cout << "按键“3”被按下,恢复原始图像\n"; 142 g_srcImage.copyTo(g_dstImage); 143 cvtColor(g_dstImage, g_grayImage, COLOR_BGR2GRAY); 144 g_maskImage = Scalar::all(0); 145 break; 146 case '4'://使用空范围的漫水填充 147 cout << "按键“4”被按下,使用空范围的漫水填充\n"; 148 g_nFillMode = 0; 149 break; 150 case '5'://使用渐变、固定范围的漫水填充 151 cout << "按键“5”被按下,使用渐变、固定范围的漫水填充\n"; 152 g_nFillMode = 1; 153 break; 154 case '6'://使用渐变、浮动范围的漫水填充 155 cout << "按键“6”被按下,使用渐变、浮动范围的漫水填充\n"; 156 g_nFillMode = 2; 157 break; 158 case '7'://操作标志符的低八位使用4位的连接模式 159 cout << "按键“7”被按下,操作标志符的低八位使用4位的连接模式\n"; 160 g_nConnectivity = 4; 161 break; 162 case '8'://操作标志符的低八位使用8位的连接模式 163 cout << "按键“8”被按下,操作标志符的低八位使用8位的连接模式\n"; 164 g_nConnectivity = 8; 165 break; 166 } 167 } 168 }