镜头覆盖范围FOV 的规划与FOV去四角
本文纲要:
TNoFourCornersRect结构及Contains函数
本文内容:
规划FOV的原因
当一次拍照不能完全覆盖目标区域时,需要分多次拍摄以完成目标识别,当需要拍照的区域较多时,为提高效率,FOV的路径规划和排序就有了必要。
FOV去四角
一个FOV的四角通常亮度较差或畸变较大,所以实际的拍照过程中,将四个角排除在兴趣区域以外,形成了如下图的形状,实际的兴趣区域类似一个十字架形状,四角的大小可以通过参数调节,这个结构体在下面有代码→TNoFourCornersRect,其中有判断Pad是否包含在这个十字架中的函数的定义:
做如下定义和声明:
假设有一块电路板:Board,
Board上是印刷锡膏的焊点作为检测目标,这些焊点定义为Pad,
Pad、Board均为矩形,使用(X,Y)坐标定义四个顶点和确定四条边,例如:如果一个Pad的LeftTop顶点坐标为(20,35),则该Pad的Left边为LeftTop的X坐标即20;
Board和Pad的四个边分别使用Left、Top、Right、Bottom代表
FOV查找思路
①首先,从Board的Left边开始规划FOV
②找到电路板上所有Pad中Left值最小的Pad,即最左边的Pad
③在兴趣区域包含第②步Pad的情况下,让FOV的Left边与该Pad的Left边重合,上下移动兴趣区域进行查找,这个过程中,记录下兴趣区域包含的Pad最多的情况,对应一个FOV位置
④第③步进行完后,Board上未被包含的Pad变少,再找到剩余Pad中最左侧的Pad,重复第①~③步
⑤开辟四个线程,分别从Left、Top、Right、Bottom开始,寻找扫描整块Board需要FOV的情况
这四步中,最终要的第③步,代码如下:
查找FOV代码
/*----------------------------------------------------------------
* 下面的扫描以Board的Left边开始,
* 可开辟四个线程,分别从Left、Top、Right、Bottom进行扫描
----------------------------------------------------------------*/ /// <summary>
/// 滑动窗口扫描,寻找FOV
/// </summary>
/// <param name="referItem">参考Pad(即最左侧的那个Pad)</param>
/// <param name="itemList">Board中剩余的未包含在已知FOV中的Pad</param>
/// <param name="boardSizeX">Board宽度</param>
/// <param name="boardSizeY">Board高度</param>
/// <param name="minPosX">扫描时X方向移动步宽</param>
/// <param name="minPosY">扫描时Y方向移动步宽</param>
/// <param name="bindingItemList">找到一个FOV是与之对应的Pad加入到此List</param>
/// <param name="left">输出参数:找到的FOV的左边</param>
/// <param name="right">输出参数:找到的FOV的右边</param>
/// <param name="top">输出参数:找到的FOV的上边</param>
/// <param name="bottom">输出参数:找到的FOV的下边</param>
/// <param name="bestPos">记录找到的FOV的起点</param>
/// <param name="pos">本文中从Left边搜索扫描</param>
protected void SlidingScan(TargetBase referItem, List<TargetBase> itemList,
double boardSizeX, double boardSizeY,
double minPosX, double minPosY, List<TargetBase> bindingItemList,
ref double left, ref double right, ref double top, ref double bottom,
ref double bestPos, SingleBoundFovDivider.DividionStarts pos)
{
double Universal = FovScheduler.FovSizeX / 8.0; //四角长宽通用数值
double redundantSafety = 1.0; //这个参数是安全冗余,double类型的值作比较,冗余很重要,
//当初程序运行一直不通过,
//最后才发现double的最后几位并不精确,导致两个double变量比较大小时结果与期望不符 //此处注意:FOV共有四个角,现讨论四个角的长度宽度相等
//都等于FovScheduler.FovSizeX / 8.0,与Universal的值也想等
//四个角的长度宽度可自定义的情况原理相同
double leftTopCornerX = FovScheduler.FovSizeX / 8.0;
double leftTopCornerY = FovScheduler.FovSizeX / 8.0;
double leftBottomCornerX = FovScheduler.FovSizeX / 8.0;
double leftBottomCornerY = FovScheduler.FovSizeX / 8.0; double startSild = ;//滑动窗口的起始位置
double endSild = ;//滑动窗口的结束位置
double fix = ;//固定的起始位置
double minStep = ;//最小的步进距离 if (pos == SingleBoundFovDivider.DividionStarts.Left)
{
//去四角的FOV滑动起点的选择比普通矩形时要复杂,注意:是包含在中间的十字架区域,并要考虑Board的边界做阈值保护
startSild = Math.Max(referItem.ScheduleRect.Bottom - (FovScheduler.FovSizeY - leftBottomCornerY - leftTopCornerY) + redundantSafety, );
endSild = Math.Min(referItem.ScheduleRect.Top - redundantSafety, boardSizeY - (FovScheduler.FovSizeY - leftBottomCornerY - leftTopCornerY));
if (pos == SingleBoundFovDivider.DividionStarts.Left)
{
fix = Math.Min(referItem.ScheduleRect.Left + leftTopCornerX - redundantSafety, boardSizeX - (FovScheduler.FovSizeX - leftTopCornerX));
}
minStep = minPosY;
} bestPos = ;
if (bindingItemList == null)
bindingItemList = new List<TargetBase>();
bindingItemList.Clear();
int validcount = ;
int maxCount = ; //开始遍历
for (double start = startSild; start <= endSild; start += minStep)
{
TNoFourCornersRect roi = new TNoFourCornersRect();
if (pos == SingleBoundFovDivider.DividionStarts.Left)
{
roi = new TNoFourCornersRect(fix, start, FovScheduler.FovSizeX, FovScheduler.FovSizeY, Universal);
} //统计在roi内的最大item数量
double leftTemp = referItem.ScheduleRect.Left;
double rightTemp = referItem.ScheduleRect.Right;
double topTemp = referItem.ScheduleRect.Top;
double bottomTemp = referItem.ScheduleRect.Bottom; var bindingItemListTemp = new List<TargetBase>();
validcount = ; for (int i = ; i < itemList.Count; i++)
{
var item = itemList[i];
var itemRect = item.ScheduleRect; if (roi.Contains(itemRect))
{
validcount++; leftTemp = roi.LeftOuter;
rightTemp = roi.RightOuter;
topTemp = roi.TopOuter;
bottomTemp = roi.BottomOuter; if (!bindingItemListTemp.Contains(item))
bindingItemListTemp.Add(item);
}
} //更新最大值
if (validcount > maxCount)
{
if (bindingItemList == null)
bindingItemList = new List<TargetBase>();
bindingItemList.Clear();
bindingItemList.AddRange(bindingItemListTemp); maxCount = validcount; bestPos = start;
left = leftTemp;
right = rightTemp;
top = topTemp;
bottom = bottomTemp;
}
}
}
TNoFourCornersRect
上述 代码中用到一个重要的结构:TNoFourCornersRect,其中有一个重要的函数,判断TNoFourCornersRect的对象是否包含一个Pad,定义如下:
public struct TNoFourCornersRect
{
//左上角的内角点坐标对应XY
public double X;
public double Y; //内宽高
public double WidthInner;
public double HeightInner; //外宽高
public double WidthOuter;
public double HeightOuter; //外界矩形四个顶点
public Point2D64 leftTop;
public Point2D64 rightTop;
public Point2D64 rightBottom;
public Point2D64 leftBottom; //内角点
public Point2D64 LeftTopInner;
public Point2D64 RightTopInner;
public Point2D64 RightBottomInner;
public Point2D64 LeftBottomInner; //外角点
public Point2D64 LeftTopLeftOuter;
public Point2D64 LeftTopTopOuter;
public Point2D64 RightTopTopOuter;
public Point2D64 RightTopRightOuter;
public Point2D64 RightBottomRightOuter;
public Point2D64 RightBottomBottomOuter;
public Point2D64 LeftBottomBottomOuter;
public Point2D64 LeftBottomLeftOuter; //内边
public double LeftInner;
public double TopInner;
public double RightInner;
public double BottomInner; //外边
public double LeftOuter;
public double TopOuter;
public double RightOuter;
public double BottomOuter; //构造函数:四个角的宽高相同
public TNoFourCornersRect(double x, double y, double widthOuter, double heigthOuter, double Universal)
{
//if (2 * Universal > WidthOuter || 2 * Universal > HeightOuter)
//{
// //增加保护,或者在外部增加保护;
//} X = x;
Y = y; WidthOuter = widthOuter;
HeightOuter = heigthOuter; WidthInner = WidthOuter - * Universal;
HeightInner = HeightOuter - * Universal; leftTop = new Point2D64(X - Universal, Y - Universal);
rightTop = new Point2D64(X + WidthInner + Universal, Y - Universal);
rightBottom = new Point2D64(X + WidthInner + Universal, Y + HeightInner + Universal);
leftBottom = new Point2D64(X - Universal, Y + HeightInner + Universal); LeftTopInner = new Point2D64(X, Y);
RightTopInner = new Point2D64(X + WidthInner, Y);
RightBottomInner = new Point2D64(X + WidthInner, Y + HeightInner);
LeftBottomInner = new Point2D64(X, Y + HeightInner); LeftTopLeftOuter = new Point2D64(X - Universal, Y);
LeftTopTopOuter = new Point2D64(X, Y - Universal);
RightTopTopOuter = new Point2D64(X + WidthInner, Y - Universal);
RightTopRightOuter = new Point2D64(X + WidthInner + Universal, Y);
RightBottomRightOuter = new Point2D64(X + WidthInner + Universal, Y + HeightInner);
RightBottomBottomOuter = new Point2D64(X + WidthInner, Y + HeightInner + Universal);
LeftBottomBottomOuter = new Point2D64(X, Y + HeightInner + Universal);
LeftBottomLeftOuter = new Point2D64(X - Universal, Y + HeightInner); LeftInner = X;
TopInner = Y;
RightInner = X + WidthInner;
BottomInner = Y + HeightInner; LeftOuter = X - Universal;
TopOuter = Y - Universal;
RightOuter = X + WidthInner + Universal;
BottomOuter = Y + HeightInner + Universal;
} /// <summary>
/// 判断Rect64的对象是否包含于此类的对象
/// </summary>
/// <param name="item">待判断的目标</param>
/// <returns></returns>
public bool Contains(Rect64 item)
{
if (item.LeftTop.X >= leftTop.X && item.LeftTop.Y >= leftTop.Y && item.RightTop.X <= rightTop.X && item.RightTop.Y >= rightTop.Y && item.RightBottom.X <= rightBottom.X && item.RightBottom.Y <= rightBottom.Y && item.LeftBottom.X >= leftBottom.X && item.LeftBottom.Y <= leftBottom.Y)
{
if (item.LeftTop.X < LeftTopInner.X && item.LeftTop.Y < LeftTopInner.Y)
{
return false;
} if (item.RightTop.X > RightTopInner.X && item.RightTop.Y < RightTopInner.Y)
{
return false;
} if (item.RightBottom.X > RightBottomInner.X && item.RightBottom.Y > RightBottomInner.Y)
{
return false;
} if (item.LeftBottom.X < LeftBottomInner.X && item.LeftBottom.Y > LeftBottomInner.Y)
{
return false;
} return true;
} return false;
}
}
其中Contains函数完全是受另一个算法的启发:判断两个矩形是否相交的算法。当时在网上看到此算法,判断一些顶点坐标之间的关系就判断两个矩形是否相交,逻辑清楚简洁,这给我带来不少启发,也因此,用了很少代码就写出了上面判断一个矩形是否包含在这个十字架形状中这个函数。
判断矩形是否相交
网上的判断矩形是否相交的函数如下:
/// <summary>
///判断两个Rect64矩形是否相交
/// </summary>
/// <param name="rect1"></param>
/// <param name="rect2"></param>
/// <returns>相交则返回为true</returns>
private bool IsRect64Intersect(Rect64 rect1, Rect64 rect2)
{
double minX, minY, maxX, maxY; minX = Math.Max(rect1.LeftTop.X, rect2.LeftTop.X);
minY = Math.Max(rect1.LeftTop.Y, rect2.LeftTop.Y);
maxX = Math.Min(rect1.RightBottom.X, rect2.RightBottom.X);
maxY = Math.Min(rect1.RightBottom.Y, rect2.RightBottom.Y); if (minX > maxX || minY > maxY)
{
return false;
}
else
{
return true;
}
}
延伸与拓展:图形验证方法的重要性
这个程序的结果并不是最重要的,重要的是,怎么保证自己写出的这些代码的正确性,还有,如果中间程序运行不下去,问题出在哪?怎么找出问题所在?程序的运行过程是抽象的,中间有很多数据处理,何况,这几个函数只是程序的一部分,再加上外层函数的调用,中间又有很多FOV生成,每个FOV都要滑动很多次,这些都是细节。
仅靠逻辑和数学等知识去冥想,甚至加上VS的调试工具,都很难发现问题出现的哪。这个过程中,我用的方法是:将程序运行的一些关键节点用直观的方式写入图像文件,例如:矩形,FOV等,这样,程序运行到哪一步出问题,出问题的时候都有哪些图形完成并呈现,哪些没有达到预期,当时的具体情况会一目了然。
例如,在写Contains函数的时候,就用了如下的方法验证:每一个Pad绑定一个bool变量,当该Pad包含在FOV时,就在图片中该Pad上面写上true,当Pad不包含在FOV 时,就在图片该位置标记为false,很容易就找出Contains函数中有哪些情况没有考虑到以及逻辑是否有问题。最后增加redundantSafety这个变量来对double变量的比较做安全冗余也是通过这样的方法。
镜头覆盖范围FOV 的规划与FOV去四角的更多相关文章
- 规划自己的生活,从使用GTD时间管理法开始
前言 为了不再浪费时间,不在茫然度过每一天,我为自己应用了GTD时间管理法,之前并不知道这种方法,实际和我自己定制的也差不太多,下面说说这个方法. 一.GTD时间管理 时间管理法有很多,而GTD( ...
- 跟我一起读postgresql源码(四)——Planer(查询规划模块)(上)
时间一晃周末就过完了,时间过得太快,不由得让人倍加珍惜.时间真是不够用哈~ 好的不废话,这次我们开始看查询规划模块的源码吧. 查询规划部分的在整个查询处理模块应该是在一个非常重要的地位上,这一步直接决 ...
- POJ 最小球覆盖 模拟退火
最小球覆盖:用半径最小的球去覆盖所有点. 纯粹的退火算法,是搞不定的,精度不够,不然就会TLE,根本跑不出答案来. 任取一点为球心,然后一点点靠近最远点.其实这才是最主要的. 因为:4个点确定一个球, ...
- 【LOJ#3197】【eJOI2019】T形覆盖 - (图论、简单推导)
题面 题解 (题目中说的四种摆放方式实际上是分别旋转0°,90°,180°,270°后的图形) 题目中关于摆放方式的描述听起来很臭,我们把它转换一下,每个拼版先覆盖"上下左右中"五 ...
- 如何透过HTC Vive拍摄Mixed Reality (混合现实)影片
https://www.vive.com/cn/forum/1706?extra=page%3D1 也许你是一位开发者,想为自己的HTC Vive游戏制作酷炫的宣传片:或者你是游戏主播,想为观众带来高 ...
- unity独立游戏开发日志2018/09/26
最近太忙,今天吃饭的时候灵感一现...想到了随机地图生成的方法,不过可能实现的比较笨...还需要优化,大佬绕过. 注释没打,最后统一解释. using System.Collections; usin ...
- three.js一个简单demo学些和讲解
叉口裁剪球体案例 二话不说先上效果图: 全部代码带注释 <!DOCTYPE html> <html lang="en"> <head> < ...
- OpenGL入门1.7:摄像机
每一个小步骤的源码都放在了Github 的内容为插入注释,可以先跳过 前言 我们已经知道了何为观察矩阵以及如何使用观察矩阵移动场景(我们向后移动了一点) OpenGL本身没有摄像机(Camera)的概 ...
- 用于激光雷达的 APD,SPAD 和 SiPM 分析
1. 术语及定义 1.1 激光雷达,Light Detection And Range, LiDAR 发射激光光束,并接收回波以获取目标三维和/或速度信息的系统: 1.2 机械旋转激光雷达,Mech ...
随机推荐
- CentOS下下删除大量文件
首先建立50万个文件 ➜ test for i in $(seq 1 500000);do echo text >>$i.txt;done 1. rm ➜ test time rm -f ...
- SSIS ->> Script Task
利用Script Task,我们可以做一些本身SSIS没能满足我们的,或者实现起来效果不够理想的.比如说我们想做一件这样的事情,去检查某个文件是否为空.如果我们通过Row Count组件来实现,性能上 ...
- [转]“WARNING: soft rlimits too low” in MongoDB with Mac OS X
转自:Programming and Technology If you get this warning when you connect to mongo shell in Mac OX X: * ...
- 不带缓存的I/O和标准(带缓存的)I/O
首先,先稍微了解系统调用的概念: 系统调用,英文名system call,每个操作系统都在内核里有一些内建的函数库,这些函数可以用来完成一些系统系统调用把应用程序的请求传给内核,调用相应的 ...
- 易犯的PHP小错误及相应分析
变量声明如果在一条语句中声明一个变量,如下所示:$var = 'value'; 编译器首先会求出语句右半部分的值,恰恰正是语句的这一部分常常会引发错误.如果使用的语法不正确,就会出现解析错误. 解析错 ...
- Linux下安装Python-3.3.2【转】
# 下载最新版本 cd /usr/local/src/ sudo wget http://www.python.org/ftp/python/3.3.2/Python-3.3.2.tar.bz2 su ...
- JavaScript —— 对象的取值与赋值
可能是因为用惯了 Java ,对一个对象取值/赋值喜欢用 setXXX() 和 getXXX() . 在 JavaScript 中使用 setValue() 时,遇到了个奇怪的问题,所以查了下 Jav ...
- poj -2010 Moo University - Financial Aid (优先队列)
http://poj.org/problem?id=2010 "Moo U"大学有一种非常严格的入学考试(CSAT) ,每头小牛都会有一个得分.然而,"Moo U&quo ...
- Hosting Your Own NuGet Feeds
Hosting Your Own NuGet Feeds Hosting the NuGet Gallery Locally in IIS https://github.com/NuGet/NuGet ...
- [C]判断一个文件是否是jpg格式
同学要帮忙写的,用opencv的imread打开文件看抛出的异常来判断这种抖机灵的姿势就不写了… 首先知道jpg文件是以0xFFD8开始,以0xFFD9结尾的.所以直接拿来fseek fread,异或 ...