One cut in grabcut(grabcut算法的非迭代实现?)

本文针对交互式图像分割中的图割算法,主要想翻译一篇英文文献。不足之处请大家指正。

这是博主近期看到的效果最好,实现最简单,运算时间最短的交互式图割算法,而且由于是发明图割算法实验室原班人马的文章和代码,所以非常值得研究。

摘要

该方法使用少量的辅助节点(这个辅助节点我没看出来代码在哪块提现的,还望高手指点)来实现高效率的分割,传统的基于梯度下降的方法的分割方法,如grabcut,可能会收敛到局部极值(在图像较大时),而实验结果表明,对于图像比较复杂的图像如果我们使用足够过的辅助节点也能得到较好的效果:一次分割时间大概一秒以内,在图割里面算很快的了。

论文的贡献如下:

1.提出了一整个简单的基于l1距离的appearance overlap(这个怎么翻译?),可以看成高级形式的一致性标号,提出了一种简单的图建立方法,避免将问题陷入np难,并且论文通过实验发现l1距离能够更好的分离颜色信息。

2.使用颜色索引(从代码中可以看出),作者使用一个grb三通道的像素值计算了一个索引,类似hash-code的东西,相同像素值的(grb)的hash-code算出来是一样的,可以作为一个相似的节点(也就是索引节点)。

3.简化的能量函数

利用种子点分割时候简化为下面形式:

下面是我写了一些注释的代码:(对原来部分代码做了修改,没改算法,改的输入输出)

配置好OpenCV就直接能用,效果非常好,甚至可以直接集成到app里面去。

//
//@inproceedings{iccv2013onecut,
// title = {Grabcut in One Cut},
// author = {Tang, Meng and Gorelick, Lena and Veksler, Olga and Boykov, Yuri},
// booktitle={International Conference on Computer Vision},
// month = {December},
// year = {2013}}
//
//THIS SOFTWARE USES maxflow/min-cut CODE THAT WAS IMPLEMENTED BY VLADIMIR KOLMOGOROV,
//THAT CAN BE DOWNLOADED FROM http://vision.csd.uwo.ca/code/.
//PLEASE USE THE FOLLOWING CITATION:
//
//@ARTICLE{Boykov01anexperimental,
// author = {Yuri Boykov and Vladimir Kolmogorov},
// title = {An Experimental Comparison of Min-Cut/Max-Flow Algorithms for Energy Minimization in Vision},
// journal = {IEEE TRANSACTIONS ON PATTERN ANALYSIS AND MACHINE INTELLIGENCE},
// year = {2001},
// volume = {26},
// pages = {359--374}}
// //
//##################################################################
//
// USAGE INSTRUCTIONS
//
// In the command line type:
//
// OneCut <imageFileName> [<beta> <numBinsPerChannel>]
//
// Default values: beta= 0.1, numBinsPerChannel=64
//
// Example: OneCut frida_small.jpg 0.1 64
// or OneCut frida_small.jpg
//
//
// Once the image is opened you can scribble with left and right
// mouse buttons on the object and the background in the
// "Scribble Image" window. Once the scribbles are given you can
// segment the image.You can keep repeatedly adding scribbles and
// segmenting until the result is satisfactory.
//
// Use the following Short Keys:
// 'q' - quit
// 's' - segment
// 'r' - reset (removes all strokes and clears all results)
// '+' - increase brush stroke radius
// '-' - decrease brush stroke radius
// 'right mouse button drug' - draw blue scribble
// 'left mouse button drug' - draw red scribble
//
//
#include <iostream> // for standard I/O
#include <string> // for strings
#include <iomanip> // for controlling float print precision
#include <sstream> // string to number conversion #include <opencv2/imgproc/imgproc.hpp> // Gaussian Blur
#include <opencv2/core/core.hpp> // Basic OpenCV structures (cv::Mat, Scalar)
#include <opencv2/highgui/highgui.hpp> // OpenCV window I/O #include "graph.h"
#include "ComputeTime.h" using namespace std;
using namespace cv; // images
Mat inputImg, showImg, binPerPixelImg, showEdgesImg, segMask, segShowImg; // mask
Mat fgScribbleMask, bgScribbleMask; // user clicked mouse buttons flags
bool rButtonDown = false;
bool lButtonDown = false;
int numUsedBins = 0;
float varianceSquared = 0;
int scribbleRadius = 5;//画笔半径 // default arguments
float bha_slope = 0.1f;
int numBinsPerChannel = 64; const float INT32_CONST = 1000;
const float HARD_CONSTRAINT_CONST = 1000; #define NEIGHBORHOOD_8_TYPE 1;
#define NEIGHBORHOOD_25_TYPE 2; const int NEIGHBORHOOD = NEIGHBORHOOD_8_TYPE; //************************************
// F u n c t i o n d e c l a r a t i o n s // init all images/vars
int init(char * imgFileName); // clear everything before closing
void destroyAll(); // mouse listener
static void onMouse( int event, int x, int y, int, void* ); // set bin index for each image pixel, store it in binPerPixelImg
void getBinPerPixel(Mat & binPerPixelImg, Mat & inputImg, int numBinsPerChannel, int & numUsedBins); // compute the variance of image edges between neighbors
void getEdgeVariance(Mat & inputImg, Mat & showEdgesImg, float & varianceSquared); typedef Graph<int,int,int> GraphType;
GraphType *myGraph; //***********************************
// M a i n /*
if( argc > 4 || argc < 2)
{
cout <<" Usage: seedsAndOverlap ImageToSegment [numBinsPerChannel bha_slope]" << endl;
return -1;
}
if (argc >= 3)
{
// get the second arg
String numBinsStr(argv[2]); // convert to int
numBinsPerChannel = atoi(numBinsStr.c_str());
cout << "Using " << numBinsPerChannel << " bins per channel " << endl;
if (argc >=4)
{
//get third argument
String bhaSlopeStr(argv[3]);
bha_slope = (float)atof(bhaSlopeStr.c_str());
cout << "Using beta = " << bha_slope << endl;
}
else
cout << "Using default beta = " << bha_slope << endl;
}
else
{
cout << "Using default " << numBinsPerChannel << " bins per channel " << endl;
cout << "Using default beta = " << bha_slope << endl;
} */ int main(int argc, char *argv[])
{ String image_name,numBinsStr,bhaSlopeStr;
cout<<"input Parameters:"<<endl;
cout<<"image name: ";
cin>>image_name;
cout<<endl<<"numBinsPerChannel: ";
cin>>numBinsStr;
cout<<endl<<"beta: ";
cin>>bhaSlopeStr; // get img name parameter
char * imgFileName = (char *)image_name.c_str(); // convert to int
numBinsPerChannel = atoi(numBinsStr.c_str());
cout << "Using " << numBinsPerChannel << " bins per channel " << endl; bha_slope = (float)atof(bhaSlopeStr.c_str());
cout << "Using beta = " << bha_slope << endl; //cout << "Using default beta = " << bha_slope << endl; ComputeTime ct_init;//计算代码运行时间的类
ct_init.Begin();
if (init(imgFileName)==-1)
{
cout << "Could not initialize" << endl ;
return -1;
} cout<<"初始化运行时间: "<<ct_init.End()<<"ms"<<endl; // Wait for a keystroke in the window
for (;;)
{
char key = waitKey(0);
switch (key)
{
case 'q':
cout << "goodbye" << endl;
destroyAll();
return 0;
case '-':
//缩小画笔直径
if (scribbleRadius > 2)
scribbleRadius --;
cout << "current radius is " << scribbleRadius << endl;
break;
case '+':
if (scribbleRadius < 100)
scribbleRadius ++;
cout << "current radius is " << scribbleRadius << endl;
break;
case 's':
{
ComputeTime ct;//计算代码运行时间的类
ct.Begin();
cout << "setting the hard constraints..." << endl;
for(int i=0; i<inputImg.rows; i++)
{
for(int j=0; j<inputImg.cols; j++)
{
// this is the node id for the current pixel
GraphType::node_id currNodeId = i * inputImg.cols + j; // add hard constraints based on scribbles
if (fgScribbleMask.at<uchar>(i,j) == 255)
myGraph->add_tweights(currNodeId,(int)ceil(INT32_CONST * HARD_CONSTRAINT_CONST + 0.5),0);
else if (bgScribbleMask.at<uchar>(i,j) == 255)
myGraph->add_tweights(currNodeId,0,(int)ceil(INT32_CONST * HARD_CONSTRAINT_CONST + 0.5));
}
}
cout << "maxflow..." << endl;
int flow = myGraph -> maxflow();
cout << "done maxflow..." << endl; // this is where we store the results
segMask = 0;
inputImg.copyTo(segShowImg);
//inputImg.copyTo(showImg); // empty scribble masks are ready to record additional scribbles for additional hard constraints
// to be used next time
fgScribbleMask = 0;
bgScribbleMask = 0; // copy the segmentation results on to the result images
for (int i = 0; i<inputImg.rows * inputImg.cols; i++)
{
// if it is foreground - color blue
if (myGraph->what_segment((GraphType::node_id)i ) == GraphType::SOURCE)
{
segMask.at<uchar>(i/inputImg.cols, i%inputImg.cols) = 255;
//(uchar)segShowImg.at<Vec3b>(i/inputImg.cols, i%inputImg.cols)[2] = 200;
}
// if it is background - color red
else
{
segMask.at<uchar>(i/inputImg.cols, i%inputImg.cols) = 0;
(uchar)segShowImg.at<Vec3b>(i/inputImg.cols, i%inputImg.cols)[0] = 0;
(uchar)segShowImg.at<Vec3b>(i/inputImg.cols, i%inputImg.cols)[1] = 0;
(uchar)segShowImg.at<Vec3b>(i/inputImg.cols, i%inputImg.cols)[2] = 0;
}
} imshow("Segmentation Mask", segMask);
imshow("Segmentation Image", segShowImg); cout<<"运行时间: "<<ct.End()<<"ms"<<endl; imwrite("seg_result.bmp",segShowImg);
waitKey(0);
break; }
case 'r':
{
cout << "resetting" << endl;
destroyAll();
if (init(imgFileName)==-1)
{
cout << "could not initialize" << std::endl ;
return -1;
}
break;
}
}
} return 0;
} // mouse listener
static void onMouse( int event, int x, int y, int, void* )
{
//cout << "On Mouse: (" << x << "," << y << ")" <<endl; if (event == CV_EVENT_LBUTTONDOWN)
{
lButtonDown = true; }
else if (event == CV_EVENT_RBUTTONDOWN)
{
rButtonDown = true; }
else if (event == CV_EVENT_LBUTTONUP)
{
lButtonDown = false;
}
else if (event == CV_EVENT_RBUTTONUP)
{
rButtonDown = false;
}
else if (event == CV_EVENT_MOUSEMOVE)
{
if (rButtonDown)
{
// scribble the background circle(bgScribbleMask,Point(x,y),scribbleRadius, 255,-1);
circle(showImg,Point(x,y),scribbleRadius, CV_RGB(0,0,255),-1); }
else if (lButtonDown)
{
// scribble the foreground circle(fgScribbleMask,Point(x,y),scribbleRadius, 255,-1);
circle(showImg,Point(x,y),scribbleRadius, CV_RGB(255,0,0),-1); //fgScribbleMask.at<char>(y,x)=(char)255;
// set variables using mask
//showImg.setTo(redColorElement,fgScribbleMask); //showImg.at<Vec3b>(y,x)[0] = 0;
//showImg.at<Vec3b>(y,x)[1] = 0;
//showImg.at<Vec3b>(y,x)[2] = 255;
} } imshow("Scribble Image", showImg);
imshow("fg mask", fgScribbleMask);
imshow("bg mask", bgScribbleMask);
} // clear everything before closing
void destroyAll()
{
// destroy all windows
destroyWindow("Input Image");
destroyWindow("Scribble Image");
destroyWindow("Bin Per Pixel");
destroyWindow("Edges");
destroyWindow("bg mask");
destroyWindow("fg mask");
destroyWindow("Segmentation Mask");
destroyWindow("Segmentation Image"); // clear all data
fgScribbleMask.release();
bgScribbleMask.release();
inputImg.release();
showImg.release();
showEdgesImg.release();
binPerPixelImg.release();
segMask.release();
segShowImg.release(); delete myGraph; } // init all images/vars
int init(char * imgFileName)
{
// Read the file
inputImg = imread(imgFileName, CV_LOAD_IMAGE_COLOR);
showImg = inputImg.clone();
segShowImg = inputImg.clone(); // Check for invalid input
if(!inputImg.data )
{
cout << "Could not open or find the image: " << imgFileName << std::endl ;
return -1;
} // this is the mask to keep the user scribbles
fgScribbleMask.create(2,inputImg.size,CV_8UC1);
fgScribbleMask = 0;
bgScribbleMask.create(2,inputImg.size,CV_8UC1);
bgScribbleMask = 0;
segMask.create(2,inputImg.size,CV_8UC1);
segMask = 0;
showEdgesImg.create(2, inputImg.size, CV_32FC1);
showEdgesImg = 0;
binPerPixelImg.create(2, inputImg.size,CV_32F); // get bin index for each image pixel, store it in binPerPixelImg
getBinPerPixel(binPerPixelImg, inputImg, numBinsPerChannel, numUsedBins); // compute the variance of image edges between neighbors
getEdgeVariance(inputImg, showEdgesImg, varianceSquared); // Create a window for display.
namedWindow( "Input Image", CV_WINDOW_AUTOSIZE );
namedWindow( "Scribble Image", CV_WINDOW_AUTOSIZE);
namedWindow("Bin Per Pixel", CV_WINDOW_AUTOSIZE );
namedWindow("Edges", CV_WINDOW_AUTOSIZE );
namedWindow("Segmentation Mask",CV_WINDOW_AUTOSIZE);
namedWindow("Segmentation Image",CV_WINDOW_AUTOSIZE);
namedWindow( "fg mask", CV_WINDOW_AUTOSIZE );
namedWindow( "bg mask", CV_WINDOW_AUTOSIZE ); //namedWindow("Input Image", CV_WINDOW_NORMAL | CV_WINDOW_KEEPRATIO | CV_GUI_EXPANDED); // Show our image inside it.
imshow( "Input Image", inputImg );
imshow( "Scribble Image", showImg );
imshow("Segmentation Mask", segMask);
imshow("Segmentation Image", segShowImg);
imshow("fg mask", fgScribbleMask);
imshow("bg mask", bgScribbleMask); moveWindow("Scribble Image", 1,1);
moveWindow("Input Image", inputImg.cols + 50,1);
moveWindow("Bin Per Pixel", 2*(inputImg.cols + 50),1);
moveWindow("Edges", 2*(inputImg.cols + 55),1); // set the callback on mouse
setMouseCallback("Scribble Image", onMouse, 0); myGraph = new GraphType(/*estimated # of nodes*/ inputImg.rows * inputImg.cols + numUsedBins,
/*estimated # of edges=11 spatial neighbors and one link to auxiliary*/ 12 * inputImg.rows * inputImg.cols); GraphType::node_id currNodeId = myGraph -> add_node((int)inputImg.cols * inputImg.rows + numUsedBins); //#pragma omp parallel for
for(int i=0; i<inputImg.rows; i++)
{
//#pragma omp parallel for
for(int j=0; j<inputImg.cols; j++)
{
// this is the node id for the current pixel
GraphType::node_id currNodeId = i * inputImg.cols + j; // add hard constraints based on scribbles
if (fgScribbleMask.at<uchar>(i,j) == 255)
myGraph->add_tweights(currNodeId,(int)ceil(INT32_CONST * HARD_CONSTRAINT_CONST + 0.5),0);
else if (bgScribbleMask.at<uchar>(i,j) == 255)
myGraph->add_tweights(currNodeId,0,(int)ceil(INT32_CONST * HARD_CONSTRAINT_CONST + 0.5)); // You can now access the pixel value with cv::Vec3b
float b = (float)inputImg.at<Vec3b>(i,j)[0];
float g = (float)inputImg.at<Vec3b>(i,j)[1];
float r = (float)inputImg.at<Vec3b>(i,j)[2]; // go over the neighbors
for (int si = -NEIGHBORHOOD; si <= NEIGHBORHOOD && si + i < inputImg.rows && si + i >= 0 ; si++)
{
for (int sj = 0; sj <= NEIGHBORHOOD && sj + j < inputImg.cols; sj++)
{
if ((si == 0 && sj == 0) ||
(si == 1 && sj == 0) ||
(si == NEIGHBORHOOD && sj == 0))
continue; // this is the node id for the neighbor
GraphType::node_id nNodeId = (i+si) * inputImg.cols + (j + sj); float nb = (float)inputImg.at<Vec3b>(i+si,j+sj)[0];
float ng = (float)inputImg.at<Vec3b>(i+si,j+sj)[1];
float nr = (float)inputImg.at<Vec3b>(i+si,j+sj)[2]; // ||I_p - I_q||^2 / 2 * sigma^2
float currEdgeStrength = exp(-((b-nb)*(b-nb) + (g-ng)*(g-ng) + (r-nr)*(r-nr))/(2*varianceSquared));
float currDist = sqrt((float)si*(float)si + (float)sj*(float)sj); // this is the edge between the current two pixels (i,j) and (i+si, j+sj)
currEdgeStrength = ((float)0.95 * currEdgeStrength + (float)0.05) /currDist;
myGraph -> add_edge(currNodeId, nNodeId, /* capacities */ (int) ceil(INT32_CONST*currEdgeStrength + 0.5), (int)ceil(INT32_CONST*currEdgeStrength + 0.5)); }
}
// add the adge to the auxiliary node
int currBin = (int)binPerPixelImg.at<float>(i,j); myGraph -> add_edge(currNodeId, (GraphType::node_id)(currBin + inputImg.rows * inputImg.cols),
/* capacities */ (int) ceil(INT32_CONST*bha_slope+ 0.5), (int)ceil(INT32_CONST*bha_slope + 0.5));
} } return 0;
} // get bin index for each image pixel, store it in binPerPixelImg
void getBinPerPixel(Mat & binPerPixelImg, Mat & inputImg, int numBinsPerChannel, int & numUsedBins)
{
// this vector is used to through away bins that were not used 计算x的y次幂。初值64*64*64空间中初值都是-1
vector<int> occupiedBinNewIdx((int)pow((double)numBinsPerChannel,(double)3),-1); // go over the image
int newBinIdx = 0; //#pragma omp parallel for
for(int i=0; i<inputImg.rows; i++)
for(int j=0; j<inputImg.cols; j++)
{
// You can now access the pixel value with cv::Vec3b
float b = (float)inputImg.at<Vec3b>(i,j)[0];
float g = (float)inputImg.at<Vec3b>(i,j)[1];
float r = (float)inputImg.at<Vec3b>(i,j)[2]; // this is the bin assuming all bins are present
int bin = (int)(floor(b/256.0 *(float)numBinsPerChannel) + (float)numBinsPerChannel * floor(g/256.0*(float)numBinsPerChannel)
+ (float)numBinsPerChannel * (float)numBinsPerChannel * floor(r/256.0*(float)numBinsPerChannel)); // if we haven't seen this bin yet
if (occupiedBinNewIdx[bin]==-1)
{
// mark it seen and assign it a new index
occupiedBinNewIdx[bin] = newBinIdx;
newBinIdx ++;
}
// if we saw this bin already, it has the new index
binPerPixelImg.at<float>(i,j) = (float)occupiedBinNewIdx[bin]; //cout << bin << endl;
} double maxBin;
minMaxLoc(binPerPixelImg,NULL,&maxBin);//图像中的最大值
numUsedBins = (int) maxBin + 1;
imshow("Bin Per Pixel", binPerPixelImg/maxBin); occupiedBinNewIdx.clear();
cout << "Num occupied bins:" << numUsedBins<< endl; } // compute the variance(变化,方差) of image edges between neighbors
void getEdgeVariance(Mat & inputImg, Mat & showEdgesImg, float & varianceSquared)
{ varianceSquared = 0;
int counter = 0; #pragma omp parallel for
for(int i=0; i<inputImg.rows; i++)
{
for(int j=0; j<inputImg.cols; j++)
{ // You can now access the pixel value with cv::Vec3b
float b = (float)inputImg.at<Vec3b>(i,j)[0];
float g = (float)inputImg.at<Vec3b>(i,j)[1];
float r = (float)inputImg.at<Vec3b>(i,j)[2];
for (int si = -NEIGHBORHOOD; si <= NEIGHBORHOOD && si + i < inputImg.rows && si + i >= 0 ; si++)
{
for (int sj = 0; sj <= NEIGHBORHOOD && sj + j < inputImg.cols ; sj++) {
if ((si == 0 && sj == 0) ||
(si == 1 && sj == 0) ||
(si == NEIGHBORHOOD && sj == 0))
continue; float nb = (float)inputImg.at<Vec3b>(i+si,j+sj)[0];
float ng = (float)inputImg.at<Vec3b>(i+si,j+sj)[1];
float nr = (float)inputImg.at<Vec3b>(i+si,j+sj)[2]; varianceSquared+= (b-nb)*(b-nb) + (g-ng)*(g-ng) + (r-nr)*(r-nr);
counter ++; } }
}
}
varianceSquared/=counter; // just for visualization
//#pragma omp parallel for
for(int i=0; i<inputImg.rows; i++)
{
for(int j=0; j<inputImg.cols; j++)
{ float edgeStrength = 0;
// You can now access the pixel value with cv::Vec3b
float b = (float)inputImg.at<Vec3b>(i,j)[0];
float g = (float)inputImg.at<Vec3b>(i,j)[1];
float r = (float)inputImg.at<Vec3b>(i,j)[2];
for (int si = -NEIGHBORHOOD; si <= NEIGHBORHOOD && si + i < inputImg.rows && si + i >= 0; si++)
{
for (int sj = 0; sj <= NEIGHBORHOOD && sj + j < inputImg.cols ; sj++)
{
if ((si == 0 && sj == 0) ||
(si == 1 && sj == 0) ||
(si == NEIGHBORHOOD && sj == 0))
continue; float nb = (float)inputImg.at<Vec3b>(i+si,j+sj)[0];
float ng = (float)inputImg.at<Vec3b>(i+si,j+sj)[1];
float nr = (float)inputImg.at<Vec3b>(i+si,j+sj)[2]; // ||I_p - I_q||^2 / 2 * sigma^2
float currEdgeStrength = exp(-((b-nb)*(b-nb) + (g-ng)*(g-ng) + (r-nr)*(r-nr))/(2*varianceSquared));
float currDist = sqrt((float)si*(float)si + (float)sj * (float)sj); // this is the edge between the current two pixels (i,j) and (i+si, j+sj)
edgeStrength = edgeStrength + ((float)0.95 * currEdgeStrength + (float)0.05) /currDist; }
}
// this is the avg edge strength for pixel (i,j) with its neighbors
showEdgesImg.at<float>(i,j) = edgeStrength; }
} double maxEdge;
Point maxPoint;
minMaxLoc(showEdgesImg,NULL,&maxEdge, NULL, &maxPoint);
//cout << showEdgesImg.at<float>(maxPoint) << endl;
imshow("Edges", showEdgesImg/maxEdge); } /*
*******************************
Mat myMat(size(3, 3), CV_32FC2); myMat.ptr<float>(y)[2*x]; // first channel
myMat.ptr<float>(y)[2*x+1]; // second channel
*/

测量时间的类:

#pragma once
/*
//计算代码段运行时间的类
//
*/
#include <iostream> #ifndef ComputeTime_h
#define ComputeTime_h class ComputeTime
{
private:
int Initialized;
__int64 Frequency;
__int64 BeginTime; public: bool Avaliable();
double End();
bool Begin();
ComputeTime();
virtual ~ComputeTime(); }; #endif #include "ComputeTime.h"
#include <iostream>
#include <Windows.h> ComputeTime::ComputeTime()
{
Initialized=QueryPerformanceFrequency((LARGE_INTEGER *)&Frequency);
} ComputeTime::~ComputeTime()
{ } bool ComputeTime::Begin()
{
if(!Initialized)
return 0; return QueryPerformanceCounter((LARGE_INTEGER *)&BeginTime);
} double ComputeTime::End()
{
if(!Initialized)
return 0; __int64 endtime; QueryPerformanceCounter((LARGE_INTEGER *)&endtime); __int64 elapsed = endtime-BeginTime; return ((double)elapsed/(double)Frequency)*1000.0; //单位毫秒
} bool ComputeTime::Avaliable()
{
return Initialized;
}

项目主页:

http://vision.csd.uwo.ca/code/

Code:http://vision.csd.uwo.ca/wiki/vision/upload/7/77/OneCutWithSeeds_v1.03.zip

Paper:http://www.csd.uwo.ca/~ygorelic/iccv13_one_cut.pdf

OpenCV代码实现grabcut::

http://www.morethantechnical.com/2010/05/05/bust-out-your-own-graphcut-based-image-segmentation-with-opencv-w-code/

我调试好的工程代码下载链接:点击打开链接

GrabCut in One Cut(基于图割算法grabcut的一次快速图像分割的OpenCV实现)----目前效果最好的图割的更多相关文章

  1. 软阈值迭代算法(ISTA)和快速软阈值迭代算法(FISTA)

    缺月挂疏桐,漏断人初静. 谁见幽人独往来,缥缈孤鸿影. 惊起却回头,有恨无人省. 拣尽寒枝不肯栖,寂寞沙洲冷.---- 苏轼 更多精彩内容请关注微信公众号 "优化与算法" ISTA ...

  2. 基于GraphCuts图割算法的图像分割----OpenCV代码与实现

    转载请注明出处:http://blog.csdn.net/wangyaninglm/article/details/44151213, 来自:shiter编写程序的艺术 1.绪论 图切割算法是组合图论 ...

  3. POJ 2914 Minimum Cut 最小割算法题解

    最标准的最小割算法应用题目. 核心思想就是缩边:先缩小最大的边.然后缩小次大的边.依此缩小 基础算法:Prime最小生成树算法 只是本题測试的数据好像怪怪的,相同的算法时间执行会区别非常大,并且一样的 ...

  4. PCL—低层次视觉—点云分割(最小割算法)

    1.点云分割的精度 在之前的两个章节里介绍了基于采样一致的点云分割和基于临近搜索的点云分割算法.基于采样一致的点云分割算法显然是意识流的,它只能割出大概的点云(可能是杯子的一部分,但杯把儿肯定没分割出 ...

  5. ZOJ 2676 Network Wars ★(最小割算法介绍 && 01分数规划)

    [题意]给出一个带权无向图,求割集,且割集的平均边权最小. [分析] 先尝试着用更一般的形式重新叙述本问题.设向量w表示边的权值,令向量c=(1, 1, 1, --, 1)表示选边的代价,于是原问题等 ...

  6. PCL—点云分割(最小割算法) 低层次点云处理

    1.点云分割的精度 在之前的两个章节里介绍了基于采样一致的点云分割和基于临近搜索的点云分割算法.基于采样一致的点云分割算法显然是意识流的,它只能割出大概的点云(可能是杯子的一部分,但杯把儿肯定没分割出 ...

  7. java数据结构_笔记(5)_图的算法

    图的算法 1 图的遍历图的遍历就是从图中某个顶点出发,按某种方法对图中所有顶点访问且仅访问一次.遍历算法是求解图的连通性问题.拓扑排序和求关键路径等算法的基础. 2 深度优先遍历从图中某个顶点V 出发 ...

  8. 从Random Walk谈到Bacterial foraging optimization algorithm(BFOA),再谈到Ramdom Walk Graph Segmentation图分割算法

    1. 从细菌的趋化性谈起 0x1:物质化学浓度梯度 类似于概率分布中概率密度的概念.在溶液中存在不同的浓度区域. 如放一颗糖在水盆里,糖慢慢溶于水,糖附近的水含糖量比远离糖的水含糖量要高,也就是糖附近 ...

  9. 基于dsp_builder的算法在FPGA上的实现

    基于dsp_builder的算法在FPGA上的实现   一.摘要 结合dsp_builder.matlab.modelsim和quartus ii等软件完成算法的FPGA实现. 二.实验平台 硬件平台 ...

随机推荐

  1. MySQL Innodb如何找出阻塞事务源头SQL

    在MySQL数据库中出现了阻塞问题,如何快速查找定位问题根源?在实验开始前,我们先梳理一下有什么工具或命令查看MySQL的阻塞,另外,我们也要一一对比其优劣,因为有些命令可能在实际环境下可能并不适用. ...

  2. linux加入Windows域-------本人生产环境上线所用

    为什么说要linux加域呢!    因为之前在公司是做vmware的,然后呢vmware的horizon桌面云虚拟化都是通过域来管理的,开始使用的都是Windows桌面,后来开发的人员说要使用linu ...

  3. Python Selenium 之数据驱动测试

    数据驱动模式的测试好处相比普通模式的测试就显而易见了吧!使用数据驱动的模式,可以根据业务分解测试数据,只需定义变量,使用外部或者自定义的数据使其参数化,从而避免了使用之前测试脚本中固定的数据.可以将测 ...

  4. C语言多维数组的指针传递

    在C语言中为了节省空间,提高运行速度经常使用指针来完成数组的传递. 对于一维数组而言可以直接传递首地址 而对于二维数组必须在传递时声明是二维数组的指针,并且调用时也要经过一些运算 首先是定义形参: 函 ...

  5. CRM客户关系管理系统(四)

    kingadmin设计开发 4.4.根据list_display配置生成数据列表 (1)kingadmin/views.py (2)kingadmin/templates/kingadmin/tabl ...

  6. Node.js 常用工具util

    util 是一个Node.js 核心模块,提供常用函数的集合,用于弥补核心JavaScript 的功能 过于精简的不足. util.inherits util.inherits(constructor ...

  7. python3.6 使用 pymysql 连接 Mysql 数据库及 简单的增删改查操作

    1.通过 pip 安装 pymysql 进入 cmd  输入  pip install pymysql   回车等待安装完成: 安装完成后出现如图相关信息,表示安装成功. 2.测试连接 import ...

  8. 20160208.CCPP体系详解(0018天)

    程序片段(01):main.c 内容概要:PointWithOutInit #include <stdio.h> #include <stdlib.h> //01.野指针详解: ...

  9. Ubuntu LTS 系统学习使用体会和实用工具软件汇总 6.04 8.04 10.04 12.04 14.04 16.04

    Ubuntu LTS 系统学习体会和工具软件汇总 6.04 8.04 10.04 12.04 14.04 16.04 ubuntu入门必备pdf:http://download.csdn.net/de ...

  10. 用Codility测试你的编码能力

    没有宏观的架构设计,没有特定的框架语言.在Codility提出的一些小问题上,用最纯粹的方式测试你最基本的编码能力. Codility第一课:算法复杂度 各种算法书的开篇大多是算法分析,而复杂度(co ...