Skip to content

Instantly share code, notes, and snippets.

@t-abe
Created June 30, 2011 04:08
Show Gist options
  • Save t-abe/1055619 to your computer and use it in GitHub Desktop.
Save t-abe/1055619 to your computer and use it in GitHub Desktop.
interactive grabcut application using OpenCV
#include <useopencv.h>
#include <string>
#include <vector>
#include <iostream>
class InteractiveGrabcut {
cv::Mat src;
cv::Mat fgd, bgd;
bool ldrag, rdrag;
std::string name;
public:
cv::Mat mask;
//cv::Rect rect;
cv::Point lstart, rstart;
cv::Scalar fg_color, bg_color;
InteractiveGrabcut()
: src(), mask(), fgd(), bgd(),
ldrag(false), rdrag(false), name("igc"),
fg_color(0, 0, 255), bg_color(255, 0, 0)
{};
InteractiveGrabcut(const cv::Mat& src_)
: src(src_), mask(), fgd(), bgd(),
ldrag(false), rdrag(false), name("igc"), fg_color(0, 0, 255), bg_color(255, 0, 0)
{
mask = cv::Mat::ones(src_.size(), CV_8U) * cv::GC_PR_BGD;
};
void prepareWindow(const std::string name){
this->name = name;
cv::namedWindow(name);
cv::setMouseCallback(name, events, this);
};
static void events( int e, int x, int y, int flags, void* s ){
InteractiveGrabcut* self = (InteractiveGrabcut*)s;
cv::Point pt(x, y);
switch(e) {
case CV_EVENT_LBUTTONDOWN:
self->ldrag = true;
self->lstart = pt;
break;
case CV_EVENT_LBUTTONUP:
self->ldrag = false;
break;
case CV_EVENT_RBUTTONDOWN:
self->rdrag = true;
self->rstart = pt;
break;
case CV_EVENT_RBUTTONUP:
self->rdrag = false;
break;
case CV_EVENT_MOUSEMOVE:
if(self->ldrag) {
cv::line(self->mask, self->lstart, pt, cv::Scalar(cv::GC_FGD), 1);
self->lstart = pt;
}
else if(self->rdrag) {
cv::line(self->mask, self->rstart, pt, cv::Scalar(cv::GC_BGD), 1);
self->rstart = pt;
}
break;
default:
break;
};
};
void show(){
cv::Mat scribbled_src = src.clone();
const float alpha = 0.7f;
for(int y=0; y < mask.rows; y++){
for(int x=0; x < mask.cols; x++){
if(mask.at<uchar>(y, x) == cv::GC_FGD) {
cv::circle(scribbled_src, cv::Point(x, y), 2, fg_color, -1);
} else if(mask.at<uchar>(y, x) == cv::GC_BGD) {
cv::circle(scribbled_src, cv::Point(x, y), 2, bg_color, -1);
} else if(mask.at<uchar>(y, x) == cv::GC_PR_BGD) {
cv::Vec3b& pix = scribbled_src.at<cv::Vec3b>(y, x);
pix[0] = (uchar)(pix[0] * alpha + bg_color[0] * (1-alpha));
pix[1] = (uchar)(pix[1] * alpha + bg_color[1] * (1-alpha));
pix[2] = (uchar)(pix[2] * alpha + bg_color[2] * (1-alpha));
} else if(mask.at<uchar>(y, x) == cv::GC_PR_FGD) {
cv::Vec3b& pix = scribbled_src.at<cv::Vec3b>(y, x);
pix[0] = (uchar)(pix[0] * alpha + fg_color[0] * (1-alpha));
pix[1] = (uchar)(pix[1] * alpha + fg_color[1] * (1-alpha));
pix[2] = (uchar)(pix[2] * alpha + fg_color[2] * (1-alpha));
}
}
}
cv::imshow(name, scribbled_src);
cv::imshow(name + "_FG", getFG());
}
void execute(){
std::cout << "computing...";
cv::grabCut(src, mask, cv::Rect(), bgd, fgd, 1, cv::GC_INIT_WITH_MASK);
std::cout << "end." << std::endl;
};
cv::Mat getBinMask(){
cv::Mat binmask(mask.size(), CV_8U);
binmask = mask & 1;
return binmask;
};
cv::Mat getFG(){
cv::Mat fg = cv::Mat::zeros(src.size(), src.type());
src.copyTo(fg, getBinMask());
return fg;
};
template <class Func>
void mainLoop(Func& func){
while(1){
int key = cv::waitKey(1);
if(key == '\x1b')
break;
func(this, key);
show();
}
};
};
int main(){
const std::string input_filename("bird.bmp");
cv::Mat input_image = cv::imread(input_filename);
InteractiveGrabcut igc(input_image);
igc.prepareWindow("bird");
igc.mainLoop([](InteractiveGrabcut* self, int key)->void {
if(key == ' '){
self->execute();
}
});
}
@calvintam
Copy link

Consider making line 114:
void mainLoop(Func& func)
into:
void mainLoop(Func&& func)
or even:
void mainLoop(const Func& func)

else compiling with g++ gives the following as an error:
no known conversion for argument 1 from ‘main(int, char**)::<lambda(InteractiveGrabcut*, int)>’ to ‘main(int, char**)::<lambda(InteractiveGrabcut*, int)>&’

(refer to http://stackoverflow.com/a/8100310 for a similar problem)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment