9 #include <boost/graph/adjacency_list.hpp>
10 #include <boost/graph/boykov_kolmogorov_max_flow.hpp>
12 #include <aliceVision/image/Image.hpp>
14 #include "distance.hpp"
15 #include "boundingBox.hpp"
16 #include "imageOps.hpp"
30 using ValueType = float;
32 using Traits = boost::adjacency_list_traits<boost::vecS,
37 using edge_descriptor =
typename Traits::edge_descriptor;
38 using vertex_descriptor =
typename Traits::vertex_descriptor;
39 using vertex_size_type =
typename Traits::vertices_size_type;
44 edge_descriptor reverse;
46 using Graph = boost::adjacency_list<boost::vecS,
54 using VertexIterator =
typename boost::graph_traits<Graph>::vertex_iterator;
58 : _graph(numNodes + 2),
59 _S(NodeType(numNodes)),
60 _T(NodeType(numNodes + 1))
62 VertexIterator vi, vi_end;
63 for (boost::tie(vi, vi_end) = vertices(_graph); vi != vi_end; ++vi)
65 _graph.m_vertices[*vi].m_out_edges.reserve(9);
67 _graph.m_vertices[numNodes].m_out_edges.reserve(numNodes);
68 _graph.m_vertices[numNodes + 1].m_out_edges.reserve(numNodes);
71 inline void addNodeToSource(NodeType n, ValueType source)
75 edge_descriptor edge(boost::add_edge(_S, n, _graph).first);
76 edge_descriptor reverseEdge(boost::add_edge(n, _S, _graph).first);
78 _graph[edge].capacity = source;
79 _graph[edge].reverse = reverseEdge;
80 _graph[reverseEdge].reverse = edge;
81 _graph[reverseEdge].capacity = source;
84 inline void addNodeToSink(NodeType n, ValueType sink)
88 edge_descriptor edge(boost::add_edge(_T, n, _graph).first);
89 edge_descriptor reverseEdge(boost::add_edge(n, _T, _graph).first);
91 _graph[edge].capacity = sink;
92 _graph[edge].reverse = reverseEdge;
93 _graph[reverseEdge].reverse = edge;
94 _graph[reverseEdge].capacity = sink;
97 inline void addEdge(NodeType n1, NodeType n2, ValueType capacity, ValueType reverseCapacity)
99 assert(capacity >= 0 && reverseCapacity >= 0);
101 edge_descriptor edge(boost::add_edge(n1, n2, _graph).first);
102 edge_descriptor reverseEdge(boost::add_edge(n2, n1, _graph).first);
103 _graph[edge].capacity = capacity;
104 _graph[edge].reverse = reverseEdge;
106 _graph[reverseEdge].capacity = reverseCapacity;
107 _graph[reverseEdge].reverse = edge;
110 void printStats()
const;
111 void printColorStats()
const;
113 inline ValueType compute()
115 vertex_size_type nbVertices(boost::num_vertices(_graph));
116 _color.resize(nbVertices, boost::white_color);
117 std::vector<edge_descriptor> pred(nbVertices);
118 std::vector<vertex_size_type> dist(nbVertices);
120 ValueType v = boost::boykov_kolmogorov_max_flow(_graph,
121 boost::get(&Edge::capacity, _graph),
122 boost::get(&Edge::residual, _graph),
123 boost::get(&Edge::reverse, _graph),
127 boost::get(boost::vertex_index, _graph),
135 inline bool isSource(NodeType n)
const {
return (_color[n] == boost::black_color); }
137 inline bool isTarget(NodeType n)
const {
return (_color[n] == boost::white_color); }
141 std::vector<boost::default_color_type> _color;
146 bool computeSeamsMap(image::Image<unsigned char>& seams,
const image::Image<IndexT>& labels);
159 using IndexedColor = std::pair<IndexT, image::RGBfColor>;
160 using PixelInfo = std::vector<IndexedColor>;
164 : _outputWidth(outputWidth),
165 _outputHeight(outputHeight),
166 _maximal_distance_change(outputWidth + outputHeight),
167 _labels(outputWidth, outputHeight, true, UndefinedIndexT)
172 std::pair<IndexT, image::RGBfColor> findIndex(
const PixelInfo& pix, IndexT
id)
174 for (
int i = 0; i < pix.size(); i++)
176 if (pix[i].first ==
id)
182 return std::make_pair(UndefinedIndexT, image::RGBfColor(0.0f));
185 bool existIndex(
const PixelInfo& pix, IndexT
id)
187 for (
int i = 0; i < pix.size(); i++)
189 if (pix[i].first ==
id)
198 bool setOriginalLabels(
const image::Image<IndexT>& existing_labels)
200 _labels = existing_labels;
205 bool append(
const image::Image<image::RGBfColor>& input,
206 const image::Image<unsigned char>& inputMask,
211 if (inputMask.width() != input.width())
216 if (inputMask.height() != input.height())
223 data.id = currentIndex;
225 data.mask = inputMask;
226 data.rect.
width = input.width();
227 data.rect.height = input.height();
228 data.rect.left = offset_x;
229 data.rect.top = offset_y;
231 _inputs[currentIndex] = data;
236 void setMaximalDistance(
int dist) { _maximal_distance_change = dist; }
238 bool createInputOverlappingObservations(image::Image<PixelInfo>& graphCutInput,
const BoundingBox& interestBbox)
240 for (
auto& otherInput : _inputs)
242 BoundingBox otherBbox = otherInput.second.rect;
243 BoundingBox otherBboxLoop = otherInput.second.rect;
244 otherBboxLoop.left = otherBbox.left - _outputWidth;
245 BoundingBox otherBboxLoopRight = otherInput.second.rect;
246 otherBboxLoopRight.left = otherBbox.left + _outputWidth;
248 BoundingBox otherInputBbox = otherBbox;
249 otherInputBbox.left = 0;
250 otherInputBbox.top = 0;
252 BoundingBox intersection = interestBbox.intersectionWith(otherBbox);
253 BoundingBox intersectionLoop = interestBbox.intersectionWith(otherBboxLoop);
254 BoundingBox intersectionLoopRight = interestBbox.intersectionWith(otherBboxLoopRight);
255 if (intersection.isEmpty() && intersectionLoop.isEmpty() && intersectionLoopRight.isEmpty())
260 image::Image<image::RGBfColor> otherColor(otherInputBbox.width, otherInputBbox.height);
261 otherColor.block(otherInputBbox.top, otherInputBbox.left, otherInputBbox.height, otherInputBbox.width) =
262 otherInput.second.color.block(otherInputBbox.top, otherInputBbox.left, otherInputBbox.height, otherInputBbox.width);
264 image::Image<unsigned char> otherMask(otherInputBbox.width, otherInputBbox.height);
265 otherMask.block(otherInputBbox.top, otherInputBbox.left, otherInputBbox.height, otherInputBbox.width) =
266 otherInput.second.mask.block(otherInputBbox.top, otherInputBbox.left, otherInputBbox.height, otherInputBbox.width);
268 if (!intersection.isEmpty())
270 BoundingBox interestOther = intersection;
271 interestOther.left -= otherBbox.left;
272 interestOther.top -= otherBbox.top;
274 BoundingBox interestThis = intersection;
275 interestThis.left -= interestBbox.left;
276 interestThis.top -= interestBbox.top;
278 for (
int y = 0; y < intersection.height; y++)
280 int y_other = interestOther.top + y;
281 int y_current = interestThis.top + y;
283 for (
int x = 0; x < intersection.width; x++)
285 int x_other = interestOther.left + x;
286 int x_current = interestThis.left + x;
288 if (!otherMask(y_other, x_other))
293 PixelInfo& pix = graphCutInput(y_current, x_current);
294 pix.push_back(std::make_pair(otherInput.first, otherColor(y_other, x_other)));
299 if (!intersectionLoop.isEmpty())
301 BoundingBox interestOther = intersectionLoop;
302 interestOther.left -= otherBboxLoop.left;
303 interestOther.top -= otherBboxLoop.top;
305 BoundingBox interestThis = intersectionLoop;
306 interestThis.left -= interestBbox.left;
307 interestThis.top -= interestBbox.top;
309 for (
int y = 0; y < intersectionLoop.height; y++)
311 int y_other = interestOther.top + y;
312 int y_current = interestThis.top + y;
314 for (
int x = 0; x < intersectionLoop.width; x++)
316 int x_other = interestOther.left + x;
317 int x_current = interestThis.left + x;
319 if (!otherMask(y_other, x_other))
324 PixelInfo& pix = graphCutInput(y_current, x_current);
325 pix.push_back(std::make_pair(otherInput.first, otherColor(y_other, x_other)));
330 if (!intersectionLoopRight.isEmpty())
332 BoundingBox interestOther = intersectionLoopRight;
333 interestOther.left -= otherBboxLoopRight.left;
334 interestOther.top -= otherBboxLoopRight.top;
336 BoundingBox interestThis = intersectionLoopRight;
337 interestThis.left -= interestBbox.left;
338 interestThis.top -= interestBbox.top;
340 for (
int y = 0; y < intersectionLoopRight.height; y++)
342 int y_other = interestOther.top + y;
343 int y_current = interestThis.top + y;
345 for (
int x = 0; x < intersectionLoopRight.width; x++)
347 int x_other = interestOther.left + x;
348 int x_current = interestThis.left + x;
350 if (!otherMask(y_other, x_other))
355 PixelInfo& pix = graphCutInput(y_current, x_current);
356 pix.push_back(std::make_pair(otherInput.first, otherColor(y_other, x_other)));
365 bool fixUpscaling(image::Image<IndexT>& labels,
const image::Image<PixelInfo>& graphCutInput)
369 for (
int y = 0; y < graphCutInput.height(); y++)
371 for (
int x = 0; x < graphCutInput.width(); x++)
373 IndexT label = labels(y, x);
375 if (label == UndefinedIndexT)
381 const PixelInfo& pix = graphCutInput(y, x);
384 auto it = existIndex(pix, label);
391 bool modified =
false;
392 bool hadUndefined =
false;
393 for (
int l = -1; l <= 1; l++)
396 if (ny < 0 || ny >= labels.height())
401 for (
int c = -1; c <= 1; c++)
404 if (nx < 0 || nx >= labels.width())
409 IndexT otherLabel = labels(ny, nx);
410 if (otherLabel == label)
415 if (otherLabel == UndefinedIndexT)
422 const PixelInfo& pixOther = graphCutInput(ny, nx);
423 auto itOther = existIndex(pixOther, otherLabel);
429 auto it = existIndex(pix, otherLabel);
432 labels(y, x) = otherLabel;
440 labels(y, x) = UndefinedIndexT;
448 bool computeInputDistanceMap(image::Image<int>& distanceMap,
const image::Image<IndexT>& localLabels, IndexT inputId)
450 image::Image<IndexT> binarizedWorld(localLabels.width(), localLabels.height());
452 for (
int y = 0; y < localLabels.height(); y++)
454 for (
int x = 0; x < localLabels.width(); x++)
456 IndexT label = localLabels(y, x);
458 if (label == inputId)
460 binarizedWorld(y, x) = 1;
464 binarizedWorld(y, x) = 0;
469 image::Image<unsigned char> seams(localLabels.width(), localLabels.height());
470 if (!computeSeamsMap(seams, binarizedWorld))
483 bool processInput(
double& newCost, InputData& input)
487 BoundingBox localBbox = input.rect.dilate(3);
488 localBbox.clampLeft();
489 localBbox.clampTop();
490 localBbox.clampBottom(_labels.
height() - 1);
493 BoundingBox outputBbox = input.rect;
494 outputBbox.left = input.rect.left - localBbox.left;
495 outputBbox.top = input.rect.top - localBbox.top;
498 image::Image<IndexT> localLabels(localBbox.width, localBbox.height);
499 if (!loopyImageExtract(localLabels, _labels, localBbox))
505 image::Image<PixelInfo> graphCutInput(localBbox.width, localBbox.height,
true);
506 if (!createInputOverlappingObservations(graphCutInput, localBbox))
512 if (!fixUpscaling(localLabels, graphCutInput))
518 BoundingBox inputBb = localBbox;
521 if (!loopyImageAssign(_labels, localLabels, localBbox, inputBb))
527 image::Image<int> distanceMap(localLabels.width(), localLabels.height());
528 if (!computeInputDistanceMap(distanceMap, localLabels, input.id))
534 for (
int i = 0; i < graphCutInput.height(); i++)
536 for (
int j = 0; j < graphCutInput.width(); j++)
538 float d2 = float(distanceMap(i, j));
541 if (d > _maximal_distance_change + 1.0f)
543 graphCutInput(i, j).clear();
548 double oldCost = cost(localLabels, graphCutInput, input.id);
549 if (!alphaExpansion(localLabels, distanceMap, graphCutInput, input.id))
553 newCost = cost(localLabels, graphCutInput, input.id);
555 if (newCost < oldCost)
557 BoundingBox inputBb = localBbox;
561 if (!loopyImageAssign(_labels, localLabels, localBbox, inputBb))
576 std::map<IndexT, double> costs;
577 for (
auto& info : _inputs)
579 costs[info.first] = std::numeric_limits<double>::max();
582 for (
int i = 0; i < 10; i++)
584 ALICEVISION_LOG_INFO(
"GraphCut processing iteration #" << i);
587 bool hasChange =
false;
590 for (
auto& info : _inputs)
593 if (!processInput(cost, info.second))
598 if (costs[info.first] != cost)
600 costs[info.first] = cost;
614 double cost(
const image::Image<IndexT> localLabels,
const image::Image<PixelInfo>& input, IndexT currentLabel)
618 for (
int y = 0; y < input.height() - 1; y++)
620 for (
int x = 0; x < input.width() - 1; x++)
625 IndexT label = localLabels(y, x);
626 IndexT labelx = localLabels(y, xp);
627 IndexT labely = localLabels(yp, x);
629 if (label == UndefinedIndexT)
631 if (labelx == UndefinedIndexT)
633 if (labely == UndefinedIndexT)
636 image::RGBfColor CColorLC;
637 image::RGBfColor CColorLX;
638 image::RGBfColor CColorLY;
639 image::RGBfColor XColorLC;
640 image::RGBfColor XColorLX;
641 image::RGBfColor YColorLC;
642 image::RGBfColor YColorLY;
651 auto it = findIndex(input(y, x), label);
652 if (it.first != UndefinedIndexT)
655 CColorLC = it.second;
658 it = findIndex(input(y, x), labelx);
659 if (it.first != UndefinedIndexT)
662 CColorLX = it.second;
665 it = findIndex(input(y, x), labely);
666 if (it.first != UndefinedIndexT)
669 CColorLY = it.second;
672 it = findIndex(input(y, xp), label);
673 if (it.first != UndefinedIndexT)
676 XColorLC = it.second;
679 it = findIndex(input(y, xp), labelx);
680 if (it.first != UndefinedIndexT)
683 XColorLX = it.second;
686 it = findIndex(input(yp, x), label);
687 if (it.first != UndefinedIndexT)
690 YColorLC = it.second;
693 it = findIndex(input(yp, x), labely);
694 if (it.first != UndefinedIndexT)
697 YColorLY = it.second;
700 if (!hasCLC || !hasXLX || !hasYLY)
725 double c1 = (CColorLC - CColorLX).norm();
726 double c2 = (CColorLC - CColorLY).norm();
727 double c3 = (XColorLC - XColorLX).norm();
728 double c4 = (YColorLC - YColorLY).norm();
730 c1 = std::min(2.0, c1);
731 c2 = std::min(2.0, c2);
732 c3 = std::min(2.0, c3);
733 c4 = std::min(2.0, c4);
735 cost += c1 + c2 + c3 + c4;
742 bool alphaExpansion(image::Image<IndexT>& labels,
const image::Image<int>& distanceMap,
const image::Image<PixelInfo>& input, IndexT currentLabel)
744 image::Image<unsigned char> mask(labels.width(), labels.height(),
true, 0);
745 image::Image<int> ids(labels.width(), labels.height(),
true, -1);
746 image::Image<image::RGBfColor> color_label(labels.width(), labels.height(),
true, image::RGBfColor(0.0f, 0.0f, 0.0f));
747 image::Image<image::RGBfColor> color_other(labels.width(), labels.height(),
true, image::RGBfColor(0.0f, 0.0f, 0.0f));
749 for (
int y = 0; y < labels.height(); y++)
751 for (
int x = 0; x < labels.width(); x++)
753 IndexT label = labels(y, x);
755 float dist = sqrt(
float(distanceMap(y, x)));
757 image::RGBfColor currentColor;
758 image::RGBfColor otherColor;
760 auto it = findIndex(input(y, x), currentLabel);
761 if (it.first != UndefinedIndexT)
763 currentColor = it.second;
767 if (label != currentLabel)
769 auto it = findIndex(input(y, x), label);
770 if (it.first != UndefinedIndexT)
772 otherColor = it.second;
774 if (dist > _maximal_distance_change)
788 color_label(y, x) = currentColor;
789 color_other(y, x) = currentColor;
791 else if (mask(y, x) == 2)
793 color_label(y, x) = otherColor;
794 color_other(y, x) = otherColor;
796 else if (mask(y, x) == 3)
798 color_label(y, x) = currentColor;
799 color_other(y, x) = otherColor;
808 for (
int y = 0; y < labels.height(); y++)
810 for (
int x = 0; x < labels.width(); x++)
823 MaxFlow_AdjList gc(count);
824 size_t countValid = 0;
826 for (
int y = 0; y < labels.height(); y++)
828 for (
int x = 0; x < labels.width(); x++)
837 int node_id = ids(y, x);
839 int ym1 = std::max(y - 1, 0);
840 int xm1 = std::max(x - 1, 0);
841 int yp1 = std::min(y + 1, labels.height() - 1);
842 int xp1 = std::min(x + 1, labels.width() - 1);
847 if (mask(ym1, xm1) == 1 && mask(ym1, x) == 1 && mask(ym1, xp1) == 1 && mask(y, xm1) == 1 && mask(y, xp1) == 1 &&
848 mask(yp1, xm1) == 1 && mask(yp1, x) == 1 && mask(yp1, xp1) == 1)
856 gc.addNodeToSource(node_id, 100000);
858 else if (mask(y, x) == 2)
861 if (mask(ym1, xm1) == 2 && mask(ym1, x) == 2 && mask(ym1, xp1) == 2 && mask(y, xm1) == 2 && mask(y, xp1) == 2 &&
862 mask(yp1, xm1) == 2 && mask(yp1, x) == 2 && mask(yp1, xp1) == 2)
870 gc.addNodeToSink(node_id, 100000);
872 else if (mask(y, x) == 3)
878 gc.addNodeToSource(node_id, 0);
879 gc.addNodeToSink(node_id, 0);
897 for (
int y = 0; y < labels.height(); y++)
899 for (
int x = 0; x < labels.width(); x++)
906 int node_id = ids(y, x);
909 if (y < mask.height() - 1)
914 int other_node_id = ids(y + 1, x);
917 if (((mask(y, x) & 1) && (mask(y + 1, x) & 2)) || ((mask(y, x) & 2) && (mask(y + 1, x) & 1)))
919 float d1 = (color_label(y, x) - color_other(y, x)).norm();
920 float d2 = (color_label(y + 1, x) - color_other(y + 1, x)).norm();
922 d1 = std::min(2.0f, d1);
923 d2 = std::min(2.0f, d2);
925 w = (d1 + d2) * 100.0 + 1.0;
928 gc.addEdge(node_id, other_node_id, w, w);
932 if (x < mask.width() - 1)
936 int other_node_id = ids(y, x + 1);
939 if (((mask(y, x) & 1) && (mask(y, x + 1) & 2)) || ((mask(y, x) & 2) && (mask(y, x + 1) & 1)))
941 float d1 = (color_label(y, x) - color_other(y, x)).norm();
942 float d2 = (color_label(y, x + 1) - color_other(y, x + 1)).norm();
944 d1 = std::min(2.0f, d1);
945 d2 = std::min(2.0f, d2);
947 w = (d1 + d2) * 100.0 + 1.0;
950 gc.addEdge(node_id, other_node_id, w, w);
959 for (
int y = 0; y < labels.height(); y++)
961 for (
int x = 0; x < labels.width(); x++)
963 IndexT label = labels(y, x);
968 if (label != currentLabel)
973 labels(y, x) = currentLabel;
981 image::Image<IndexT>& getLabels() {
return _labels; }
984 std::map<IndexT, InputData> _inputs;
988 size_t _maximal_distance_change;
989 image::Image<IndexT> _labels;