算法介绍见:图像分割—基于图的图像分割(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; } #endifdisjoint-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