前言
对形状识别了解的不是太多,本篇算是个简单入门。因为最近比较忙闲暇时间不多了解的不算深入写的有不对的地方还请评论区留言。
声明下 :还有这里的识别没有用深度学习最近在学demo,也没用模板匹配类的(很久前写了一个象棋棋谱识别的例子用的模板一个一个匹配的:象棋棋谱之棋子识别(三))。
这里学习参考了网上的一些思路用java写了个demo,形状的话常见的有三角形、正方形、长方形、平行四边形、梯形因为定义公式也比较简单所以识别也相对容易;多边形类的就需要自己发现特点然后去处理了。
思路
- 首先轮廓提取。
- 因为轮廓是由一个一个点连线得到的所以需要获取轮廓的点的坐标和个数。
- 得到轮廓凸包的坐标点的索引。(凸包就是最外层的连线下图红色。所有的点是通过轮廓得到的。中间的排除掉了是凸包得到的就是最外层的点)
- 能拿到点我们也就能做很多东西了。常见的形状识别例如正方形:是四个点、四条边长相等、每个角度是90°、我们也可以不通过角度通过计算用面积是否跟正方形的面积相等进行判断。对于边长和角度我们可以对坐标点排序(也就是顺时针、逆时针)然后计算得到,角度应该有API的。
本篇的箭头识别demo
效果
这里再说下凸包看下下图就明白了绿线
处理的方式
先说下外面的7个点,这是个逆时针标识的
- 首先获取轮廓,参数意思可以在网上搜这里不在多写了。或者参考我之前写的一篇(java调用opencv轮廓检测)
bash
Imgproc.findContours(clone, contours, hierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
- 首先这个箭头是由七个坐标点组成的,进行坐标点个数判断。
arduino
//多边形包围轮廓
Imgproc.approxPolyDP(curve, approxCurve, 0.025 * Imgproc.arcLength(curve, true), true);
//这个参数就是返回的坐标点
approxCurve.toArray().length
- 然后凸包得到的坐标点会比正常的点数少2个。
vbscript
//true=顺时针方向 false=逆时针方向 这里我观察了下开始是从上面中间的位置开始的
//hull.toArray().length 返回的是上面approxPolyDP得到的坐标位置
Imgproc.convexHull(CommonCVUtils.poit2fTOPoint(approxCurve), hull, true);
int arrIndex[] = hull.toArray();
- 箭头的顶点判断做了些特殊处理,是根据少的点数(中间的两个点)判断出来的根据先后顺序。首先得到少的是那些顶点。方法可能有些笨(可以在评论区指出)
ini
//第一个参数是 2多边形包围轮廓的顶点
//第二个参数是 4凸包得到的顶点索引(第一个参数的索引)
public static Point findTip(MatOfPoint contour, MatOfInt indexes) {
int arrIndex[] = indexes.toArray();
Point arrContour[] = contour.toArray();
//得到少的顶点位置
int indices[] = new int[2];
int k=0;
for(int j=0;j<arrContour.length;j++){
boolean flag = false;
for(int i=0;i<arrIndex.length;i++){
if(arrIndex[i]==j){
flag=true;
break;
}
}
if(!flag){
indices[k++]=j;
}
}
//得到最小的顶点和最大的顶点坐标
int index0=Math.min(indices[0],indices[1]);
int index1=Math.max(indices[0],indices[1]);
//箭头朝左 凸包内的点第二个就是
if(index1==arrContour.length-1 && index0 - 2>-1){
return arrContour[index0 - 2];
}
//箭头朝下
if(index1-index0==4) {
return arrContour[index0+2];
}else{
//这里的判断是上和右
if (index1 + 2 > arrContour.length - 1) {
return arrContour[arrContour.length - index1 - 2];
} else {
return arrContour[index1 + 2];
}
}
}
结束。