algoLib/sourceCode/WD_particleSizeMeasure.cpp

559 lines
18 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <vector>
#include "SG_baseDataType.h"
#include "SG_baseAlgo_Export.h"
#include "WD_particleSizeMeasure_Export.h"
#include <opencv2/opencv.hpp>
#include <limits>
std::string m_strVersion = "1.0.0";
const char* wd_particleSegVersion(void)
{
return m_strVersion.c_str();
}
//计算一个平面调平参数。
//数据输入中可以有一个地平面和参考调平平面,以最高的平面进行调平
//旋转矩阵为调平参数,即将平面法向调整为垂直向量的参数
SSG_planeCalibPara wd_getBaseCalibPara(
std::vector< std::vector<SVzNL3DPosition>>& scanLines)
{
return sg_getPlaneCalibPara2(scanLines);
}
//相机姿态调平,并去除地面
void wd_lineDataR(
std::vector< SVzNL3DPosition>& a_line,
const double* camPoseR,
double groundH)
{
lineDataRT_vector(a_line, camPoseR, groundH);
}
//粒径检测
void wd_particleSizeMeasure(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const SWD_paricleSizeParam particleSizeParam,
const SSG_planeCalibPara groundCalibPara,
const SWD_PSM_algoParam algoParam,
std::vector<SWD_ParticlePosInfo>& particles,
int* errCode)
{
int lineNum = (int)scanLines.size();
if (lineNum == 0)
{
*errCode = SG_ERR_3D_DATA_NULL;
return;
}
//噪点过滤
//垂直方向过滤
wd_noiseFilter(scanLines, algoParam.filterParam, errCode);
if (*errCode != 0)
return;
///开始数据处理
//提取特征跳变、低于z阈值、V及L型
//垂直数据处理
std::vector<SSG_lineFeature> all_vLineFeatures;
for (int i = 0; i < lineNum; i++)
{
if (i == 202)
int k = 1;
SSG_lineFeature a_line_features;
a_line_features.lineIdx = i;
wd_getLineCornerFeature_PSM(
&scanLines[i][0],
(int)scanLines[i].size(),
i,
groundCalibPara.planeHeight,
algoParam.cornerParam,
&a_line_features);
all_vLineFeatures.push_back(a_line_features); //空行也加入,保证能按行号索引
}
//生成水平扫描数据
int nPointCnt = (int)scanLines[0].size();
std::vector<std::vector<SVzNL3DPosition>> hLines;
hLines.resize(nPointCnt);
for (int i = 0; i < nPointCnt; i++)
hLines[i].resize(lineNum);
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < nPointCnt; j++)
{
scanLines[line][j].nPointIdx = 0;;
hLines[j][line] = scanLines[line][j];
hLines[j][line].pt3D.x = scanLines[line][j].pt3D.y;
hLines[j][line].pt3D.y = scanLines[line][j].pt3D.x;
}
}
std::vector<SSG_lineFeature> all_hLineFeatures;
//逐行提取特征
for (int hLine = 0; hLine < nPointCnt; hLine++)
{
if (hLine == 14)
int kkk = 1;
SSG_lineFeature a_hLine_featrues;
a_hLine_featrues.lineIdx = hLine;
wd_getLineCornerFeature_PSM(
&hLines[hLine][0],
(int)hLines[hLine].size(),
hLine,
groundCalibPara.planeHeight,
algoParam.cornerParam,
&a_hLine_featrues);
//if ((a_hLine_featrues.features.size() > 0) || (a_hLine_featrues.endings.size() > 0))
all_hLineFeatures.push_back(a_hLine_featrues);//空行也加入,保证能按行号索引
}
/// 统计整个视野大小
SWD_pointCloudPara pntCloudPara = wd_getPointCloudPara(scanLines);
SVzNLRangeD x_range = pntCloudPara.xRange;
SVzNLRangeD y_range = pntCloudPara.yRange;
SSG_ROIRectD globalROI;
globalROI.left = pntCloudPara.xRange.min;
globalROI.right = pntCloudPara.xRange.max;
globalROI.top = pntCloudPara.yRange.min;
globalROI.bottom = pntCloudPara.yRange.max;
//垂直方向特征生长(相机扫描方向)
std::vector<SSG_featureTree> v_feature_trees;
std::vector<SSG_featureTree> v_ending_trees;
//特征生长
wd_getFeatureGrowingTrees_noTypeMatch(
all_vLineFeatures,
v_feature_trees,
v_ending_trees,
algoParam.growParam);
//水平方向特征生长
std::vector<SSG_featureTree> h_feature_trees;
std::vector<SSG_featureTree> h_ending_trees;
//特征生长
wd_getFeatureGrowingTrees_noTypeMatch(
all_hLineFeatures,
h_feature_trees,
h_ending_trees,
algoParam.growParam);
//生成Mask
double scale;
if ((pntCloudPara.scale_x < 0) || (pntCloudPara.scale_y < 0))
{
*errCode = SG_ERR_INVLD_Q_SCALE;
return;
}
if(pntCloudPara.scale_x < pntCloudPara.scale_y)
scale = pntCloudPara.scale_x;
else
scale = pntCloudPara.scale_y;
//距离变换Mask以1mm为量化尺度
double inerPolateDistTh = scale * 10; //插值门限, 两间距离大于此阈值不插值
int edgeSkip = 2;
int maskX = (int)(x_range.max - x_range.min)/scale + 1;
int maskY = (int)(y_range.max - y_range.min)/scale + 1;
if ((maskX < 16) || (maskY < 16))
return;
maskY = maskY + edgeSkip * 2;
maskX = maskX + edgeSkip * 2;
cv::Mat distTranformMask(maskY, maskX, CV_32FC1, 0.0f); //距离变换Mask初始化为一个极大值1e+6
cv::Mat distTranformIndexing(maskY, maskX, CV_32SC2, cv::Vec2i(0, 0)); //坐标索引
cv::Mat featureMask = cv::Mat::zeros(nPointCnt, lineNum, CV_32SC4);
pointClout2DProjection(
scanLines,
x_range,
y_range,
scale,
-1.0,
edgeSkip,
inerPolateDistTh, //插值阈值。大于此阈值的不进行量化插值
distTranformMask,//投影量化数据初始化为一个极大值1e+6
distTranformIndexing //标记坐标索引用于回找3D坐标
);
std::vector<SSG_treeInfo> allTreesInfo; //不包含边界
//标注:垂直
SSG_treeInfo a_nullTree;
memset(&a_nullTree, 0, sizeof(SSG_treeInfo));
allTreesInfo.push_back(a_nullTree); //保持存储位置与treeIdx相同位置方便索引
//标注垂直边界
int treeID = 1;
for (int i = 0, i_max = (int)v_ending_trees.size(); i < i_max; i++)
{
SSG_featureTree* a_vEdgeTree = &v_ending_trees[i];
//记录Tree的信息
SSG_treeInfo a_treeInfo;
a_treeInfo.vTreeFlag = 1;
a_treeInfo.treeIdx = treeID;
a_treeInfo.treeType = a_vEdgeTree->treeType;
a_treeInfo.sLineIdx = a_vEdgeTree->sLineIdx;
a_treeInfo.eLineIdx = a_vEdgeTree->eLineIdx;
a_treeInfo.roi = a_vEdgeTree->roi;
allTreesInfo.push_back(a_treeInfo);
//在原始点云上标记同时有Mask上标记
for (int j = 0, j_max = (int)a_vEdgeTree->treeNodes.size(); j < j_max; j++)
{
SSG_basicFeature1D* a_feature = &a_vEdgeTree->treeNodes[j];
if (scanLines[a_feature->jumpPos2D.x][a_feature->jumpPos2D.y].pt3D.z > 1e-4)//虚假目标过滤后点会置0
{
scanLines[a_feature->jumpPos2D.x][a_feature->jumpPos2D.y].nPointIdx = a_feature->featureType;
scanLines[a_feature->jumpPos2D.x][a_feature->jumpPos2D.y].nPointIdx &= 0xffff;
scanLines[a_feature->jumpPos2D.x][a_feature->jumpPos2D.y].nPointIdx += treeID << 16;
featureMask.at<cv::Vec4i>(a_feature->jumpPos2D.y, a_feature->jumpPos2D.x)[0] = treeID; //edgeID
featureMask.at<cv::Vec4i>(a_feature->jumpPos2D.y, a_feature->jumpPos2D.x)[1] = a_vEdgeTree->treeType;
featureMask.at<cv::Vec4i>(a_feature->jumpPos2D.y, a_feature->jumpPos2D.x)[2] = 1; //vscan
int px = (int)((scanLines[a_feature->jumpPos2D.x][a_feature->jumpPos2D.y].pt3D.x - x_range.min)/scale) + edgeSkip;
int py = (int)((scanLines[a_feature->jumpPos2D.x][a_feature->jumpPos2D.y].pt3D.y - y_range.min)/scale) + edgeSkip;
distTranformMask.at<float>(py, px) = 0;
}
}
treeID++;
}
//标注水平边界
for (int i = 0, i_max = (int)h_ending_trees.size(); i < i_max; i++)
{
SSG_featureTree* a_hEdgeTree = &h_ending_trees[i];
//记录Tree的信息
SSG_treeInfo a_treeInfo;
a_treeInfo.vTreeFlag = 0;
a_treeInfo.treeIdx = treeID;
a_treeInfo.treeType = a_hEdgeTree->treeType;
a_treeInfo.sLineIdx = a_hEdgeTree->sLineIdx;
a_treeInfo.eLineIdx = a_hEdgeTree->eLineIdx;
a_treeInfo.roi.left = a_hEdgeTree->roi.top; //水平扫描xy是交换的
a_treeInfo.roi.right = a_hEdgeTree->roi.bottom;
a_treeInfo.roi.top = a_hEdgeTree->roi.left;
a_treeInfo.roi.bottom = a_hEdgeTree->roi.right;
allTreesInfo.push_back(a_treeInfo);
//在原始点云上标记同时有Mask上标记
for (int j = 0, j_max = (int)a_hEdgeTree->treeNodes.size(); j < j_max; j++)
{
SSG_basicFeature1D* a_feature = &a_hEdgeTree->treeNodes[j];
if (scanLines[a_feature->jumpPos2D.y][a_feature->jumpPos2D.x].pt3D.z > 1e-4)//虚假目标过滤后点会置0
{
int existEdgeId = scanLines[a_feature->jumpPos2D.y][a_feature->jumpPos2D.x].nPointIdx >> 16;
if (existEdgeId == 0)
{
scanLines[a_feature->jumpPos2D.y][a_feature->jumpPos2D.x].nPointIdx += a_feature->featureType << 4;
scanLines[a_feature->jumpPos2D.y][a_feature->jumpPos2D.x].nPointIdx &= 0xffff;
scanLines[a_feature->jumpPos2D.y][a_feature->jumpPos2D.x].nPointIdx += treeID << 16;
featureMask.at<cv::Vec4i>(a_feature->jumpPos2D.x, a_feature->jumpPos2D.y)[0] = treeID;
featureMask.at<cv::Vec4i>(a_feature->jumpPos2D.x, a_feature->jumpPos2D.y)[1] += a_hEdgeTree->treeType << 4;
featureMask.at<cv::Vec4i>(a_feature->jumpPos2D.x, a_feature->jumpPos2D.y)[2] = 2;//hsan flag
int px = (int)((scanLines[a_feature->jumpPos2D.y][a_feature->jumpPos2D.x].pt3D.x - x_range.min)/scale) + edgeSkip;
int py = (int)((scanLines[a_feature->jumpPos2D.y][a_feature->jumpPos2D.x].pt3D.y - y_range.min)/scale) + edgeSkip;
distTranformMask.at<float>(py, px) = 0;
}
}
}
treeID++;
}
//垂直特征标注
int hvTreeIdx = treeID;
int vTreeStart = treeID;
for (int i = 0, i_max = (int)v_feature_trees.size(); i < i_max; i++)
{
SSG_featureTree* a_vTree = &v_feature_trees[i];
//记录Tree的信息
SSG_treeInfo a_treeInfo;
a_treeInfo.vTreeFlag = 1;
a_treeInfo.treeIdx = hvTreeIdx;
a_treeInfo.treeType = a_vTree->treeType;
a_treeInfo.sLineIdx = a_vTree->sLineIdx;
a_treeInfo.eLineIdx = a_vTree->eLineIdx;
a_treeInfo.roi = a_vTree->roi;
allTreesInfo.push_back(a_treeInfo);
//在原始点云上标记同时有Mask上标记
for (int j = 0, j_max = (int)a_vTree->treeNodes.size(); j < j_max; j++)
{
SSG_basicFeature1D* a_feature = &a_vTree->treeNodes[j];
if (scanLines[a_feature->jumpPos2D.x][a_feature->jumpPos2D.y].pt3D.z > 1e-4)//虚假目标过滤后点会置0
{
int existEdgeId = scanLines[a_feature->jumpPos2D.x][a_feature->jumpPos2D.y].nPointIdx >> 16;
if (existEdgeId == 0)
{
scanLines[a_feature->jumpPos2D.x][a_feature->jumpPos2D.y].nPointIdx = a_feature->featureType;
scanLines[a_feature->jumpPos2D.x][a_feature->jumpPos2D.y].nPointIdx &= 0xffff;
scanLines[a_feature->jumpPos2D.x][a_feature->jumpPos2D.y].nPointIdx += hvTreeIdx << 16;
featureMask.at<cv::Vec4i>(a_feature->jumpPos2D.y, a_feature->jumpPos2D.x)[0] = hvTreeIdx; //edgeID
featureMask.at<cv::Vec4i>(a_feature->jumpPos2D.y, a_feature->jumpPos2D.x)[1] = a_vTree->treeType;
featureMask.at<cv::Vec4i>(a_feature->jumpPos2D.y, a_feature->jumpPos2D.x)[2] = 1; //vscan
int px = (int)((scanLines[a_feature->jumpPos2D.x][a_feature->jumpPos2D.y].pt3D.x - x_range.min)/scale) + edgeSkip;
int py = (int)((scanLines[a_feature->jumpPos2D.x][a_feature->jumpPos2D.y].pt3D.y - y_range.min)/scale) + edgeSkip;
distTranformMask.at<float>(py, px) = 0;
}
}
}
hvTreeIdx++;
}
int hTreeStart = hvTreeIdx;
//标注:水平特征
for (int i = 0, i_max = (int)h_feature_trees.size(); i < i_max; i++)
{
SSG_featureTree* a_hTree = &h_feature_trees[i];
//记录Tree的信息
SSG_treeInfo a_treeInfo;
a_treeInfo.vTreeFlag = 0;
a_treeInfo.treeIdx = hvTreeIdx;
a_treeInfo.treeType = a_hTree->treeType;
a_treeInfo.sLineIdx = a_hTree->sLineIdx;
a_treeInfo.eLineIdx = a_hTree->eLineIdx;
a_treeInfo.roi.left = a_hTree->roi.top; //水平扫描xy是交换的
a_treeInfo.roi.right = a_hTree->roi.bottom;
a_treeInfo.roi.top = a_hTree->roi.left;
a_treeInfo.roi.bottom = a_hTree->roi.right;
allTreesInfo.push_back(a_treeInfo);
//在原始点云上标记同时有Mask上标记
for (int j = 0, j_max = (int)a_hTree->treeNodes.size(); j < j_max; j++)
{
SSG_basicFeature1D* a_feature = &a_hTree->treeNodes[j];
if (scanLines[a_feature->jumpPos2D.y][a_feature->jumpPos2D.x].pt3D.z > 1e-4)//虚假目标过滤后点会置0
{
int existEdgeId = scanLines[a_feature->jumpPos2D.y][a_feature->jumpPos2D.x].nPointIdx >> 16;
if (existEdgeId == 0)
{
scanLines[a_feature->jumpPos2D.y][a_feature->jumpPos2D.x].nPointIdx += a_feature->featureType << 4;
scanLines[a_feature->jumpPos2D.y][a_feature->jumpPos2D.x].nPointIdx &= 0xffff;
scanLines[a_feature->jumpPos2D.y][a_feature->jumpPos2D.x].nPointIdx += hvTreeIdx << 16;
featureMask.at<cv::Vec4i>(a_feature->jumpPos2D.x, a_feature->jumpPos2D.y)[0] = hvTreeIdx;
featureMask.at<cv::Vec4i>(a_feature->jumpPos2D.x, a_feature->jumpPos2D.y)[1] += a_hTree->treeType << 4;
featureMask.at<cv::Vec4i>(a_feature->jumpPos2D.x, a_feature->jumpPos2D.y)[2] = 2;//hsan flag
int px = (int)((scanLines[a_feature->jumpPos2D.y][a_feature->jumpPos2D.x].pt3D.x - x_range.min)/scale) + edgeSkip;
int py = (int)((scanLines[a_feature->jumpPos2D.y][a_feature->jumpPos2D.x].pt3D.y - y_range.min)/scale) + edgeSkip;
distTranformMask.at<float>(py, px) = 0;
}
}
}
hvTreeIdx++;
}
int hvTreeSize = hvTreeIdx;
double x_scale = pntCloudPara.scale_x;
double y_scale = pntCloudPara.scale_y;
//进行距离变换,然后使用分水岭算法进行分割
cv::Mat distTransform;
sg_distanceTrans(distTranformMask, distTransform, 0);
#if OUTPUT_DEBUG //debug
cv::Mat maskImage;
cv::normalize(distTranformMask, maskImage, 0, 255, cv::NORM_MINMAX, CV_8U);
cv::imwrite("distTransformMask.png", maskImage);
cv::Mat dtImage;
cv::normalize(distTransform, dtImage, 0, 255, cv::NORM_MINMAX, CV_8U);
cv::Mat dtImage_color;
cv::cvtColor(dtImage, dtImage_color, cv::COLOR_GRAY2BGR);
cv::imwrite("distTransform.png", dtImage_color);
#endif
//寻找Peak。Peak确定后以Peak为种子点进行分水岭方法分割
double minW = particleSizeParam.minSize.width;
SSG_localPkParam searchWin;
searchWin.seachW_lines = (int)(minW * 0.4/scale);
searchWin.searchW_pts = (int)(minW * 0.4/scale);
std::vector<SSG_2DValueI> dt_peaks;
sg_getLocalPeaks_distTransform(distTransform, dt_peaks, searchWin);
//以大小进行过滤
double minDistTh = minW * 0.4 / scale;
std::vector<SSG_2DValueI> filter_dt_peaks;
for (int i = 0, i_max = (int)dt_peaks.size(); i < i_max; i++)
{
if (dt_peaks[i].valueD > minDistTh)
filter_dt_peaks.push_back(dt_peaks[i]);
}
//以距离进行过滤距离变换相当于最小内切圆。不同的目标所在的内切圆一定不相交因此R1+R2 < 圆心距离
int filterSize = (int)filter_dt_peaks.size();
for (int i = 0; i < filterSize; i++)
{
SSG_2DValueI& obj_0 = filter_dt_peaks[i];
for (int j = i + 1; j < filterSize; j++)
{
SSG_2DValueI& obj_1 = filter_dt_peaks[j];
double dist = sqrt(pow(obj_0.x - obj_1.x, 2) + pow(obj_0.y - obj_1.y, 2));
double distTh = dist * 1.2;
if ((obj_0.valueD + obj_1.valueD) > distTh) //合并
{
if (obj_0.valueD < obj_1.valueD)
obj_0.value = -1;
else
obj_1.value = -1;
}
}
}
//有效种子
std::vector<SSG_2DValueI> vld_dt_peaks;
for (int i = 0; i < filterSize; i++)
{
if (filter_dt_peaks[i].value < 0)
continue;
vld_dt_peaks.push_back(filter_dt_peaks[i]);
}
#if OUTPUT_DEBUG //debug
int dbg_seedNum = (int)vld_dt_peaks.size();
for (int i = 0; i < dbg_seedNum; i++)
{
int dbg_px = vld_dt_peaks[i].x;
int dbg_py = vld_dt_peaks[i].y;
//dtImage_color.at<cv::Vec3b>(dbg_py, dbg_px) = cv::Vec3b(0, 0, 255);
cv::circle(dtImage_color, cv::Point(dbg_px, dbg_py), 3, cv::Scalar(0, 0, 255), -1);
}
cv::imwrite("distTransform_seed.png", dtImage_color);
#endif
//分水岭分割
//扫描边界,建立相邻目标边界集合
//通过目标相邻边界判断是否合并相邻目标
//获取最大最小值
double minVal, maxVal;
cv::Point minLoc, maxLoc;
// 调用minMaxLoc函数
cv::minMaxLoc(distTransform, &minVal, &maxVal, &minLoc, &maxLoc);
//分水岭算法进行分割
SWD_waterShedImage wsImg;
wsImg.width = distTransform.cols;
wsImg.height = distTransform.rows;
wsImg.gray.resize(wsImg.height, std::vector<int>(wsImg.width));
wsImg.markers.resize(wsImg.height, std::vector<int>(wsImg.width, 1)); // 初始化标记图为1,背景
int maxValue = (int)maxVal + 2;
int maxLevel = (int)(maxVal - minVal);
for (int i = 0; i < distTransform.rows; i++)
{
if (i == 758)
int kkk = 1;
float* rowPtr = distTransform.ptr<float>(i);
for (int j = 0; j < distTransform.cols; j++)
{
if (j == 171)
int kkk = 1;
float disValue = rowPtr[j];
if (disValue < 1e-4) //边界和背景
wsImg.gray[i][j] = maxValue;
else
{
wsImg.gray[i][j] = (int)(maxVal - disValue);
wsImg.markers[i][j] = 0;
}
}
}
int startMarkerID = 2;
wd_seedWatershed(wsImg, vld_dt_peaks, maxLevel, startMarkerID);
#if OUTPUT_DEBUG //debug
cv::Mat waterShedResult(wsImg.height, wsImg.width, CV_8UC3);
for (int i = 0; i < wsImg.height; ++i) {
for (int j = 0; j < wsImg.width; ++j) {
if (wsImg.markers[i][j] == -1) { // 分水岭边界(红色)
waterShedResult.at<cv::Vec3b>(i, j) = cv::Vec3b(0,0,255);
}
else if (wsImg.markers[i][j] < 2)
{
waterShedResult.at<cv::Vec3b>(i, j) = cv::Vec3b(200, 200, 200);
}
else
{ // 区域(根据标记值生成不同颜色)
int color_r = (wsImg.markers[i][j] * 97) % 256;
int color_g = (wsImg.markers[i][j] * 73) % 256;
int color_b = (wsImg.markers[i][j] * 59) % 256;
waterShedResult.at<cv::Vec3b>(i, j) = cv::Vec3b(color_b, color_g, color_r);
}
}
}
cv::imwrite("watershed.png", waterShedResult);
#endif
//生成分割后的目标
//检查每个3D点在投影上的位置生成目标的3D点
std::vector<std::vector< SVzNL3DPoint>> segObjs;
int maxMkID = startMarkerID + (int)vld_dt_peaks.size();
segObjs.resize(maxMkID);
for (int line = 0; line < lineNum; line++)
{
for (int i = 0; i < nPointCnt; i++)
{
SVzNL3DPosition* pt3D = &scanLines[line][i];
pt3D->nPointIdx = 0;
if (pt3D->pt3D.z < 1e-4)
continue;
double x = pt3D->pt3D.x;
double y = pt3D->pt3D.y;
int px = (int)(x - x_range.min) / scale + edgeSkip;
int py = (int)(y - y_range.min) / scale + edgeSkip;
int marker = wsImg.markers[py][px];
if ((marker >= startMarkerID)&&( marker <= maxMkID))
{
pt3D->nPointIdx = marker;
segObjs[marker].push_back(pt3D->pt3D);
}
}
}
//生成目标
for (int i = startMarkerID; i < maxMkID; i++)
{
if (segObjs[i].size() < 10)
continue;
//获取投影,并统计最小Z和最大Z
double minZ = -1;
double maxZ = 0;
std::vector<cv::Point2f> points;
int ptSize = (int)segObjs[i].size();
for (int m = 0; m < ptSize; m++)
{
float x = (float)segObjs[i][m].x;
float y = (float)segObjs[i][m].y;
points.push_back(cv::Point2f(x, y));
if (minZ < 0)
{
minZ = segObjs[i][m].z;
maxZ = segObjs[i][m].z;
}
else
{
if (minZ > segObjs[i][m].z) minZ = segObjs[i][m].z;
if (maxZ < segObjs[i][m].z) maxZ = segObjs[i][m].z;
}
}
if (points.size() == 0)
continue;
//最小外接矩
// 最小外接矩形
cv::RotatedRect rect = minAreaRect(points);
cv::Point2f vertices[4];
rect.points(vertices);
double width = rect.size.width; //投影的宽和高, 对应袋子的长和宽
double height = rect.size.height;
if (width < height)
{
double tmp = height;
height = width;
width = tmp;
}
SWD_ParticlePosInfo a_obj;
a_obj.size.length = width;
a_obj.size.width = height;
a_obj.size.height = maxZ - minZ;
for (int m = 0; m < 4; m++)
{
SVzNL3DPoint vPt_btm = { vertices[m].x, vertices[m].y, maxZ };
SVzNL3DPoint vPt_top = { vertices[m].x, vertices[m].y, minZ };
a_obj.vertix[m] = vPt_btm;
a_obj.vertix[m + 4] = vPt_top;
}
particles.push_back(a_obj);
}
}