记一个题目的思路。题面网络可以搜索到。
1. 如何编写
- 使用矩形框划出目标, 再在矩形框里分析
- 特征归一化, 比较不同的对象需要有相同的差距大的指标
- 最简单最常见的图像特征就是图形上特征点直接的距离或者是某些距离的比值(符合不同大小的归一化要求)
- 善用辅助线, 线段是矩形内最好的工具!
2. 然后复习迷宫的BFS/DFS以及在图论算法应用的一些feature, 经常做过和迷宫有关的DFS,
首先要知道 BFS 能够找到路径的端点。这一点十分重要(其实DFS也可以, 但是DFS的走到端点是在过程中而不是在最后!)
我们给BFS随便丢一个非闭合曲线路径上的点, BFS是能够走到尽头的, 而且他会在某个尽头结束。
- 假设BFS走到是上述线段的一个尽头First, 根据BFS的入队顺序, 已经无法继续BFS下去了, 他的下一个队列元素将是其入口的下一个方向! 即只要存在未遍历的地方, 他就能够走回去. 直到另一头的End. (这里十分关键! 是迷宫寻路的关键, 是路径端点查找的关键!)
- 那么你随便丢一个点给BFS只能最后返回一个端点有什么用 要判断另一个端点麻烦事, 而且给的起点是端点那不更难判断了
- 我们继续分析
对于任一点BFS的结果是其中一个端点不错, 我们再看, 假如起点是一个端点会怎么样呢?
- 对,如果BFS的起点是某非闭合曲线路径上的一点, 那么他就能到达另一个端点
由此我们只需要进行两次BFS就能找到闭合曲线的两个端点
- 其实这就是迷宫问题的一种等价类抽象问题 考虑单入口单出口迷宫问题:
有两个特征: 从迷宫中任意点出发BFS就能到达入口和出口中的一个
从迷宫中一个入口出发BFS最后会到达迷宫的出口! 而且BFS是在不知道入口/出口的情况下到达入口/出口(因为他以点向外扩散, 最后停止的地方必在某个最大偏序处!), 而DFS相当于轮扫/线扫, 是没有这种效果的.
接下来是对M和S利用最简洁的方法来进行特征构造! 调参
特征还是很多的!
尤其是我们根据矩形框和非闭合曲线端点的角度去找, 有一大堆特征能用来区分M和S的
- M的矩形中心很有可能是白的, S则黑
- M的两端点在矩形中呈边缘两侧, S则在对角两侧
- M的两端点的中点离矩形中心较远, S则较近(而且有不小的概率重合)
值得注意的是由于M/S存在转, 缩放的缘故, 这个是绝对不能用绝对特征来提取的!
我们需要进行一些处理(因为程序里提取矩形框并不能旋转!参考下图)
根据上面的3个特征, 结合旋转等干扰, 我们直接用1 3 来作为我们的过滤器判断特征! 考虑到时间问题, 这里直接使用3来进行判断.
同时考虑到归一化的问题(缩放干扰), 我们也不能用距离来判定. 我们构造一个标准化, 这里就用c的距离和矩形对角线的距离比值来作为一个标准化特征!
万事俱备, 编写代码即可..
- 优化时空复杂度, 对于BFS我们需要visited剪枝, 两次BFS就要有两种VISITED flag
然后只需要在其中一个BFS里进行矩形框计算就行了.
由于BFS随便丢一个点就能跑, 所以直接在遍历整张大图的时候就去BFS, VISITED2的点就不需要再BFS了.
void filter(){
for(int i = 0;i<length;i++){
if(can_go(i)&&!is_vis(i,VIS2)){
point lt, rb;//left_top, right_bottom, for rectangle
point current(i);
point end1 = bfs(current, VIS1, lt,rb);
point end2 = bfs(end1, VIS2);
point end_mid = mid(end1, end2);
point img_mid = mid(lt,rb);
double diagonal_dis = distance_sqare(lt,rb);
double mid_end_dis = distance_sqare(end_mid,img_mid);
double percentage = mid_end_dis/(diagonal_dis/100);
double m_lower_limit = 3.0;
if(percentage>m_lower_limit) m++;
else s++;
}
}
}
由于BFS, 点距离计算等都是常规, 这里给出的特征提取的关键代码