镜头覆盖范围FOV 的拍照路径规划与FOV去四角

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,重复第①~③步

这四步中,最终要的第③步,代码如下:

/*----------------------------------------------------------------
 * 下面的扫描以Board的Left边开始,
 * 可开辟四个线程,分别从Left、Top、Right、Bottom进行扫描
----------------------------------------------------------------*/
        /// <summary>
        /// 滑动窗口扫描
        /// </summary>
        /// <param name="referItem">参考的item</param>
        /// <param name="itemList">搜索的item的列表</param>
        /// <param name="minPosX">x方向的最小步进距离</param>
        /// <param name="minPosY">y方向的最小步进距离</param>
        /// <param name="bindingItemList">绑定的Item列表</param>
        /// <param name="left">包含最多矩形的fov的左边</param>
        /// <param name="right">包含最多矩形的fov的右边</param>
        /// <param name="top">包含最多矩形的fov的上边</param>
        /// <param name="bottom">包含最多矩形的fov的下边</param>
        /// <param name="bestPos">最佳位置</param>
        /// <param name="pos">搜索的方向</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 = 0;//滑动窗口的起始位置
            double endSild = 0;//滑动窗口的结束位置
            double fix = 0;//固定的起始位置
            double minStep = 0;//最小的步进距离

            if (pos == SingleBoundFovDivider.DividionStarts.Left)
            {
                //去四角的FOV滑动起点的选择比普通矩形时要复杂,注意:是包含在中间的十字架区域,并要考虑Board的边界做阈值保护
                startSild = Math.Max(referItem.ScheduleRect.Bottom - (FovScheduler.FovSizeY - leftBottomCornerY - leftTopCornerY) + redundantSafety, 0);
                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 = 0;
            if (bindingItemList == null)
                bindingItemList = new List<TargetBase>();
            bindingItemList.Clear();
            int validcount = 0;
            int maxCount = 0;

            //开始遍历
            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 = 0;

                for (int i = 0; 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的对象是否包含一个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 - 2 * Universal;
        HeightInner = HeightOuter - 2 * 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;
            }
        }

延伸与拓展

这个程序的结果并不是最重要的,重要的是,程序的运行过程很抽象,中间有很多数据处理,如果中途有bug,很难发现问题出在哪里,在调试的过程中,我发现,将一些程序运行的重要节点写入图像文件,但后观察是否是自己想要的结果这个方法非常实用,例如,每一个Pad绑定一个bool变量,当该Pad包含在FOV时,就在图片中该Pad上面写上false,很容易就找出Contains函数中有哪些情况没有考虑到。所以,对一个程序的直观验证比逻辑、数学知识要强大的多。

镜头覆盖范围FOV 的拍照路径规划与FOV去四角

时间: 2024-10-09 03:11:34

镜头覆盖范围FOV 的拍照路径规划与FOV去四角的相关文章

PRM路径规划算法

路径规划作为机器人完成各种任务的基础,一直是研究的热点.研究人员提出了许多规划方法:如人工势场法.单元分解法.随机路标图(PRM)法.快速搜索树(RRT)法等.传统的人工势场.单元分解法需要对空间中的障碍物进行精确建模,当环境中的障碍物较为复杂时,将导致规划算法计算量较大.基于随机采样技术的PRM法可以有效解决高维空间和复杂约束中的路径规划问题. PRM是一种基于图搜索的方法,它将连续空间转换成离散空间,再利用A*等搜索算法在路线图上寻找路径,以提高搜索效率.这种方法能用相对少的随机采样点来找到

机器人自主移动的秘密:SLAM与路径规划有什么关系?(三)

博客转载自:https://www.leiphone.com/news/201612/lvDXqY82OGNqEiyl.html 雷锋网(公众号:雷锋网)按:本文作者SLAMTEC(思岚科技公号slamtec-sh)技术顾问,专注SLAM及相关传感器研发应用. 我们先来看看SLAM与路径规划的关系 实际上,SLAM算法本身只是完成了机器人的定位和地图构建两件事情,与我们说的导航定位并不是完全等价的.这里的导航,其实是SLAM算法做不了的.它在业内叫做运动规划(Motion Planning).

【BZOJ-3627】路径规划 分层图 + Dijkstra + spfa

3627: [JLOI2014]路径规划 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 186  Solved: 70[Submit][Status][Discuss] Description 相信大家都用过地图上的路径规划功能,只要输入起点终点就能找出一条最优路线.现在告诉你一张地图的信息,请你找出最优路径(即最短路径).考虑到实际情况,一辆车加满油能开的时间有限,所以在地图上增加了几个加油站. 地图由点和双向边构成,每个点代表一个路口,也有可

Unity路径规划

Unity路径规划 背景 酷跑游戏中涉及到弯道.不规则道路. 找来一些酷跑游戏的案例来看,很多都是只有直线道路,也就不存在所谓的路径问题,设置一个方向即可,本文主要给出几种我自己用过.看过的Unity中可以用做路径规划的一些资料. 方案一 Unity自带解决方案Navigation,可以参考小赵的文章: unity自带寻路Navmesh入门教程(一) unity自带寻路Navmesh入门教程(二) unity自带寻路Navmesh入门教程(三) 这几篇文章还是不错的,配合官方文档,对其功能就能就

iOS百度地图路径规划和POI检索详细总结-b

路径规划.png 百度地图的使用 百度地图API的导入网上说了许多坑,不过我遇到的比较少,这里就放两个比较常见的吧.坑一: 奥联WIFI_xcodeproj.png 如上图所示,在infoplist里加入这个字段,而且这里还可以设置提示的内容.不加的话,嘿嘿嘿,期待你的尝试.坑二:如下图 Pasted_Graphic_jpg.png 导入百度地图API运行之后报上图错误大约18到20个左右,解决方法添加libstdc++.6.0.9 的库.填完坑之后看一下我们今天要演示的效果吧. 路线规划图.g

路径规划

机器人路径规划研究综述 1.什么是路径规划 路径规划技术是机器人研究领域中的一个重要分支.所谓机器人的最优路径规划问题,就是依据某个或某些优化准则(如工作代价最小,行走路线最短,行走时间最短等),在其工作空间中找到一条从起始状态到目标状态的能避开障碍物的最优路径. 依据某种最优准则,在工作空间中寻找一条从起始状态到目标状态的避开障碍物的最优路径. 需要解决的问题: 1.始于初始点止于目标点. 2.避障. 3.尽可能优化的路径. 2.路径规划技术分类 a.静态结构化环境下的路径规划,比如说slam

基于分层路网的路径规划算法

http://www.docin.com/p-1308678703.html 路径规划一般包括如下几种情况: 最短路径规划 最短时间规划 最经济规划 通过赋予图中的边以不同的权值来满足用户不同的需求 最短路径由于普通道路限速问题,未必是最短时间到达目的地, 可能上高速更快一些 分层思想 对于一个点P,以自身为中心,不断的扩大半径进行搜索,查看当前是否包括一个升级到高层路网的节点 如果有,说明当设置P为起点,然后设置T为终点,而且当distance(pt)> R,说明应该进行路网的升层 选择一个查

ios百度地图-路径规划

百度地图的路径规划功能, 在使用百度路径的时候,出现了一些小问题,在此,分享一下自己的最简单的一个路径小demo 当然,前面的百度配置问题,我就不和大家讲了,因为这方面的资料太多了!现在,我来介绍一下这个小demo AppDelegate.m文件如下, #import "AppDelegate.h" import "rootViewController.h" @implementation AppDelegate (BOOL)application:(UIAppli

高德地图路径规划

高德地图导航sdk的路径规划获取行程信息主要用到AMapNaviManager这个类 然后调下面的方法 /*! @brief 带起点的驾车路径计算 @param startPoints 起点坐标.支持多个起点,起点列表的尾点为实际导航起点,其他坐标点为辅助信息,带有方向性,可有效避免算路到马路的另一侧. @param endPoints 终点坐标.支持多个终点,终点列表的尾点为实际导航终点,其他坐标点为辅助信息,带有方向性,可有效避免算路到马路的另一侧. @param wayPoints 途经点