diff --git a/felz/CMakeLists.txt b/felz/CMakeLists.txt index 3909905..09b686f 100644 --- a/felz/CMakeLists.txt +++ b/felz/CMakeLists.txt @@ -31,9 +31,9 @@ target_link_libraries(segment_felz ${PYTHON_LIBRARIES} ) -add_executable(segment src/segment.cpp src/conversion.cpp) -target_link_libraries(segment - ${Boost_LIBRARIES} - ${OpenCV_LIBRARIES} - ${PYTHON_LIBRARIES} -) +#add_executable(segment src/segment.cpp src/conversion.cpp) +#target_link_libraries(segment +# ${Boost_LIBRARIES} +# ${OpenCV_LIBRARIES} +# ${PYTHON_LIBRARIES} +#) diff --git a/felz/src/CVBoostConverter.hpp b/felz/src/CVBoostConverter.hpp deleted file mode 100644 index 5bbf456..0000000 --- a/felz/src/CVBoostConverter.hpp +++ /dev/null @@ -1,423 +0,0 @@ -/* - * CVBoostConverter.hpp - * - * Created on: Mar 20, 2014 - * Author: Gregory Kramida - * Copyright: (c) 2014 Gregory Kramida - * License: MIT - */ - -#ifndef CVBOOSTCONVERTER_HPP_ -#define CVBOOSTCONVERTER_HPP_ - -#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION -#include -#include -#include -#include -#include -#include - -using namespace cv; -namespace bcvt{ -static PyObject* opencv_error = 0; - -class PyAllowThreads { -public: - PyAllowThreads() : - _state(PyEval_SaveThread()) { - } - ~PyAllowThreads() { - PyEval_RestoreThread(_state); - } -private: - PyThreadState* _state; -}; - -class PyEnsureGIL { -public: - PyEnsureGIL() : - _state(PyGILState_Ensure()) { - } - ~PyEnsureGIL() { - PyGILState_Release(_state); - } -private: - PyGILState_STATE _state; -}; -#define ERRWRAP2(expr) \ -try \ -{ \ - PyAllowThreads allowThreads; \ - expr; \ -} \ -catch (const cv::Exception &e) \ -{ \ - PyErr_SetString(opencv_error, e.what()); \ - return 0; \ -} - -class NumpyAllocator: public MatAllocator { -public: - NumpyAllocator() { - stdAllocator = Mat::getStdAllocator(); - } - ~NumpyAllocator() { - } - - UMatData* allocate(PyObject* o, int dims, const int* sizes, int type, - size_t* step) const { - UMatData* u = new UMatData(this); - u->data = u->origdata = (uchar*) PyArray_DATA((PyArrayObject*) o); - npy_intp* _strides = PyArray_STRIDES((PyArrayObject*) o); - for (int i = 0; i < dims - 1; i++) - step[i] = (size_t) _strides[i]; - step[dims - 1] = CV_ELEM_SIZE(type); - u->size = sizes[0] * step[0]; - u->userdata = o; - return u; - } - - UMatData* allocate(int dims0, const int* sizes, int type, void* data, - size_t* step, int flags, UMatUsageFlags usageFlags) const { - if (data != 0) { - CV_Error(Error::StsAssert, "The data should normally be NULL!"); - // probably this is safe to do in such extreme case - return stdAllocator->allocate(dims0, sizes, type, data, step, flags, - usageFlags); - } - PyEnsureGIL gil; - - int depth = CV_MAT_DEPTH(type); - int cn = CV_MAT_CN(type); - const int f = (int) (sizeof(size_t) / 8); - int typenum = - depth == CV_8U ? NPY_UBYTE : - depth == CV_8S ? NPY_BYTE : - depth == CV_16U ? NPY_USHORT : - depth == CV_16S ? NPY_SHORT : - depth == CV_32S ? NPY_INT : - depth == CV_32F ? NPY_FLOAT : - depth == CV_64F ? - NPY_DOUBLE : f * NPY_ULONGLONG + (f ^ 1) * NPY_UINT; - int i, dims = dims0; - cv::AutoBuffer _sizes(dims + 1); - for (i = 0; i < dims; i++) - _sizes[i] = sizes[i]; - if (cn > 1) - _sizes[dims++] = cn; - PyObject* o = PyArray_SimpleNew(dims, _sizes, typenum); - if (!o) - CV_Error_(Error::StsError, - ("The numpy array of typenum=%d, ndims=%d can not be created", typenum, dims)); - return allocate(o, dims0, sizes, type, step); - } - - bool allocate(UMatData* u, int accessFlags, - UMatUsageFlags usageFlags) const { - return stdAllocator->allocate(u, accessFlags, usageFlags); - } - - void deallocate(UMatData* u) const { - if (u) { - PyEnsureGIL gil; - PyObject* o = (PyObject*) u->userdata; - Py_XDECREF(o); - delete u; - } - } - - const MatAllocator* stdAllocator; -}; - -NumpyAllocator g_numpyAllocator; - -PyObject* fromMatToNDArray(const Mat& m) { - if (!m.data) - Py_RETURN_NONE; - Mat temp, - *p = (Mat*) &m; - if (!p->u || p->allocator != &g_numpyAllocator) { - temp.allocator = &g_numpyAllocator; - ERRWRAP2(m.copyTo(temp)); - p = &temp; - } - PyObject* o = (PyObject*) p->u->userdata; - Py_INCREF(o); - return o; -} - -static int failmsg(const char *fmt, ...) { - char str[1000]; - - va_list ap; - va_start(ap, fmt); - vsnprintf(str, sizeof(str), fmt, ap); - va_end(ap); - - PyErr_SetString(PyExc_TypeError, str); - return 0; -} - -Mat fromNDArrayToMat(PyObject* o) { - cv::Mat m; - bool allowND = true; - if (!PyArray_Check(o)) { - failmsg("argument is not a numpy array"); - if (!m.data) - m.allocator = &g_numpyAllocator; - } else { - PyArrayObject* oarr = (PyArrayObject*) o; - - bool needcopy = false, needcast = false; - int typenum = PyArray_TYPE(oarr), new_typenum = typenum; - int type = typenum == NPY_UBYTE ? CV_8U : typenum == NPY_BYTE ? CV_8S : - typenum == NPY_USHORT ? CV_16U : - typenum == NPY_SHORT ? CV_16S : - typenum == NPY_INT ? CV_32S : - typenum == NPY_INT32 ? CV_32S : - typenum == NPY_FLOAT ? CV_32F : - typenum == NPY_DOUBLE ? CV_64F : -1; - - if (type < 0) { - if (typenum == NPY_INT64 || typenum == NPY_UINT64 - || type == NPY_LONG) { - needcopy = needcast = true; - new_typenum = NPY_INT; - type = CV_32S; - } else { - failmsg("Argument data type is not supported"); - m.allocator = &g_numpyAllocator; - return m; - } - } - -#ifndef CV_MAX_DIM - const int CV_MAX_DIM = 32; -#endif - - int ndims = PyArray_NDIM(oarr); - if (ndims >= CV_MAX_DIM) { - failmsg("Dimensionality of argument is too high"); - if (!m.data) - m.allocator = &g_numpyAllocator; - return m; - } - - int size[CV_MAX_DIM + 1]; - size_t step[CV_MAX_DIM + 1]; - size_t elemsize = CV_ELEM_SIZE1(type); - const npy_intp* _sizes = PyArray_DIMS(oarr); - const npy_intp* _strides = PyArray_STRIDES(oarr); - bool ismultichannel = ndims == 3 && _sizes[2] <= CV_CN_MAX; - - for (int i = ndims - 1; i >= 0 && !needcopy; i--) { - // these checks handle cases of - // a) multi-dimensional (ndims > 2) arrays, as well as simpler 1- and 2-dimensional cases - // b) transposed arrays, where _strides[] elements go in non-descending order - // c) flipped arrays, where some of _strides[] elements are negative - if ((i == ndims - 1 && (size_t) _strides[i] != elemsize) - || (i < ndims - 1 && _strides[i] < _strides[i + 1])) - needcopy = true; - } - - if (ismultichannel && _strides[1] != (npy_intp) elemsize * _sizes[2]) - needcopy = true; - - if (needcopy) { - - if (needcast) { - o = PyArray_Cast(oarr, new_typenum); - oarr = (PyArrayObject*) o; - } else { - oarr = PyArray_GETCONTIGUOUS(oarr); - o = (PyObject*) oarr; - } - - _strides = PyArray_STRIDES(oarr); - } - - for (int i = 0; i < ndims; i++) { - size[i] = (int) _sizes[i]; - step[i] = (size_t) _strides[i]; - } - - // handle degenerate case - if (ndims == 0) { - size[ndims] = 1; - step[ndims] = elemsize; - ndims++; - } - - if (ismultichannel) { - ndims--; - type |= CV_MAKETYPE(0, size[2]); - } - - if (ndims > 2 && !allowND) { - failmsg("%s has more than 2 dimensions"); - } else { - - m = Mat(ndims, size, type, PyArray_DATA(oarr), step); - m.u = g_numpyAllocator.allocate(o, ndims, size, type, step); - m.addref(); - - if (!needcopy) { - Py_INCREF(o); - } - } - m.allocator = &g_numpyAllocator; - } - return m; -} - -struct matToNDArrayBoostConverter { - static PyObject* convert(Mat const& m) { - if (!m.data) - Py_RETURN_NONE; - Mat *p = (Mat*) &m; - Mat temp; - if (!p->u || p->allocator != &g_numpyAllocator) { - temp.allocator = &g_numpyAllocator; - ERRWRAP2(m.copyTo(temp)); - p = &temp; - } - PyObject* o = (PyObject*) p->u->userdata; - return boost::python::incref(o); - } -}; - -struct matFromNDArrayBoostConverter { - - matFromNDArrayBoostConverter() { - boost::python::converter::registry::push_back(convertible, construct, - boost::python::type_id()); - } - - /// @brief Check if PyObject is an array and can be converted to OpenCV matrix. - static void* convertible(PyObject* object) { - if (!PyArray_Check(object)) { - return NULL; - } -#ifndef CV_MAX_DIM - const int CV_MAX_DIM = 32; -#endif - PyArrayObject* oarr = (PyArrayObject*) object; - - int typenum = PyArray_TYPE(oarr); - if (typenum != NPY_INT64 && typenum != NPY_UINT64 && typenum != NPY_LONG - && typenum != NPY_UBYTE && typenum != NPY_BYTE - && typenum != NPY_USHORT && typenum != NPY_SHORT - && typenum != NPY_INT && typenum != NPY_INT32 - && typenum != NPY_FLOAT && typenum != NPY_DOUBLE) { - return NULL; - } - int ndims = PyArray_NDIM(oarr); //data type not supported - - if (ndims >= CV_MAX_DIM) { - return NULL; //too many dimensions - } - return object; - } - - /// @brief Construct a Mat from an NDArray object. - static void construct(PyObject* object, - boost::python::converter::rvalue_from_python_stage1_data* data) { - namespace python = boost::python; - // Object is a borrowed reference, so create a handle indicting it is - // borrowed for proper reference counting. - python::handle<> handle(python::borrowed(object)); - - // Obtain a handle to the memory block that the converter has allocated - // for the C++ type. - typedef python::converter::rvalue_from_python_storage storage_type; - void* storage = reinterpret_cast(data)->storage.bytes; - - // Allocate the C++ type into the converter's memory block, and assign - // its handle to the converter's convertible variable. The C++ - // container is populated by passing the begin and end iterators of - // the python object to the container's constructor. - PyArrayObject* oarr = (PyArrayObject*) object; - - bool needcopy = false, needcast = false; - int typenum = PyArray_TYPE(oarr), new_typenum = typenum; - int type = typenum == NPY_UBYTE ? CV_8U : typenum == NPY_BYTE ? CV_8S : - typenum == NPY_USHORT ? CV_16U : - typenum == NPY_SHORT ? CV_16S : - typenum == NPY_INT ? CV_32S : - typenum == NPY_INT32 ? CV_32S : - typenum == NPY_FLOAT ? CV_32F : - typenum == NPY_DOUBLE ? CV_64F : -1; - - if (type < 0) { - needcopy = needcast = true; - new_typenum = NPY_INT; - type = CV_32S; - } - -#ifndef CV_MAX_DIM - const int CV_MAX_DIM = 32; -#endif - int ndims = PyArray_NDIM(oarr); - - int size[CV_MAX_DIM + 1]; - size_t step[CV_MAX_DIM + 1]; - size_t elemsize = CV_ELEM_SIZE1(type); - const npy_intp* _sizes = PyArray_DIMS(oarr); - const npy_intp* _strides = PyArray_STRIDES(oarr); - bool ismultichannel = ndims == 3 && _sizes[2] <= CV_CN_MAX; - - for (int i = ndims - 1; i >= 0 && !needcopy; i--) { - // these checks handle cases of - // a) multi-dimensional (ndims > 2) arrays, as well as simpler 1- and 2-dimensional cases - // b) transposed arrays, where _strides[] elements go in non-descending order - // c) flipped arrays, where some of _strides[] elements are negative - if ((i == ndims - 1 && (size_t) _strides[i] != elemsize) - || (i < ndims - 1 && _strides[i] < _strides[i + 1])) - needcopy = true; - } - - if (ismultichannel && _strides[1] != (npy_intp) elemsize * _sizes[2]) - needcopy = true; - - if (needcopy) { - - if (needcast) { - object = PyArray_Cast(oarr, new_typenum); - oarr = (PyArrayObject*) object; - } else { - oarr = PyArray_GETCONTIGUOUS(oarr); - object = (PyObject*) oarr; - } - - _strides = PyArray_STRIDES(oarr); - } - - for (int i = 0; i < ndims; i++) { - size[i] = (int) _sizes[i]; - step[i] = (size_t) _strides[i]; - } - - // handle degenerate case - if (ndims == 0) { - size[ndims] = 1; - step[ndims] = elemsize; - ndims++; - } - - if (ismultichannel) { - ndims--; - type |= CV_MAKETYPE(0, size[2]); - } - if (!needcopy) { - Py_INCREF(object); - } - cv::Mat* m = new (storage) cv::Mat(ndims, size, type, PyArray_DATA(oarr), step); - m->u = g_numpyAllocator.allocate(object, ndims, size, type, step); - m->allocator = &g_numpyAllocator; - m->addref(); - data->convertible = storage; - } -}; -} // end namespace bcvt -#endif /* CVBOOSTCONVERTER_HPP_ */ diff --git a/felz/src/convolve.h b/felz/src/convolve.h index 5bb85ae..3026ad6 100755 --- a/felz/src/convolve.h +++ b/felz/src/convolve.h @@ -24,10 +24,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include -// #include "image.h" +#include "image.h" /* convolve src with mask. dst is flipped! */ -/*static void convolve_even(image *src, image *dst, +static void convolve_even(image *src, image *dst, std::vector &mask) { int width = src->width(); int height = src->height(); @@ -44,22 +44,24 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA imRef(dst, y, x) = sum; } } -}*/ +} /* convolve src with mask. dst is flipped! */ -static void convolve_even(const cv::Mat & src, cv::Mat & dst, - std::vector &mask) { - int width = src.cols; - int height = src.rows; +static void convolve_odd(image *src, image *dst, + std::vector &mask) { + int width = src->width(); + int height = src->height(); int len = mask.size(); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - float sum = mask[0] * src.at(y, x); + float sum = mask[0] * imRef(src, x, y); for (int i = 1; i < len; i++) { - sum += mask[i] * (src.at(y, std::max(x-i,0)) + src.at(y, std::min(x+i, width-1))); + sum += mask[i] * + (imRef(src, std::max(x-i,0), y) - + imRef(src, std::min(x+i, width-1), y)); } - dst.at(y, x) = sum; + imRef(dst, y, x) = sum; } } } diff --git a/felz/src/filter.h b/felz/src/filter.h old mode 100644 new mode 100755 index cfdebab..2ce3d3b --- a/felz/src/filter.h +++ b/felz/src/filter.h @@ -23,10 +23,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #include -// #include "image.h" +#include "image.h" #include "misc.h" #include "convolve.h" -// #include "imconv.h" +#include "imconv.h" #define WIDTH 4.0 @@ -58,7 +58,7 @@ static std::vector make_ ## name (float sigma) { \ MAKE_FILTER(fgauss, exp(-0.5*square(i/sigma))); /* convolve image with gaussian filter */ -/*static image *smooth(image *src, float sigma) { +static image *smooth(image *src, float sigma) { std::vector mask = make_fgauss(sigma); normalize(mask); @@ -68,35 +68,19 @@ MAKE_FILTER(fgauss, exp(-0.5*square(i/sigma))); convolve_even(tmp, dst, mask); delete tmp; - return dst; -}*/ - -/* convolve image with gaussian filter */ -static cv::Mat smooth(const cv::Mat & src, float sigma) { - std::vector mask = make_fgauss(sigma); - normalize(mask); - - cv::Mat tmp; - src.convertTo(tmp, CV_32FC1); - - cv::Mat dst(src.size(), CV_32FC1); - cv::Mat tmp2(src.size(), CV_32FC1); - convolve_even(tmp, tmp2, mask); - convolve_even(tmp2, dst, mask); - return dst; } /* convolve image with gaussian filter */ -/*cv::Mat smooth(const cv::Mat & src, float sigma) { +image *smooth(image *src, float sigma) { image *tmp = imageUCHARtoFLOAT(src); image *dst = smooth(tmp, sigma); delete tmp; return dst; -}*/ +} /* compute laplacian */ -/*static image *laplacian(image *src) { +static image *laplacian(image *src) { int width = src->width(); int height = src->height(); image *dst = new image(width, height); @@ -111,6 +95,6 @@ static cv::Mat smooth(const cv::Mat & src, float sigma) { } } return dst; -}*/ +} #endif diff --git a/felz/src/segment-graph.h b/felz/src/segment-graph.h index da3cec0..0768552 100644 --- a/felz/src/segment-graph.h +++ b/felz/src/segment-graph.h @@ -1,31 +1,38 @@ +/* +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 "edge.h" -#include "cluster.h" +#include +#include +#include "disjoint-set.h" // threshold function #define THRESHOLD(size, c) (c/size) -int find(std::vector & clusters, int x) { - int y = x; - while (y != clusters[y].id()) { - y = clusters[y].id(); - } - clusters[x].setId(y); - return y; -} +typedef struct { + float w; + int a, b; +} edge; -void join(std::vector & clusters, int x, int y) { - if (clusters[x].rank() > clusters[y].rank()) { - clusters[y].setId(x); - clusters[x].setSize(clusters[x].size() + clusters[y].size()); - } else { - clusters[x].setId(y); - clusters[y].setSize(clusters[y].size() + clusters[x].size()); - if (clusters[x].rank() == clusters[y].rank()) - clusters[y].setRank(clusters[y].rank() + 1); - } +bool operator<(const edge &a, const edge &b) { + return a.w < b.w; } /* @@ -38,38 +45,39 @@ void join(std::vector & clusters, int x, int y) { * edges: array of edges. * c: constant for treshold function. */ -std::vector segment_graph(int num_vertices, int num_edges, std::vector & edges, float c) { - // sort edges by weight - std::sort(edges.begin(), edges.begin() + num_edges); +universe *segment_graph(int num_vertices, int num_edges, edge *edges, + float c) { + // sort edges by weight + std::sort(edges, edges + num_edges); - // make a disjoint-set forest - std::vector clusters; - clusters.resize(num_vertices); + // make a disjoint-set forest + universe *u = new universe(num_vertices); - // init thresholds - for (int i = 0; i < num_vertices; i++) { - clusters[i].setId(i); - clusters[i].setThreshold(THRESHOLD(1,c)); - } + // init thresholds + float *threshold = new float[num_vertices]; + for (int i = 0; i < num_vertices; i++) + threshold[i] = THRESHOLD(1,c); - // for each edge, in non-decreasing weight order... - for (int i = 0; i < num_edges; i++) { - Edge & edge = edges[i]; + // 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 = find(clusters, edge.first()); - int b = find(clusters, edge.second()); - if (a != b) { - if ((edge.weight() <= clusters[a].threshold()) && - (edge.weight() <= clusters[b].threshold())) { - join(clusters, a, b); - a = find(clusters, a); - clusters[a].setThreshold(edge.weight() + THRESHOLD(clusters[a].size(), c)); - } - } - } + // 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])) { + u->join(a, b); + a = u->find(a); + threshold[a] = pedge->w + THRESHOLD(u->size(a), c); + } + } + } - return clusters; + // free up + delete threshold; + return u; } #endif diff --git a/felz/src/segment-image.h b/felz/src/segment-image.h index 2a17e1e..0aa2433 100644 --- a/felz/src/segment-image.h +++ b/felz/src/segment-image.h @@ -1,31 +1,49 @@ +/* +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_IMAGE #define SEGMENT_IMAGE -#include "segment-graph.h" +#include #include +#include "image.h" +#include "misc.h" #include "filter.h" - -#define square(x) ((x)*(x)) -//#define COLORED_OUTPUT +#include "segment-graph.h" // random color -cv::Vec3b random_rgb(){ - cv::Vec3b c; - double r; - c[0] = (uchar)random(); - c[1] = (uchar)random(); - c[2] = (uchar)random(); - return c; +rgb random_rgb(){ + rgb c; + double r; + + c.r = (uchar)random(); + c.g = (uchar)random(); + c.b = (uchar)random(); + + return c; } -/// Difference between two pixels p1, p2 in image -static inline float diff(const cv::Mat & image, int p1, int p2) { - if (image.channels() == 3) { - cv::Vec3f a = image.at(p1); - cv::Vec3f b = image.at(p2); - return sqrt(square(a[0] - b[0]) + square(a[1] - b[1]) + square(a[2] - b[2])); - } else - return fabs(image.at(p1) - image.at(p2)); +// 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))); } /* @@ -37,101 +55,105 @@ static inline float diff(const cv::Mat & image, int p1, int p2) { * 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. */ -cv::Mat segment_image(cv::Mat image, float sigma, float c, int min_size) { - image.convertTo(image, CV_MAKETYPE(CV_32F, image.channels())); -// cv::GaussianBlur(image, image, cv::Size(5, 5), sigma); - std::vector channels; - cv::split(image, channels); - channels[0] = smooth(channels[0], sigma); - channels[1] = smooth(channels[1], sigma); - channels[2] = smooth(channels[2], sigma); - cv::merge(channels, image); - - // build graph - std::vector edges; - edges.resize(image.total() * 4); - int index = 0; - for (int i = 0; i < image.rows; i++) { - for (int j = 0; j < image.cols; j++) { - int v1 = i * image.cols + j; - - if (j < image.cols - 1) { - int v2 = i * image.cols + (j+1); - Edge & e = edges[index++]; - e.setFirst(v1); - e.setSecond(v2); - e.setWeight(diff(image, v1, v2)); - } - if (i < image.rows - 1) { - int v2 = (i+1) * image.cols + j; - Edge & e = edges[index++]; - e.setFirst(v1); - e.setSecond(v2); - e.setWeight(diff(image, v1, v2)); - } - if (i < image.rows - 1 && j < image.cols - 1) { - int v2 = (i+1) * image.cols + (j+1); - Edge & e = edges[index++]; - e.setFirst(v1); - e.setSecond(v2); - e.setWeight(diff(image, v1, v2)); - } - if (i > 0 && j < image.cols - 1) { - int v2 = (i-1) * image.cols + (j+1); - Edge & e = edges[index++]; - e.setFirst(v1); - e.setSecond(v2); - e.setWeight(diff(image, v1, v2)); - } - } - } - - // segment - std::vector clusters = segment_graph(image.total(), index, edges, c); - - // post process small components - if (min_size) { - for (int i = 0; i < index; i++) { - int a = find(clusters, edges[i].first()); - int b = find(clusters, edges[i].second()); - if ((a != b) && ((clusters[a].size() < min_size) || (clusters[b].size() < min_size))) { - join(clusters, a, b); - } - } - } - -#ifdef COLORED_OUTPUT - cv::Mat output(image.size(), CV_8UC3); - - // pick random colors for each component - std::vector colors; - colors.resize(image.total()); - for (int i = 0; i < image.total(); i++) - colors[i] = random_rgb(); +image *segment_image(image *im, float sigma, float c, int min_size, + int *num_ccs) { + int width = im->width(); + int height = im->height(); + + image *r = new image(width, height); + image *g = new image(width, height); + image *b = new image(width, height); + + // smooth each color channel + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + imRef(r, x, y) = imRef(im, x, y).r; + imRef(g, x, y) = imRef(im, x, y).g; + imRef(b, x, y) = imRef(im, x, y).b; + } + } + image *smooth_r = smooth(r, sigma); + image *smooth_g = smooth(g, sigma); + image *smooth_b = smooth(b, sigma); + delete r; + delete g; + delete b; + + // 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) { + edges[num].a = y * width + x; + edges[num].b = y * width + (x+1); + edges[num].w = diff(smooth_r, smooth_g, smooth_b, x, y, x+1, y); + num++; + } + + if (y < height-1) { + 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); + num++; + } + + if ((x < width-1) && (y < height-1)) { + 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); + num++; + } + + if ((x < width-1) && (y > 0)) { + 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); + num++; + } + } + } + delete smooth_r; + delete smooth_g; + delete smooth_b; + + // segment + universe *u = segment_graph(width*height, num, edges, c); - for (int y = 0; y < image.rows; y++) { - for (int x = 0; x < image.cols; x++) { - int comp = find(clusters, y * image.cols + x); - output.at(y, x) = colors[comp]; - } - } -#else - cv::Mat output(image.size(), CV_16UC1); - std::map cluster_ids; - uint16_t max_id = 1; - - for (int y = 0; y < image.rows; y++) { - for (int x = 0; x < image.cols; x++) { - int comp = find(clusters, y * image.cols + x); - if (cluster_ids.find(comp) == cluster_ids.end()) - cluster_ids[comp] = max_id++; - output.at(y, x) = cluster_ids[comp]; - } - } -#endif + // 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); + } + delete [] edges; + *num_ccs = u->num_sets(); + + image *output = new image(width, height); + std::map cluster_ids; + uint16_t max_id = 1; + + // pick random colors for each component +// rgb *colors = new rgb[width*height]; +// for (int i = 0; i < width*height; i++) +// colors[i] = random_rgb(); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int comp = u->find(y * width + x); + if (cluster_ids.find(comp) == cluster_ids.end()) + cluster_ids[comp] = max_id++; + imRef(output, x, y) = cluster_ids[comp]; + } + } + +// delete [] colors; + delete u; - return output; + return output; } #endif diff --git a/felz/src/segment.cpp b/felz/src/segment.cpp index 80663e7..c4af88a 100644 --- a/felz/src/segment.cpp +++ b/felz/src/segment.cpp @@ -1,18 +1,62 @@ -#include -#include -#include -#include +/* +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 +#include "image.h" +#include "misc.h" +#include "pnmfile.h" #include "segment-image.h" -#include -//#include "CVBoostConverter.hpp" #include "conversion.h" #include using namespace boost::python; +cv::Mat segment_image(cv::Mat & im, float sigma, int k, int min_size) { + int w = im.cols; + int h = im.rows; + image input(w, h); + + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + cv::Vec3b p = im.at(i, j); + input.data[i*w + j].b = p[0]; + input.data[i*w + j].g = p[1]; + input.data[i*w + j].r = p[2]; + } + } + + int num_ccs; + image * seg = segment_image(&input, sigma, k, min_size, &num_ccs); + + cv::Mat output(h, w, CV_16UC1); + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + output.at(i, j) = seg->data[i*w + j]; + } + } + delete seg; + + return output; +} + PyObject * segment(PyObject * image_, float sigma, int k, int min_size) { NDArrayConverter cvt; - int ndims = PyArray_NDIM(image_); cv::Mat image = cvt.toMat(image_); return cvt.toNDArray(segment_image(image, sigma, k, min_size)); } @@ -25,38 +69,6 @@ static void init_ar() { BOOST_PYTHON_MODULE(segment_felz) { init_ar(); - //initialize converters - /*to_python_converter(); - bcvt::matFromNDArrayBoostConverter();*/ - def("segment", segment); } -int main(int argc, char **argv) { - if (argc != 6) { - std::cerr << "usage: " << argv[0] << " sigma k min image output" << std::endl; - return 1; - } - - float sigma = atof(argv[1]); - int k = atoi(argv[2]); - int min_size = atoi(argv[3]); - - cv::Mat image = cv::imread(argv[4], cv::IMREAD_GRAYSCALE); - //cv::resize(image, image, cv::Size(image.cols * 0.4, image.rows * 0.4)); - //int num_ccs; - - std::cout << "Processing..." << std::endl; - - std::clock_t begin = std::clock(); - cv::Mat seg = segment_image(image, sigma, k, min_size); - std::clock_t end = std::clock(); - double elapsed_secs = double(end - begin) / CLOCKS_PER_SEC; - cv::imwrite(argv[5], seg); - std::cout << "Time passed in seconds: " << elapsed_secs << std::endl; - - //std::cout << "Got " << num_ccs << " components." << std::endl; - return 0; -} - diff --git a/random/CMakeLists.txt b/random/CMakeLists.txt index 1e7716e..da8e216 100644 --- a/random/CMakeLists.txt +++ b/random/CMakeLists.txt @@ -47,6 +47,14 @@ target_link_libraries(ss_selection ${PYTHON_LIBRARIES} ) +add_executable(objectness src/objectness.cpp src/conversion.cpp) +target_link_libraries(objectness + ${Boost_LIBRARIES} + ${OpenCV_LIBRARIES} + ${PYTHON_LIBRARIES} + ${OBJECTNESS_LIBRARIES} +) + #add_library(level_selection MODULE src/levels.cpp src/conversion.cpp) #set_target_properties(level_selection PROPERTIES PREFIX "") #target_link_libraries(level_selection