vins-mono 报错(opencv3->opencv4)
- 报错原因
- error: ‘CV_RGB2GRAY’ was not declared in this scope
- error: ‘CV_BGR2GRAY’ was not declared in this scope
- error: ‘CV_ADAPTIVE_THRESH_MEAN_C’ was not declared in this scope
- error: ‘CV_THRESH_BINARY’ was not declared in this scope
- error: ‘CV_SHAPE_CROSS’ was not declared in this scope
- error: ‘CV_RETR_CCOMP’ was not declared in this scope
- error: ‘CV_CHAIN_APPROX_SIMPLE’ was not declared in this scope
- error: ‘CV_THRESH_BINARY’ was not declared in this scope
- error: ‘CV_RETR_CCOMP’ was not declared in this scope
- error: ‘CV_CHAIN_APPROX_SIMPLE’ was not declared in this scope
- error: ‘CV_THRESH_BINARY_INV’ was not declared in this scope
- 主要修改的文件为catkin_ws/src/VINS-Mono/camera_model/src/chessboard/Chessboard.cc,以下是修改好的Chessboard.cc
- opencv其他错误
- 参考链接
报错原因
opencv3和4版本不兼容,ros中自带的opencv4与自己编译安装的opencv3不兼容
error: ‘CV_RGB2GRAY’ was not declared in this scope
修改方法:
cv::COLOR_RGB2GRAY替换原来的代码CV_RGB2GRAY
error: ‘CV_BGR2GRAY’ was not declared in this scope
修改方法:
将CV_BGR2GRAY改为cv::COLOR_BGR2GRAY
error: ‘CV_ADAPTIVE_THRESH_MEAN_C’ was not declared in this scope
error: ‘CV_THRESH_BINARY’ was not declared in this scope
修改方法:找到文件报错位置,这两个在一个地方
修改前:
cv::adaptiveThreshold(img, thresh_img, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, blockSize, (k/2)*5);
修改后:
cv::adaptiveThreshold(img, thresh_img, 255,cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, blockSize, (k/2)*5);
如果在其他地方遇到error: ‘CV_THRESH_BINARY’ was not declared in this scope,只需CV_THRESH_BINARY改为cv::THRESH_BINARY即可(注意cv的大小写)
error: ‘CV_SHAPE_CROSS’ was not declared in this scope
修改方法:找到文件报错位置
修改前:
cv::Mat kernel1 = cv::getStructuringElement(CV_SHAPE_CROSS, cv::Size(3,3), cv::Point(1,1));
cv::Mat kernel2 = cv::getStructuringElement(CV_SHAPE_RECT, cv::Size(3,3), cv::Point(1,1));
修改后:
cv::Mat kernel1 = cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3,3), cv::Point(1,1));
cv::Mat kernel2 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3,3), cv::Point(1,1));
error: ‘CV_RETR_CCOMP’ was not declared in this scope
error: ‘CV_CHAIN_APPROX_SIMPLE’ was not declared in this scope
修改方法:找到文件报错位置
修改前:
cv::findContours(image, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
修改后:
cv::findContours(image, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE);
error: ‘CV_THRESH_BINARY’ was not declared in this scope
修改方法:找到文件报错位置
修改前:
cv::threshold(white, thresh, threshLevel + blackWhiteGap, 255, CV_THRESH_BINARY);
修改后:
cv::threshold(white, thresh, threshLevel + blackWhiteGap, 255, cv::THRESH_BINARY);
error: ‘CV_RETR_CCOMP’ was not declared in this scope
error: ‘CV_CHAIN_APPROX_SIMPLE’ was not declared in this scope
修改方法:找到文件报错位置
修改前:
cv::findContours(thresh, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
修改后:
cv::findContours(thresh, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE);
error: ‘CV_THRESH_BINARY_INV’ was not declared in this scope
修改方法:找到文件报错位置
修改前:
cv::threshold(black, thresh, threshLevel, 255, CV_THRESH_BINARY_INV);
修改后:
cv::threshold(black, thresh, threshLevel, 255, cv::THRESH_BINARY_INV);
主要修改的文件为catkin_ws/src/VINS-Mono/camera_model/src/chessboard/Chessboard.cc,以下是修改好的Chessboard.cc
#include "camodocal/chessboard/Chessboard.h"
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "camodocal/chessboard/ChessboardQuad.h"
#include "camodocal/chessboard/Spline.h"
#include <opencv2/calib3d/calib3d_c.h>
#define MAX_CONTOUR_APPROX 7
namespace camodocal
{
Chessboard::Chessboard(cv::Size boardSize, cv::Mat& image)
: mBoardSize(boardSize)
, mCornersFound(false)
{
if (image.channels() == 1)
{
cv::cvtColor(image, mSketch, cv::COLOR_RGB2GRAY);
image.copyTo(mImage);
}
else
{
image.copyTo(mSketch);
cv::cvtColor(image, mImage, cv::COLOR_RGB2GRAY);
}
}
void
Chessboard::findCorners(bool useOpenCV)
{
mCornersFound = findChessboardCorners(mImage, mBoardSize, mCorners,
CV_CALIB_CB_ADAPTIVE_THRESH +
CV_CALIB_CB_NORMALIZE_IMAGE +
CV_CALIB_CB_FILTER_QUADS +
CV_CALIB_CB_FAST_CHECK,
useOpenCV);
if (mCornersFound)
{
cv::drawChessboardCorners(mSketch, mBoardSize, mCorners, mCornersFound);
}
}
const std::vector<cv::Point2f>&
Chessboard::getCorners(void) const
{
return mCorners;
}
bool
Chessboard::cornersFound(void) const
{
return mCornersFound;
}
const cv::Mat&
Chessboard::getImage(void) const
{
return mImage;
}
const cv::Mat&
Chessboard::getSketch(void) const
{
return mSketch;
}
bool
Chessboard::findChessboardCorners(const cv::Mat& image,
const cv::Size& patternSize,
std::vector<cv::Point2f>& corners,
int flags, bool useOpenCV)
{
if (useOpenCV)
{
return cv::findChessboardCorners(image, patternSize, corners, flags);
}
else
{
return findChessboardCornersImproved(image, patternSize, corners, flags);
}
}
bool
Chessboard::findChessboardCornersImproved(const cv::Mat& image,
const cv::Size& patternSize,
std::vector<cv::Point2f>& corners,
int flags)
{
const int minDilations = 0;
const int maxDilations = 7;
std::vector<ChessboardQuadPtr> outputQuadGroup;
if (image.depth() != CV_8U || image.channels() == 2)
{
return false;
}
if (patternSize.width < 2 || patternSize.height < 2)
{
return false;
}
if (patternSize.width > 127 || patternSize.height > 127)
{
return false;
}
cv::Mat img = image;
if (image.channels() != 1 || (flags & CV_CALIB_CB_NORMALIZE_IMAGE))
{
cv::Mat norm_img(image.rows, image.cols, CV_8UC1);
if (image.channels() != 1)
{
cv::cvtColor(image, norm_img, cv::COLOR_BGR2GRAY);
img = norm_img;
}
if (flags & CV_CALIB_CB_NORMALIZE_IMAGE)
{
cv::equalizeHist(image, norm_img);
img = norm_img;
}
}
if (flags & CV_CALIB_CB_FAST_CHECK)
{
if (!checkChessboard(img, patternSize))
{
return false;
}
}
int prevSqrSize = 0;
bool found = false;
std::vector<ChessboardCornerPtr> outputCorners;
for (int k = 0; k < 6; ++k)
{
for (int dilations = minDilations; dilations <= maxDilations; ++dilations)
{
if (found)
{
break;
}
cv::Mat thresh_img;
if (flags & CV_CALIB_CB_ADAPTIVE_THRESH)
{
int blockSize = lround(prevSqrSize == 0 ?
std::min(img.cols,img.rows)*(k%2 == 0 ? 0.2 : 0.1): prevSqrSize*2)|1;
cv::adaptiveThreshold(img, thresh_img, 255,cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, blockSize, (k/2)*5);
}
else
{
double mean = (cv::mean(img))[0];
int thresh_level = lround(mean - 10);
thresh_level = std::max(thresh_level, 10);
cv::threshold(img, thresh_img, thresh_level, 255, cv::THRESH_BINARY);
}
cv::Mat kernel1 = cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3,3), cv::Point(1,1));
cv::Mat kernel2 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3,3), cv::Point(1,1));
if (dilations >= 1)
cv::dilate(thresh_img, thresh_img, kernel1);
if (dilations >= 2)
cv::dilate(thresh_img, thresh_img, kernel2);
if (dilations >= 3)
cv::dilate(thresh_img, thresh_img, kernel1);
if (dilations >= 4)
cv::dilate(thresh_img, thresh_img, kernel2);
if (dilations >= 5)
cv::dilate(thresh_img, thresh_img, kernel1);
if (dilations >= 6)
cv::dilate(thresh_img, thresh_img, kernel2);
cv::rectangle(thresh_img, cv::Point(0,0),
cv::Point(thresh_img.cols - 1, thresh_img.rows - 1),
CV_RGB(255,255,255), 3, 8);
std::vector<ChessboardQuadPtr> quads;
generateQuads(quads, thresh_img, flags, dilations, true);
if (quads.empty())
{
continue;
}
findQuadNeighbors(quads, dilations);
for (int group_idx = 0; ; ++group_idx)
{
std::vector<ChessboardQuadPtr> quadGroup;
findConnectedQuads(quads, quadGroup, group_idx, dilations);
if (quadGroup.empty())
{
break;
}
cleanFoundConnectedQuads(quadGroup, patternSize);
labelQuadGroup(quadGroup, patternSize, true);
found = checkQuadGroup(quadGroup, outputCorners, patternSize);
float sumDist = 0;
int total = 0;
for (int i = 0; i < (int)outputCorners.size(); ++i)
{
int ni = 0;
float avgi = outputCorners.at(i)->meanDist(ni);
sumDist += avgi * ni;
total += ni;
}
prevSqrSize = lround(sumDist / std::max(total, 1));
if (found && !checkBoardMonotony(outputCorners, patternSize))
{
found = false;
}
}
}
}
if (!found)
{
return false;
}
else
{
corners.clear();
corners.reserve(outputCorners.size());
for (size_t i = 0; i < outputCorners.size(); ++i)
{
corners.push_back(outputCorners.at(i)->pt);
}
cv::cornerSubPix(image, corners, cv::Size(11, 11), cv::Size(-1,-1),
cv::TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1));
return true;
}
}
void
Chessboard::cleanFoundConnectedQuads(std::vector<ChessboardQuadPtr>& quadGroup,
cv::Size patternSize)
{
cv::Point2f center(0.0f, 0.0f);
int count = ((patternSize.width + 1)*(patternSize.height + 1) + 1)/2;
if ((int)quadGroup.size() <= count)
{
return;
}
std::vector<cv::Point2f> centers;
centers.resize(quadGroup.size());
for (size_t i = 0; i < quadGroup.size(); ++i)
{
cv::Point2f ci(0.0f, 0.0f);
ChessboardQuadPtr& q = quadGroup[i];
for (int j = 0; j < 4; ++j)
{
ci += q->corners[j]->pt;
}
ci *= 0.25f;
centers[i] = ci;
center += ci;
}
center *= 1.0f / quadGroup.size();
while ((int)quadGroup.size() > count)
{
double minBoxArea = DBL_MAX;
int minBoxAreaIndex = -1;
for (size_t skip = 0; skip < quadGroup.size(); ++skip)
{
cv::Point2f temp = centers[skip];
centers[skip] = center;
std::vector<cv::Point2f> hull;
cv::convexHull(centers, hull, true, true);
centers[skip] = temp;
double hull_area = fabs(cv::contourArea(hull));
if (hull_area < minBoxArea)
{
minBoxArea = hull_area;
minBoxAreaIndex = skip;
}
}
ChessboardQuadPtr& q0 = quadGroup[minBoxAreaIndex];
for (size_t i = 0; i < quadGroup.size(); ++i)
{
ChessboardQuadPtr& q = quadGroup.at(i);
for (int j = 0; j < 4; ++j)
{
if (q->neighbors[j] == q0)
{
q->neighbors[j].reset();
q->count--;
for (int k = 0; k < 4; ++k)
{
if (q0->neighbors[k] == q)
{
q0->neighbors[k].reset();
q0->count--;
break;
}
}
break;
}
}
}
quadGroup.at(minBoxAreaIndex) = quadGroup.back();
centers.at(minBoxAreaIndex) = centers.back();
quadGroup.pop_back();
centers.pop_back();
}
}
void
Chessboard::findConnectedQuads(std::vector<ChessboardQuadPtr>& quads,
std::vector<ChessboardQuadPtr>& group,
int group_idx, int dilation)
{
ChessboardQuadPtr q;
for (size_t i = 0; i < quads.size(); ++i)
{
ChessboardQuadPtr& quad = quads.at(i);
if (quad->count > 0 && quad->group_idx < 0)
{
q = quad;
break;
}
}
if (q.get() == 0)
{
return;
}
std::vector<ChessboardQuadPtr> stack;
stack.push_back(q);
group.push_back(q);
q->group_idx = group_idx;
while (!stack.empty())
{
q = stack.back();
stack.pop_back();
for (int i = 0; i < 4; ++i)
{
ChessboardQuadPtr& neighbor = q->neighbors[i];
if (neighbor.get() && neighbor->count > 0 && neighbor->group_idx < 0)
{
stack.push_back(neighbor);
group.push_back(neighbor);
neighbor->group_idx = group_idx;
}
}
}
}
void
Chessboard::labelQuadGroup(std::vector<ChessboardQuadPtr>& quadGroup,
cv::Size patternSize, bool firstRun)
{
if (firstRun)
{
int mark = -1;
int maxNeighborCount = 0;
for (size_t i = 0; i < quadGroup.size(); ++i)
{
ChessboardQuadPtr& q = quadGroup.at(i);
if (q->count > maxNeighborCount)
{
mark = i;
maxNeighborCount = q->count;
if (maxNeighborCount == 4)
{
break;
}
}
}
ChessboardQuadPtr& q = quadGroup.at(mark);
q->labeled = true;
q->corners[0]->row = 0;
q->corners[0]->column = 0;
q->corners[1]->row = 0;
q->corners[1]->column = 1;
q->corners[2]->row = 1;
q->corners[2]->column = 1;
q->corners[3]->row = 1;
q->corners[3]->column = 0;
}
bool flagChanged = true;
while (flagChanged)
{
flagChanged = false;
for (int i = quadGroup.size() - 1; i >= 0; --i)
{
ChessboardQuadPtr& quad = quadGroup.at(i);
if (!quad->labeled)
{
for (int j = 0; j < 4; j++)
{
if (quad->neighbors[j])
{
ChessboardQuadPtr& quadNeighbor = quad->neighbors[j];
if (quadNeighbor->labeled)
{
int connectedNeighborCornerId = -1;
for (int k = 0; k < 4; ++k)
{
if (quadNeighbor->neighbors[k] == quad)
{
connectedNeighborCornerId = k;
break;
}
}
ChessboardCornerPtr& conCorner = quadNeighbor->corners[connectedNeighborCornerId];
ChessboardCornerPtr& conCornerCW1 = quadNeighbor->corners[(connectedNeighborCornerId+1)%4];
ChessboardCornerPtr& conCornerCW2 = quadNeighbor->corners[(connectedNeighborCornerId+2)%4];
ChessboardCornerPtr& conCornerCW3 = quadNeighbor->corners[(connectedNeighborCornerId+3)%4];
quad->corners[j]->row = conCorner->row;
quad->corners[j]->column = conCorner->column;
quad->corners[(j+1)%4]->row = conCorner->row - conCornerCW2->row + conCornerCW3->row;
quad->corners[(j+1)%4]->column = conCorner->column - conCornerCW2->column + conCornerCW3->column;
quad->corners[(j+2)%4]->row = conCorner->row + conCorner->row - conCornerCW2->row;
quad->corners[(j+2)%4]->column = conCorner->column + conCorner->column - conCornerCW2->column;
quad->corners[(j+3)%4]->row = conCorner->row - conCornerCW2->row + conCornerCW1->row;
quad->corners[(j+3)%4]->column = conCorner->column - conCornerCW2->column + conCornerCW1->column;
quad->labeled = true;
flagChanged = true;
break;
}
}
}
}
}
}
int min_row = 127;
int max_row = -127;
int min_column = 127;
int max_column = -127;
for (int i = 0; i < (int)quadGroup.size(); ++i)
{
ChessboardQuadPtr& q = quadGroup.at(i);
for (int j = 0; j < 4; ++j)
{
ChessboardCornerPtr& c = q->corners[j];
if (c->row > max_row)
{
max_row = c->row;
}
if (c->row < min_row)
{
min_row = c->row;
}
if (c->column > max_column)
{
max_column = c->column;
}
if (c->column < min_column)
{
min_column = c->column;
}
}
}
for (int i = min_row; i <= max_row; ++i)
{
for (int j = min_column; j <= max_column; ++j)
{
bool flag = false;
int cornerID;
int quadID;
for (int k = 0; k < (int)quadGroup.size(); ++k)
{
ChessboardQuadPtr& q = quadGroup.at(k);
for (int l = 0; l < 4; ++l)
{
if ((q->corners[l]->row == i) && (q->corners[l]->column == j))
{
if (flag)
{
q->corners[l]->needsNeighbor = false;
quadGroup[quadID]->corners[cornerID]->needsNeighbor = false;
}
else
{
q->corners[l]->needsNeighbor = true;
cornerID = l;
quadID = k;
}
flag = true;
}
}
}
}
}
for (int i = min_row; i <= max_row; ++i)
{
for (int j = min_column; j <= max_column; ++j)
{
int number = 1;
int cornerID;
int quadID;
for (int k = 0; k < (int)quadGroup.size(); ++k)
{
ChessboardQuadPtr& q = quadGroup.at(k);
for (int l = 0; l < 4; ++l)
{
if ((q->corners[l]->row == i) && (q->corners[l]->column == j))
{
if (number == 1)
{
cornerID = l;
quadID = k;
}
else if (number == 2)
{
cv::Point2f delta = q->corners[l]->pt - quadGroup[quadID]->corners[cornerID]->pt;
if (delta.x != 0.0f || delta.y != 0.0f)
{
q->corners[l]->pt -= delta * 0.5f;
quadGroup[quadID]->corners[cornerID]->pt += delta * 0.5f;
}
}
else if (number > 2)
{
}
++number;
}
}
}
}
}
int largerDimPattern = std::max(patternSize.height,patternSize.width);
int smallerDimPattern = std::min(patternSize.height,patternSize.width);
bool flagSmallerDim1 = false;
bool flagSmallerDim2 = false;
if ((largerDimPattern + 1) == max_column - min_column)
{
flagSmallerDim1 = true;
for (int k = 0; k < (int)quadGroup.size(); ++k)
{
ChessboardQuadPtr& q = quadGroup.at(k);
for (int l = 0; l < 4; ++l)
{
ChessboardCornerPtr& c = q->corners[l];
if (c->column == min_column || c->column == max_column)
{
c->needsNeighbor = false;
}
}
}
}
if ((largerDimPattern + 1) == max_row - min_row)
{
flagSmallerDim2 = true;
for (int k = 0; k < (int)quadGroup.size(); ++k)
{
ChessboardQuadPtr& q = quadGroup.at(k);
for (int l = 0; l < 4; ++l)
{
ChessboardCornerPtr& c = q->corners[l];
if (c->row == min_row || c->row == max_row)
{
c->needsNeighbor = false;
}
}
}
}
if ((flagSmallerDim1 == false && flagSmallerDim2 == true))
{
if ((smallerDimPattern + 1) == max_column - min_column)
{
for (int k = 0; k < (int)quadGroup.size(); ++k)
{
ChessboardQuadPtr& q = quadGroup.at(k);
for (int l = 0; l < 4; ++l)
{
ChessboardCornerPtr& c = q->corners[l];
if (c->column == min_column || c->column == max_column)
{
c->needsNeighbor = false;
}
}
}
}
}
if ((flagSmallerDim1 == true && flagSmallerDim2 == false))
{
if ((smallerDimPattern + 1) == max_row - min_row)
{
for (int k = 0; k < (int)quadGroup.size(); ++k)
{
ChessboardQuadPtr& q = quadGroup.at(k);
for (int l = 0; l < 4; ++l)
{
ChessboardCornerPtr& c = q->corners[l];
if (c->row == min_row || c->row == max_row)
{
c->needsNeighbor = false;
}
}
}
}
}
if ((flagSmallerDim1 == false && flagSmallerDim2 == false) && smallerDimPattern + 1 < max_column - min_column)
{
if ((smallerDimPattern + 1) == max_row - min_row)
{
for (int k = 0; k < (int)quadGroup.size(); ++k)
{
ChessboardQuadPtr& q = quadGroup.at(k);
for (int l = 0; l < 4; ++l)
{
ChessboardCornerPtr& c = q->corners[l];
if (c->row == min_row || c->row == max_row)
{
c->needsNeighbor = false;
}
}
}
}
}
if ((flagSmallerDim1 == false && flagSmallerDim2 == false) && smallerDimPattern + 1 < max_row - min_row)
{
if ((smallerDimPattern + 1) == max_column - min_column)
{
for (int k = 0; k < (int)quadGroup.size(); ++k)
{
ChessboardQuadPtr& q = quadGroup.at(k);
for (int l = 0; l < 4; ++l)
{
ChessboardCornerPtr& c = q->corners[l];
if (c->column == min_column || c->column == max_column)
{
c->needsNeighbor = false;
}
}
}
}
}
}
void
Chessboard::findQuadNeighbors(std::vector<ChessboardQuadPtr>& quads, int dilation)
{
const float thresh_dilation = (float)(2*dilation+3)*(2*dilation+3)*2;
for (size_t idx = 0; idx < quads.size(); ++idx)
{
ChessboardQuadPtr& curQuad = quads.at(idx);
for (int i = 0; i < 4; ++i)
{
float minDist = FLT_MAX;
int closestCornerIdx = -1;
ChessboardQuadPtr closestQuad;
if (curQuad->neighbors[i])
{
continue;
}
cv::Point2f pt = curQuad->corners[i]->pt;
for (size_t k = 0; k < quads.size(); ++k)
{
if (k == idx)
{
continue;
}
ChessboardQuadPtr& quad = quads.at(k);
for (int j = 0; j < 4; ++j)
{
if (quad->neighbors[j])
{
continue;
}
cv::Point2f dp = pt - quad->corners[j]->pt;
float dist = dp.dot(dp);
if (dist < minDist &&
dist <= (curQuad->edge_len + thresh_dilation) &&
dist <= (quad->edge_len + thresh_dilation) )
{
if (matchCorners(curQuad, i, quad, j))
{
closestCornerIdx = j;
closestQuad = quad;
minDist = dist;
}
}
}
}
if (closestCornerIdx >= 0 && minDist < FLT_MAX)
{
ChessboardCornerPtr closestCorner = closestQuad->corners[closestCornerIdx];
bool valid = true;
for (int j = 0; j < 4; ++j)
{
if (closestQuad->neighbors[j] == curQuad)
{
valid = false;
break;
}
}
if (!valid)
{
continue;
}
closestCorner->pt = (pt + closestCorner->pt) * 0.5f;
curQuad->count++;
curQuad->neighbors[i] = closestQuad;
curQuad->corners[i] = closestCorner;
closestQuad->count++;
closestQuad->neighbors[closestCornerIdx] = curQuad;
closestQuad->corners[closestCornerIdx] = closestCorner;
}
}
}
}
int
Chessboard::augmentBestRun(std::vector<ChessboardQuadPtr>& candidateQuads, int candidateDilation,
std::vector<ChessboardQuadPtr>& existingQuads, int existingDilation)
{
const float thresh_dilation = (2*candidateDilation+3)*(2*existingDilation+3)*2;
for (size_t idx = 0; idx < existingQuads.size(); ++idx)
{
ChessboardQuadPtr& curQuad = existingQuads.at(idx);
for (int i = 0; i < 4; ++i)
{
float minDist = FLT_MAX;
int closestCornerIdx = -1;
ChessboardQuadPtr closestQuad;
if (!curQuad->corners[i]->needsNeighbor)
{
continue;
}
cv::Point2f pt = curQuad->corners[i]->pt;
for (size_t k = 0; k < candidateQuads.size(); ++k)
{
ChessboardQuadPtr& candidateQuad = candidateQuads.at(k);
if (candidateQuad->labeled)
{
continue;
}
for (int j = 0; j < 4; ++j)
{
cv::Point2f dp = pt - candidateQuad->corners[j]->pt;
float dist = dp.dot(dp);
if ((dist < minDist) &&
dist <= (curQuad->edge_len + thresh_dilation) &&
dist <= (candidateQuad->edge_len + thresh_dilation))
{
if (matchCorners(curQuad, i, candidateQuad, j))
{
closestCornerIdx = j;
closestQuad = candidateQuad;
minDist = dist;
}
}
}
}
if (closestCornerIdx >= 0 && minDist < FLT_MAX)
{
ChessboardCornerPtr closestCorner = closestQuad->corners[closestCornerIdx];
closestCorner->pt = (pt + closestCorner->pt) * 0.5f;
curQuad->corners[i]->pt = closestCorner->pt;
curQuad->neighbors[i] = closestQuad;
closestQuad->corners[closestCornerIdx]->pt = closestCorner->pt;
closestQuad->labeled = true;
ChessboardQuadPtr newQuad(new ChessboardQuad);
newQuad->count = 1;
newQuad->edge_len = closestQuad->edge_len;
newQuad->group_idx = curQuad->group_idx;
newQuad->labeled = false;
curQuad->neighbors[i] = newQuad;
newQuad->neighbors[closestCornerIdx] = curQuad;
for (int j = 0; j < 4; j++)
{
newQuad->corners[j].reset(new ChessboardCorner);
newQuad->corners[j]->pt = closestQuad->corners[j]->pt;
}
existingQuads.push_back(newQuad);
return -1;
}
}
}
return 1;
}
void
Chessboard::generateQuads(std::vector<ChessboardQuadPtr>& quads,
cv::Mat& image, int flags,
int dilation, bool firstRun)
{
int minSize = lround(image.cols * image.rows * .03 * 0.01 * 0.92 * 0.1);
std::vector< std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(image, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE);
std::vector< std::vector<cv::Point> > quadContours;
for (size_t i = 0; i < contours.size(); ++i)
{
std::vector<cv::Point>& contour = contours.at(i);
if (hierarchy[i][3] == -1 || cv::contourArea(contour) < minSize)
{
continue;
}
int min_approx_level = 1, max_approx_level;
if (firstRun)
{
max_approx_level = 3;
}
else
{
max_approx_level = MAX_CONTOUR_APPROX;
}
std::vector<cv::Point> approxContour;
for (int approx_level = min_approx_level; approx_level <= max_approx_level; approx_level++)
{
cv::approxPolyDP(contour, approxContour, approx_level, true);
if (approxContour.size() == 4)
{
break;
}
}
if (approxContour.size() == 4 && cv::isContourConvex(approxContour))
{
double p = cv::arcLength(approxContour, true);
double area = cv::contourArea(approxContour);
cv::Point pt[4];
for (int i = 0; i < 4; i++)
{
pt[i] = approxContour[i];
}
cv::Point dp = pt[0] - pt[2];
double d1 = sqrt(dp.dot(dp));
dp = pt[1] - pt[3];
double d2 = sqrt(dp.dot(dp));
dp = pt[0] - pt[1];
double d3 = sqrt(dp.dot(dp));
dp = pt[1] - pt[2];
double d4 = sqrt(dp.dot(dp));
if (!(flags & CV_CALIB_CB_FILTER_QUADS) ||
(d3*4 > d4 && d4*4 > d3 && d3*d4 < area*1.5 && area > minSize &&
d1 >= 0.15 * p && d2 >= 0.15 * p))
{
quadContours.push_back(approxContour);
}
}
}
quads.resize(quadContours.size());
for (size_t idx = 0; idx < quadContours.size(); ++idx)
{
ChessboardQuadPtr& q = quads.at(idx);
std::vector<cv::Point>& contour = quadContours.at(idx);
q.reset(new ChessboardQuad);
assert(contour.size() == 4);
for (int i = 0; i < 4; ++i)
{
cv::Point2f pt = contour.at(i);
ChessboardCornerPtr corner(new ChessboardCorner);
corner->pt = pt;
q->corners[i] = corner;
}
for (int i = 0; i < 4; ++i)
{
cv::Point2f dp = q->corners[i]->pt - q->corners[(i+1)&3]->pt;
float d = dp.dot(dp);
if (q->edge_len > d)
{
q->edge_len = d;
}
}
}
}
bool
Chessboard::checkQuadGroup(std::vector<ChessboardQuadPtr>& quads,
std::vector<ChessboardCornerPtr>& corners,
cv::Size patternSize)
{
bool flagRow = false;
bool flagColumn = false;
int height = -1;
int width = -1;
int min_row = 127;
int max_row = -127;
int min_col = 127;
int max_col = -127;
for (size_t i = 0; i < quads.size(); ++i)
{
ChessboardQuadPtr& q = quads.at(i);
for (int j = 0; j < 4; ++j)
{
ChessboardCornerPtr& c = q->corners[j];
if (c->row > max_row)
{
max_row = c->row;
}
if (c->row < min_row)
{
min_row = c->row;
}
if (c->column > max_col)
{
max_col = c->column;
}
if (c->column < min_col)
{
min_col = c->column;
}
}
}
for (size_t i = 0; i < quads.size(); ++i)
{
ChessboardQuadPtr& q = quads.at(i);
for (int j = 0; j < 4; ++j)
{
ChessboardCornerPtr& c = q->corners[j];
if (c->column == max_col && c->row != min_row && c->row != max_row && !c->needsNeighbor)
{
flagColumn = true;
}
if (c->row == max_row && c->column != min_col && c->column != max_col && !c->needsNeighbor)
{
flagRow = true;
}
}
}
if (flagColumn)
{
if (max_col - min_col == patternSize.width + 1)
{
width = patternSize.width;
height = patternSize.height;
}
else
{
width = patternSize.height;
height = patternSize.width;
}
}
else if (flagRow)
{
if (max_row - min_row == patternSize.width + 1)
{
height = patternSize.width;
width = patternSize.height;
}
else
{
height = patternSize.height;
width = patternSize.width;
}
}
else
{
width = std::max(patternSize.width, patternSize.height);
height = std::max(patternSize.width, patternSize.height);
}
++min_row;
++min_col;
max_row = min_row + height - 1;
max_col = min_col + width - 1;
corners.clear();
int linkedBorderCorners = 0;
for (int i = min_row; i <= max_row; ++i)
{
for (int j = min_col; j <= max_col; ++j)
{
int iter = 1;
for (int k = 0; k < (int)quads.size(); ++k)
{
ChessboardQuadPtr& quad = quads.at(k);
for (int l = 0; l < 4; ++l)
{
ChessboardCornerPtr& c = quad->corners[l];
if (c->row == i && c->column == j)
{
bool boardEdge = false;
if (i == min_row || i == max_row ||
j == min_col || j == max_col)
{
boardEdge = true;
}
if ((iter == 1 && boardEdge) || (iter == 2 && !boardEdge))
{
corners.push_back(quad->corners[l]);
}
if (iter == 2 && boardEdge)
{
++linkedBorderCorners;
}
if (iter > 2)
{
return false;
}
iter++;
}
}
}
}
}
if ((int)corners.size() != patternSize.width * patternSize.height ||
linkedBorderCorners < (patternSize.width * 2 + patternSize.height * 2 - 2) * 0.75f)
{
return false;
}
float border = 5.0f;
for (int i = 0; i < (int)corners.size(); ++i)
{
ChessboardCornerPtr& c = corners.at(i);
if (c->pt.x < border || c->pt.x > mImage.cols - border ||
c->pt.y < border || c->pt.y > mImage.rows - border)
{
return false;
}
}
if (width != patternSize.width)
{
std::swap(width, height);
std::vector<ChessboardCornerPtr> outputCorners;
outputCorners.resize(corners.size());
for (int i = 0; i < height; ++i)
{
for (int j = 0; j < width; ++j)
{
outputCorners.at(i * width + j) = corners.at(j * height + i);
}
}
corners = outputCorners;
}
cv::Point2f p0 = corners.at(0)->pt;
cv::Point2f p1 = corners.at(width-1)->pt;
cv::Point2f p2 = corners.at(width)->pt;
if ((p1 - p0).cross(p2 - p0) < 0.0f)
{
for (int i = 0; i < height; ++i)
{
for (int j = 0; j < width / 2; ++j)
{
std::swap(corners.at(i * width + j), corners.at(i * width + width - j - 1));
}
}
}
p0 = corners.at(0)->pt;
p2 = corners.at(width)->pt;
if (p2.y < p0.y)
{
std::vector<ChessboardCornerPtr> outputCorners;
outputCorners.resize(corners.size());
for (int i = 0; i < height; ++i)
{
for (int j = 0; j < width; ++j)
{
outputCorners.at(i * width + j) = corners.at((height - i - 1) * width + width - j - 1);
}
}
corners = outputCorners;
}
return true;
}
void
Chessboard::getQuadrangleHypotheses(const std::vector< std::vector<cv::Point> >& contours,
std::vector< std::pair<float, int> >& quads,
int classId) const
{
const float minAspectRatio = 0.2f;
const float maxAspectRatio = 5.0f;
const float minBoxSize = 10.0f;
for (size_t i = 0; i < contours.size(); ++i)
{
cv::RotatedRect box = cv::minAreaRect(contours.at(i));
float boxSize = std::max(box.size.width, box.size.height);
if (boxSize < minBoxSize)
{
continue;
}
float aspectRatio = box.size.width / std::max(box.size.height, 1.0f);
if (aspectRatio < minAspectRatio || aspectRatio > maxAspectRatio)
{
continue;
}
quads.push_back(std::pair<float, int>(boxSize, classId));
}
}
bool less_pred(const std::pair<float, int>& p1, const std::pair<float, int>& p2)
{
return p1.first < p2.first;
}
void countClasses(const std::vector<std::pair<float, int> >& pairs, size_t idx1, size_t idx2, std::vector<int>& counts)
{
counts.assign(2, 0);
for (size_t i = idx1; i != idx2; ++i)
{
counts[pairs[i].second]++;
}
}
bool
Chessboard::checkChessboard(const cv::Mat& image, cv::Size patternSize) const
{
const int erosionCount = 1;
const float blackLevel = 20.f;
const float whiteLevel = 130.f;
const float blackWhiteGap = 70.f;
cv::Mat white = image.clone();
cv::Mat black = image.clone();
cv::erode(white, white, cv::Mat(), cv::Point(-1,-1), erosionCount);
cv::dilate(black, black, cv::Mat(), cv::Point(-1,-1), erosionCount);
cv::Mat thresh(image.rows, image.cols, CV_8UC1);
bool result = false;
for (float threshLevel = blackLevel; threshLevel < whiteLevel && !result; threshLevel += 20.0f)
{
cv::threshold(white, thresh, threshLevel + blackWhiteGap, 255, cv::THRESH_BINARY);
std::vector< std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(thresh, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE);
std::vector<std::pair<float, int> > quads;
getQuadrangleHypotheses(contours, quads, 1);
cv::threshold(black, thresh, threshLevel, 255, cv::THRESH_BINARY_INV);
cv::findContours(thresh, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE);
getQuadrangleHypotheses(contours, quads, 0);
const size_t min_quads_count = patternSize.width * patternSize.height / 2;
std::sort(quads.begin(), quads.end(), less_pred);
const float sizeRelDev = 0.4f;
for (size_t i = 0; i < quads.size(); ++i)
{
size_t j = i + 1;
for (; j < quads.size(); ++j)
{
if (quads[j].first / quads[i].first > 1.0f + sizeRelDev)
{
break;
}
}
if (j + 1 > min_quads_count + i)
{
std::vector<int> counts;
countClasses(quads, i, j, counts);
const int blackCount = lroundf(ceilf(patternSize.width / 2.0f) * ceilf(patternSize.height / 2.0f));
const int whiteCount = lroundf(floorf(patternSize.width / 2.0f) * floorf(patternSize.height / 2.0f));
if (counts[0] < blackCount * 0.75f ||
counts[1] < whiteCount * 0.75f)
{
continue;
}
result = true;
break;
}
}
}
return result;
}
bool
Chessboard::checkBoardMonotony(std::vector<ChessboardCornerPtr>& corners,
cv::Size patternSize)
{
const float threshFactor = 0.2f;
Spline splineXY, splineYX;
splineXY.setLowBC(Spline::PARABOLIC_RUNOUT_BC);
splineXY.setHighBC(Spline::PARABOLIC_RUNOUT_BC);
splineYX.setLowBC(Spline::PARABOLIC_RUNOUT_BC);
splineYX.setHighBC(Spline::PARABOLIC_RUNOUT_BC);
for (int i = 0; i < patternSize.height; ++i)
{
splineXY.clear();
splineYX.clear();
cv::Point2f p[3];
p[0] = corners.at(i * patternSize.width)->pt;
p[1] = corners.at(i * patternSize.width + patternSize.width / 2)->pt;
p[2] = corners.at(i * patternSize.width + patternSize.width - 1)->pt;
for (int j = 0; j < 3; ++j)
{
splineXY.addPoint(p[j].x, p[j].y);
splineYX.addPoint(p[j].y, p[j].x);
}
for (int j = 1; j < patternSize.width - 1; ++j)
{
cv::Point2f& p_j = corners.at(i * patternSize.width + j)->pt;
float thresh = std::numeric_limits<float>::max();
if (i > 0)
{
cv::Point2f& neighbor = corners.at((i - 1) * patternSize.width + j)->pt;
thresh = fminf(thresh, cv::norm(neighbor - p_j));
}
if (i < patternSize.height - 1)
{
cv::Point2f& neighbor = corners.at((i + 1) * patternSize.width + j)->pt;
thresh = fminf(thresh, cv::norm(neighbor - p_j));
}
{
cv::Point2f& neighbor = corners.at(i * patternSize.width + j - 1)->pt;
thresh = fminf(thresh, cv::norm(neighbor - p_j));
}
{
cv::Point2f& neighbor = corners.at(i * patternSize.width + j + 1)->pt;
thresh = fminf(thresh, cv::norm(neighbor - p_j));
}
thresh *= threshFactor;
if (fminf(fabsf(splineXY(p_j.x) - p_j.y), fabsf(splineYX(p_j.y) - p_j.x)) > thresh)
{
return false;
}
}
}
for (int j = 0; j < patternSize.width; ++j)
{
splineXY.clear();
splineYX.clear();
cv::Point2f p[3];
p[0] = corners.at(j)->pt;
p[1] = corners.at(patternSize.height / 2 * patternSize.width + j)->pt;
p[2] = corners.at((patternSize.height - 1) * patternSize.width + j)->pt;
for (int i = 0; i < 3; ++i)
{
splineXY.addPoint(p[i].x, p[i].y);
splineYX.addPoint(p[i].y, p[i].x);
}
for (int i = 1; i < patternSize.height - 1; ++i)
{
cv::Point2f& p_i = corners.at(i * patternSize.width + j)->pt;
float thresh = std::numeric_limits<float>::max();
{
cv::Point2f& neighbor = corners.at((i - 1) * patternSize.width + j)->pt;
thresh = fminf(thresh, cv::norm(neighbor - p_i));
}
{
cv::Point2f& neighbor = corners.at((i + 1) * patternSize.width + j)->pt;
thresh = fminf(thresh, cv::norm(neighbor - p_i));
}
if (j > 0)
{
cv::Point2f& neighbor = corners.at(i * patternSize.width + j - 1)->pt;
thresh = fminf(thresh, cv::norm(neighbor - p_i));
}
if (j < patternSize.width - 1)
{
cv::Point2f& neighbor = corners.at(i * patternSize.width + j + 1)->pt;
thresh = fminf(thresh, cv::norm(neighbor - p_i));
}
thresh *= threshFactor;
if (fminf(fabsf(splineXY(p_i.x) - p_i.y), fabsf(splineYX(p_i.y) - p_i.x)) > thresh)
{
return false;
}
}
}
return true;
}
bool
Chessboard::matchCorners(ChessboardQuadPtr& quad1, int corner1,
ChessboardQuadPtr& quad2, int corner2) const
{
float x1 = (quad1->corners[corner1]->pt.x + quad1->corners[(corner1+1)%4]->pt.x)/2;
float y1 = (quad1->corners[corner1]->pt.y + quad1->corners[(corner1+1)%4]->pt.y)/2;
float x2 = (quad1->corners[(corner1+2)%4]->pt.x + quad1->corners[(corner1+3)%4]->pt.x)/2;
float y2 = (quad1->corners[(corner1+2)%4]->pt.y + quad1->corners[(corner1+3)%4]->pt.y)/2;
float x3 = (quad1->corners[corner1]->pt.x + quad1->corners[(corner1+3)%4]->pt.x)/2;
float y3 = (quad1->corners[corner1]->pt.y + quad1->corners[(corner1+3)%4]->pt.y)/2;
float x4 = (quad1->corners[(corner1+1)%4]->pt.x + quad1->corners[(corner1+2)%4]->pt.x)/2;
float y4 = (quad1->corners[(corner1+1)%4]->pt.y + quad1->corners[(corner1+2)%4]->pt.y)/2;
float a1 = x1 - x2;
float b1 = y1 - y2;
float c11 = quad1->corners[corner1]->pt.x - x2;
float d11 = quad1->corners[corner1]->pt.y - y2;
float c12 = quad2->corners[corner2]->pt.x - x2;
float d12 = quad2->corners[corner2]->pt.y - y2;
float sign11 = a1*d11 - c11*b1;
float sign12 = a1*d12 - c12*b1;
float a2 = x3 - x4;
float b2 = y3 - y4;
float c21 = quad1->corners[corner1]->pt.x - x4;
float d21 = quad1->corners[corner1]->pt.y - y4;
float c22 = quad2->corners[corner2]->pt.x - x4;
float d22 = quad2->corners[corner2]->pt.y - y4;
float sign21 = a2*d21 - c21*b2;
float sign22 = a2*d22 - c22*b2;
float c13 = quad2->corners[(corner2+2)%4]->pt.x - x2;
float d13 = quad2->corners[(corner2+2)%4]->pt.y - y2;
float c23 = quad2->corners[(corner2+2)%4]->pt.x - x4;
float d23 = quad2->corners[(corner2+2)%4]->pt.y - y4;
float sign13 = a1*d13 - c13*b1;
float sign23 = a2*d23 - c23*b2;
float u1 = (quad2->corners[corner2]->pt.x + quad2->corners[(corner2+1)%4]->pt.x)/2;
float v1 = (quad2->corners[corner2]->pt.y + quad2->corners[(corner2+1)%4]->pt.y)/2;
float u2 = (quad2->corners[(corner2+2)%4]->pt.x + quad2->corners[(corner2+3)%4]->pt.x)/2;
float v2 = (quad2->corners[(corner2+2)%4]->pt.y + quad2->corners[(corner2+3)%4]->pt.y)/2;
float u3 = (quad2->corners[corner2]->pt.x + quad2->corners[(corner2+3)%4]->pt.x)/2;
float v3 = (quad2->corners[corner2]->pt.y + quad2->corners[(corner2+3)%4]->pt.y)/2;
float u4 = (quad2->corners[(corner2+1)%4]->pt.x + quad2->corners[(corner2+2)%4]->pt.x)/2;
float v4 = (quad2->corners[(corner2+1)%4]->pt.y + quad2->corners[(corner2+2)%4]->pt.y)/2;
float a3 = u1 - u2;
float b3 = v1 - v2;
float c31 = quad1->corners[corner1]->pt.x - u2;
float d31 = quad1->corners[corner1]->pt.y - v2;
float c32 = quad2->corners[corner2]->pt.x - u2;
float d32 = quad2->corners[corner2]->pt.y - v2;
float sign31 = a3*d31-c31*b3;
float sign32 = a3*d32-c32*b3;
float a4 = u3 - u4;
float b4 = v3 - v4;
float c41 = quad1->corners[corner1]->pt.x - u4;
float d41 = quad1->corners[corner1]->pt.y - v4;
float c42 = quad2->corners[corner2]->pt.x - u4;
float d42 = quad2->corners[corner2]->pt.y - v4;
float sign41 = a4*d41-c41*b4;
float sign42 = a4*d42-c42*b4;
float c33 = quad1->corners[(corner1+2)%4]->pt.x - u2;
float d33 = quad1->corners[(corner1+2)%4]->pt.y - v2;
float c43 = quad1->corners[(corner1+2)%4]->pt.x - u4;
float d43 = quad1->corners[(corner1+2)%4]->pt.y - v4;
float sign33 = a3*d33-c33*b3;
float sign43 = a4*d43-c43*b4;
float x5 = quad1->corners[corner1]->pt.x;
float y5 = quad1->corners[corner1]->pt.y;
float x6 = quad1->corners[(corner1+1)%4]->pt.x;
float y6 = quad1->corners[(corner1+1)%4]->pt.y;
float x7 = x5;
float y7 = y5;
float x8 = quad1->corners[(corner1+3)%4]->pt.x;
float y8 = quad1->corners[(corner1+3)%4]->pt.y;
float a5 = x6 - x5;
float b5 = y6 - y5;
float c51 = quad1->corners[(corner1+2)%4]->pt.x - x5;
float d51 = quad1->corners[(corner1+2)%4]->pt.y - y5;
float c52 = quad2->corners[corner2]->pt.x - x5;
float d52 = quad2->corners[corner2]->pt.y - y5;
float sign51 = a5*d51 - c51*b5;
float sign52 = a5*d52 - c52*b5;
float a6 = x8 - x7;
float b6 = y8 - y7;
float c61 = quad1->corners[(corner1+2)%4]->pt.x - x7;
float d61 = quad1->corners[(corner1+2)%4]->pt.y - y7;
float c62 = quad2->corners[corner2]->pt.x - x7;
float d62 = quad2->corners[corner2]->pt.y - y7;
float sign61 = a6*d61 - c61*b6;
float sign62 = a6*d62 - c62*b6;
float u5 = quad2->corners[corner2]->pt.x;
float v5 = quad2->corners[corner2]->pt.y;
float u6 = quad2->corners[(corner2+1)%4]->pt.x;
float v6 = quad2->corners[(corner2+1)%4]->pt.y;
float u7 = u5;
float v7 = v5;
float u8 = quad2->corners[(corner2+3)%4]->pt.x;
float v8 = quad2->corners[(corner2+3)%4]->pt.y;
float a7 = u6 - u5;
float b7 = v6 - v5;
float c71 = quad1->corners[corner1]->pt.x - u5;
float d71 = quad1->corners[corner1]->pt.y - v5;
float c72 = quad2->corners[(corner2+2)%4]->pt.x - u5;
float d72 = quad2->corners[(corner2+2)%4]->pt.y - v5;
float sign71 = a7*d71-c71*b7;
float sign72 = a7*d72-c72*b7;
float a8 = u8 - u7;
float b8 = v8 - v7;
float c81 = quad1->corners[corner1]->pt.x - u7;
float d81 = quad1->corners[corner1]->pt.y - v7;
float c82 = quad2->corners[(corner2+2)%4]->pt.x - u7;
float d82 = quad2->corners[(corner2+2)%4]->pt.y - v7;
float sign81 = a8*d81-c81*b8;
float sign82 = a8*d82-c82*b8;
if (((sign11 < 0 && sign12 < 0) || (sign11 > 0 && sign12 > 0)) &&
((sign21 < 0 && sign22 < 0) || (sign21 > 0 && sign22 > 0)) &&
((sign31 < 0 && sign32 < 0) || (sign31 > 0 && sign32 > 0)) &&
((sign41 < 0 && sign42 < 0) || (sign41 > 0 && sign42 > 0)) &&
((sign11 < 0 && sign13 < 0) || (sign11 > 0 && sign13 > 0)) &&
((sign21 < 0 && sign23 < 0) || (sign21 > 0 && sign23 > 0)) &&
((sign31 < 0 && sign33 < 0) || (sign31 > 0 && sign33 > 0)) &&
((sign41 < 0 && sign43 < 0) || (sign41 > 0 && sign43 > 0)) &&
((sign51 < 0 && sign52 > 0) || (sign51 > 0 && sign52 < 0)) &&
((sign61 < 0 && sign62 > 0) || (sign61 > 0 && sign62 < 0)) &&
((sign71 < 0 && sign72 > 0) || (sign71 > 0 && sign72 < 0)) &&
((sign81 < 0 && sign82 > 0) || (sign81 > 0 && sign82 < 0)))
{
return true;
}
else
{
return false;
}
}
}
opencv其他错误
链接: opencv常见用法和opencv3->opencv4版本切换
参考链接
链接: 修复 ROS Noetic版本
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)