diff --git a/CMakeLists.txt b/CMakeLists.txt index a81348d..28d3695 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,7 @@ set(GC_SOURCES Gc/System/Log.cpp Gc/System/Collection/Tuple.h Gc/System/Collection/Array.h + Gc/System/Collection/Pair.h Gc/System/Collection/ArrayBuilder.h Gc/System/Collection/IArrayMask.h Gc/System/Collection/BoolArrayMask.h diff --git a/Doc/ChangeLog.txt b/Doc/ChangeLog.txt index 15f56d6..0301883 100644 --- a/Doc/ChangeLog.txt +++ b/Doc/ChangeLog.txt @@ -1,3 +1,19 @@ +2012-03-05 Ondrej Danek + * Matlab examples: CreateNeighbourhood function removed as it is no longer necessary + when the Gc::Energy::Neighbourhood object supports also N98 neighbourhood in the + Common method. + * Gc/Flow/Grid/DanekLabels.h: Small documentation improvements. + +2012-03-03 Ondrej Danek + * Gc/System/Collection/Pair.h: Pair container added. + * Gc/System/Algo/Sort/Heap.h: Simultaneous sorting removed (now pairs of values + are sorted to achieve the same effect) and predicate option added. + * Gc/Energy/Potential/Metric/RiemannianDanek.*: Slightly faster specialization of the + approximation in 2D added. + +2012-02-10 Ondrej Danek + * Gc/Flow/Grid/DanekLabels.cpp: Bug fixed. + 2011-11-15 Ondrej Danek * Gc/Energy/Neighbourhood.h: Added 98-neighbourhood to the Common() method. Exception is thrown when unexpected neighbourhood size is supplied. diff --git a/Examples/Matlab/ChanVese/GcChanVese.cpp b/Examples/Matlab/ChanVese/GcChanVese.cpp index 557b2a0..d4d202f 100644 --- a/Examples/Matlab/ChanVese/GcChanVese.cpp +++ b/Examples/Matlab/ChanVese/GcChanVese.cpp @@ -48,8 +48,7 @@ static void Segment(const mxArray *mx_in, const mxArray *mx_mask, T l1, T l2, T } // Create neighbourhood object - Gc::Energy::Neighbourhood nb; - Gc::Examples::Matlab::CreateNeighbourhood(str_nb, nb); + Gc::Energy::Neighbourhood nb((Gc::Size)atoi(str_nb + 1), false); // Segment Gc::System::Collection::Array seg; diff --git a/Examples/Matlab/ChanVese/GcChanVeseLab.cpp b/Examples/Matlab/ChanVese/GcChanVeseLab.cpp index c47e19e..74b607e 100644 --- a/Examples/Matlab/ChanVese/GcChanVeseLab.cpp +++ b/Examples/Matlab/ChanVese/GcChanVeseLab.cpp @@ -59,8 +59,7 @@ static void Segment(const mxArray *mx_in, const mxArray *mx_interface, const mxA } // Create neighbourhood object - Gc::Energy::Neighbourhood nb; - Gc::Examples::Matlab::CreateNeighbourhood(str_nb, nb); + Gc::Energy::Neighbourhood nb((Gc::Size)atoi(str_nb + 1), false); // Segment Gc::System::Collection::Array seg; diff --git a/Examples/Matlab/ChanVese/GcChanVeseTp.cpp b/Examples/Matlab/ChanVese/GcChanVeseTp.cpp index d868e13..a595e58 100644 --- a/Examples/Matlab/ChanVese/GcChanVeseTp.cpp +++ b/Examples/Matlab/ChanVese/GcChanVeseTp.cpp @@ -58,8 +58,7 @@ static void Segment(const mxArray *mx_in, const mxArray *mx_interface, const mxA } // Create neighbourhood object - Gc::Energy::Neighbourhood nb; - Gc::Examples::Matlab::CreateNeighbourhood(str_nb, nb); + Gc::Energy::Neighbourhood nb((Gc::Size)atoi(str_nb + 1), false); // Segment Gc::System::Collection::Array seg; diff --git a/Examples/Matlab/ChanVese/gc_chan_vese_lab.m b/Examples/Matlab/ChanVese/gc_chan_vese_lab.m index fddd9d1..5426cd2 100644 --- a/Examples/Matlab/ChanVese/gc_chan_vese_lab.m +++ b/Examples/Matlab/ChanVese/gc_chan_vese_lab.m @@ -1,5 +1,6 @@ -% gc_chan_vese_lab Calculate label preserving Chan-Vese segmentation via -% graph cuts. +% gc_chan_vese_lab Calculate graph cut based Chan-Vese segmentation +% integrated with object indication function. +% % % SYNOPSIS: % [seg energy iter fc1 fc2] = gc_chan_vese_lab(img, ilab, mu, lambda1, @@ -14,10 +15,10 @@ % [seg2 energy iter c1 c2] = gc_chan_vese_lab(s2n, seg1, 1, 10, 10); % % PARAMETERS: -% ilab: Initial labeling of the image. +% ilab: Initial object indicators. % See gc_chan_vese for the documentation of the remaining input parameters. % -% seg: Segmentation mask. +% seg: Final segmentation (object indicators). % energy: Energy of the solution. % iter: Number of iterations performed. % fc1: Final background mean intensity. @@ -57,8 +58,8 @@ % c1, c2 Initialized using gc_weighted_kmeans function % % DESCRIPTION: -% Computes a label preserving Chan-Vese segmentation of the input image -% with given weights via graph-cut based optimization. TODO. +% Computes the Chan-Vese segmentation of the input image integrated with +% object indication function via graph-cut based optimization. TODO. % % NOTES: % See gc_chan_vese. diff --git a/Examples/Matlab/GcMatlabTools.h b/Examples/Matlab/GcMatlabTools.h index b16d75f..66650f4 100644 --- a/Examples/Matlab/GcMatlabTools.h +++ b/Examples/Matlab/GcMatlabTools.h @@ -21,43 +21,6 @@ namespace Gc { /*************************************************************************************/ - // Create neighbourhood corresponding to a string identifier - // Supported neighbourhoods are: - // for 2D: N4, N8, N16, N32 - // for 3D: N6, N18, N26, N98 - template - void CreateNeighbourhood(const char *str_nb, Gc::Energy::Neighbourhood &nb) - { - if (N == 2) - { - if (!strcmp(str_nb, "N4") || !strcmp(str_nb, "N8") || - !strcmp(str_nb, "N16") || !strcmp(str_nb, "N32")) - { - nb.Common((Gc::Size)atoi(str_nb + 1), false); - return; - } - } - - if (N == 3) - { - if (!strcmp(str_nb, "N6") || !strcmp(str_nb, "N18") || - !strcmp(str_nb, "N26")) - { - nb.Common((Gc::Size)atoi(str_nb + 1), false); - return; - } - if (!strcmp(str_nb, "N98")) - { - nb.Box(Gc::Math::Algebra::Vector(2), true, false); - return; - } - } - - mexErrMsgTxt("Unsupported neighbourhood type for given image dimensionality."); - } - - /*************************************************************************************/ - // Create general max-flow algorithm corresponging to given string identifier template static Gc::Flow::IMaxFlow *CreateGeneralMaxFlow(const char *name) diff --git a/Examples/Matlab/MumfordShah/GcMumfordShah.cpp b/Examples/Matlab/MumfordShah/GcMumfordShah.cpp index 243b1ab..8f656ed 100644 --- a/Examples/Matlab/MumfordShah/GcMumfordShah.cpp +++ b/Examples/Matlab/MumfordShah/GcMumfordShah.cpp @@ -37,8 +37,7 @@ static void Segment(const mxArray *mx_in, Gc::Size k, T lambda, T conv, img.SetSpacing(Gc::Math::Algebra::Vector(1)); // Create neighbourhood object - Gc::Energy::Neighbourhood nb; - Gc::Examples::Matlab::CreateNeighbourhood(str_nb, nb); + Gc::Energy::Neighbourhood nb((Gc::Size)atoi(str_nb + 1), false); // Weights - same weight lambda for all partitions Gc::System::Collection::Array<1,T> l(k, lambda); diff --git a/Examples/Matlab/RoussonDeriche/GcRoussonDeriche.cpp b/Examples/Matlab/RoussonDeriche/GcRoussonDeriche.cpp index c91d327..408db41 100644 --- a/Examples/Matlab/RoussonDeriche/GcRoussonDeriche.cpp +++ b/Examples/Matlab/RoussonDeriche/GcRoussonDeriche.cpp @@ -35,8 +35,7 @@ static void Segment(const mxArray *mx_in, T lambda, T conv, Gc::Size max_iter, img.SetSpacing(Gc::Math::Algebra::Vector(1)); // Create neighbourhood object - Gc::Energy::Neighbourhood nb; - Gc::Examples::Matlab::CreateNeighbourhood(str_nb, nb); + Gc::Energy::Neighbourhood nb((Gc::Size)atoi(str_nb + 1), false); // Initialization Gc::Algo::Segmentation::RoussonDeriche::Params rdpar; diff --git a/Gc/Energy/Potential/Metric/RiemannianDanek.cpp b/Gc/Energy/Potential/Metric/RiemannianDanek.cpp index ea0c695..6301cd6 100644 --- a/Gc/Energy/Potential/Metric/RiemannianDanek.cpp +++ b/Gc/Energy/Potential/Metric/RiemannianDanek.cpp @@ -28,6 +28,7 @@ #include "../../../Math/Constant.h" #include "../../../Math/Geometry/Voronoi.h" +#include "../../../System/Algo/Sort/Heap.h" #include "RiemannianDanek.h" namespace Gc @@ -84,11 +85,11 @@ namespace Gc // Calculate delta rho and capacities T coef = CauchyCroftonCoef(); - T cell_area = mt.Determinant(); + T gridCellArea = mt.Determinant(); for (Size i = 0; i < m_nb.Elements(); i++) { - T drho = cell_area / mt.Mul(m_nb[i]).Length(); + T drho = gridCellArea / mt.Mul(m_nb[i]).Length(); m_rw[i] = (dphi[i] * drho) / coef; } @@ -106,6 +107,85 @@ namespace Gc /** @endcond */ /******************************************************************************/ + + template + struct OrientationPred + { + bool operator()(const System::Collection::Pair,SZ>& v1, + const System::Collection::Pair,SZ>& v2) const + { + return (Math::Algebra::AngularOrientation(v1.m_first) < + Math::Algebra::AngularOrientation(v2.m_first)); + } + }; + + /******************************************************************************/ + + template + RiemannianDanek2D::RiemannianDanek2D(const Neighbourhood<2,T> &n) + { + // Init members + m_rw.Resize(n.Elements()); + + // Sort neighbourhood according to the angular orientation + // but remember indexes to the original array + m_nb.Resize(n.Elements()); + + for (Size i = 0; i < n.Elements(); i++) + { + m_nb[i].m_first = n[i]; + m_nb[i].m_second = (Uint8)i; + } + + // Sort angular orientations and the indexes + System::Algo::Sort::Heap(m_nb.Begin(), m_nb.End(), OrientationPred()); + } + + /******************************************************************************/ + + template + RiemannianDanek2D& RiemannianDanek2D::SetTransformationMatrix + (const Math::Algebra::SquareMatrix<2,T> &mt) + { + Math::Algebra::Vector<2,T> vecPrev, vecCur, vecNext; + T gridCellArea = mt.Determinant(); + + // Working set of transformed vectors + vecPrev = mt.Mul(m_nb[m_nb.Elements() - 1].m_first); + vecCur = mt.Mul(m_nb[0].m_first); + + for (Size i = 0; i < m_nb.Elements(); i++) + { + // Following edge + Size nextIdx = (i == m_nb.Elements() - 1) ? 0 : (i + 1); + vecNext = mt.Mul(m_nb[nextIdx].m_first); + + // Delta phi + T dphi = Math::Algebra::ClockwiseAngle(vecNext, vecPrev) / T(2); + + // Delta rho + T drho = gridCellArea / vecCur.Length(); + + // Edge weight + m_rw[m_nb[i].m_second] = (dphi * drho) / T(2.0); + + // Shift working set + vecPrev = vecCur; + vecCur = vecNext; + } + + return *this; + } + + /******************************************************************************/ + + // Explicit instantiations + /** @cond */ + template class GC_DLL_EXPORT RiemannianDanek2D; + template class GC_DLL_EXPORT RiemannianDanek2D; + /** @endcond */ + + /******************************************************************************/ } } } diff --git a/Gc/Energy/Potential/Metric/RiemannianDanek.h b/Gc/Energy/Potential/Metric/RiemannianDanek.h index a2bc080..6d13747 100644 --- a/Gc/Energy/Potential/Metric/RiemannianDanek.h +++ b/Gc/Energy/Potential/Metric/RiemannianDanek.h @@ -32,6 +32,7 @@ #include "../../../Core.h" #include "../../../Type.h" #include "../../../Math/Algebra/SquareMatrix.h" +#include "../../../System/Collection/Pair.h" #include "../../Neighbourhood.h" namespace Gc @@ -150,18 +151,60 @@ namespace Gc RiemannianDanek& SetTransformationMatrix (const Math::Algebra::SquareMatrix &mt); - /** Get edge weights approximating the Euclidean metric. */ + /** Get edge weights approximating the Riemannian metric. */ const System::Collection::Array<1,T>& EdgeWeights() const { return m_rw; } - /** Get edge weight for i-th neighbour. */ + /** Get edge weight for i-th neighbourhood vector. */ T operator[] (Size i) const { return m_rw[i]; } }; + + /** Faster specialization of RiemannianDanek for a 2D space. + + This class does the same as RiemannianDanek. It offers faster recomputation + when the transformation matrix changes, but supports only 2D space and requires + slightly more memory. + + @see RiemannianDanek. + + @tparam T Precision. + */ + template + class GC_DLL_EXPORT RiemannianDanek2D + { + protected: + /** Neighbourhood sorted according to the angular orientation and cross indexes. */ + System::Collection::Array<1,System::Collection::Pair,Uint8> > m_nb; + /** Calculated edge weights. */ + System::Collection::Array<1,T> m_rw; + + public: + /** Constructor. */ + RiemannianDanek2D(const Neighbourhood<2,T> &n); + + /** Specify the transformation of the Riemannian space. */ + RiemannianDanek2D& SetTransformationMatrix + (const Math::Algebra::SquareMatrix<2,T> &mt); + + /** Get edge weights approximating the Riemannian metric. */ + const System::Collection::Array<1,T>& EdgeWeights() const + { + return m_rw; + } + + /** Get edge weight for i-th neighbourhood vector. */ + T operator[](Size i) const + { + return m_rw[i]; + } + + protected: + }; } } } diff --git a/Gc/Flow/Grid/DanekLabels.cpp b/Gc/Flow/Grid/DanekLabels.cpp index 9c23957..92df95a 100644 --- a/Gc/Flow/Grid/DanekLabels.cpp +++ b/Gc/Flow/Grid/DanekLabels.cpp @@ -338,15 +338,15 @@ namespace Gc { if (head->m_tag >= 8) // Free node { - head->m_parent_arc = Sister(i); - head->m_tag = n->m_tag; - head->m_label = n->m_label; - - if ((n->m_tag & 1) != (head->m_tag & 1)) + if (n->m_label != head->m_label) { head->m_dist = n->m_dist - 1; } + head->m_parent_arc = Sister(i); + head->m_tag = n->m_tag; + head->m_label = n->m_label; + EnqueueNode(head); } else if ((n->m_tag & 2) == (head->m_tag & 2)) // Same tree diff --git a/Gc/Flow/Grid/DanekLabels.h b/Gc/Flow/Grid/DanekLabels.h index e4ebb3a..b12369b 100644 --- a/Gc/Flow/Grid/DanekLabels.h +++ b/Gc/Flow/Grid/DanekLabels.h @@ -19,14 +19,15 @@ /** @file - Label preserving maximum flow algorithm for directed grid graphs. + Maximum flow algorithm (for directed grid graphs) integrated with the object + indication function. @author Ondrej Danek @date 2010 */ -#ifndef GC_FLOW_GRID_ZENGDANEK_H -#define GC_FLOW_GRID_ZENGDANEK_H +#ifndef GC_FLOW_GRID_DANEKLABELS_H +#define GC_FLOW_GRID_DANEKLABELS_H #include #include "../../Core.h" @@ -38,7 +39,8 @@ namespace Gc { namespace Grid { - /** Label preserving maximum flow algorithm for directed grid graphs. + /** Maximum flow algorithm (for directed grid graphs) integrated with the object + indication function. @todo Documentation. @@ -95,7 +97,7 @@ namespace Gc - bit 3 - 0 = non-free node, 1 = free node */ Uint8 m_tag; - /** Current node label. */ + /** Current object indicator. */ LAB m_label; }; @@ -263,7 +265,7 @@ namespace Gc private: /** Node array. */ System::Collection::Array<1,Node> m_node_list; - /** Initial labelling. */ + /** Initial object indicators. */ const System::Collection::Array *m_ilab; /** Initial distance map. */ System::Collection::Array m_dmap; @@ -308,14 +310,14 @@ namespace Gc virtual void Dispose(); - /** Set initial labelling. + /** Set initial object indicators. @warning Only pointer to the object is taken, so this object should not be deleted before or during the computation! */ void SetInitialLabelingRef(const System::Collection::Array &ilab); - /** Get final node labels. */ + /** Get the final object indicator for a given node. */ LAB NodeLabel(Size node) const; private: diff --git a/Gc/Math/Algebra/Vector.h b/Gc/Math/Algebra/Vector.h index 9fa2cf0..430c662 100644 --- a/Gc/Math/Algebra/Vector.h +++ b/Gc/Math/Algebra/Vector.h @@ -645,7 +645,7 @@ namespace Gc /** Cross product of two 3D vectors. */ template - Vector<3,T> CrossProduct (const Vector<3,T> &v1, const Vector<3,T> &v2) + Vector<3,T> CrossProduct(const Vector<3,T> &v1, const Vector<3,T> &v2) { Vector<3,T> nv; @@ -658,7 +658,7 @@ namespace Gc /** Calculates the clockwise angle between two 2D vectors. */ template - T ClockwiseAngle (const Vector<2,T> &v1, const Vector<2,T> &v2) + T ClockwiseAngle(const Vector<2,T> &v1, const Vector<2,T> &v2) { T ang = v1.Angle(v2); @@ -670,6 +670,14 @@ namespace Gc return ang; } + + /** Calculate the angular orientation of a vector. */ + template + T AngularOrientation(const Vector<2,T> &v) + { + T angle = acos(v[0] / v.Length()); + return (v[1] < 0) ? (2 * T(Constant::Pi) - angle) : angle; + } } } } diff --git a/Gc/Math/Geometry/Voronoi.cpp b/Gc/Math/Geometry/Voronoi.cpp index 50f3d82..bb424ba 100644 --- a/Gc/Math/Geometry/Voronoi.cpp +++ b/Gc/Math/Geometry/Voronoi.cpp @@ -26,6 +26,8 @@ */ #include "../../System/ArgumentException.h" +#include "../../System/Collection/Pair.h" +#include "../../System/Algo/Sort/Heap.h" #include "../Constant.h" #include "Voronoi.h" #include "ConvexHull.h" @@ -169,6 +171,18 @@ namespace Gc /************************************************************************/ + template + struct OrientationPred + { + bool operator()(const System::Collection::Pair& v1, + const System::Collection::Pair& v2) const + { + return (v1.m_first < v2.m_first); + } + }; + + /************************************************************************/ + template void HypersphereVoronoiDiagram (const System::Collection::Array<1,Vector<2,T> > &points, System::Collection::Array<1,T> &vd) @@ -180,40 +194,33 @@ namespace Gc } // Sort vectors according to their angular orientation - System::Collection::Array<1,T> ao(points.Elements()); - System::Collection::Array<1,Size> ai(points.Elements()); + System::Collection::Array<1,System::Collection::Pair > p(points.Elements()); for (Size i = 0; i < points.Elements(); i++) { - // Calculate angular orientation of the vector - ao[i] = acos(points[i][0] / points[i].Length()); - if (points[i][1] < 0) - { - ao[i] = 2 * T(Constant::Pi) - ao[i]; - } - - // Save vector index - ai[i] = i; + // Calculate the angular orientation of the vector and save its index + p[i].m_first = Math::Algebra::AngularOrientation(points[i]); + p[i].m_second = i; } // Sort angular orientations - System::Algo::Sort::HeapSimultaneous(ao.Begin(), ao.End(), ai.Begin()); + System::Algo::Sort::Heap(p.Begin(), p.End(), OrientationPred()); // Calculate Voronoi partitioning (average of neighbouring angular deltas) vd.Resize(points.Elements()); Size li = points.Elements() - 1; // First point - vd[ai[0]] = T(Constant::Pi) + (ao[1] - ao[li]) / 2; + vd[p[0].m_second] = T(Constant::Pi) + (p[1].m_first - p[li].m_first) / 2; // Middle points for (Size i = 1; i < li; i++) { - vd[ai[i]] = (ao[i + 1] - ao[i - 1]) / 2; + vd[p[i].m_second] = (p[i+1].m_first - p[i-1].m_first) / 2; } // Last point - vd[ai[li]] = T(Constant::Pi) + (ao[0] - ao[li - 1]) / 2; + vd[p[li].m_second] = T(Constant::Pi) + (p[0].m_first - p[li-1].m_first) / 2; } /************************************************************************/ diff --git a/Gc/System/Algo/Sort/Heap.h b/Gc/System/Algo/Sort/Heap.h index 4abec15..c3c8fce 100644 --- a/Gc/System/Algo/Sort/Heap.h +++ b/Gc/System/Algo/Sort/Heap.h @@ -30,6 +30,7 @@ #include "../../../Type.h" #include "../Iterator.h" +#include "../Predicate.h" namespace Gc { @@ -47,12 +48,13 @@ namespace Gc @param[in] begin Random access iterator pointing to the first element. @param[in] end Random access iterator pointing to the end of the sequence. + @param[in] pred Comparison predicate. */ - template - void Heap(RndAccIter begin, RndAccIter end) + template + void Heap(RanIter begin, RanIter end, Pred pred) { Size n = (end - begin), parent = n/2, index, child; - typename IteratorTraits::ValueType temp; + typename IteratorTraits::ValueType temp; while (n > 1) { @@ -71,12 +73,12 @@ namespace Gc child = index * 2 + 1; while (child < n) { - if (child + 1 < n && begin[child + 1] > begin[child]) + if (child + 1 < n && pred(begin[child], begin[child + 1])) { child++; } - if (begin[child] > temp) + if (pred(temp, begin[child])) { begin[index] = begin[child]; index = child; @@ -92,68 +94,18 @@ namespace Gc } } - /** Simultaneous heap sort algorithm. + /** Heap sort algorithm. - Sorts elements of the first array from the smallest to the largest - using heap sort algorithm and simultaneously rearranges elements - in the second collection. - - @param[in] d1Beg Random access iterator pointing to the first element - in the first collection (to be sorted). - @param[in] d1End Random access iterator to the end of the first sequence. - @param[in] d2Beg Random access iterator pointing to the first element - in the second collection. - */ - template - void HeapSimultaneous(RndAccIter1 d1Beg, RndAccIter1 d1End, RndAccIter2 d2Beg) - { - // Sort - Size n = (d1End - d1Beg), parent = n/2, index, child; - typename IteratorTraits::ValueType temp1; - typename IteratorTraits::ValueType temp2; - - while (n > 1) - { - if (parent) - { - parent--; - temp1 = d1Beg[parent]; - temp2 = d2Beg[parent]; - } - else - { - n--; - temp1 = d1Beg[n]; - temp2 = d2Beg[n]; - d1Beg[n] = d1Beg[0]; - d2Beg[n] = d2Beg[0]; - } - - index = parent; - child = index * 2 + 1; - while (child < n) - { - if (child + 1 < n && d1Beg[child + 1] > d1Beg[child]) - { - child++; - } - - if (d1Beg[child] > temp1) - { - d1Beg[index] = d1Beg[child]; - d2Beg[index] = d2Beg[child]; - index = child; - child = index * 2 + 1; - } - else - { - break; - } - } + Sorts elements from the smallest to the largest using heap sort + algorithm and System::Algo::Predicate::Less. - d1Beg[index] = temp1; - d2Beg[index] = temp2; - } + @param[in] begin Random access iterator pointing to the first element. + @param[in] end Random access iterator pointing to the end of the sequence. + */ + template + void Heap(RanIter begin, RanIter end) + { + Heap(begin, end, Predicate::Less::ValueType>()); } } } diff --git a/Gc/System/Collection/Pair.h b/Gc/System/Collection/Pair.h new file mode 100644 index 0000000..0c5a4a3 --- /dev/null +++ b/Gc/System/Collection/Pair.h @@ -0,0 +1,77 @@ +/* + This file is part of Graph Cut (Gc) combinatorial optimization library. + Copyright (C) 2008-2010 Centre for Biomedical Image Analysis (CBIA) + Copyright (C) 2008-2010 Ondrej Danek + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gc 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Graph Cut library. If not, see . +*/ + +/** + @file + A pair of values which may be of different data type. + + @author Ondrej Danek + @date 2012 +*/ + +#ifndef GC_SYSTEM_COLLECTION_PAIR_H +#define GC_SYSTEM_COLLECTION_PAIR_H + +namespace Gc +{ + namespace System + { + namespace Collection + { + /** A pair of values which may be of a different data type. */ + template + class Pair + { + public: + /** First element. */ + T1 m_first; + /** Second element. */ + T2 m_second; + + public: + /** Default constructor. */ + Pair() + {} + + /** Constructor. */ + Pair(const T1& v1, const T2& v2) + : m_first(v1), m_second(v2) + {} + + /** Copy constructor. */ + Pair(const Pair& p) + : m_first(p.m_first), m_second(p.m_second) + {} + + /** Assignment operator. */ + Pair& operator=(const Pair& p) + { + if (this != &p) + { + m_first = p.m_first; + m_second = p.m_second; + } + return *this; + } + }; + } + } +} + +#endif