From 9938e0f6b28d8c236c9316d852d75baabf94782a Mon Sep 17 00:00:00 2001 From: unknown <10r_sha@gml72.lab.graphicon.ru> Date: Wed, 23 Jan 2013 19:31:15 +0400 Subject: [PATCH 1/6] 1) generalized energy type in C++ wrapper 2) added pairwise potential callbacks to the C++ wrapper, so that arbitrary pairwise potential suitable for expansion/swap can be specified --- example.py | 13 +++- gco_python.pyx | 195 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 185 insertions(+), 23 deletions(-) diff --git a/example.py b/example.py index b03b477..084e4ae 100644 --- a/example.py +++ b/example.py @@ -1,6 +1,6 @@ import matplotlib.pyplot as plt import numpy as np -from pygco import cut_simple, cut_from_graph +from pygco import cut_simple, cut_simple_gen_potts, cut_from_graph, cut_from_graph_gen_potts def example_binary(): @@ -16,9 +16,15 @@ def example_binary(): unaries = (10 * np.dstack([unaries, -unaries]).copy("C")).astype(np.int32) # create potts pairwise pairwise = -10 * np.eye(2, dtype=np.int32) - + # do simple cut result = cut_simple(unaries, pairwise) + + # generalized Potts potentials + pix_nums = np.r_[:10*10].reshape(10,10) + pairwise_cost = dict([(tuple(sorted(pair)), 30) for pair in zip(pix_nums[:,:-1].flatten(), pix_nums[:,1:].flatten())] + + [(tuple(sorted(pair)),0) for pair in zip(pix_nums[:-1,:].flatten(), pix_nums[1:,:].flatten())]) + result_gp = cut_simple_gen_potts(unaries, pairwise_cost) # use the gerneral graph algorithm # first, we construct the grid graph @@ -29,6 +35,9 @@ def example_binary(): # we flatten the unaries result_graph = cut_from_graph(edges, unaries.reshape(-1, 2), pairwise) + + # generalized Potts potentials + result_graph_gp = cut_from_graph_gen_potts(unaries.reshape(-1, 2), pairwise_cost) # plot results plt.subplot(231, title="original") diff --git a/gco_python.pyx b/gco_python.pyx index 8f34670..800b1ec 100644 --- a/gco_python.pyx +++ b/gco_python.pyx @@ -1,40 +1,90 @@ +# cython: experimental_cpp_class_def=True + import numpy as np cimport numpy as np +from libcpp.map cimport map +from libcpp.pair cimport pair + +DEF DEBUG_CHECKS = True # true if laborious parameter checks are needed + +# This is the energy type; should match the EnergyType and EnergyTermType in GCOptimization.h +DEF NRG_TYPE_STR = int + +IF NRG_TYPE_STR == int: + ctypedef np.int32_t NRG_DTYPE_t + ctypedef int NRG_TYPE + ctypedef map[pair[int,int],int] PW_MAP_T # map (s1, s2) -> strength +ELSE: + ctypedef np.float64_t NRG_DTYPE_t + ctypedef double NRG_TYPE + ctypedef map[pair[int,int],double] PW_MAP_T # map (s1, s2) -> strength + + np.import_array() cdef extern from "GCoptimization.h": cdef cppclass GCoptimizationGridGraph: + cppclass SmoothCostFunctor: + NRG_TYPE compute(int s1, int s2, int l1, int l2) + GCoptimizationGridGraph(int width, int height, int n_labels) - void setDataCost(int *) - void setSmoothCost(int *) + void setDataCost(NRG_TYPE *) + void setSmoothCost(NRG_TYPE *) void expansion(int n_iterations) void swap(int n_iterations) - void setSmoothCostVH(int* pairwise, int* V, int* H) + void setSmoothCostVH(NRG_TYPE* pairwise, NRG_TYPE* V, NRG_TYPE* H) + void setSmoothCostFunctor(SmoothCostFunctor* f) int whatLabel(int node) + + cdef cppclass GCoptimizationGeneralGraph: GCoptimizationGeneralGraph(int n_vertices, int n_labels) - void setDataCost(int *) - void setSmoothCost(int *) + void setDataCost(NRG_TYPE *) + void setSmoothCost(NRG_TYPE *) void setNeighbors(int, int) void setNeighbors(int, int, int) void expansion(int n_iterations) void swap(int n_iterations) + void setSmoothCostFunctor(GCoptimizationGridGraph.SmoothCostFunctor* f) # yep, it works int whatLabel(int node) + + +cdef cppclass PottsFunctor(GCoptimizationGridGraph.SmoothCostFunctor): + NRG_TYPE strength_ + + __init__(NRG_TYPE strength): + this.strength_ = strength + + NRG_TYPE compute(int s1, int s2, int l1, int l2): + return -this.strength_ if l1 == l2 else 0 + +cdef cppclass GeneralizedPottsFunctor(GCoptimizationGridGraph.SmoothCostFunctor): + PW_MAP_T data_ + + __init__(object data): + this.data_ = data + + NRG_TYPE compute(int s1, int s2, int l1, int l2): + if l1 != l2: + return 0 + else: + pair = tuple(sorted([s1,s2])) + return -this.data_[pair] -def cut_simple(np.ndarray[np.int32_t, ndim=3, mode='c'] unary_cost, - np.ndarray[np.int32_t, ndim=2, mode='c'] pairwise_cost, n_iter=5, +def cut_simple(np.ndarray[NRG_DTYPE_t, ndim=3, mode='c'] unary_cost, + np.ndarray[NRG_DTYPE_t, ndim=2, mode='c'] pairwise_cost, n_iter=5, algorithm='expansion'): """ Apply multi-label graphcuts to grid graph. Parameters ---------- - unary_cost: ndarray, int32, shape=(width, height, n_labels) + unary_cost: ndarray, double, shape=(width, height, n_labels) Unary potentials - pairwise_cost: ndarray, int32, shape=(n_labels, n_labels) + pairwise_cost: ndarray, double, shape=(n_labels, n_labels) Pairwise potentials for label compatibility n_iter: int, (default=5) Number of iterations @@ -57,8 +107,61 @@ def cut_simple(np.ndarray[np.int32_t, ndim=3, mode='c'] unary_cost, raise ValueError("pairwise_cost must be symmetric.") cdef GCoptimizationGridGraph* gc = new GCoptimizationGridGraph(h, w, n_labels) - gc.setDataCost(unary_cost.data) - gc.setSmoothCost(pairwise_cost.data) + gc.setDataCost(unary_cost.data) + gc.setSmoothCost(pairwise_cost.data) + if algorithm == 'swap': + gc.swap(n_iter) + elif algorithm == 'expansion': + gc.expansion(n_iter) + else: + raise ValueError("algorithm should be either `swap` or `expansion`. Got: %s" % algorithm) + + cdef np.npy_intp result_shape[2] + result_shape[0] = w + result_shape[1] = h + cdef np.ndarray[np.int32_t, ndim=2] result = np.PyArray_SimpleNew(2, result_shape, np.NPY_INT32) + cdef int * result_ptr = result.data + for i in xrange(w * h): + result_ptr[i] = gc.whatLabel(i) + return result + + +def cut_simple_gen_potts(np.ndarray[NRG_DTYPE_t, ndim=3, mode='c'] unary_cost, + object pairwise_cost, n_iter=5, + algorithm='expansion'): + """ + Apply multi-label graphcuts to grid graph. + + Parameters + ---------- + unary_cost: ndarray, double, shape=(width, height, n_labels) + Unary potentials + pairwise_cost: dict: (site1, site2) -> strength, where site1 < site2. + Pixels are ordered by rows of the grid first + n_iter: int, (default=5) + Number of iterations + algorithm: string, `expansion` or `swap`, default=expansion + Whether to perform alpha-expansion or alpha-beta-swaps. + """ + + cdef int h = unary_cost.shape[1] + cdef int w = unary_cost.shape[0] + cdef int n_labels = unary_cost.shape[2] + + IF DEBUG_CHECKS: + cdef np.ndarray[np.int32_t, ndim=2] pix_nums = np.r_[:h*w].reshape(h,w) + edges = [tuple(sorted(pair)) for pair in zip(pix_nums[:,:-1].flatten(), pix_nums[:,1:].flatten())] + \ + [tuple(sorted(pair)) for pair in zip(pix_nums[:-1,:].flatten(), pix_nums[1:,:].flatten())] + for edge in edges: + if edge not in pairwise_cost: + raise ValueError("Pairwise potential for the edge (%d,%d) is not given" % edge) + if pairwise_cost[edge] < 0: + raise ValueError("Pairwise potential for the edge (%d,%d) is negative, " + "which is not allowed in generalized Potts" % edge) + + cdef GCoptimizationGridGraph* gc = new GCoptimizationGridGraph(h, w, n_labels) + gc.setDataCost(unary_cost.data) + gc.setSmoothCostFunctor(new GeneralizedPottsFunctor(pairwise_cost)) if algorithm == 'swap': gc.swap(n_iter) elif algorithm == 'expansion': @@ -74,11 +177,12 @@ def cut_simple(np.ndarray[np.int32_t, ndim=3, mode='c'] unary_cost, for i in xrange(w * h): result_ptr[i] = gc.whatLabel(i) return result + -def cut_simple_vh(np.ndarray[np.int32_t, ndim=3, mode='c'] unary_cost, - np.ndarray[np.int32_t, ndim=2, mode='c'] pairwise_cost, - np.ndarray[np.int32_t, ndim=2, mode='c'] costV, - np.ndarray[np.int32_t, ndim=2, mode='c'] costH, +def cut_simple_vh(np.ndarray[NRG_DTYPE_t, ndim=3, mode='c'] unary_cost, + np.ndarray[NRG_DTYPE_t, ndim=2, mode='c'] pairwise_cost, + np.ndarray[NRG_DTYPE_t, ndim=2, mode='c'] costV, + np.ndarray[NRG_DTYPE_t, ndim=2, mode='c'] costH, n_iter=5, algorithm='expansion'): """ @@ -117,8 +221,8 @@ def cut_simple_vh(np.ndarray[np.int32_t, ndim=3, mode='c'] unary_cost, raise ValueError("incorrect costV or costH dimensions.") cdef GCoptimizationGridGraph* gc = new GCoptimizationGridGraph(h, w, n_labels) - gc.setDataCost(unary_cost.data) - gc.setSmoothCostVH(pairwise_cost.data, costV.data, costH.data) + gc.setDataCost(unary_cost.data) + gc.setSmoothCostVH(pairwise_cost.data, costV.data, costH.data) if algorithm == 'swap': gc.swap(n_iter) elif algorithm == 'expansion': @@ -137,8 +241,8 @@ def cut_simple_vh(np.ndarray[np.int32_t, ndim=3, mode='c'] unary_cost, def cut_from_graph(np.ndarray[np.int32_t, ndim=2, mode='c'] edges, - np.ndarray[np.int32_t, ndim=2, mode='c'] unary_cost, - np.ndarray[np.int32_t, ndim=2, mode='c'] pairwise_cost, n_iter=5, + np.ndarray[NRG_DTYPE_t, ndim=2, mode='c'] unary_cost, + np.ndarray[NRG_DTYPE_t, ndim=2, mode='c'] pairwise_cost, n_iter=5, algorithm='expansion'): """ Apply multi-label graphcuts to arbitrary graph given by `edges`. @@ -177,8 +281,57 @@ def cut_from_graph(np.ndarray[np.int32_t, ndim=2, mode='c'] edges, gc.setNeighbors(e[0], e[1], e[2]) else: gc.setNeighbors(e[0], e[1]) - gc.setDataCost(unary_cost.data) - gc.setSmoothCost(pairwise_cost.data) + gc.setDataCost(unary_cost.data) + gc.setSmoothCost(pairwise_cost.data) + if algorithm == 'swap': + gc.swap(n_iter) + elif algorithm == 'expansion': + gc.expansion(n_iter) + else: + raise ValueError("algorithm should be either `swap` or `expansion`. Got: %s" % algorithm) + + cdef np.npy_intp result_shape[1] + result_shape[0] = n_vertices + cdef np.ndarray[np.int32_t, ndim=1] result = np.PyArray_SimpleNew(1, result_shape, np.NPY_INT32) + cdef int * result_ptr = result.data + for i in xrange(n_vertices): + result_ptr[i] = gc.whatLabel(i) + return result + + +def cut_from_graph_gen_potts( + np.ndarray[NRG_DTYPE_t, ndim=2, mode='c'] unary_cost, + object pairwise_cost, n_iter=5, + algorithm='expansion'): + """ + Apply multi-label graphcuts to arbitrary graph given by `edges`. + + Parameters + ---------- + unary_cost: ndarray, int32, shape=(n_vertices, n_labels) + Unary potentials + pairwise_cost: dict: (site1, site2) -> strength, where site1 < site2. + The order of nodes is the same as in unary_cost + n_iter: int, (default=5) + Number of iterations + algorithm: string, `expansion` or `swap`, default=expansion + Whether to perform alpha-expansion or alpha-beta-swaps. + """ + + cdef int n_vertices = unary_cost.shape[0] + cdef int n_labels = unary_cost.shape[1] + + cdef GCoptimizationGeneralGraph* gc = new GCoptimizationGeneralGraph(n_vertices, n_labels) + for edge, strength in pairwise_cost.items(): + gc.setNeighbors(edge[0], edge[1]) + if edge[0] >= edge[1]: + raise ValueError("The order of sites in the edge (%d,%d) should be ascending" % edge) + if strength < 0: + raise ValueError("Pairwise potential for the edge (%d,%d) is negative, " + "which is not allowed in generalized Potts" % edge) + + gc.setDataCost(unary_cost.data) + gc.setSmoothCostFunctor(new GeneralizedPottsFunctor(pairwise_cost)) if algorithm == 'swap': gc.swap(n_iter) elif algorithm == 'expansion': From e19467a88eff4c97f605090cdce9241f6254265b Mon Sep 17 00:00:00 2001 From: shapovalov Date: Thu, 24 Jan 2013 14:04:40 +0400 Subject: [PATCH 2/6] Just testing authentification --- example.py | 1 - 1 file changed, 1 deletion(-) diff --git a/example.py b/example.py index 084e4ae..c135f6c 100644 --- a/example.py +++ b/example.py @@ -87,6 +87,5 @@ def example_multinomial(): plt.imshow(result_1d, interpolation="nearest") plt.show() - example_binary() example_multinomial() From 39ccd76ff4ea7031b253ad1bb76df7eab433f24e Mon Sep 17 00:00:00 2001 From: shapovalov Date: Thu, 24 Jan 2013 19:45:48 +0400 Subject: [PATCH 3/6] Edge-dependent pairwise potentials now work for double energy type. General bug also fixed. --- gco_python.pyx | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/gco_python.pyx b/gco_python.pyx index 800b1ec..2487eb3 100644 --- a/gco_python.pyx +++ b/gco_python.pyx @@ -44,7 +44,7 @@ cdef extern from "GCoptimization.h": void setDataCost(NRG_TYPE *) void setSmoothCost(NRG_TYPE *) void setNeighbors(int, int) - void setNeighbors(int, int, int) + void setNeighbors(int, int, NRG_TYPE) void expansion(int n_iterations) void swap(int n_iterations) void setSmoothCostFunctor(GCoptimizationGridGraph.SmoothCostFunctor* f) # yep, it works @@ -243,7 +243,7 @@ def cut_simple_vh(np.ndarray[NRG_DTYPE_t, ndim=3, mode='c'] unary_cost, def cut_from_graph(np.ndarray[np.int32_t, ndim=2, mode='c'] edges, np.ndarray[NRG_DTYPE_t, ndim=2, mode='c'] unary_cost, np.ndarray[NRG_DTYPE_t, ndim=2, mode='c'] pairwise_cost, n_iter=5, - algorithm='expansion'): + algorithm='expansion', np.ndarray[NRG_DTYPE_t, ndim=1, mode='c'] weights=None): """ Apply multi-label graphcuts to arbitrary graph given by `edges`. @@ -272,15 +272,26 @@ def cut_from_graph(np.ndarray[np.int32_t, ndim=2, mode='c'] edges, pairwise_cost.shape[0], pairwise_cost.shape[1])) if pairwise_cost.shape[1] != pairwise_cost.shape[0]: raise ValueError("pairwise_cost must be a square matrix.") + if weights is not None and edges.shape[1] == 3: + raise ValueError("weights parameter is ambiguous when edges is a 3-column array.") + if weights is not None and weights.shape[0] != edges.shape[0]: + raise ValueError("weights vector should contain one weight per edge.") + cdef int n_vertices = unary_cost.shape[0] cdef int n_labels = pairwise_cost.shape[0] cdef GCoptimizationGeneralGraph* gc = new GCoptimizationGeneralGraph(n_vertices, n_labels) - for e in edges: - if e.shape[0] == 3: - gc.setNeighbors(e[0], e[1], e[2]) - else: - gc.setNeighbors(e[0], e[1]) + + if weights is None: + for e in edges: + if e.shape[1] == 3: + gc.setNeighbors(e[0], e[1], e[2]) + else: + gc.setNeighbors(e[0], e[1]) + else: + for e,w in zip(edges, weights): + gc.setNeighbors(e[0], e[1], w) + gc.setDataCost(unary_cost.data) gc.setSmoothCost(pairwise_cost.data) if algorithm == 'swap': From e03ba712106e683e575863afdf910b1b14d527c2 Mon Sep 17 00:00:00 2001 From: shapovalov Date: Thu, 31 Jan 2013 17:55:23 +0400 Subject: [PATCH 4/6] Functions return energy value. Memory leak fixed. --- gco_python.pyx | 53 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/gco_python.pyx b/gco_python.pyx index 2487eb3..58df6c1 100644 --- a/gco_python.pyx +++ b/gco_python.pyx @@ -31,8 +31,8 @@ cdef extern from "GCoptimization.h": GCoptimizationGridGraph(int width, int height, int n_labels) void setDataCost(NRG_TYPE *) void setSmoothCost(NRG_TYPE *) - void expansion(int n_iterations) - void swap(int n_iterations) + NRG_TYPE expansion(int n_iterations) + NRG_TYPE swap(int n_iterations) void setSmoothCostVH(NRG_TYPE* pairwise, NRG_TYPE* V, NRG_TYPE* H) void setSmoothCostFunctor(SmoothCostFunctor* f) int whatLabel(int node) @@ -45,8 +45,8 @@ cdef extern from "GCoptimization.h": void setSmoothCost(NRG_TYPE *) void setNeighbors(int, int) void setNeighbors(int, int, NRG_TYPE) - void expansion(int n_iterations) - void swap(int n_iterations) + NRG_TYPE expansion(int n_iterations) + NRG_TYPE swap(int n_iterations) void setSmoothCostFunctor(GCoptimizationGridGraph.SmoothCostFunctor* f) # yep, it works int whatLabel(int node) @@ -109,10 +109,11 @@ def cut_simple(np.ndarray[NRG_DTYPE_t, ndim=3, mode='c'] unary_cost, cdef GCoptimizationGridGraph* gc = new GCoptimizationGridGraph(h, w, n_labels) gc.setDataCost(unary_cost.data) gc.setSmoothCost(pairwise_cost.data) + cdef NRG_TYPE nrg if algorithm == 'swap': - gc.swap(n_iter) + nrg = gc.swap(n_iter) elif algorithm == 'expansion': - gc.expansion(n_iter) + nrg = gc.expansion(n_iter) else: raise ValueError("algorithm should be either `swap` or `expansion`. Got: %s" % algorithm) @@ -123,7 +124,9 @@ def cut_simple(np.ndarray[NRG_DTYPE_t, ndim=3, mode='c'] unary_cost, cdef int * result_ptr = result.data for i in xrange(w * h): result_ptr[i] = gc.whatLabel(i) - return result + + del gc + return result, nrg def cut_simple_gen_potts(np.ndarray[NRG_DTYPE_t, ndim=3, mode='c'] unary_cost, @@ -162,10 +165,11 @@ def cut_simple_gen_potts(np.ndarray[NRG_DTYPE_t, ndim=3, mode='c'] unary_cost, cdef GCoptimizationGridGraph* gc = new GCoptimizationGridGraph(h, w, n_labels) gc.setDataCost(unary_cost.data) gc.setSmoothCostFunctor(new GeneralizedPottsFunctor(pairwise_cost)) + cdef NRG_TYPE nrg if algorithm == 'swap': - gc.swap(n_iter) + nrg = gc.swap(n_iter) elif algorithm == 'expansion': - gc.expansion(n_iter) + nrg = gc.expansion(n_iter) else: raise ValueError("algorithm should be either `swap` or `expansion`. Got: %s" % algorithm) @@ -176,7 +180,9 @@ def cut_simple_gen_potts(np.ndarray[NRG_DTYPE_t, ndim=3, mode='c'] unary_cost, cdef int * result_ptr = result.data for i in xrange(w * h): result_ptr[i] = gc.whatLabel(i) - return result + + del gc + return result, nrg def cut_simple_vh(np.ndarray[NRG_DTYPE_t, ndim=3, mode='c'] unary_cost, @@ -223,10 +229,11 @@ def cut_simple_vh(np.ndarray[NRG_DTYPE_t, ndim=3, mode='c'] unary_cost, cdef GCoptimizationGridGraph* gc = new GCoptimizationGridGraph(h, w, n_labels) gc.setDataCost(unary_cost.data) gc.setSmoothCostVH(pairwise_cost.data, costV.data, costH.data) + cdef NRG_TYPE nrg if algorithm == 'swap': - gc.swap(n_iter) + nrg = gc.swap(n_iter) elif algorithm == 'expansion': - gc.expansion(n_iter) + nrg = gc.expansion(n_iter) else: raise ValueError("algorithm should be either `swap` or `expansion`. Got: %s" % algorithm) @@ -237,7 +244,9 @@ def cut_simple_vh(np.ndarray[NRG_DTYPE_t, ndim=3, mode='c'] unary_cost, cdef int * result_ptr = result.data for i in xrange(w * h): result_ptr[i] = gc.whatLabel(i) - return result + + del gc + return result, nrg def cut_from_graph(np.ndarray[np.int32_t, ndim=2, mode='c'] edges, @@ -294,10 +303,11 @@ def cut_from_graph(np.ndarray[np.int32_t, ndim=2, mode='c'] edges, gc.setDataCost(unary_cost.data) gc.setSmoothCost(pairwise_cost.data) + cdef NRG_TYPE nrg if algorithm == 'swap': - gc.swap(n_iter) + nrg = gc.swap(n_iter) elif algorithm == 'expansion': - gc.expansion(n_iter) + nrg = gc.expansion(n_iter) else: raise ValueError("algorithm should be either `swap` or `expansion`. Got: %s" % algorithm) @@ -307,7 +317,9 @@ def cut_from_graph(np.ndarray[np.int32_t, ndim=2, mode='c'] edges, cdef int * result_ptr = result.data for i in xrange(n_vertices): result_ptr[i] = gc.whatLabel(i) - return result + + del gc + return result, nrg def cut_from_graph_gen_potts( @@ -343,10 +355,11 @@ def cut_from_graph_gen_potts( gc.setDataCost(unary_cost.data) gc.setSmoothCostFunctor(new GeneralizedPottsFunctor(pairwise_cost)) + cdef NRG_TYPE nrg if algorithm == 'swap': - gc.swap(n_iter) + nrg = gc.swap(n_iter) elif algorithm == 'expansion': - gc.expansion(n_iter) + nrg = gc.expansion(n_iter) else: raise ValueError("algorithm should be either `swap` or `expansion`. Got: %s" % algorithm) @@ -356,4 +369,6 @@ def cut_from_graph_gen_potts( cdef int * result_ptr = result.data for i in xrange(n_vertices): result_ptr[i] = gc.whatLabel(i) - return result + + del gc + return result, nrg From 5a27564d92cf582d57ab7790373b7cd7d8a60fc7 Mon Sep 17 00:00:00 2001 From: Forest Gregg Date: Tue, 14 May 2013 21:37:04 -0500 Subject: [PATCH 5/6] changed check of dimension to check of length --- gco_python.pyx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gco_python.pyx b/gco_python.pyx index 58df6c1..4db3f7f 100644 --- a/gco_python.pyx +++ b/gco_python.pyx @@ -8,7 +8,8 @@ from libcpp.pair cimport pair DEF DEBUG_CHECKS = True # true if laborious parameter checks are needed -# This is the energy type; should match the EnergyType and EnergyTermType in GCOptimization.h +# This is the energy type; should match the EnergyType and +# EnergyTermType in GCOptimization.h DEF NRG_TYPE_STR = int IF NRG_TYPE_STR == int: @@ -293,7 +294,7 @@ def cut_from_graph(np.ndarray[np.int32_t, ndim=2, mode='c'] edges, if weights is None: for e in edges: - if e.shape[1] == 3: + if len(e) == 3: gc.setNeighbors(e[0], e[1], e[2]) else: gc.setNeighbors(e[0], e[1]) From bc726ffd929c9a9c2e131969fa40705233ee6244 Mon Sep 17 00:00:00 2001 From: Forest Gregg Date: Wed, 15 May 2013 14:09:37 -0500 Subject: [PATCH 6/6] implemented energy_assignment function: --- gco_python.pyx | 71 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/gco_python.pyx b/gco_python.pyx index 4db3f7f..ab70eea 100644 --- a/gco_python.pyx +++ b/gco_python.pyx @@ -37,6 +37,10 @@ cdef extern from "GCoptimization.h": void setSmoothCostVH(NRG_TYPE* pairwise, NRG_TYPE* V, NRG_TYPE* H) void setSmoothCostFunctor(SmoothCostFunctor* f) int whatLabel(int node) + void setLabel(int node, int label) + NRG_TYPE compute_energy() + + @@ -50,6 +54,8 @@ cdef extern from "GCoptimization.h": NRG_TYPE swap(int n_iterations) void setSmoothCostFunctor(GCoptimizationGridGraph.SmoothCostFunctor* f) # yep, it works int whatLabel(int node) + void setLabel(int node, int label) + NRG_TYPE compute_energy() cdef cppclass PottsFunctor(GCoptimizationGridGraph.SmoothCostFunctor): @@ -125,8 +131,10 @@ def cut_simple(np.ndarray[NRG_DTYPE_t, ndim=3, mode='c'] unary_cost, cdef int * result_ptr = result.data for i in xrange(w * h): result_ptr[i] = gc.whatLabel(i) - + + del gc + return result, nrg @@ -181,7 +189,8 @@ def cut_simple_gen_potts(np.ndarray[NRG_DTYPE_t, ndim=3, mode='c'] unary_cost, cdef int * result_ptr = result.data for i in xrange(w * h): result_ptr[i] = gc.whatLabel(i) - + + del gc return result, nrg @@ -249,6 +258,62 @@ def cut_simple_vh(np.ndarray[NRG_DTYPE_t, ndim=3, mode='c'] unary_cost, del gc return result, nrg +def energy_of_graph_assignment(np.ndarray[np.int32_t, ndim=2, mode='c'] edges, + np.ndarray[NRG_DTYPE_t, ndim=2, mode='c'] unary_cost, + np.ndarray[NRG_DTYPE_t, ndim=2, mode='c'] pairwise_cost, + np.ndarray[np.int32_t, ndim=1, mode='c'] assignment) : + """ + Calculate the energy of a particular assignment of labels to a graph + + Parameters + ---------- + edges: ndarray, int32, shape(n_edges, 2 or 3) + Rows correspond to edges in graph, given as vertex indices. + if edges is n_edges x 3 then third parameter is used as edge weight + unary_cost: ndarray, int32, shape=(n_vertices, n_labels) + Unary potentials + pairwise_cost: ndarray, int32, shape=(n_labels, n_labels) + Pairwise potentials for label compatibility + assigment : ndarray, int32, shape= (n_vertices,) + Assignments of labels to nodes + """ + + if (pairwise_cost != pairwise_cost.T).any(): + raise ValueError("pairwise_cost must be symmetric.") + + if unary_cost.shape[1] != pairwise_cost.shape[0]: + raise ValueError("unary_cost and pairwise_cost have incompatible shapes.\n" + "unary_cost must be height x width x n_labels, pairwise_cost must be n_labels x n_labels.\n" + "Got: unary_cost: (%d, %d), pairwise_cost: (%d, %d)" + %(unary_cost.shape[0], unary_cost.shape[1], + pairwise_cost.shape[0], pairwise_cost.shape[1])) + if pairwise_cost.shape[1] != pairwise_cost.shape[0]: + raise ValueError("pairwise_cost must be a square matrix.") + + cdef int n_vertices = unary_cost.shape[0] + cdef int n_labels = pairwise_cost.shape[0] + + cdef GCoptimizationGeneralGraph* gc = new GCoptimizationGeneralGraph(n_vertices, n_labels) + + for e in edges: + if len(e) == 3: + gc.setNeighbors(e[0], e[1], e[2]) + else: + gc.setNeighbors(e[0], e[1]) + + gc.setDataCost(unary_cost.data) + gc.setSmoothCost(pairwise_cost.data) + + for i in xrange(n_vertices): + gc.setLabel(i, assignment[i]) + + + nrg = gc.compute_energy() + + return nrg + + + def cut_from_graph(np.ndarray[np.int32_t, ndim=2, mode='c'] edges, np.ndarray[NRG_DTYPE_t, ndim=2, mode='c'] unary_cost, @@ -318,7 +383,7 @@ def cut_from_graph(np.ndarray[np.int32_t, ndim=2, mode='c'] edges, cdef int * result_ptr = result.data for i in xrange(n_vertices): result_ptr[i] = gc.whatLabel(i) - + del gc return result, nrg