643 const std::vector<int>& faces1,
644 const std::vector<int>& faces2,
645 const std::vector<Eigen::Vector3d>& V1,
646 const std::vector<Eigen::Vector3d>& V2,
650 const size_t nF1 = faces1.size() / 3;
651 const size_t nF2 = faces2.size() / 3;
652 std::vector<bool> CT1(nF1,
true);
653 std::vector<bool> CT2(nF2,
true);
654 if (nF1 == 0 || nF2 == 0)
return {CT1, CT2};
663 const double meanEdge =
665 if (meanEdge <= 0.0)
return {CT1, CT2};
666 const double rayCloseThresh = 2.0 * meanEdge;
668 std::vector<bool> logicNoHit1(V1.size(),
false);
669 std::vector<bool> logicNoHit2(V2.size(),
false);
672 const size_t maxIter = nF1 + nF2 + 16;
673 size_t removed_phase1_s1 = 0, removed_phase1_s2 = 0;
674 size_t removed_phase2_s1 = 0, removed_phase2_s2 = 0;
677 for (
size_t iter = 0; iter < maxIter; ++iter) {
680 if (F1cur.empty() || F2cur.empty())
break;
686 std::set<int> b1set, b2set;
687 for (
int v : Eb1) b1set.insert(v);
688 for (
int v : Eb2) b2set.insert(v);
689 std::vector<int> ib1, ib2;
690 ib1.reserve(b1set.size()); ib2.reserve(b2set.size());
692 if (
static_cast<size_t>(v) < logicNoHit1.size() && !logicNoHit1[v])
695 if (
static_cast<size_t>(v) < logicNoHit2.size() && !logicNoHit2[v])
698 if (ib1.empty() && ib2.empty())
break;
700 int worst_v1 = -1, first_close_v1 = -1;
701 int worst_v2 = -1, first_close_v2 = -1;
702 double worstQ1 = -std::numeric_limits<double>::infinity();
703 double worstQ2 = -std::numeric_limits<double>::infinity();
706 std::vector<Eigen::Vector3d> O(ib1.size()), D(ib1.size());
707 for (
size_t i = 0; i < ib1.size(); ++i) { O[i] = V1[ib1[i]]; D[i] = N1[ib1[i]]; }
709 for (
size_t i = 0; i < ib1.size(); ++i) {
710 bool close = !trace[i].hasNaN() &&
711 (V1[ib1[i]] - trace[i]).norm() < rayCloseThresh;
713 logicNoHit1[ib1[i]] =
true;
715 if (first_close_v1 < 0) first_close_v1 = ib1[i];
716 double q = Q1[ib1[i]];
717 if (std::isfinite(q) && q > worstQ1) { worstQ1 = q; worst_v1 = ib1[i]; }
722 std::vector<Eigen::Vector3d> O(ib2.size()), D(ib2.size());
723 for (
size_t i = 0; i < ib2.size(); ++i) { O[i] = V2[ib2[i]]; D[i] = N2[ib2[i]]; }
725 for (
size_t i = 0; i < ib2.size(); ++i) {
726 bool close = !trace[i].hasNaN() &&
727 (V2[ib2[i]] - trace[i]).norm() < rayCloseThresh;
729 logicNoHit2[ib2[i]] =
true;
731 if (first_close_v2 < 0) first_close_v2 = ib2[i];
732 double q = Q2[ib2[i]];
733 if (std::isfinite(q) && q > worstQ2) { worstQ2 = q; worst_v2 = ib2[i]; }
738 bool any_close_1 = (first_close_v1 >= 0);
739 bool any_close_2 = (first_close_v2 >= 0);
740 if (!any_close_1 && !any_close_2)
break;
744 if (any_close_1 && any_close_2) {
746 remove_from_1 = (!pair_forced) && (worstQ1 > worstQ2);
748 remove_from_1 = any_close_1;
751 int v_remove = remove_from_1 ? (worst_v1 >= 0 ? worst_v1 : first_close_v1)
752 : (worst_v2 >= 0 ? worst_v2 : first_close_v2);
753 if (v_remove < 0)
break;
755 bool ok = remove_from_1
759 if (remove_from_1) ++removed_phase1_s1;
else ++removed_phase1_s2;
770 if (F1cur.empty() || F2cur.empty()) {
773 for (
size_t iter = 0; iter < maxIter; ++iter) {
776 if (F1cur.empty() || F2cur.empty())
break;
780 if (Eb1.empty() || Eb2.empty())
break;
782 std::set<int> b1set, b2set;
783 for (
int v : Eb1) b1set.insert(v);
784 for (
int v : Eb2) b2set.insert(v);
785 std::vector<int> ib1(b1set.begin(), b1set.end());
786 std::vector<int> ib2(b2set.begin(), b2set.end());
789 std::vector<Eigen::Vector3d> tgt1, tgt2;
790 tgt1.reserve(ib1.size() + (Eb1.size() / 2) * 3);
791 tgt2.reserve(ib2.size() + (Eb2.size() / 2) * 3);
792 for (
int v : ib1) tgt1.push_back(V1[v]);
793 for (
int v : ib2) tgt2.push_back(V2[v]);
794 size_t nEb1 = Eb1.size() / 2, nEb2 = Eb2.size() / 2;
795 for (
size_t i = 0; i < nEb1; ++i) {
796 int a = Eb1[i*2], b = Eb1[i*2+1];
797 if (
static_cast<size_t>(a) >= V1.size() ||
798 static_cast<size_t>(b) >= V1.size())
continue;
799 const auto& A = V1[a];
const auto& B = V1[b];
800 if (A.hasNaN() || B.hasNaN())
continue;
801 tgt1.push_back(0.5 * (A + B));
802 tgt1.push_back(0.25 * A + 0.75 * B);
803 tgt1.push_back(0.75 * A + 0.25 * B);
805 for (
size_t i = 0; i < nEb2; ++i) {
806 int a = Eb2[i*2], b = Eb2[i*2+1];
807 if (
static_cast<size_t>(a) >= V2.size() ||
808 static_cast<size_t>(b) >= V2.size())
continue;
809 const auto& A = V2[a];
const auto& B = V2[b];
810 if (A.hasNaN() || B.hasNaN())
continue;
811 tgt2.push_back(0.5 * (A + B));
812 tgt2.push_back(0.25 * A + 0.75 * B);
813 tgt2.push_back(0.75 * A + 0.25 * B);
816 int worst_v1 = -1, first_close_v1 = -1;
817 int worst_v2 = -1, first_close_v2 = -1;
818 double worstQ1 = -std::numeric_limits<double>::infinity();
819 double worstQ2 = -std::numeric_limits<double>::infinity();
820 size_t close1 = 0, close2 = 0;
823 const auto& P = V1[v];
824 if (P.hasNaN())
continue;
825 double dmin = std::numeric_limits<double>::infinity();
826 for (
const auto& p : tgt2) {
827 if (p.hasNaN())
continue;
828 double d = (P - p).norm();
829 if (d < dmin) dmin = d;
831 if (dmin < min_gap) {
833 if (first_close_v1 < 0) first_close_v1 = v;
835 if (std::isfinite(q) && q > worstQ1) { worstQ1 = q; worst_v1 = v; }
839 const auto& P = V2[v];
840 if (P.hasNaN())
continue;
841 double dmin = std::numeric_limits<double>::infinity();
842 for (
const auto& p : tgt1) {
843 if (p.hasNaN())
continue;
844 double d = (P - p).norm();
845 if (d < dmin) dmin = d;
847 if (dmin < min_gap) {
849 if (first_close_v2 < 0) first_close_v2 = v;
851 if (std::isfinite(q) && q > worstQ2) { worstQ2 = q; worst_v2 = v; }
855 if (close1 == 0 && close2 == 0)
break;
858 if (close1 > 0 && close2 > 0) {
859 remove_from_1 = (worstQ1 > worstQ2);
861 remove_from_1 = (close1 > 0);
864 int v_remove = remove_from_1
865 ? (worst_v1 >= 0 ? worst_v1 : first_close_v1)
866 : (worst_v2 >= 0 ? worst_v2 : first_close_v2);
867 if (v_remove < 0)
break;
869 bool ok = remove_from_1
873 if (remove_from_1) ++removed_phase2_s1;
else ++removed_phase2_s2;
878 LOG_DEBUG <<
" removeOverlapSurfaces: phase1 removed " << removed_phase1_s1
879 <<
" faces from S1, " << removed_phase1_s2 <<
" from S2;"
880 <<
" phase2 removed " << removed_phase2_s1 <<
" from S1, "
881 << removed_phase2_s2 <<
" from S2 (meanEdge=" << meanEdge
882 <<
", minGap=" << min_gap <<
")";
1078 const std::vector<int>& pair_order,
1080 if (all_pairs.empty()) {
1084 if (pair_order.empty()) {
1085 LOG_WARN <<
"Empty pair order, using simple stitching";
1089 LOG_INFO <<
"=== Geometric Stitching (with overlap removal) ===";
1091 std::ostringstream order_oss;
1092 for (
int idx : pair_order) order_oss << idx <<
" ";
1093 LOG_INFO <<
" Stitching order: " << order_oss.str();
1095 LOG_INFO <<
" pair_forced=" << (pair_forced ?
"true" :
"false");
1097 size_t first_idx = pair_order[0] - 1;
1098 if (first_idx >= all_pairs.size()) {
1099 LOG_ERROR <<
"Invalid pair index in pair_order";
1103 size_t nPairs = all_pairs.size();
1104 size_t nFrames = all_pairs[first_idx].Points3D.size();
1111 std::vector<int> cur_faces = all_pairs[first_idx].Faces;
1112 std::vector<double> cur_faceColors = all_pairs[first_idx].FaceColors;
1113 std::vector<int> cur_facePairInds(cur_faces.size() / 3, pair_order[0]);
1115 std::vector<Point3DFrame> cur_pts3d = all_pairs[first_idx].Points3D;
1116 std::vector<std::vector<double>> cur_corrComb = all_pairs[first_idx].corrComb;
1117 std::vector<int> cur_pointPairInds(cur_pts3d[0].x.size(), pair_order[0]);
1120 std::vector<int> pairIndices(nPairs * 2, 0);
1121 if (all_pairs[first_idx].cameraPairInd.size() >= 2) {
1122 pairIndices[first_idx * 2] = all_pairs[first_idx].cameraPairInd[0];
1123 pairIndices[first_idx * 2 + 1] = all_pairs[first_idx].cameraPairInd[1];
1126 LOG_INFO <<
" Pair " << pair_order[0] <<
" (base): "
1127 << cur_pts3d[0].x.size() <<
" points, "
1128 << cur_faces.size() / 3 <<
" faces";
1131 for (
size_t ipair = 1; ipair < pair_order.size(); ++ipair) {
1132 size_t pair_idx = pair_order[ipair] - 1;
1133 if (pair_idx >= all_pairs.size()) {
1134 LOG_ERROR <<
"Invalid pair index " << pair_order[ipair];
1138 const auto& next = all_pairs[pair_idx];
1139 std::vector<int> next_faces = next.Faces;
1140 std::vector<double> next_faceColors = next.FaceColors;
1143 size_t nV1 = cur_pts3d[0].x.size();
1144 size_t nV2 = next.Points3D[0].x.size();
1145 std::vector<Eigen::Vector3d> V1(nV1), V2(nV2);
1146 for (
size_t i = 0; i < nV1; ++i)
1147 V1[i] = Eigen::Vector3d(cur_pts3d[0].x[i], cur_pts3d[0].y[i], cur_pts3d[0].z[i]);
1148 for (
size_t i = 0; i < nV2; ++i)
1149 V2[i] = Eigen::Vector3d(next.Points3D[0].x[i], next.Points3D[0].y[i], next.Points3D[0].z[i]);
1153 size_t nF1 = cur_faces.size() / 3;
1154 std::vector<bool> keep1(nF1,
true);
1155 for (
size_t i = 0; i < nF1; ++i) {
1156 int v0=cur_faces[i*3], v1=cur_faces[i*3+1], v2=cur_faces[i*3+2];
1157 if (
static_cast<size_t>(v0)>=V1.size()||
static_cast<size_t>(v1)>=V1.size()||
static_cast<size_t>(v2)>=V1.size()||
1158 V1[v0].hasNaN()||V1[v1].hasNaN()||V1[v2].hasNaN())
1163 cur_facePairInds =
filterByMask(cur_facePairInds, keep1);
1166 size_t nF2 = next_faces.size() / 3;
1167 std::vector<bool> keep2(nF2,
true);
1168 for (
size_t i = 0; i < nF2; ++i) {
1169 int v0=next_faces[i*3], v1=next_faces[i*3+1], v2=next_faces[i*3+2];
1170 if (
static_cast<size_t>(v0)>=V2.size()||
static_cast<size_t>(v1)>=V2.size()||
static_cast<size_t>(v2)>=V2.size()||
1171 V2[v0].hasNaN()||V2[v1].hasNaN()||V2[v2].hasNaN())
1175 next_faceColors =
filterByMask(next_faceColors, keep2);
1181 double meanEdge = 0.0;
1183 for (
double e : edgeLens1) {
if (!std::isnan(e)) { meanEdge += e; cnt++; } }
1184 for (
double e : edgeLens2) {
if (!std::isnan(e)) { meanEdge += e; cnt++; } }
1185 if (cnt > 0) meanEdge /= cnt;
1187 double minGap = 0.4 * meanEdge;
1188 double minDistValue = 5.0 * minGap;
1193 cur_faces, next_faces, V1, V2, minGap, pair_forced);
1195 size_t rem1 = std::count(keep1.begin(), keep1.end(),
false);
1196 size_t rem2 = std::count(keep2.begin(), keep2.end(),
false);
1197 LOG_DEBUG <<
" Overlap removal: " << rem1 <<
" faces from S1, " << rem2 <<
" from S2";
1202 cur_facePairInds =
filterByMask(cur_facePairInds, keep1);
1204 next_faceColors =
filterByMask(next_faceColors, keep2);
1210 size_t remB1 = std::count(abf1.begin(), abf1.end(),
false);
1214 cur_facePairInds =
filterByMask(cur_facePairInds, abf1);
1215 LOG_DEBUG <<
" Removed " << remB1 <<
" all-boundary faces from S1";
1221 size_t remB2 = std::count(abf2.begin(), abf2.end(),
false);
1225 LOG_DEBUG <<
" Removed " << remB2 <<
" all-boundary faces from S2";
1236 std::vector<Eigen::Vector3d> V_combined;
1237 V_combined.reserve(nV1 + nV2);
1238 V_combined.insert(V_combined.end(), V1.begin(), V1.end());
1239 V_combined.insert(V_combined.end(), V2.begin(), V2.end());
1242 std::vector<int> next_faces_offset;
1243 next_faces_offset.reserve(next_faces.size());
1244 for (
int idx : next_faces) next_faces_offset.push_back(idx +
static_cast<int>(nV1));
1247 std::vector<int> bnd2_offset;
1248 bnd2_offset.reserve(bnd2.size());
1249 for (
int idx : bnd2) bnd2_offset.push_back(idx +
static_cast<int>(nV1));
1255 std::vector<int> zip_faces;
1257 for (
size_t ib1 = 0; ib1 < groups1.size(); ++ib1) {
1259 std::vector<int> edges_g1;
1260 for (
int eidx : groups1[ib1]) {
1261 edges_g1.push_back(bnd1[eidx*2]);
1262 edges_g1.push_back(bnd1[eidx*2+1]);
1265 if (curve1.size() < 2)
continue;
1267 for (
size_t ib2 = 0; ib2 < groups2.size(); ++ib2) {
1268 std::vector<int> edges_g2;
1269 for (
int eidx : groups2[ib2]) {
1270 edges_g2.push_back(bnd2_offset[eidx*2]);
1271 edges_g2.push_back(bnd2_offset[eidx*2+1]);
1274 if (curve2.size() < 2)
continue;
1277 double group_min_dist = std::numeric_limits<double>::infinity();
1278 for (
int v1_idx : curve1) {
1279 if (V_combined[v1_idx].hasNaN())
continue;
1280 for (
int v2_idx : curve2) {
1281 if (V_combined[v2_idx].hasNaN())
continue;
1282 double d = (V_combined[v1_idx] - V_combined[v2_idx]).norm();
1283 group_min_dist = std::min(group_min_dist, d);
1288 if (group_min_dist > minDistValue)
continue;
1292 std::vector<double> d12(curve1.size(), std::numeric_limits<double>::infinity());
1293 for (
size_t ci = 0; ci < curve1.size(); ++ci) {
1294 if (V_combined[curve1[ci]].hasNaN())
continue;
1295 for (
int v2_idx : curve2) {
1296 if (V_combined[v2_idx].hasNaN())
continue;
1297 double d = (V_combined[curve1[ci]] - V_combined[v2_idx]).norm();
1298 d12[ci] = std::min(d12[ci], d);
1303 std::vector<int> sub_curve1;
1304 for (
size_t ci = 0; ci < curve1.size(); ++ci) {
1305 if (d12[ci] < minDistValue) {
1306 sub_curve1.push_back(curve1[ci]);
1309 if (sub_curve1.size() < 2)
continue;
1313 std::vector<size_t> closest_c2_indices;
1314 for (
int v1_idx : sub_curve1) {
1315 double best_d = std::numeric_limits<double>::infinity();
1317 for (
size_t j = 0; j < curve2.size(); ++j) {
1318 if (V_combined[curve2[j]].hasNaN())
continue;
1319 double d = (V_combined[v1_idx] - V_combined[curve2[j]]).norm();
1320 if (d < best_d) { best_d = d; best_j = j; }
1322 closest_c2_indices.push_back(best_j);
1326 size_t c2_min = *std::min_element(closest_c2_indices.begin(), closest_c2_indices.end());
1327 size_t c2_max = *std::max_element(closest_c2_indices.begin(), closest_c2_indices.end());
1329 std::vector<int> sub_curve2;
1330 for (
size_t ci = c2_min; ci <= c2_max; ++ci) {
1331 sub_curve2.push_back(curve2[ci]);
1333 if (sub_curve2.size() < 2)
continue;
1336 double d_start_start = (V_combined[sub_curve1.front()] - V_combined[sub_curve2.front()]).norm();
1337 double d_start_end = (V_combined[sub_curve1.front()] - V_combined[sub_curve2.back()]).norm();
1338 if (d_start_end < d_start_start) {
1339 std::reverse(sub_curve2.begin(), sub_curve2.end());
1343 while (sub_curve1.size() >= 2 && sub_curve2.size() >= 2) {
1344 double d_cur = (V_combined[sub_curve1.front()] - V_combined[sub_curve2.front()]).norm();
1345 double d_adv1 = (V_combined[sub_curve1[1]] - V_combined[sub_curve2.front()]).norm();
1346 double d_adv2 = (V_combined[sub_curve1.front()] - V_combined[sub_curve2[1]]).norm();
1347 if (d_adv1 < d_cur) { sub_curve1.erase(sub_curve1.begin()); }
1348 else if (d_adv2 < d_cur) { sub_curve2.erase(sub_curve2.begin()); }
1351 while (sub_curve1.size() >= 2 && sub_curve2.size() >= 2) {
1352 double d_cur = (V_combined[sub_curve1.back()] - V_combined[sub_curve2.back()]).norm();
1353 double d_adv1 = (V_combined[sub_curve1[sub_curve1.size()-2]] - V_combined[sub_curve2.back()]).norm();
1354 double d_adv2 = (V_combined[sub_curve1.back()] - V_combined[sub_curve2[sub_curve2.size()-2]]).norm();
1355 if (d_adv1 < d_cur) { sub_curve1.pop_back(); }
1356 else if (d_adv2 < d_cur) { sub_curve2.pop_back(); }
1360 if (sub_curve1.size() < 2 || sub_curve2.size() < 2)
continue;
1364 zip_faces.insert(zip_faces.end(), zipped.begin(), zipped.end());
1368 size_t nZipFaces = zip_faces.size() / 3;
1369 if (nZipFaces > 0) {
1370 LOG_DEBUG <<
" Zipped " << nZipFaces <<
" new faces between boundaries";
1375 std::vector<int> combined_faces;
1376 combined_faces.insert(combined_faces.end(), cur_faces.begin(), cur_faces.end());
1377 combined_faces.insert(combined_faces.end(), next_faces_offset.begin(), next_faces_offset.end());
1378 combined_faces.insert(combined_faces.end(), zip_faces.begin(), zip_faces.end());
1381 std::vector<double> combined_faceColors;
1382 combined_faceColors.insert(combined_faceColors.end(), cur_faceColors.begin(), cur_faceColors.end());
1383 combined_faceColors.insert(combined_faceColors.end(), next_faceColors.begin(), next_faceColors.end());
1385 if (!cur_faceColors.empty() && !next_faceColors.empty()) {
1386 double avg_col = 0.0;
1387 for (
double c : cur_faceColors) avg_col += c;
1388 for (
double c : next_faceColors) avg_col += c;
1389 avg_col /= (cur_faceColors.size() + next_faceColors.size());
1390 combined_faceColors.insert(combined_faceColors.end(), nZipFaces, avg_col);
1394 std::vector<int> combined_facePairInds;
1395 combined_facePairInds.insert(combined_facePairInds.end(), cur_facePairInds.begin(), cur_facePairInds.end());
1396 combined_facePairInds.insert(combined_facePairInds.end(), next_faces.size() / 3, pair_order[ipair]);
1398 combined_facePairInds.insert(combined_facePairInds.end(), nZipFaces,
static_cast<int>(nPairs + ipair));
1401 std::vector<Point3DFrame> combined_pts3d(nFrames);
1402 std::vector<std::vector<double>> combined_corrComb(nFrames);
1403 for (
size_t f = 0; f < nFrames; ++f) {
1405 combined_pts3d[f].x = cur_pts3d[f].x;
1406 combined_pts3d[f].x.insert(combined_pts3d[f].x.end(), next.Points3D[f].x.begin(), next.Points3D[f].x.end());
1407 combined_pts3d[f].y = cur_pts3d[f].y;
1408 combined_pts3d[f].y.insert(combined_pts3d[f].y.end(), next.Points3D[f].y.begin(), next.Points3D[f].y.end());
1409 combined_pts3d[f].z = cur_pts3d[f].z;
1410 combined_pts3d[f].z.insert(combined_pts3d[f].z.end(), next.Points3D[f].z.begin(), next.Points3D[f].z.end());
1413 std::set<int> referenced_verts;
1414 for (
size_t fi = 0; fi < combined_faces.size(); ++fi) {
1415 referenced_verts.insert(combined_faces[fi]);
1417 size_t totalV = combined_pts3d[f].x.size();
1418 for (
size_t vi = 0; vi < totalV; ++vi) {
1419 if (referenced_verts.find(
static_cast<int>(vi)) == referenced_verts.end()) {
1420 combined_pts3d[f].x[vi] = std::nan(
"");
1421 combined_pts3d[f].y[vi] = std::nan(
"");
1422 combined_pts3d[f].z[vi] = std::nan(
"");
1427 if (f < cur_corrComb.size() && f < next.corrComb.size()) {
1428 combined_corrComb[f] = cur_corrComb[f];
1429 combined_corrComb[f].insert(combined_corrComb[f].end(), next.corrComb[f].begin(), next.corrComb[f].end());
1434 std::vector<int> combined_pointPairInds;
1435 combined_pointPairInds.insert(combined_pointPairInds.end(), cur_pointPairInds.begin(), cur_pointPairInds.end());
1436 combined_pointPairInds.insert(combined_pointPairInds.end(), nV2, pair_order[ipair]);
1439 cur_faces = std::move(combined_faces);
1440 cur_faceColors = std::move(combined_faceColors);
1441 cur_facePairInds = std::move(combined_facePairInds);
1442 cur_pts3d = std::move(combined_pts3d);
1443 cur_corrComb = std::move(combined_corrComb);
1444 cur_pointPairInds = std::move(combined_pointPairInds);
1446 if (next.cameraPairInd.size() >= 2) {
1447 pairIndices[pair_idx * 2] = next.cameraPairInd[0];
1448 pairIndices[pair_idx * 2 + 1] = next.cameraPairInd[1];
1451 LOG_INFO <<
" Pair " << pair_order[ipair] <<
" stitched: "
1452 << cur_pts3d[0].x.size() <<
" points, "
1453 << cur_faces.size() / 3 <<
" faces";
1459 size_t totalV = cur_pts3d[0].x.size();
1460 std::vector<Eigen::Vector3d> V_final(totalV);
1461 for (
size_t i = 0; i < totalV; ++i)
1462 V_final[i] = Eigen::Vector3d(cur_pts3d[0].x[i], cur_pts3d[0].y[i], cur_pts3d[0].z[i]);
1468 for (
const auto& group : final_groups) {
1470 std::vector<int> edges_g;
1471 for (
int eidx : group) {
1472 edges_g.push_back(final_bnd[eidx*2]);
1473 edges_g.push_back(final_bnd[eidx*2+1]);
1477 if (edges_g.size() == 6) {
1478 std::set<int> verts_set;
1479 for (
int v : edges_g) verts_set.insert(v);
1480 if (verts_set.size() == 3) {
1481 auto it = verts_set.begin();
1482 int v0 = *it++, v1 = *it++, v2 = *it;
1483 cur_faces.push_back(v0);
1484 cur_faces.push_back(v1);
1485 cur_faces.push_back(v2);
1488 int fill_pair_ind = 0;
1489 if (!cur_facePairInds.empty()) {
1490 fill_pair_ind = cur_facePairInds[cur_facePairInds.size() / 2];
1492 cur_facePairInds.push_back(fill_pair_ind);
1495 double fill_color = 128.0;
1496 if (!cur_faceColors.empty()) {
1498 for (
double c : cur_faceColors) sum += c;
1499 fill_color = sum / cur_faceColors.size();
1501 cur_faceColors.push_back(fill_color);
1507 LOG_DEBUG <<
" Filled " << filled <<
" single-triangle holes";
1512 std::set<int> stitched_pairs_set(pair_order.begin(), pair_order.end());
1513 for (
size_t ip = 0; ip < nPairs; ++ip) {
1514 int pairId =
static_cast<int>(ip + 1);
1515 if (stitched_pairs_set.count(pairId))
continue;
1517 const auto& extra = all_pairs[ip];
1518 size_t voff = cur_pts3d[0].x.size();
1519 size_t extra_nFaces = extra.Faces.size() / 3;
1520 size_t extra_nVerts = extra.Points3D[0].x.size();
1523 for (
int idx : extra.Faces) {
1524 cur_faces.push_back(idx +
static_cast<int>(voff));
1526 cur_faceColors.insert(cur_faceColors.end(), extra.FaceColors.begin(), extra.FaceColors.end());
1527 cur_facePairInds.insert(cur_facePairInds.end(), extra_nFaces, pairId);
1530 for (
size_t f = 0; f < nFrames && f < extra.Points3D.size(); ++f) {
1531 cur_pts3d[f].x.insert(cur_pts3d[f].x.end(), extra.Points3D[f].x.begin(), extra.Points3D[f].x.end());
1532 cur_pts3d[f].y.insert(cur_pts3d[f].y.end(), extra.Points3D[f].y.begin(), extra.Points3D[f].y.end());
1533 cur_pts3d[f].z.insert(cur_pts3d[f].z.end(), extra.Points3D[f].z.begin(), extra.Points3D[f].z.end());
1534 if (f < extra.corrComb.size() && f < cur_corrComb.size()) {
1535 cur_corrComb[f].insert(cur_corrComb[f].end(), extra.corrComb[f].begin(), extra.corrComb[f].end());
1538 cur_pointPairInds.insert(cur_pointPairInds.end(), extra_nVerts, pairId);
1540 if (extra.cameraPairInd.size() >= 2) {
1541 pairIndices[ip * 2] = extra.cameraPairInd[0];
1542 pairIndices[ip * 2 + 1] = extra.cameraPairInd[1];
1545 LOG_INFO <<
" Pair " << pairId <<
" appended (not stitched): "
1546 << extra_nVerts <<
" points, " << extra_nFaces <<
" faces";
1551 result.
Faces = std::move(cur_faces);
1552 result.
FaceColors = std::move(cur_faceColors);
1554 result.
Points3D = std::move(cur_pts3d);
1555 result.
corrComb = std::move(cur_corrComb);
1560 size_t total_faces = result.
Faces.size() / 3;
1561 size_t total_points = result.
Points3D.empty() ? 0 : result.
Points3D[0].x.size();
1568 for (
size_t f = 0; f < nFrames; ++f) {
1570 result.
FaceCorrComb[f].resize(total_faces, std::nan(
""));
1571 for (
size_t i = 0; i < total_faces; ++i) {
1572 int v0 = result.
Faces[i*3], v1 = result.
Faces[i*3+1], v2 = result.
Faces[i*3+2];
1573 if (
static_cast<size_t>(v0) < result.
corrComb[f].size() &&
1574 static_cast<size_t>(v1) < result.
corrComb[f].size() &&
1575 static_cast<size_t>(v2) < result.
corrComb[f].size()) {
1581 result.
FaceCentroids[f].resize(total_faces * 3, std::nan(
""));
1582 for (
size_t i = 0; i < total_faces; ++i) {
1583 int v0 = result.
Faces[i*3], v1 = result.
Faces[i*3+1], v2 = result.
Faces[i*3+2];
1584 if (
static_cast<size_t>(v0) < total_points &&
static_cast<size_t>(v1) < total_points &&
static_cast<size_t>(v2) < total_points) {
1592 result.
Disp.
DispVec[f].resize(total_points * 3, std::nan(
""));
1593 result.
Disp.
DispMgn[f].resize(total_points, std::nan(
""));
1594 for (
size_t k = 0; k < total_points; ++k) {
1601 result.
Disp.
DispMgn[f][k] = std::sqrt(dx*dx + dy*dy + dz*dz);
1606 for (
const auto& pair : all_pairs) {
1610 for (
const auto& pair : all_pairs) {
1615 LOG_INFO <<
" Geometric stitching complete: "
1616 << total_points <<
" points, " << total_faces <<
" faces";