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

简介:  One cut in grabcut(grabcut算法的非迭代实现?) 本文针对交互式图像分割中的图割算法,主要想翻译一篇英文文献。不足之处请大家指正。     这是博主近期看到的效果最好,实现最简单,运算时间最短的交互式图割算法,而且由于是发明图割算法实验室原班人马的文章和代码,所以非常值得研究。


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/


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

相关文章
|
6月前
|
算法 计算机视觉
OpenCV(四十一):图像分割-分水岭法
OpenCV(四十一):图像分割-分水岭法
101 0
|
9天前
|
算法 数据挖掘 数据安全/隐私保护
基于FCM模糊聚类算法的图像分割matlab仿真
本项目展示了基于模糊C均值(FCM)算法的图像分割技术。算法运行效果良好,无水印。使用MATLAB 2022a开发,提供完整代码及中文注释,附带操作步骤视频。FCM算法通过隶属度矩阵和聚类中心矩阵实现图像分割,适用于灰度和彩色图像,广泛应用于医学影像、遥感图像等领域。
|
1月前
|
算法 计算机视觉 Python
圆形检测算法-基于颜色和形状(opencv)
该代码实现了一个圆检测算法,用于识别视频中的红色、白色和蓝色圆形。通过将图像从RGB转换为HSV颜色空间,并设置对应颜色的阈值范围,提取出目标颜色的区域。接着对这些区域进行轮廓提取和面积筛选,使用霍夫圆变换检测圆形,并在原图上绘制检测结果。
64 0
|
3月前
|
计算机视觉 C++
基于VS2019和Opencv4,对hsv颜色空间的图像分割原理以及实现
这篇文章介绍了基于HSV颜色空间的图像分割原理,包括HSV模型的基本概念和如何在OpenCV中通过设置HSV的色彩范围来实现图像中特定颜色的物体分割,并通过示例代码展示了在静态图像和视频流中进行颜色分割的方法。
基于VS2019和Opencv4,对hsv颜色空间的图像分割原理以及实现
|
3月前
|
算法 定位技术 vr&ar
一文了解PnP算法,python opencv中的cv2.solvePnP()的使用,以及使用cv2.sovlePnP()方法标定相机和2D激光雷达
一文了解PnP算法,python opencv中的cv2.solvePnP()的使用,以及使用cv2.sovlePnP()方法标定相机和2D激光雷达
497 0
一文了解PnP算法,python opencv中的cv2.solvePnP()的使用,以及使用cv2.sovlePnP()方法标定相机和2D激光雷达
|
5月前
|
机器学习/深度学习 算法 计算机视觉
基于opencv的SVM算法的车牌识别系统设计与实现
基于opencv的SVM算法的车牌识别系统设计与实现
148 3
基于opencv的SVM算法的车牌识别系统设计与实现
|
5月前
|
算法 Java 数据挖掘
使用MeanShift算法进行图像分割的实现
使用MeanShift算法进行图像分割的实现
|
5月前
|
移动开发 算法 计算机视觉
技术笔记:openCV特征点识别与findHomography算法过滤
技术笔记:openCV特征点识别与findHomography算法过滤
97 0
|
5月前
|
机器学习/深度学习 编译器 算法框架/工具
OpenCV算法库
numba是一个用于编译Python数组和数值计算函数的编译器,这个编译器能够大幅提高直接使用Python编写的函数的运算速度。
|
6月前
|
算法 TensorFlow 算法框架/工具
基于直方图的图像阈值计算和分割算法FPGA实现,包含tb测试文件和MATLAB辅助验证
这是一个关于图像处理的算法实现摘要,主要包括四部分:展示了四张算法运行的效果图;提到了使用的软件版本为VIVADO 2019.2和matlab 2022a;介绍了算法理论,即基于直方图的图像阈值分割,通过灰度直方图分布选取阈值来区分图像区域;并提供了部分Verilog代码,该代码读取图像数据,进行处理,并输出结果到&quot;result.txt&quot;以供MATLAB显示图像分割效果。