要求:当无人叉车接收到前面给定物资的具体坐标后,将车上物资全部运走,剩下空车,这时候需要对空车再进行一遍扫描,确定空车可以摆放多少件物资,以及摆放物资的大概位置(我理解的这一个步骤获取只是为了在界面上展示好看一点),单单只是普通的空车,计算上面能够摆放多少件物资并不难,难得是有些空车的货箱前半部分会稍微高一点。
为了表达清晰。将普通车型记为 C,将车身前微高一点的车型记为C_h
思路:
大致思路还是利用到了最小外接矩,找到角点。
1、首先要判断车型,即是否是前面稍高的车型(图中红线标记的地方,两侧灰度值明显不同),设置二值化阈值(阈值设置是个重要的点),利用最小外接矩找到车身轮廓的四个角点(图中绿色的点),如果被扫描车型是C_h,阈值设置不当会导致外接矩找到较小矩形的四个角点。
看图:
根据绿色角点坐标,找到蓝色点,计算两个蓝色点之间的灰度值,设置阈值,即可判断车型,因为灰度值是通过车高转换得到的,所以就可以根据两点的灰度值来判断具体车型。
部分代码:
A.x = static_cast<int>((rectB[2].x + rectB[3].x) / 2);
A.y = static_cast<int>((rectB[2].y + rectB[3].y) / 2 + 50);
Ag = matGray.at<uchar>(Point(A.x, A.y)); //A点灰度值
circle(matSrc, Point(A.x, A.y), 5, Scalar(255, 0, 0), -1, 8);
B.x = (rectB[1].x + rectB[0].x) / 2;
B.y = (rectB[1].y + rectB[0].y) / 2 - 50;
Bg = matGray.at<uchar>(Point(B.x, B.y)); //B点灰度值
circle(matSrc, Point(B.x, B.y), 5, Scalar(255, 0, 0), -1, 8);
在灰度图中获取某一点的灰度值的时候,遇到了一个没有整明白原因的错误,
Ag = matGray.at<uchar>(Point(A.x, A.y)); //A点灰度值
之前获取多通道的像素值可以直接使用 mat.at<Vec3b>(x,y)[0]直接获取,而获取灰度值的时候,直接敲坐标会报如下错误:Assertion failed <y==0||<data && dims>+1 && <unsigned>y<<unsigned>size.p[0]>>
解决办法,就是用Point ,具体原因,同事说是图片(217*1492)像素值太大,超过电脑的分辨率(1920*1080),将图片倒着就可以了,下午我也以为是这个原因,到了晚上刚刚又实验了一下,竟然通过了,搞不懂~不过总算是找到方法解决了
2、经过对两点灰度值的计算,就可以判定具体车型了,车型C比较简单,这里写一下C_h,
由于车身前面高出来一部分,要根据物资的长宽计算高出来的那一部分可以放多少个,才不会由于摆放的原因,对物资造成损坏
同样的,根据设置阈值,计算高出矩形的最小外接矩,计算长度之后和物资长度进行比较,设定阈值,即可判定高出部分具体可以摆放物资的数量。
车身其余部分,根据车身长度和物资长度等分即可。写到这里突然觉得我得表述能力很有问题~我直接上图把。根据等分点公式,找到红色点,描述完了,突然感觉没有什么技术含量的东西,我搞了两三天,比较low,可是如果可以将简单得问题,实现,并运用到项目中,我也是比较骄傲的。
上一段判断车型的代码吧,感觉现场调参很重要,因为要根据具体车高,调整二值化的阈值。
#include<iostream>
#include<opencv2\opencv.hpp>
using namespace std;
using namespace cv;
cv::Mat GetStructElement(int elem, int size)
{
int type;
if (elem == 0) { type = cv::MORPH_RECT; }
else if (elem == 1) { type = cv::MORPH_CROSS; }
else if (elem == 2) { type = cv::MORPH_ELLIPSE; }
return cv::getStructuringElement(type, cv::Size(2 * size + 1, 2 * size + 1), cv::Point(size, size));
}
int main()
{
Mat matSrc = imread("car_high_up.jpg");
Mat matGray;
cvtColor(matSrc, matGray, CV_BGR2GRAY);
Point2f rectB[4];
Point2f rectS[4];
Point2f P0, P1, P2, P3, P4, P5;
//Big
Mat matBinaryB;
threshold(matGray, matBinaryB, 225, 255, CV_THRESH_BINARY);
Mat elementB = GetStructElement(MORPH_RECT, (30, 30));
morphologyEx(matBinaryB, matBinaryB, MORPH_CLOSE, elementB);
Mat matB = matBinaryB.clone();
vector<vector<Point>> contoursB;
vector<Vec4i> hierarchyB;
findContours(matB, contoursB, hierarchyB, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
for (size_t i = 0; i < contoursB.size(); i++)
{
double area = contourArea(contoursB[i]);
if (area > 10000 && area < 200000)
{
RotatedRect box = minAreaRect(contoursB[i]);
box.points(rectB);
for (int i = 0; i < 4; i++)
{
circle(matSrc, Point(rectB[i].x, rectB[i].y), 5, Scalar(255, 0, 0), -1, 8);
}
P0.x = ((4 - 1)*rectB[0].x + 1 * rectB[1].x) / 4;
P0.y = ((4 - 1)*rectB[0].y + 1 * rectB[1].y) / 4;
circle(matSrc, Point(P0.x, P0.y), 5, Scalar(0, 255, 0), -1, 8);
P1.x = ((4 - 3)*rectB[0].x + 3 * rectB[1].x) / 4;
P1.y = ((4 - 3)*rectB[0].y + 3 * rectB[1].y) / 4;
circle(matSrc, Point(P1.x, P1.y), 5, Scalar(0, 255, 0), -1, 8);
P2.x = ((4 - 1)*rectB[2].x + 1 * rectB[3].x) / 4;
P2.y = ((4 - 1)*rectB[2].y + 1 * rectB[3].y) / 4;
circle(matSrc, Point(P2.x, P2.y), 5, Scalar(0, 255, 0), -1, 8);
P3.x = ((4 - 3)*rectB[2].x + 3 * rectB[3].x) / 4;
P3.y = ((4 - 3)*rectB[2].y + 3 * rectB[3].y) / 4;
circle(matSrc, Point(P3.x, P3.y), 5, Scalar(0, 255, 0), -1, 8);
}
}
//Small
Mat matBinaryS;
threshold(matGray, matBinaryS, 220, 255, CV_THRESH_BINARY);
Mat elementS = GetStructElement(MORPH_RECT, (30, 30));
morphologyEx(matBinaryS, matBinaryS, MORPH_CLOSE, elementS);
Mat matS = matBinaryS.clone();
vector<vector<Point>> contoursS;
vector<Vec4i> hierarchyS;
findContours(matS, contoursS, hierarchyS, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
for (size_t i = 0; i < contoursS.size(); i++)
{
double area = contourArea(contoursS[i]);
if (area > 10000 && area < 200000)
{
RotatedRect box = minAreaRect(contoursS[i]);
box.points(rectS);
circle(matSrc, Point(rectS[3].x, rectS[3].y), 5, Scalar(255, 0, 0), -1, 8);
circle(matSrc, Point(rectS[0].x, rectS[0].y), 5, Scalar(255, 0, 0), -1, 8);
P5.x = ((4 - 1)*rectS[0].x + 1 * rectS[3].x) / 4;
P5.y = ((4 - 1)*rectS[0].y + 1 * rectS[3].y) / 4;
circle(matSrc, Point(P5.x, P5.y), 5, Scalar(0, 255, 0), -1, 8);
P4.x = ((4 - 3)*rectS[0].x + 3 * rectS[3].x) / 4;
P4.y = ((4 - 3)*rectS[0].y + 3 * rectS[3].y) / 4;
circle(matSrc, Point(P4.x, P4.y), 5, Scalar(0, 255, 0), -1, 8);
}
}
cout << "******************************************************" << endl;
double dis = sqrt( (rectS[1].x - rectS[0].x) * (rectS[1].x - rectS[0].x) + (rectS[1].y - rectS[0].y) * (rectS[1].y - rectS[0].y));
if (dis >= 383) //当凸出来的面积高度大于2.8 米,也就是在像素中大于383时 ,可以放下两个变压器
{
for (int i = 1; i < 4; i = i + 2)
{
Point2f Pc1,Pc2;
Pc1.x = ((4 - i)*P2.x + i*P5.x) / 4;
Pc1.y = ((4 - i)*P2.y + i*P5.y) / 4;
circle(matSrc, Point(Pc1.x, Pc1.y), 6, Scalar(0, 0, 255), -1, 8);
Pc2.x = ((4 - i)*P3.x + i*P4.x) / 4;
Pc2.y = ((4 - i)*P3.y + i*P4.y) / 4;
circle(matSrc, Point(Pc2.x, Pc2.y), 6, Scalar(0, 0, 255), -1, 8);
cout << " " << "(" << Pc1.x << "," << Pc1.y << ")" << " " << "(" << Pc2.x << "," << Pc2.y << ")" << endl;
}
}
else
{
Point2f Pc1, Pc2;
Pc1.x = (P2.x + P5.x) / 2;
Pc1.y = (P2.y + P5.y) / 2;
circle(matSrc, Point(Pc1.x, Pc1.y), 6, Scalar(0, 0, 255), -1, 8);
Pc2.x = (P3.x + P4.x) / 2;
Pc2.y = (P3.y + P4.y) / 2;
circle(matSrc, Point(Pc2.x, Pc2.y), 6, Scalar(0, 0, 255), -1, 8);
cout << " " << "(" << Pc1.x << "," << Pc1.y << ")" << " " << "(" << Pc2.x << "," << Pc2.y << ")" << endl;
}
//确定n的值
double car_h = sqrt((P5.x - P1.x) * (P5.x - P1.x) + (P5.y - P1.y) * (P5.y - P1.y));
double T_h = 200; //最大变压器的像素值
int N = floor(car_h / T_h);
int n = N * 2;
for (int i = 1; i < n; i = i + 2)
{
Point2f Pc1,Pc2;
Pc1.x = ((n - i)*P5.x + i*P1.x) / n;
Pc1.y = ((n - i)*P5.y + i*P1.y) / n;
circle(matSrc, Point(Pc1.x, Pc1.y), 6, Scalar(0, 0, 255), -1, 8);
Pc2.x = ((n - i)*P4.x + i*P0.x) / n;
Pc2.y = ((n - i)*P4.y + i*P0.y) / n;
circle(matSrc, Point(Pc2.x, Pc2.y), 6, Scalar(0, 0, 255), -1, 8);
cout << " " << "(" << Pc1.x << "," << Pc1.y << ")" << " " << "(" << Pc2.x << "," << Pc2.y << ")" << endl;
}
namedWindow("picture",WINDOW_NORMAL);
imshow("picture", matSrc);
waitKey(0);
return 0;
}
运行结果:
标记后的效果图,即为上图。