TEST2
/**
* Copyright 2018, ftdlyc <yclu.cn@gmail.com>
*
* 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.
*
* This library 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 this library; if not, see <http://www.gnu.org/licenses/>.
*/
/*
% Copyright 2012. All rights reserved.
% Author: Andreas Geiger
% Institute of Measurement and Control Systems (MRT)
% Karlsruhe Institute of Technology (KIT), Germany
% This is free software; you can redistribute it and/or modify it under the
% terms of the GNU General Public License as published by the Free Software
% Foundation; either version 3 of the License, or any later version.
% This software 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 General Public License for more details.
% You should have received a copy of the GNU General Public License along with
% this software; if not, write to the Free Software Foundation, Inc., 51 Franklin
% Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <algorithm>
#include <chrono>
#include <iostream>
#include <random>
#include <vector>
#include <opencv2/opencv.hpp>
#include "libcbdetect/board_energy.h"
#include "libcbdetect/boards_from_corners.h"
#include "libcbdetect/config.h"
#include "libcbdetect/filter_board.h"
#include "libcbdetect/grow_board.h"
#include "libcbdetect/init_board.h"
namespace cbdetect {
/*该函数的作用是在图像上显示角点和提议的位置,并在图像上显示方向。
具体实现过程是先将灰度图像转换为BGR图像,然后在图像上绘制角点和提议的位置,最后在图像上显示方向。*/
void debug_grow_process(const cv::Mat& img, const Corner& corners, const Board& board,
const std::vector<cv::Point2i>& proposal, int direction, bool type) {
cv::Mat img_show;
if(img.channels() != 3) {
#if CV_VERSION_MAJOR >= 4
cv::cvtColor(img, img_show, cv::COLOR_GRAY2BGR);
#else
cv::cvtColor(img, img_show, CV_GRAY2BGR);
#endif
} else {
img_show = img.clone();
}
cv::Point2d mean(0.0, 0.0);
for(int i = 0; i < board.idx.size(); ++i) {
for(int j = 0; j < board.idx[i].size(); ++j) {
if(board.idx[i][j] < 0) {
continue;
}
cv::circle(img_show, corners.p[board.idx[i][j]], 4, cv::Scalar(255, 0, 0), -1);//蓝色
cv::putText(img_show, std::to_string(board.idx[i][j]),
cv::Point2i(corners.p[board.idx[i][j]].x - 12, corners.p[board.idx[i][j]].y - 6),
cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 1);
mean += corners.p[board.idx[i][j]];
}
}
mean /= (double)(board.num);
mean.x -= 10;
mean.y += 10;
cv::putText(img_show, std::to_string(direction), mean,
cv::FONT_HERSHEY_SIMPLEX, 1.3, cv::Scalar(196, 196, 0), 2);
for(const auto& i : proposal) {
if(board.idx[i.y][i.x] < 0) {
continue;
}
if(type) {
cv::circle(img_show, corners.p[board.idx[i.y][i.x]], 4, cv::Scalar(0, 255, 0), -1);//绿色
} else {
cv::circle(img_show, corners.p[board.idx[i.y][i.x]], 4, cv::Scalar(0, 0, 255), -1);//红色
}
}
cv::imshow("grow_process", img_show);
cv::waitKey();
}
/*
1.在棋盘扩展的过程中,记录每个棋盘格的边界,即记录每个棋盘格的最小和最大行列坐标。
2.在新生成的棋盘格与已有的棋盘格进行比较时,判断它们的边界是否相邻,如果相邻,则将它们合并成为一个新的棋盘格。
3.合并棋盘格时,需要将它们的角点和标记向量合并,并更新新的棋盘格的边界。
4.最后,将新的棋盘格加入棋盘格向量中。
*/
void boards_from_corners(const cv::Mat& img, const Corner& corners, std::vector<Board>& boards, const Params& params) {
// intialize boards
boards.clear();
Board board;
std::vector<int> used(corners.p.size(), 0);
int start = 0;
if(!params.overlay)
{
std::default_random_engine e;
auto time = std::chrono::system_clock::now().time_since_epoch();
e.seed(static_cast<unsigned long>(time.count()));
start = e() % corners.p.size();
}
int n = 0;
while(n++ < corners.p.size())
{
int i = (n + start) % corners.p.size();
if(used[i] == 1 || !init_board(corners, used, board, i))
{
continue;
}
cv::Point3i maxE_pos = board_energy(corners, board, params);
double energy = board.energy[maxE_pos.y][maxE_pos.x][maxE_pos.z];
if(energy > -6.0)
{
for(int jj = 0; jj < 3; ++jj)
{
for(int ii = 0; ii < 3; ++ii)
{
used[board.idx[jj][ii]] = 0;
}
}
continue;
}
while(1)
{
int num_corners = board.num;
for(int j = 0; j < (params.corner_type == MonkeySaddlePoint ? 6 : 4); ++j)
{
std::vector<cv::Point2i> proposal;
GrowType grow_type = grow_board(corners, used, board, proposal, j, params);
if(grow_type == GrowType_Failure)
{
continue;
}
if(params.show_grow_processing)
{
for(int ii = 0; ii < board.idx.size(); ++ii)
{
for(int jj = 0; jj < board.idx[ii].size(); ++jj)
{
std::cout << board.idx[ii][jj] << " ";
}
std::cout << "\n";
}
std::cout << "\n";
debug_grow_process(img, corners, board, proposal, j, false);
}
filter_board(corners, used, board, proposal, energy, params);
if(params.show_grow_processing) {
for(int ii = 0; ii < board.idx.size(); ++ii)
{
for(int jj = 0; jj < board.idx[ii].size(); ++jj)
{
std::cout << board.idx[ii][jj] << " ";
}
std::cout << "\n";
}
std::cout << "\n";
debug_grow_process(img, corners, board, proposal, j, true);
}
if(grow_type == GrowType_Inside) {
--j;
}
}
if(board.num == num_corners) {
break;
}
}
// check if new chessboard proposal overlaps with existing chessboards
bool merged = false;
for(int j = 0; j < boards.size(); ++j)
{
// check if new chessboard proposal overlaps with existing chessboards
bool overlap = false;
for(int k1 = 0; k1 < board.idx.size(); ++k1)
{
for(int k2 = 0; k2 < board.idx[0].size(); ++k2)
{
for(int l1 = 0; l1 < boards[j].idx.size(); ++l1)
{
for(int l2 = 0; l2 < boards[j].idx[0].size(); ++l2)
{
if(board.idx[k1][k2] != -1 && board.idx[k1][k2] != -2 && board.idx[k1][k2] == boards[j].idx[l1][l2])
{
overlap = true;
goto GOTO_BREAK;
}
}
}
}
}
GOTO_BREAK:;
if(overlap)
{
// check if the two boards are adjacent
bool adjacent = false;
for(int k1 = 0; k1 < board.idx.size(); ++k1)
{
for(int k2 = 0; k2 < board.idx[0].size(); ++k2)
{
if(board.idx[k1][k2] != -1 && board.idx[k1][k2] != -2)
{
for(int l1 = 0; l1 < boards[j].idx.size(); ++l1)
{
for(int l2 = 0; l2 < boards[j].idx[0].size(); ++l2)
{
if(boards[j].idx[l1][l2] != -1 && boards[j].idx[l1][l2] != -2)
{
if(abs(board.idx[k1][k2] - boards[j].idx[l1][l2]) == 1 || abs(board.idx[k1][k2] - boards[j].idx[l1][l2]) == params.board_w)
{
adjacent = true;
goto GOTO_BREAK2;
}
}
}
}
}
}
}
GOTO_BREAK2:;
if(adjacent)
{
// merge the two boards
Board new_board;
std::vector<int> new_used(corners.p.size(), 0);
for(int k1 = 0; k1 < board.idx.size(); ++k1)
{
for(int k2 = 0; k2 < board.idx[0].size(); ++k2)
{
if(board.idx[k1][k2] != -1 && board.idx[k1][k2] != -2)
{
new_board.idx[k1][k2] = board.idx[k1][k2];
new_board.energy[k1][k2] = board.energy[k1][k2];
new_used[board.idx[k1][k2]] = 1;
}
}
}
for(int k1 = 0; k1 < boards[j].idx.size(); ++k1)
{
for(int k2 = 0; k2 < boards[j].idx[0].size(); ++k2)
{
if(boards[j].idx[k1][k2] != -1 && boards[j].idx[k1][k2] != -2)
{
new_board.idx[k1 + board.idx.size() - 1][k2] = boards[j].idx[k1][k2];
new_board.energy[k1 + board.idx.size() - 1][k2] = boards[j].energy[k1][k2];
new_used[boards[j].idx[k1][k2]] = 1;
}
}
}
new_board.num = board.num + boards[j].num - 2;
new_board.min_row = std::min(board.min_row, boards[j].min_row);
new_board.max_row = std::max(board.max_row, boards[j].max_row);
new_board.min_col = std::min(board.min_col, boards[j].min_col);
new_board.max_col = std::max(board.max_col, boards[j].max_col);
board = new_board;
used = new_used;
merged = true;
break;
}
}
}
if(!merged)
{
boards.emplace_back(board);
}
std::fill(used.begin(), used.end(), 0);
n += 2;
}
}
TEST3
void boards_from_corners(const cv::Mat& img, const Corner& corners, std::vector<Board>& boards, const Params& params) {
// intialize boards
boards.clear();
Board board;
std::vector<int> used(corners.p.size(), 0);
int start = 0;
if(!params.overlay)
{
std::default_random_engine e;
auto time = std::chrono::system_clock::now().time_since_epoch();
e.seed(static_cast<unsigned long>(time.count()));
start = e() % corners.p.size();
}
int n = 0;
while(n++ < corners.p.size())
{
int i = (n + start) % corners.p.size();
if(used[i] == 1 || !init_board(corners, used, board, i))
{
continue;
}
cv::Point3i maxE_pos = board_energy(corners, board, params);
double energy = board.energy[maxE_pos.y][maxE_pos.x][maxE_pos.z];
if(energy > -6.0)
{
for(int jj = 0; jj < 3; ++jj)
{
for(int ii = 0; ii < 3; ++ii)
{
used[board.idx[jj][ii]] = 0;
}
}
continue;
}
while(1)
{
int num_corners = board.num;
for(int j = 0; j < (params.corner_type == MonkeySaddlePoint ? 6 : 4); ++j)
{
std::vector<cv::Point2i> proposal;
GrowType grow_type = grow_board(corners, used, board, proposal, j, params);
if(grow_type == GrowType_Failure)
{
continue;
}
if(params.show_grow_processing)
{
for(int ii = 0; ii < board.idx.size(); ++ii)
{
for(int jj = 0; jj < board.idx[ii].size(); ++jj)
{
std::cout << board.idx[ii][jj] << " ";
}
std::cout << "\n";
}
std::cout << "\n";
debug_grow_process(img, corners, board, proposal, j, false);
}
filter_board(corners, used, board, proposal, energy, params);
if(params.show_grow_processing) {
for(int ii = 0; ii < board.idx.size(); ++ii)
{
for(int jj = 0; jj < board.idx[ii].size(); ++jj)
{
std::cout << board.idx[ii][jj] << " ";
}
std::cout << "\n";
}
std::cout << "\n";
debug_grow_process(img, corners, board, proposal, j, true);
}
if(grow_type == GrowType_Inside) {
--j;
}
}
if(board.num == num_corners) {
break;
}
}
// check if new chessboard proposal overlaps with existing chessboards
std::vector<int> overlap_idx;
for(int j = 0; j < boards.size(); ++j)
{
bool is_overlap = false;
for(int k1 = 0; k1 < board.idx.size(); ++k1)
{
for(int k2 = 0; k2 < board.idx[0].size(); ++k2)
{
for(int l1 = 0; l1 < boards[j].idx.size(); ++l1)
{
for(int l2 = 0; l2 < boards[j].idx[0].size(); ++l2)
{
if(board.idx[k1][k2] != -1 && board.idx[k1][k2] != -2 && board.idx[k1][k2] == boards[j].idx[l1][l2])
{
is_overlap = true;
overlap_idx.push_back(j);
break;
}
}
if(is_overlap) {
break;
}
}
if(is_overlap) {
break;
}
}
if(is_overlap) {
break;
}
}
}
// merge overlapping chessboards
if(!overlap_idx.empty())
{
Board merged_board;
std::vector<int> merged_used(corners.p.size(), 0);
for(int j = 0; j < overlap_idx.size(); ++j)
{
for(int k1 = 0; k1 < boards[overlap_idx[j]].idx.size(); ++k1)
{
for(int k2 = 0; k2 < boards[overlap_idx[j]].idx[0].size(); ++k2)
{
if(boards[overlap_idx[j]].idx[k1][k2] != -1 && boards[overlap_idx[j]].idx[k1][k2] != -2)
{
merged_board.idx[k1][k2] = boards[overlap_idx[j]].idx[k1][k2];
merged_used[boards[overlap_idx[j]].idx[k1][k2]] = 1;
merged_board.num++;
}
}
}
boards.erase(boards.begin() + overlap_idx[j]);
for(int k = j + 1; k < overlap_idx.size(); ++k)
{
if(overlap_idx[k] > overlap_idx[j])
{
overlap_idx[k]--;
}
}
}
for(int j = 0; j < board.idx.size(); ++j)
{
for(int k = 0; k < board.idx[0].size(); ++k)
{
if(board.idx[j][k] != -1 && board.idx[j][k] != -2 && !merged_used[board.idx[j][k]])
{
merged_board.idx[j][k] = board.idx[j][k];
merged_board.num++;
}
}
}
boards.push_back(merged_board);
}
else
{
boards.push_back(board);
}
std::fill(used.begin(), used.end(), 0);
n += 2;
}
}
TEST4
/**
* Copyright 2018, ftdlyc <yclu.cn@gmail.com>
*
* 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.
*
* This library 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 this library; if not, see <http://www.gnu.org/licenses/>.
*/
/*
% Copyright 2012. All rights reserved.
% Author: Andreas Geiger
% Institute of Measurement and Control Systems (MRT)
% Karlsruhe Institute of Technology (KIT), Germany
% This is free software; you can redistribute it and/or modify it under the
% terms of the GNU General Public License as published by the Free Software
% Foundation; either version 3 of the License, or any later version.
% This software 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 General Public License for more details.
% You should have received a copy of the GNU General Public License along with
% this software; if not, write to the Free Software Foundation, Inc., 51 Franklin
% Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <algorithm>
#include <chrono>
#include <iostream>
#include <random>
#include <vector>
#include <opencv2/opencv.hpp>
#include "libcbdetect/board_energy.h"
#include "libcbdetect/boards_from_corners.h"
#include "libcbdetect/config.h"
#include "libcbdetect/filter_board.h"
#include "libcbdetect/grow_board.h"
#include "libcbdetect/init_board.h"
namespace cbdetect {
/*该函数的作用是在图像上显示角点和提议的位置,并在图像上显示方向。
具体实现过程是先将灰度图像转换为BGR图像,然后在图像上绘制角点和提议的位置,最后在图像上显示方向。*/
void debug_grow_process(const cv::Mat& img, const Corner& corners, const Board& board,
const std::vector<cv::Point2i>& proposal, int direction, bool type) {
cv::Mat img_show;
if(img.channels() != 3) {
#if CV_VERSION_MAJOR >= 4
cv::cvtColor(img, img_show, cv::COLOR_GRAY2BGR);
#else
cv::cvtColor(img, img_show, CV_GRAY2BGR);
#endif
} else {
img_show = img.clone();
}
cv::Point2d mean(0.0, 0.0);
for(int i = 0; i < board.idx.size(); ++i) {
for(int j = 0; j < board.idx[i].size(); ++j) {
if(board.idx[i][j] < 0) {
continue;
}
cv::circle(img_show, corners.p[board.idx[i][j]], 4, cv::Scalar(255, 0, 0), -1);//蓝色
cv::putText(img_show, std::to_string(board.idx[i][j]),
cv::Point2i(corners.p[board.idx[i][j]].x - 12, corners.p[board.idx[i][j]].y - 6),
cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 1);
mean += corners.p[board.idx[i][j]];
}
}
mean /= (double)(board.num);
mean.x -= 10;
mean.y += 10;
cv::putText(img_show, std::to_string(direction), mean,
cv::FONT_HERSHEY_SIMPLEX, 1.3, cv::Scalar(196, 196, 0), 2);
for(const auto& i : proposal) {
if(board.idx[i.y][i.x] < 0) {
continue;
}
if(type) {
cv::circle(img_show, corners.p[board.idx[i.y][i.x]], 4, cv::Scalar(0, 255, 0), -1);//绿色
} else {
cv::circle(img_show, corners.p[board.idx[i.y][i.x]], 4, cv::Scalar(0, 0, 255), -1);//红色
}
}
cv::imshow("grow_process", img_show);
cv::waitKey();
}
double distance(const cv::Point2f& p1, const cv::Point2f& p2) {
return std::sqrt(std::pow(p1.x - p2.x, 2) + std::pow(p1.y - p2.y, 2));
}
/*三、从给定的角点中提取棋盘
参数:
角点Corners是一个结构体,包含了角点的坐标信息
棋盘格向量boards是一个存储棋盘格的向量
参数params是一个结构体,包含了一些参数信息
*/
void boards_from_corners(const cv::Mat& img, const Corner& corners, std::vector<Board>& boards, const Params& params) {
// intialize boards
//1.清空棋盘格向量boards,初始化一个棋盘格和一个标记向量,用于记录角点是否被使用过
boards.clear();
Board board;
std::vector<int> used(corners.p.size(), 0);
//2.从一个随机的角点开始
int start = 0;
if(!params.overlay)
{
// start from random index
std::default_random_engine e;
auto time = std::chrono::system_clock::now().time_since_epoch();
e.seed(static_cast<unsigned long>(time.count()));
start = e() % corners.p.size();
}
// for all seed corners do
//3.初始化一个3*3的棋盘格
int n = 0;
while(n++ < corners.p.size())
{
// init 3x3 board from seed i
int i = (n + start) % corners.p.size();
if(used[i] == 1 || !init_board(corners, used, board, i))
{
continue;
}
// check if this is a useful initial guess
//4.检查这个棋盘格是否是一个有用的初始猜测
//如果是,函数会尝试扩展这个棋盘格,直到无法再扩展为止,如果扩展成功,则将这个棋盘格加入棋盘格向量boards中
//board_energy函数返回一个三维坐标,表示具有最大结构能量的位置
cv::Point3i maxE_pos = board_energy(corners, board, params);
double energy = board.energy[maxE_pos.y][maxE_pos.x][maxE_pos.z];
if(energy > -6.0)
{
for(int jj = 0; jj < 3; ++jj)
{
for(int ii = 0; ii < 3; ++ii)
{
used[board.idx[jj][ii]] = 0;
}
}
continue;
}
// grow boards
while(1)
{
int num_corners = board.num;
for(int j = 0; j < (params.corner_type == MonkeySaddlePoint ? 6 : 4); ++j)
{
std::vector<cv::Point2i> proposal;
//调用grow_board函数扩展棋盘格
/*函数的输入参数包括棋盘的四个角点、已使用的角点、棋盘、建议的角点、生长方向和一些参数。
函数的输出是一个枚举类型,表示生长的类型,包括内部角点、边界角点和失败。*/
GrowType grow_type = grow_board(corners, used, board, proposal, j, params);
if(grow_type == GrowType_Failure)
{
continue;
}
if(params.show_grow_processing)
{
for(int ii = 0; ii < board.idx.size(); ++ii)
{
for(int jj = 0; jj < board.idx[ii].size(); ++jj)
{
std::cout << board.idx[ii][jj] << " ";
}
std::cout << "\n";
}
std::cout << "\n";
//调用debug_grow_process函数显示扩展过程的中间结果
debug_grow_process(img, corners, board, proposal, j, false);//打印的是蓝色
}
//调用filter_board函数过滤掉一些不合适的棋盘格方案
filter_board(corners, used, board, proposal, energy, params);
if(params.show_grow_processing) {
for(int ii = 0; ii < board.idx.size(); ++ii)
{
for(int jj = 0; jj < board.idx[ii].size(); ++jj)
{
std::cout << board.idx[ii][jj] << " ";
}
std::cout << "\n";
}
std::cout << "\n";
//调用debug_grow_process函数显示扩展过程的中间结果
debug_grow_process(img, corners, board, proposal, j, true);//打印的是绿色
}
if(grow_type == GrowType_Inside) {
--j;
}
}
// exit loop
if(board.num == num_corners) {
break;
}
}
//如果参数params中的overlay为真,则函数会检查新生成的棋盘格是否与已有的棋盘格重叠
if(!params.overlay) {
boards.emplace_back(board);
continue;
}
std::vector<std::pair<int, double>> overlap;
for(int j = 0; j < boards.size(); ++j)
{
// check if new chessboard proposal overlaps with existing chessboards
//检查新棋盘方案是否与现有棋盘重叠
for(int k1 = 0; k1 < board.idx.size(); ++k1)
{
for(int k2 = 0; k2 < board.idx[0].size(); ++k2)
{
for(int l1 = 0; l1 < boards[j].idx.size(); ++l1)
{
for(int l2 = 0; l2 < boards[j].idx[0].size(); ++l2)
{
if(board.idx[k1][k2] != -1 && board.idx[k1][k2] != -2 && board.idx[k1][k2] == boards[j].idx[l1][l2])
{
cv::Point3i maxE_pos_tmp = board_energy(corners, boards[j], params);
overlap.emplace_back(std::make_pair(j, boards[j].energy[maxE_pos_tmp.y][maxE_pos_tmp.x][maxE_pos_tmp.z]));
goto GOTO_BREAK;
}
}
}
}
}
}
GOTO_BREAK:;
//判断是否有重叠,如果没有重叠,则将新棋盘格加入棋盘格向量中
if(overlap.empty())
{
boards.emplace_back(board);
}
else
{
//如果有重叠,则比较新棋盘格与已有棋盘格的能量值
//如果新棋盘格的能量值更低,则将新棋盘格加入棋盘格向量boards中,并将原有的棋盘格从棋盘格向量中删除
bool is_better = true;
for(int j = 0; j < overlap.size(); ++j)
{
if(overlap[j].second <= energy)
{
is_better = false;
break;
}
}
if(is_better)
{
std::vector<Board> tmp;
for(int j = 0, k = 0; j < boards.size(); ++j)
{
if(overlap[k].first == j) {
continue;
++k;
}
tmp.emplace_back(boards[j]);
}
std::swap(tmp, boards);
boards.emplace_back(board);
}
}
std::fill(used.begin(), used.end(), 0);
n += 2;
}
for(int i = 0; i < boards.size(); ++i) {
for(int j = i + 1; j < boards.size(); ++j) {
// check if two boards have adjacent corners
bool has_adjacent_corners = false;
for(int k1 = 0; k1 < boards[i].idx.size(); ++k1) {
for(int k2 = 0; k2 < boards[i].idx[0].size(); ++k2) {
if(boards[i].idx[k1][k2] != -1 && boards[i].idx[k1][k2] != -2) {
for(int l1 = 0; l1 < boards[j].idx.size(); ++l1) {
for(int l2 = 0; l2 < boards[j].idx[0].size(); ++l2) {
if(boards[j].idx[l1][l2] != -1 && boards[j].idx[l1][l2] != -2) {
double d = distance(corners.p[boards[i].idx[k1][k2]], corners.p[boards[j].idx[l1][l2]]);
if(d <= max_distance) {
has_adjacent_corners = true;
goto GOTO_BREAK2;
}
}
}
}
}
}
}
GOTO_BREAK2:;
// merge two boards if they have adjacent corners
if(has_adjacent_corners) {
Board merged_board;
// copy corners from the first board
for(int k1 = 0; k1 < boards[i].idx.size(); ++k1) {
for(int k2 = 0; k2 < boards[i].idx[0].size(); ++k2) {
if(boards[i].idx[k1][k2] != -1 && boards[i].idx[k1][k2] != -2) {
merged_board.idx[k1][k2] = boards[i].idx[k1][k2];
merged_board.num++;
}
}
}
// copy corners from the second board
for(int l1 = 0; l1 < boards[j].idx.size(); ++l1) {
for(int l2 = 0; l2 < boards[j].idx[0].size(); ++l2) {
if(boards[j].idx[l1][l2] != -1 && boards[j].idx[l1][l2] != -2) {
bool is_duplicate = false;
for(int k1 = 0; k1 < boards[i].idx.size(); ++k1) {
for(int k2 = 0; k2 < boards[i].idx[0].size(); ++k2) {
if(boards[i].idx[k1][k2] == boards[j].idx[l1][l2]) {
is_duplicate = true;
goto GOTO_CONTINUE;
}
}
}
GOTO_CONTINUE:;
if(!is_duplicate) {
merged_board.idx[l1][l2] = boards[j].idx[l1][l2];
merged_board.num++;
}
}
}
}
// remove the two original boards and add the merged board
//移除两个原始板并添加合并后的板
std::vector<Board> tmp;
for(int k = 0; k < boards.size(); ++k) {
if(k != i && k != j) {
tmp.emplace_back(boards[k]);
}
}
tmp.emplace_back(merged_board);
std::swap(tmp, boards);
// start over
i = -1;
break;
}
}
}
}
}
// namespace cbdetect