算法介绍见:图像分割—基于图的图像分割(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
#include
#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<
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
//#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 *r, image *g, image *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(y, x),img_.at(y,x + 1));//by deafualt,normType=NORM_L2
edges[num].w = norm(img_.at(y, x), img_.at(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(y, x), img_.at(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(y, x), img_.at(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(y, x), img_.at(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(y, x) = colors.at(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
#include
#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 &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
#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
inline T abs(const T &x) { return (x > 0 ? x : -x); };
template
inline int sign(const T &x) { return (x >= 0 ? 1 : -1); };
template
inline T square(const T &x) { return x*x; };
template
inline T bound(const T &x, const T &min, const T &max) {
return (x < min ? min : (x > max ? max : x));
}
template
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