EmguCV调用PCACompute时如何将VectorOfPoint轮廓转换为可输入的Mat
EmguCV 轮廓PCA分析实现修正方案
原有代码核心问题
PCACompute要求输入样本矩阵为每行/每列对应一个样本点的浮点类型(Cv32F/Cv64F)矩阵,你初始化的mean_mat、eigen_vectors用的是8U灰度图格式,不符合参数要求- 直接调用
Mat.SetTo()传入Point数组无法正确填充矩阵数据,需要手动构造符合PCA要求的样本矩阵结构 - 直接传入
VectorOfVectorOfPoint类型的轮廓集合到PCACompute是无效输入,需提取单个轮廓的点集构造样本矩阵
正确实现逻辑
1. 构造PCA输入样本矩阵
提取单个轮廓的点集后,构造行数等于轮廓点数量、列数为2(对应x、y坐标)的32位浮点矩阵,逐点填充坐标值:
Point[] targetContour = contours.ToArrayOfArray()[0]; Mat sampleMat = new Mat(targetContour.Length, 2, DepthType.Cv32F, 1); var matIndexer = sampleMat.GetGenericIndexer<float>(); for (int i = 0; i < targetContour.Length; i++) { matIndexer[i, 0] = targetContour[i].X; matIndexer[i, 1] = targetContour[i].Y; }
2. PCA计算参数修正
均值矩阵、特征向量矩阵无需提前指定大小,PCACompute会自动计算后填充,可通过最后一个参数指定保留的主成分数量:
Mat meanMat = new Mat(); Mat eigenVectors = new Mat(); // 保留2个主成分 CvInvoke.PCACompute(sampleMat, meanMat, eigenVectors, 2);
3. 轮廓朝向计算(可选)
如果需要获取轮廓的主方向,读取第一个主成分的分量计算角度即可:
var vecIndexer = eigenVectors.GetGenericIndexer<float>(); float vecX = vecIndexer[0, 0]; float vecY = vecIndexer[0, 1]; // 弧度转角度,统一为0-360范围 double angle = Math.Atan2(vecY, vecX) * 180 / Math.PI; angle = angle < 0 ? angle + 360 : angle;
完整修正代码
public static double GetContourOrientation(Image<Gray, byte> inputImage) { Image<Gray, Byte> cannyGray = inputImage.Canny(85, 255); VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint(); CvInvoke.FindContours(cannyGray, contours, null, RetrType.External, ChainApproxMethod.ChainApproxSimple); // 无检测到轮廓直接返回 if (contours.Size == 0) { cannyGray.Dispose(); contours.Dispose(); return 0; } Point[] targetContour = contours.ToArrayOfArray()[0]; // 构造PCA输入样本矩阵 Mat sampleMat = new Mat(targetContour.Length, 2, DepthType.Cv32F, 1); var matIndexer = sampleMat.GetGenericIndexer<float>(); for (int i = 0; i < targetContour.Length; i++) { matIndexer[i, 0] = targetContour[i].X; matIndexer[i, 1] = targetContour[i].Y; } // PCA计算 Mat meanMat = new Mat(); Mat eigenVectors = new Mat(); CvInvoke.PCACompute(sampleMat, meanMat, eigenVectors, 2); // 计算朝向角度 var vecIndexer = eigenVectors.GetGenericIndexer<float>(); float vecX = vecIndexer[0, 0]; float vecY = vecIndexer[0, 1]; double angle = Math.Atan2(vecY, vecX) * 180 / Math.PI; angle = angle < 0 ? angle + 360 : angle; // 释放非托管资源 cannyGray.Dispose(); contours.Dispose(); sampleMat.Dispose(); meanMat.Dispose(); eigenVectors.Dispose(); return angle; }
内容的提问来源于stack exchange,提问作者ss_mj
相关产品推荐
相关产品推荐

