图像分割—基于图的图像分割(OpenCV源码注解)

算法介绍见:图像分割—基于图的图像分割(Graph-Based Image Segmentation)

原文是Linux平台,图像格式PPM,比较不习惯,我改成了OpenCV,效果有一点点差别,应该是高斯滤波效果有差异。

此外,原算对边排序时,直接比较结构体,std::sort(edges, edges + num_edges); 按理说只比较边才对啊,于是我又改了std::sort(edges, edges + num_edges, [](const edge &e1, const edge &e2)  {return e1.w < e2.w; });……效果会有很大差异!原因,下次再说

我加的注释是语句的右边//开头的部分,不好意思是用英文写的,原因在于我写的能力太差,权当练习,如有错误,纯属正常

main.cpp

#include <vector>
#include <ctime>
#include "opencv2/opencv.hpp"
#include "segmentimage.h"

using namespace std;
using namespace cv;
// Computer Vision A Reference Guide
int main()
{
	//const char* imagename = "G:\\Pic\\101087_big.jpg";
	const char* imagename = "G:/Pic/beach.png";
	//const char* imagename = "grain.bmp";
	//const char* imagename = "person_272.png";
	//从文件中读入图像
	Mat img = imread(imagename);

	//如果读入图像失败
	if (img.empty())
	{
		fprintf(stderr, "Can not load image %s\n", imagename);
		return -1;
	}

	//显示图像
	imshow("image", img);
	//cvtColor(img, img, CV_BGR2Lab);// May be using lab color space will be better
	Mat gray;
	cvtColor(img, gray, CV_BGR2GRAY);
	img.convertTo(img,CV_32FC3);

	float sigma = 0.5;
	float k = 500;
	int min_size = 100;
	int num_ccs;
	clock_t tt = clock();
	Mat result = segment_image(img, sigma, k, min_size, &num_ccs);


	tt = clock() - tt;
	float process_time = (float)tt / CLOCKS_PER_SEC;
	cout << "get " << num_ccs << " components" << endl;

	cout << "process time: " << process_time<<endl;
	imshow("process result", result);
	
	cvtColor(gray, gray, CV_GRAY2BGR);
	imshow("overlay result", gray*0.25 + result*0.75);

	//此函数等待按键,按键盘任意键就返回
	waitKey();

	return 0;

}

segment_image.cpp

/*
Copyright (C) 2006 Pedro Felzenszwalb

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
*/



#include <cstdlib>

//#include "filter.h"
#include "segmentgraph.h"
#include "segmentimage.h"
#include "time.h"

// random color
rgb random_rgb(){ 
  rgb c;
  double r;
  srand(unsigned(time(NULL)));
  c.r = rand()%256;
  c.g = rand()%256;
  c.b = rand()%256;

  return c;
}

//// dissimilarity measure between pixels
//static inline float diff(image<float> *r, image<float> *g, image<float> *b,
//			 int x1, int y1, int x2, int y2) 
//{
//  return sqrt(square(imRef(r, x1, y1)-imRef(r, x2, y2)) +
//	      square(imRef(g, x1, y1)-imRef(g, x2, y2)) +
//	      square(imRef(b, x1, y1)-imRef(b, x2, y2)));
//}

/*
 * Segment an image
 *
 * Returns a color image representing the segmentation.
 *
 * im: image to segment.
 * sigma: to smooth the image.
 * c: constant for treshold function.
 * min_size: minimum component size (enforced by post-processing stage).
 * num_ccs: number of connected components in the segmentation.
 */
Mat segment_image(Mat &img, float sigma, float c, int min_size,
			  int *num_ccs) 
{
  int width = img.size().width;
  int height = img.size().height;
  //Mat img_(height, width, CV_8UC3); //using u8 will produce too many component
  Mat img_(height, width, CV_32FC3);

  // gaussian blur
  int kSize = cvCeil(4*sigma)+1;
  GaussianBlur(img, img_, Size(kSize, kSize), sigma, sigma);

  // build graph
  edge *edges = new edge[width*height*4];
  int num = 0;
  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
      if (x < width-1) //right
	  {
		edges[num].a = y * width + x;
		edges[num].b = y * width + (x+1);


		//edges[num].w = diff(rgbs, x, y, x + 1, y);
		//edges[num].w = norm(img_.at<Vec3b>(y, x),img_.at<Vec3b>(y,x + 1));//by deafualt,normType=NORM_L2
		edges[num].w = norm(img_.at<Vec3f>(y, x), img_.at<Vec3f>(y, x + 1));//by deafualt,normType=NORM_L2
		num++;
      }

      if (y < height-1)//bottom 
	  {
		edges[num].a = y * width + x;
		edges[num].b = (y+1) * width + x;

		//edges[num].w = diff(smooth_r, smooth_g, smooth_b, x, y, x, y+1);
		edges[num].w = norm(img_.at<Vec3f>(y, x), img_.at<Vec3f>(y + 1, x));
		num++;
      }

      if ((x < width-1) && (y < height-1)) {//bottom right
		edges[num].a = y * width + x;
		edges[num].b = (y+1) * width + (x+1);

		//edges[num].w = diff(smooth_r, smooth_g, smooth_b, x, y, x+1, y+1);
		edges[num].w = norm(img_.at<Vec3f>(y, x), img_.at<Vec3f>(y + 1, x + 1));

		num++;
      }

      if ((x < width-1) && (y > 0))//up right 
	  {
		edges[num].a = y * width + x;// find the component id to which they are subject  
		edges[num].b = (y-1) * width + (x+1);
		//edges[num].w = diff(smooth_r, smooth_g, smooth_b, x, y, x+1, y-1);
		edges[num].w = norm(img_.at<Vec3f>(y, x), img_.at<Vec3f>(y - 1, x + 1));

		num++;
      }
    }
  }

  // segment the graph to sub graphs
  /*
  * width*height: component number before mergence 
  * num :         component number after mergence 
  * c:            correspond to the k in fomula (5)
  */
  universe *u = segment_graph(width*height, num, edges, c);
  // post process small components

  for (int i = 0; i < num; i++) {
    int a = u->find(edges[i].a);
    int b = u->find(edges[i].b);
    if ((a != b) && ((u->size(a) < min_size) || (u->size(b) < min_size)))
      u->join(a, b); // as we have ranked the edges by weights so ,
				     // if the compent'size is less than the threshold it will merge with the most similar component  
  }
  delete [] edges;
  *num_ccs = u->num_sets();

  Mat output(height,width,CV_8UC3);

  // pick random colors for each component
  //rgb *colors = new rgb[width*height];
  //for (int i = 0; i < width*height; i++)
  //  colors[i] = random_rgb();
  Mat colors(1, num, CV_8UC3);
  randu(colors, Scalar::all(0), Scalar::all(255));
  
  for (int y = 0; y < height; y++) 
  {
    for (int x = 0; x < width; x++) 
	{
      int comp = u->find(y * width + x);
	  //if(u->elts[comp].rank>2)
	  output.at<Vec3b>(y, x) = colors.at<Vec3b>(0,comp);

    }
  }   
  delete u;
  return output;
}



segmentgraph.h

/*
Copyright (C) 2006 Pedro Felzenszwalb

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
*/

#ifndef SEGMENT_GRAPH
#define SEGMENT_GRAPH

#include <algorithm>
#include <cmath>
#include "disjoint-set.h"

// threshold function
#define THRESHOLD(size, c) (c/size)

typedef struct {
	//float w;
	double w;
	int a, b;
} edge;

bool operator<(const edge &a, const edge &b) {
	return a.w < b.w;
}

bool cmpEdge(const edge &edge1, const edge &edge2)
{
	return (edge1.w < edge2.w);
}
/*
* Segment a graph
*
* Returns a disjoint-set forest representing the segmentation.
*
* num_vertices: number of vertices in graph.
* num_edges: number of edges in graph
* edges: array of edges.
* c: constant for treshold function.
*/
universe *segment_graph(int num_vertices, int num_edges, edge *edges,
	float c) {
	// sort edges by weight
	// how do you specify the weight as compared component 
	//std::sort(edges, edges + num_edges); //original
	//std::sort(edges, edges + num_edges, cmpEdge);//Sorts the elements in the range [first,last) into ascending order.
	std::sort(edges, edges + num_edges, 
		[](const edge &e1, const edge &e2)
	     {return e1.w < e2.w; });//lambda expression
	// make a disjoint-set forest
	universe *u = new universe(num_vertices);

	// init thresholds
	//float *threshold = new float[num_vertices];
	double *threshold = new double[num_vertices];
	for (int i = 0; i < num_vertices; i++)
		threshold[i] = THRESHOLD(1, c); //c == k, reference to fomula (5)

	// for each edge, in non-decreasing weight order...


	for (int i = 0; i < num_edges; i++) {
		edge *pedge = &edges[i];

		// components conected by this edge
		int a = u->find(pedge->a);
		int b = u->find(pedge->b);
		if (a != b) {

			if ((pedge->w <= threshold[a]) &&(pedge->w <= threshold[b]))
		    //Note 1: as the edges was sorted by weight in ascending order,
			// so so the pedge->w is minimum weight edge connecting a,and b namely Dif(a,a) 
		    //Note 2: less than the min(a,b) the must less than both a and b ,reference  fomula (3)
			{
				//int sa = u->size(a), sb = u->size(b);

				u->join(a, b); // the id of merged component is the a/b which is determined by thier rank 
				a = u->find(a); // the id of merged component ,using b will produce the same result

				threshold[a] = pedge->w + THRESHOLD(u->size(a), c); // c/size(a)
				//Note: as the edges was sorted by weight in ascending order 
				//,so the pedge->w is the largest weight in the new component,namely Int( Cnew) reference to fomula (1)
			}
		}
	}
	// free up
	delete threshold;
	return u;
}

#endif
disjoint-set.h


/*
Copyright (C) 2006 Pedro Felzenszwalb

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
*/

#ifndef DISJOINT_SET
#define DISJOINT_SET

// disjoint-set forests using union-by-rank and path compression (sort of).

typedef struct {
  int rank; // 
  int p;
  int size;
} uni_elt;

class universe {
public:
  universe(int elements);
  ~universe();
  int find(int x);  
  void join(int x, int y);
  int size(int x) const { return elts[x].size; }
  int num_sets() const { return num; }
//private :
public :
  uni_elt *elts;
  int num;
};

universe::universe(int elements) {
  elts = new uni_elt[elements];
  num = elements;
  for (int i = 0; i < elements; i++) {
    elts[i].rank = 0;
    elts[i].size = 1;
    elts[i].p = i;
  }
}
  
universe::~universe() {
  delete [] elts;
}

int universe::find(int x) {
  int y = x;
  while (y != elts[y].p) // it was first initialized with itself ,
    y = elts[y].p;       // if it doesn't point to itself then it must have merged to other component. 
                         // elts[y] indicate to which component it connect, flowing this link iteratively we can find the component Id which they formed at last.  
  elts[x].p = y;         // keep track with the latest component to avoid indirect finding the latest component to save time  
  return y;
}

void universe::join(int x, int y) {
  if (elts[x].rank > elts[y].rank) {
    elts[y].p = x; //update the head of y to x 
    elts[x].size += elts[y].size; //size after merged 
  } else {
    elts[x].p = y;
    elts[y].size += elts[x].size;
    if (elts[x].rank == elts[y].rank) // there must be only one head  
      elts[y].rank++;  // ,so just appoint the later as leader 
  }
  num--; // component number remain 
}

#endif


segmentimage.h

#ifndef SEGMENT_IMAGE
#define SEGMENT_IMAGE
typedef unsigned char uchar;
#include "opencv2/opencv.hpp"
#include "misc.h"
using namespace cv;
//typedef struct {
//	uchar r, g, b; 
//} rgb;

rgb random_rgb();

//static inline float diff(std::vector<Mat> &rgbs	int x1, int y1, int x2, int y2)
//{
//	return norm(rgbs)
//}
Mat segment_image(Mat &img, float sigma, float c, int min_size,	int *num_ccs);



#endif


misc.h

/*
Copyright (C) 2006 Pedro Felzenszwalb

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
*/

/* random stuff */

#ifndef MISC_H
#define MISC_H

#include <cmath>

#ifndef M_PI
#define M_PI 3.141592653589793
#endif

typedef unsigned char uchar;

typedef struct { uchar r, g, b; } rgb;

inline bool operator==(const rgb &a, const rgb &b) {
  return ((a.r == b.r) && (a.g == b.g) && (a.b == b.b));
}

template <class T>
inline T abs(const T &x) { return (x > 0 ? x : -x); };

template <class T>
inline int sign(const T &x) { return (x >= 0 ? 1 : -1); };

template <class T>
inline T square(const T &x) { return x*x; };

template <class T>
inline T bound(const T &x, const T &min, const T &max) {
  return (x < min ? min : (x > max ? max : x));
}

template <class T>
inline bool check_bound(const T &x, const T&min, const T &max) {
  return ((x < min) || (x > max));
}

inline int vlib_round(float x) { return (int)(x + 0.5F); }

inline int vlib_round(double x) { return (int)(x + 0.5); }

inline double gaussian(double val, double sigma) {
  return exp(-square(val/sigma)/2)/(sqrt(2*M_PI)*sigma);
}

#endif







你可能感兴趣的:(源码,image,opencv,segmentation)