在OpenCV提供更多函数中,grabcut能够实现抠图,inpaint能够实现修补。那么把两者结合起来,就能够实现简单的“人像去除”功能,也就是框选一个人后,使用周围的景象对人像进行修补。虽然效果比不上PS,但是也差强人意。
 
 
 
 
#include "stdafx.h"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/photo.hpp" #include <iostream> using namespace std;
using namespace cv; /************************************************************************/
/* 9. Write an image-processing function to interactively remove people from an
image. Use cv::grabCut() to segment the person, and then use cv::inpaint()
to fill in the resulting hole (recall that we learned about cv::inpaint() in the
previous chapter). */
/************************************************************************/ static void help()
{
cout << "\nThis program demonstrates GrabCut segmentation -- select an object in a region\n"
"and then grabcut will attempt to segment it out.\n"
"Call:\n"
"./grabcut <image_name>\n"
"\nSelect a rectangular area around the object you want to segment\n" <<
"\nHot keys: \n"
"\tESC - quit the program\n"
"\tr - restore the original image\n"
"\tn - next iteration\n"
"\n"
"\tleft mouse button - set rectangle\n"
"\n"
"\tCTRL+left mouse button - set GC_BGD pixels\n"
"\tSHIFT+left mouse button - set GC_FGD pixels\n"
"\n"
"\tCTRL+right mouse button - set GC_PR_BGD pixels\n"
"\tSHIFT+right mouse button - set GC_PR_FGD pixels\n" << endl;
} const Scalar RED = Scalar(0,0,255);
const Scalar PINK = Scalar(230,130,255);
const Scalar BLUE = Scalar(255,0,0);
const Scalar LIGHTBLUE = Scalar(255,255,160);
const Scalar GREEN = Scalar(0,255,0); const int BGD_KEY = EVENT_FLAG_CTRLKEY;
const int FGD_KEY = EVENT_FLAG_SHIFTKEY; static void getBinMask( const Mat& comMask, Mat& binMask )
{
if( comMask.empty() || comMask.type()!=CV_8UC1 )
CV_Error( Error::StsBadArg, "comMask is empty or has incorrect type (not CV_8UC1)" );
if( binMask.empty() || binMask.rows!=comMask.rows || binMask.cols!=comMask.cols )
binMask.create( comMask.size(), CV_8UC1 );
binMask = comMask & 1;
} class GCApplication
{
public:
enum{ NOT_SET = 0, IN_PROCESS = 1, SET = 2 };
static const int radius = 2;
static const int thickness = -1; void reset();
void setImageAndWinName( const Mat& _image, const string& _winName );
void showImage() const;
void mouseClick( int event, int x, int y, int flags, void* param );
int nextIter();
int getIterCount() const { return iterCount; }
private:
void setRectInMask();
void setLblsInMask( int flags, Point p, bool isPr ); const string* winName;
const Mat* image;
Mat mask;
Mat bgdModel, fgdModel; uchar rectState, lblsState, prLblsState;
bool isInitialized; Rect rect;
vector<Point> fgdPxls, bgdPxls, prFgdPxls, prBgdPxls;
int iterCount;
}; void GCApplication::reset()
{
if( !mask.empty() )
mask.setTo(Scalar::all(GC_BGD));
bgdPxls.clear(); fgdPxls.clear();
prBgdPxls.clear(); prFgdPxls.clear(); isInitialized = false;
rectState = NOT_SET;
lblsState = NOT_SET;
prLblsState = NOT_SET;
iterCount = 0;
} void GCApplication::setImageAndWinName( const Mat& _image, const string& _winName )
{
if( _image.empty() || _winName.empty() )
return;
image = &_image;
winName = &_winName;
mask.create( image->size(), CV_8UC1);
reset();
} void GCApplication::showImage() const
{
if( image->empty() || winName->empty() )
return; Mat res;
Mat binMask;
if( !isInitialized )
image->copyTo( res );
else
{
getBinMask( mask, binMask );
image->copyTo( res, binMask );
// mask and source code
Mat src = *image;
Mat mask = binMask;
//inpaint
Mat inpainted;
inpaint(src, mask, inpainted, 3, CV_INPAINT_TELEA);
imshow("inpainted result",inpainted);
} vector<Point>::const_iterator it;
for( it = bgdPxls.begin(); it != bgdPxls.end(); ++it )
circle( res, *it, radius, BLUE, thickness );
for( it = fgdPxls.begin(); it != fgdPxls.end(); ++it )
circle( res, *it, radius, RED, thickness );
for( it = prBgdPxls.begin(); it != prBgdPxls.end(); ++it )
circle( res, *it, radius, LIGHTBLUE, thickness );
for( it = prFgdPxls.begin(); it != prFgdPxls.end(); ++it )
circle( res, *it, radius, PINK, thickness ); if( rectState == IN_PROCESS || rectState == SET )
rectangle( res, Point( rect.x, rect.y ), Point(rect.x + rect.width, rect.y + rect.height ), GREEN, 2); imshow( *winName, res );
} void GCApplication::setRectInMask()
{
CV_Assert( !mask.empty() );
mask.setTo( GC_BGD );
rect.x = max(0, rect.x);
rect.y = max(0, rect.y);
rect.width = min(rect.width, image->cols-rect.x);
rect.height = min(rect.height, image->rows-rect.y);
(mask(rect)).setTo( Scalar(GC_PR_FGD) );
} void GCApplication::setLblsInMask( int flags, Point p, bool isPr )
{
vector<Point> *bpxls, *fpxls;
uchar bvalue, fvalue;
if( !isPr )
{
bpxls = &bgdPxls;
fpxls = &fgdPxls;
bvalue = GC_BGD;
fvalue = GC_FGD;
}
else
{
bpxls = &prBgdPxls;
fpxls = &prFgdPxls;
bvalue = GC_PR_BGD;
fvalue = GC_PR_FGD;
}
if( flags & BGD_KEY )
{
bpxls->push_back(p);
circle( mask, p, radius, bvalue, thickness );
}
if( flags & FGD_KEY )
{
fpxls->push_back(p);
circle( mask, p, radius, fvalue, thickness );
}
} void GCApplication::mouseClick( int event, int x, int y, int flags, void* )
{
// TODO add bad args check
switch( event )
{
case EVENT_LBUTTONDOWN: // set rect or GC_BGD(GC_FGD) labels
{
bool isb = (flags & BGD_KEY) != 0,
isf = (flags & FGD_KEY) != 0;
if( rectState == NOT_SET && !isb && !isf )
{
rectState = IN_PROCESS;
rect = Rect( x, y, 1, 1 );
}
if ( (isb || isf) && rectState == SET )
lblsState = IN_PROCESS;
}
break;
case EVENT_RBUTTONDOWN: // set GC_PR_BGD(GC_PR_FGD) labels
{
bool isb = (flags & BGD_KEY) != 0,
isf = (flags & FGD_KEY) != 0;
if ( (isb || isf) && rectState == SET )
prLblsState = IN_PROCESS;
}
break;
case EVENT_LBUTTONUP:
if( rectState == IN_PROCESS )
{
rect = Rect( Point(rect.x, rect.y), Point(x,y) );
rectState = SET;
setRectInMask();
CV_Assert( bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty() );
showImage();
}
if( lblsState == IN_PROCESS )
{
setLblsInMask(flags, Point(x,y), false);
lblsState = SET;
showImage();
}
break;
case EVENT_RBUTTONUP:
if( prLblsState == IN_PROCESS )
{
setLblsInMask(flags, Point(x,y), true);
prLblsState = SET;
showImage();
}
break;
case EVENT_MOUSEMOVE:
if( rectState == IN_PROCESS )
{
rect = Rect( Point(rect.x, rect.y), Point(x,y) );
CV_Assert( bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty() );
showImage();
}
else if( lblsState == IN_PROCESS )
{
setLblsInMask(flags, Point(x,y), false);
showImage();
}
else if( prLblsState == IN_PROCESS )
{
setLblsInMask(flags, Point(x,y), true);
showImage();
}
break;
}
} int GCApplication::nextIter()
{
if( isInitialized )
grabCut( *image, mask, rect, bgdModel, fgdModel, 1 );
else
{
if( rectState != SET )
return iterCount; if( lblsState == SET || prLblsState == SET )
grabCut( *image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_MASK );
else
grabCut( *image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_RECT ); isInitialized = true;
}
iterCount++; bgdPxls.clear(); fgdPxls.clear();
prBgdPxls.clear(); prFgdPxls.clear(); return iterCount;
} GCApplication gcapp; static void on_mouse( int event, int x, int y, int flags, void* param )
{
gcapp.mouseClick( event, x, y, flags, param );
} int main( int argc, char** argv )
{
cv::CommandLineParser parser(argc, argv, "{help h||}{@input||}");
if (parser.has("help"))
{
help();
return 0;
}
string filename = parser.get<string>("@input");
if( filename.empty() )
{
cout << "\nDurn, empty filename" << endl;
return 1;
}
Mat image = imread( filename, 1 );
if( image.empty() )
{
cout << "\n Durn, couldn't read image filename " << filename << endl;
return 1;
} help(); const string winName = "image";
namedWindow( winName, WINDOW_AUTOSIZE );
setMouseCallback( winName, on_mouse, 0 ); gcapp.setImageAndWinName( image, winName );
gcapp.showImage(); for(;;)
{
char c = (char)waitKey(0);
switch( c )
{
case '\x1b':
cout << "Exiting ..." << endl;
goto exit_main;
case 'r':
cout << endl;
gcapp.reset();
gcapp.showImage();
break;
case 'n':
int iterCount = gcapp.getIterCount();
cout << "<" << iterCount << "... ";
int newIterCount = gcapp.nextIter();
if( newIterCount > iterCount )
{
gcapp.showImage();
cout << iterCount << ">" << endl;
}
else
cout << "rect must be determined>" << endl;
break;
}
} exit_main:
destroyWindow( winName );
return 0;
}

核心的部分在这里

getBinMask( mask, binMask );
image->copyTo( res, binMask );
// mask and source code
Mat src = *image;
Mat mask = binMask;
//inpaint
Mat inpainted;
inpaint(src, mask, inpainted, 3, CV_INPAINT_TELEA);
imshow("inpainted result",inpainted);

结合grabcut和inpaint,实现人像去除的更多相关文章

  1. 使用inpaint例子,去除水印

    http://www.opencv.org.cn/forum.php?mod=viewthread&tid=33151 #include "stdafx.h" #inclu ...

  2. opencv —— inpaint 图像修补、去除指定区域物体

    实现图像修补.物体去除:inpaint 函数 void inpaint(InputArray src, InputArray inpaintMask, OutputArray dst, double ...

  3. Atitit.去除水印的方案

    Atitit.去除水印的方案 1.1. 查找水印的位置 Kegwa imgd posit zo ok le .. Auto find d zo troub ...manu easy 1.2. 还原去除 ...

  4. 基于python的图片修复程序-可用于水印去除

    图片修复程序-可用于水印去除 在现实的生活中,我们可能会遇到一些美好的或是珍贵的图片被噪声干扰,比如旧照片的折痕,比如镜头上的灰尘或污渍,更或者是某些我们想为我所用但有讨厌水印,那么有没有一种办法可以 ...

  5. OpenCV 学习笔记 04 深度估计与分割——GrabCut算法与分水岭算法

    1 使用普通摄像头进行深度估计 1.1 深度估计原理 这里会用到几何学中的极几何(Epipolar Geometry),它属于立体视觉(stereo vision)几何学,立体视觉是计算机视觉的一个分 ...

  6. python利用opencv去除水印方法

    OpenCV(Open Source Computer Vision Library)是一个跨平台计算机视觉库,实现了图像处理和计算机视觉方面的很多通用算法 在python中可以利用opencv来去除 ...

  7. 人像美妆---妆容迁移算法研究(Makeup transfer)

    原文:人像美妆---妆容迁移算法研究(Makeup transfer) 对于人像美妆算法,现在的美妆相机.玩美彩妆之类的app已经做的比较成熟了,但是具体算法,基本网络上是杳无可查,今天本人介绍一种自 ...

  8. 图片去水印工具:Inpaint 7.2中文专业破解版下载及使用方法

    下载地址: 点我 Inpaint 是一款可以从图片上去除不必要的物体,让您轻松摆脱照片上的水印.划痕.污渍.标志等瑕疵的实用型软件:简单说来,Inpaint 就是一款强大实用的图片去水印软件,您的图片 ...

  9. Python黑科技神奇去除马赛克

    图片修复程序-可用于水印去除 在现实的生活中,我们可能会遇到一些美好的或是珍贵的图片被噪声干扰,比如旧照片的折痕,比如镜头上的灰尘或污渍,更或者是某些我们想为我所用但有讨厌水印,那么有没有一种办法可以 ...

随机推荐

  1. 【转】Go Channels

    转自: http://kdf5000.com/2017/07/16/Go-Channels/ Golang使用Groutine和channels实现了CSP(Communicating Sequent ...

  2. JQuery自定义用户控件方法汇总

    首先必用 JQuery.fn.extend方法 使用方法: jQuery.fn.extend({}); 其次 相应控件配置参数设置: jQuery.fn.extend({ uploadPreview: ...

  3. mysql使用笔记(一)

    一.安装 使用免安装的版本进行安装: 1. 解压到安装目录 2. 拷贝目录下的 my-default.ini 文件为 my.ini 文件 3. 修改my.ini 文件内容为 [client] port ...

  4. 我觉得epoll和select最大的区别

    最近在用epoll,网速资料很多,大家都说epoll和select的区别比较大,而且select要不停遍历所有的fd,效率要低,而且fd有限制. 但是我认为二者最大的区别在于 先看代码 while ( ...

  5. MQTT协议笔记之连接和心跳

    前言 本篇会把连接(CONNECT).心跳(PINGREQ/PINGRESP).确认(CONNACK).断开连接(DISCONNECT)和在一起. CONNECT 像前面所说,MQTT有关字符串部分采 ...

  6. Windows域的相关操作

    一.windows域账户组操作: net group /domain #查看所有组 net group GROUP-NAME /domain #查看某一个组 net group GROUP-NAME ...

  7. SpringMVC中使用@ResponseBody注解将任意POJO对象返回值转换成json进行返回

    @ResponseBody 作用: 该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区. ...

  8. 【css预处理器】------sass的基本语法------【巷子】

    001.安装sass 1.删除gem源:gem sources --remove https://rubygems.org/ 2.添加国内源:gem sources -a http://gems.ru ...

  9. Spring源码学习之IOC实现原理(二)-ApplicationContext

    一.Spring核心组件结构 总的来说Spring共有三个核心组件,分别为Core,Context,Bean.三大核心组件的协同工作主要表现在 :Bean是包装我们应用程序自定义对象Object的,O ...

  10. Windows安装使用git

    下载安装Windows安装文档Git-2.16.2-64-bit双击安装(安装过程不详述) 打开git客户端 新建代码命令 mkdir /c/code 进入该目录(对应windows的c盘下面的目录) ...