利用拟合直线的交点寻找圆角矩形的左上点
有时候为了确定一些物体在图像中的位置,我们需要寻找一些物体的独有特征,物体的形状也是一个很好的选择,
由于本次的项目是几个可以简单固定的矩形物体,所以选择寻找矩形的左上点来确定物体的偏转角度。
首先,考虑到工业相机的畸变,首先要做一下矫正,在这里利用cv::calibrateCamera方法cv::undistort方法进行图像校正,得到校正后的图像。
然后对校正后的图像进行后续操作,先做一个灰度处理,然后将图像进行二值化,再对二值化之后的图像进行查找边框。
在findContours这个方法中设置cv::RETR_EXTERNAL这个参数,可以直接获取图像的最外轮廓,由于物体在图像中占的比例较大,
所以可以选取面积最大的最外轮廓,利用cv::contourArea方法计算轮廓面积。
确定了轮廓之后,就要计算左上点的坐标了,由于矩形工件的角是圆角,所以这个点应该是矩形两个边交点,首先为
矩形边框画出最小外接矩形,利用cv::minAreaRect可以获得最小外接矩形的四个角点,选取x,y坐标相加最小的点,这个点是
最小外接矩形的左上角点,确定了左上点之后,由于cv::minAreaRect保存的点是顺时针顺序的,所以可以通过左上点
确定其上一个点和下一个点的坐标,在最小外包矩形左上点与其他两点左边范围内的选取轮廓上的点,然后分别拟合直线,
就可以得到矩形的边框拟合的直线,求两直线的交点,就可以得到矩形工件的近似左上点,通过拟合直线与水平方向的偏转角度,
就可以得到矩形工件的具体位置。
代码如下:
class CorrectImage
{
protected:
cv::Mat cameraMatrix_;
cv::Mat distCoeffs_;
double realBs_[2];
double center[2] = {1920, 1080};
public:
CorrectImage()
{
realBs_[0] = realBs_[1] = 0;
}
bool_t Init(const cv::Mat* images, int imageCount, int rowCount, int colCount, double lengthOfSide)
{
rowCount--;
colCount--;
std::vector< cv::Point3f > worldPoints;
for (int j = 0; j < colCount; ++j)
{
for (int k = 0; k < rowCount; ++k)
{
worldPoints.push_back(cv::Point3f(k*1.0, j*1.0, 0.0f));
}
}
std::vector< cv::Mat > rvecs, tvecs;
std::vector< cv::Point2f > corners;
std::vector< std::vector< cv::Point2f > > corners2;
std::vector< std::vector< cv::Point3f > > worldPoints2;
for (int i = 0; i < imageCount; ++i)
{
bool_t found = cv::findChessboardCorners(images[i], cv::Size(rowCount, colCount), corners, cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_NORMALIZE_IMAGE);
corners2.push_back(corners);
worldPoints2.push_back(worldPoints);
}
cv::calibrateCamera(worldPoints2, corners2, images[0].size(), cameraMatrix_, distCoeffs_, rvecs, tvecs, cv::CALIB_FIX_PRINCIPAL_POINT);
bool_t GetRes(cv::Mat image, double* distance, double *angle)
{
cv::Mat img;
undistort(image, img, cameraMatrix_, distCoeffs_);
cv::Mat gray;
cv::Mat thresh;
cvtColor(img, gray, cv::COLOR_BGR2GRAY);
// Debug...
threshold(gray, thresh, 140, 255, cv::THRESH_BINARY);
bitwise_not(thresh, thresh);
std::vector< std::vector< cv::Point > > contours;
std::vector< cv::Vec4i > hierarchy;
std::vector< cv::Point > lineP1, lineP2;
// 计算边缘轮廓
findContours(thresh, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
double conArea = contourArea(contours[0], true), area;
int i, k = 0;
for (i = 1; i < (int)contours.size(); ++i)
{
area = abs(contourArea(contours[i], true));
if (conArea <= area)
{
conArea = area;
k = i;
}
}
cv::RotatedRect rRect = minAreaRect(contours[k]);
cv::Point2f vertices[4];
rRect.points(vertices);
double x_y, flag = 0;
x_y = vertices[0].x + vertices[0].y;
for (i = 0; i < 4; ++i)
{
if (x_y > vertices[i].x + vertices[i].y)
{
x_y = vertices[i].x + vertices[i].y;
flag = i;
}
}
if (flag == 2)
{
for (i = 0; i < (int)contours[k].size(); ++i)
{
if (contours[k][i].x > vertices[2].x + 5 && contours[k][i].x < vertices[3].x - 5 && contours[k][i].y < vertices[3].y + 5 && contours[k][i].y > vertices[2].y - 5) {
lineP1.push_back(contours[k][i]);
}
if (contours[k][i].x > vertices[1].x - 5 && contours[k][i].x < vertices[2].x + 5 && contours[k][i].y < vertices[1].y - 5 && contours[k][i].y > vertices[2].y + 5) {
lineP2.push_back(contours[k][i]);
}
}
}
else {
for (int i = 0; i < (int)contours[k].size(); i++) {
if (contours[k][i].x > vertices[1].x + 5 && contours[k][i].x < vertices[2].x - 5 && contours[k][i].y > vertices[2].y - 5 && contours[k][i].y < vertices[1].y + 5) {
lineP1.push_back(contours[k][i]);
}
if (contours[k][i].x < vertices[0].x + 5 && contours[k][i].x > vertices[1].x - 5 && contours[k][i].y < vertices[0].y - 5 && contours[k][i].y > vertices[1].y + 5) {
lineP2.push_back(contours[k][i]);
}
}
}
cv::Vec4f line_para1, line_para2;
cv::fitLine(lineP1, line_para1, cv::DIST_L2, 0, 0.01, 0.01);
cv::fitLine(lineP2, line_para2, cv::DIST_L2, 0, 0.01, 0.01);
double k1, k2;
k1 = line_para1[1] / line_para1[0];
k2 = line_para2[1] / line_para2[0];
double x, y;
x = (k1 * line_para1[2] - k2 * line_para2[2] + line_para2[3] - line_para1[3]) / (k1 - k2);
y = k1 * (x - line_para1[2]) + line_para1[3];
distance[0] = (x - img.cols / 2) * realBs_[0];
distance[1] = (y - img.rows / 2) * realBs_[1];
//*angle = -atan(k1);
*angle = -atan2(line_para1[1], line_para1[0]);
return true;
}
};
int main()
{
Mat images[3];
images[0] = cv::imread("E:\\CPP\\rectImage\\1\\1.bmp");
images[1] = cv::imread("E:\\CPP\\rectImage\\1\\2.bmp");
images[2] = cv::imread("E:\\CPP\\rectImage\\1\\3.bmp");
CorrectImage ci;
ci.Init(images, 3,8,7,25);
ci.TranslateCoord(pts, 2);
Mat img1 = cv::imread("E:\\CPP\\rectImage\\1\\5.bmp");
double distance[2];
double angle[1];
ci.GetRes(img1, distance, angle);
}