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 }

 

 

posted @ 2017-09-27 21:26  进击的小猴子  阅读(17753)  评论(1编辑  收藏  举报