Alpha Expansion Library
C++ library for the Alpha-Expansion graph-cut algorithm with Python bindings
Loading...
Searching...
No Matches
Implementing a Custom Max-Flow Solver

MaxFlowSolver<T> is the interface between AlphaExpansion and the underlying graph-cut backend. Subclass it to plug in any max-flow library.

Interface

// src/solvers/MaxFlowSolver.hpp
template <typename T>
public:
typedef int Var;
virtual Var add_variable() = 0;
virtual void add_constant(T E) = 0;
virtual void add_term1(Var x, T E0, T E1) = 0;
virtual void add_term2(Var x, Var y, T E00, T E01, T E10, T E11) = 0;
virtual T minimize() = 0;
virtual int get_var(Var x) = 0;
virtual ~MaxFlowSolver() = default;
};
Abstract interface for a binary max-flow / QPBO solver.
Definition MaxFlowSolver.hpp:15
virtual void add_constant(T E)=0
Adds a constant term to the energy function.
virtual Var add_variable()=0
Introduces a new binary variable and returns its handle.
virtual void add_term1(Var x, T E0, T E1)=0
Adds a unary term E(x) where x ∈ {0, 1}.
virtual T minimize()=0
Minimizes the energy and returns the minimum value.
int Var
Integer handle identifying a binary variable.
Definition MaxFlowSolver.hpp:20
virtual ~MaxFlowSolver()=default
virtual void add_term2(Var x, Var y, T E00, T E01, T E10, T E11)=0
Adds a pairwise term E(x, y) where x, y ∈ {0, 1}.
virtual int get_var(Var x)=0
Returns the optimal value of variable x (0 or 1) after minimize().

Step-by-Step Example

The following example wraps a hypothetical LibFoo max-flow library.

1. Create the header

// src/solvers/FooSolver.hpp
#pragma once
#include <libfoo/maxflow.h>
template <typename T>
class FooSolver : public MaxFlowSolver<T> {
libfoo::Graph graph_;
int num_vars_ = 0;
public:
FooSolver() = default;
typename MaxFlowSolver<T>::Var add_variable() override {
graph_.add_node();
return num_vars_++;
}
void add_constant(T E) override {
// if LibFoo has no constant term, store it and add it in minimize()
}
void add_term1(typename MaxFlowSolver<T>::Var x, T E0, T E1) override {
// E0 = cost when x=0 (alpha label), E1 = cost when x=1 (keep current label)
// check your library's source/sink convention
graph_.add_tweights(x, E1, E0);
}
T E00, T E01, T E10, T E11) override {
// AlphaExpansion always satisfies E00 + E11 <= E01 + E10 for Potts energies
graph_.add_edge(x, y, E01 - E00, E10 - E11);
}
T minimize() override {
return static_cast<T>(graph_.solve());
}
int get_var(typename MaxFlowSolver<T>::Var x) override {
// return 0 if the node is on the source side (= assigned alpha label)
return graph_.what_segment(x);
}
};

2. Pass it via a factory lambda

#include "solvers/FooSolver.hpp"
EnergyModel<int> model(100, 3);
// ... set up costs and graph ...
auto factory = [](int v, int e) {
return std::make_unique<FooSolver<int>>();
};
AlphaExpansion<int> optimizer(model, factory);
strategy.execute(optimizer, model);
Performs alpha-expansion moves on an EnergyModel using a pluggable max-flow solver.
Definition AlphaExpansion.hpp:20
Stores the graph and energy costs for the Alpha-Expansion algorithm.
Definition EnergyModel.hpp:17
Expansion strategy that cycles through labels in fixed order 0, 1, …, K-1.
Definition SequentialStrategy.hpp:14

Conventions

  • Variable 0 = alpha label, variable 1 = keep current label. get_var() must return 0 for nodes that should switch to alpha.
  • **add_term2 receives a submodular matrix.** For Potts energies, E00 + E11 <= E01 + E10 always holds, so you do not need to handle the non-submodular case.
  • The factory is called once per expansion move, so allocate per-move state in the constructor.