摘要
在工业生产中,在进行目标定位任务时,常用激光扫描仪进行环境数据的提取,提取的数据表示目标点到激光扫描仪光心的距离,往往需要根据空间点的距离信息进行目标定位与提取。本案例主要是为了提取空旷环境中卡车车身上面的物资信息,并进行检测与定位。具体过程主要包括:1. 扫描仪数据获取 2.数据预处理(将数据转换成所需要的状态) 3. 将高度信息归一化0-255的单通道的uint8型灰度图 4. 图像预处理 5.目标检测与定位,本文旨在处理相关存在背景信息的干扰图像,并进行目标检测与定位的操作。其余部分将进行简单的介绍
1. 扫描仪数据获取
在对设备配置之后,获取的数据转换成txt或者xlsx文件格式,进行数据的读取,在本案例中由于只是一个仿真操作,我们通过matlab的xlsread函数进行xlsx文件的读取,返回二维矩阵数据。
data=xlsread('F:\vs2015\ZKKJ2019\行车\Book1.xlsx','sheet1');%1492*218
2.数据预处理
根据data信息提取出所有的高度信息,实际安装过程中,扫描仪相对地面的最大高度12.13f,需要对数据过滤,在大于12.13的区域归一化到12.13,这样做的前提是我们的环境较为简单,只有地面和来的物资信息,物资信息较高,相对扫描仪的距离相对较低,进行这种过滤操作的前提是背景与前景的灰度差较为明显,并不影响我们之后的图像处理操作。
3. 三维到二维的转换
将预处理之后的高度图归一化到0-255的灰度图。具体通过每个点pixel/max(高度最大值)*255:
maxHht=max(max(MatrixHht));%%%可以直接赋值12.13
%将高度信息归一化到灰度信息
for i=1:height2
for j=1:width2
MatrixPoint(i,j)=(MatrixHht(i,j)*255)/maxHht;
end
end
4. 图像预处理
如上图,空白处为地面背景信息,较亮的灰度信息为车的信息距离扫描仪相对较远处理之后相对物资处较亮,较暗的为车上的物资信息。我们需要一种鲁棒性较强的算法将车身与物资信息进行分割,由于背景占图像的大部分,我们首先需要过滤掉背景的白色区域,然后统计车身与物资之间的类间方差通过OTSU算法进行阈值分割,这样封装的接口会根据不同的输入返回一个合适的阈值,鲁棒性更强。具体代码如下:
l=0;
for i=1:height2
for j=1:width2
if MatrixPoint(i,j)~=255
l=l+1;
MatrixUse(l)=MatrixPoint(i,j);
end
end
end
%%%otsu二值阈值,根据类间方差求得阈值,为后续的二值化操作准备。
% thresh=graythresh(MatrixUse);
% Bin=im2bw(MatrixPoint,thresh);
%[height2,width2]=size(MatrixUse);%%%处理之后新的宽高信息 。
sumPix=zeros(1,256);
proDis=zeros(1,256);
for i=1:k
%内部+1原因在于像素范围在0-255,而matlab中索引从1开始
sumPix(MatrixUse(i)+1)=sumPix(MatrixUse(i)+1)+1;%%%统计直方图
end
for i=1:256
proDis(i)=sumPix(i)/k;
end
delta_max = 0;%初始化最大类间方差
for i =1:256
w0 = 0; w1 = 0; u0_tmp =0;
u1_tmp = 0;
for j = 1:256
if j < i
w0 = w0 + proDis(j);
u0_tmp = u0_tmp + j*proDis(j);
else
w1 = w1+ proDis(j);
u1_tmp = u1_tmp + j*proDis(j);
end
u0 = u0_tmp / w0;
u1 = u1_tmp / w1;
delta_tmp = w0*w1*power((u0 - u1), 2);
if delta_tmp > delta_max
delta_max = delta_tmp;
thresh = i/255;
end
end
end
%二值化
Bin=im2bw(uint8(MatrixPoint),thresh);%double型转uint8类型
可能有些人觉得代码冗余量较大,这是为了避免matlab2016的警告(可以忽略)。具体的显示结果如下
上图可以看出,可以很好地将车身与背景信息进行分割,还需要进行形态学的膨胀操作将物资与车身进行彻底的分割,这里需要注意的是根据物资的大小进行形态学的mask模板进行定义,具体代码如下:
%%%%%形态学操作
se=strel('rectangle',[20 160]);
Bin2=imclose(Bin,se);
figure;
imshow(Bin2,[]);
title('形态学处理之后的图像');
%imwrite(Bin2,'F:\vs2015\ZKKJ2019\行车\形态学效果图.bmp');
Bin3=~Bin2;
figure;
imshow(Bin3,[]);
title('二值图取反效果图');
%imwrite(Bin3,'F:\vs2015\ZKKJ2019\行车\取反结果图.bmp');
由于图像常以黑色作为背景,白色作为前景,因此我们需要对图像进行取反操作,最终的显示结果如下:
5. 目标定位与提取
在进行图像的预处理操作之后,我们将保存的图像通过OpenCV进行处理,根据minAreaRect函数获取目标点的最小外接圆,根据外接圆的宽高信息进行进行中心点的定位,并根据字典搜索的思想根据关键点的行列信息定位到原高度矩阵,对应位置获取目标点的高度以及自己根据参考点定义的相对位置信息。其中目标提取的源代码如下:
#include "core.hpp"
#include "highgui.hpp"
#include "imgproc.hpp"
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
//1. load image 原图是一个二值图像
Mat srcBin = imread("取反结果图.bmp", IMREAD_GRAYSCALE);
//bitwise_not(srcBin,srcBin);
//threshold(srcBin, srcBin, 0, 255, THRESH_OTSU);
if (srcBin.empty())
{
cout << "图像未正常读入error" << endl;
return -1;
}
imshow("src", srcBin);
Mat srcColor;
cvtColor(srcBin, srcColor, COLOR_GRAY2BGR);
//int nChannels = srcBin2.channels();
//cout << nChannels << endl; // 3 通道图片
//imshow("srcBin", srcBin);
//imshow("srcColor", srcColor);
//2. 进行二值图像的轮廓提取操作
vector<vector<Point>> vContours;
vector<Vec4i> vhie;
findContours(srcBin, vContours, vhie, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
//CV_CHAIN_APPROX_NONE表示提取出轮廓所有点的信息。
vector<float> vAreas;
for (unsigned int i = 0; i < vContours.size(); i++)
{
float fArea = contourArea(vContours[i]);
vAreas.push_back(fArea);
cout << "第"<<i<<"个轮廓面积 = "<< fArea << endl;
}
//现对第一个轮廓进行提取
//drawContours(srcColor, vContours, 0, Scalar(0, 0, 255), 3);
//imshow("srcColor", srcColor);
//可以进行一次轮廓过滤运算。
//3 通过vContours找线并进行直线拟合。
int nSize = vContours[0].size();//439
//cout << vContours[0][1] << endl;
//求轮廓的外接矩形得到长宽信息
vector<Rect> boundRect; //定义外接矩形集合
vector<RotatedRect> box; //定义最小外接矩形集合
Point2f rect[4];//存储最小外接矩形的四个点
for (int i = 0; i < vContours.size(); i++)
{
boundRect.push_back(boundingRect(vContours[i]));//求外接矩形
box.push_back(minAreaRect(vContours[i]));//求最小面积的外接矩形
//输出最小面积外接矩形的偏转角度。
cout << box[i].angle << endl;//最后可以根据实际要求进行角度的偏转。
box[i].points(rect);
if (i > 0)
{
for (int j = 0; j < 4; j++)
{
line(srcColor, rect[j], rect[(j + 1) % 4], Scalar(0, 0, 255), 3);
}
}
}
rectangle(srcColor, boundRect[0], Scalar(255, 0, 0), 3);
imshow("srcColor", srcColor);
waitKey(0);
return 0;
}
具体的提取结果如下
上图可以看出,目标信息很好的被提取了出来。