目标跟踪-边缘跟踪

边缘跟踪算法实现过程

网上大部分关于边缘跟踪的算法不是目标跟踪领域,而是串接目标的边缘,和区域生长法作用一样。极少有文献是关于目标跟踪领域的边缘跟踪算法。

边缘跟踪算法主要包括4个步骤:

  1. 帧间波门内差分;
  2. 波门内阈值分割;
  3. 波门内去噪声;
  4. 计算边缘点(中心)

前面三步和质心跟踪算法一样,第4步如果是计算单个边缘点,就是单边缘跟踪,如果是计算边缘点的中心,就是双边缘中心跟踪。这里以双边缘中心跟踪为例。

帧间波门内差分

相邻帧之间在波门内进行差分操作。为了仅体现相邻帧之间的变化量,这里我做了绝对值差分操作。即
D i f f x , y = ∣ f x , y i + 1 − f x , y i ∣ Diff_{x,y} = |f^{i+1}_{x,y} - f^i_{x,y}| Diffx,y=fx,yi+1fx,yi
f i + 1 f^{i+1} fi+1和f^i是相邻帧的波门框定图像。x, y表示具体坐标。通过绝对差分可以表征帧间目标的变化量。具体函数如下:

cv::absdiff(gray_image(_roi), this->gray_image(_roi), diff);

波门内阈值分割

在得到差分图像以后,有可能差分图像本身的对比度较低。我们需要对差分图像进行二值化来进一步体现目标的变化量。一般的图像二值化可以固定一个阈值比如127u,但是差分图像需要自适应地选择阈值来进行分割。此处选择最大类间方差算法来选择分割阈值。具体思想可以参见:https://blog.csdn.net/zhu_hongji/article/details/80967776

int EdgeTracker::getThresholdValue(cv::Mat& image);

选择了最优阈值之后,就对图像进行二值分割。

void EdgeTracker::segmentByThreshold(cv::Mat& src, cv::Mat& dst, int threshold);

波门内去噪声

去噪声这一步是可选的,在实现里我采用腐蚀+膨胀的方法(被注释掉了),腐蚀是为了去除白噪点,膨胀是为了突出目标的变化量。结构元素3x3。

计算边缘的中心

计算边缘点的中心是根据阈值分割后的图来的,这一步思想非常简单,就是找目标的上下左右边界。形式化表述如下:
y u p p e r = max ⁡ { y ∗ ∣ ∀ y < y ∗ , 0 < x < W , I ( x , y ) = 0 } y l o w e r = min ⁡ { y ∗ ∣ ∀ y > y ∗ , 0 < x < W , I ( x , y ) = 0 } x l e f t = max ⁡ { x ∗ ∣ ∀ x < x ∗ , 0 < y < H , I ( x , y ) = 0 } x r i g h t = min ⁡ { x ∗ ∣ ∀ x > x ∗ , 0 < y < H , I ( x , y ) = 0 } y_{upper} = \max \{y^* | \forall y < y^*, 0 < x < W, I(x, y) = 0 \} \\ y_{lower} = \min \{y^* | \forall y > y^*, 0 < x < W, I(x, y) = 0 \} \\ x_{left} = \max \{x^* | \forall x < x^*, 0 < y < H, I(x, y) = 0 \} \\ x_{right} = \min \{x^* | \forall x > x^*, 0 < y < H, I(x, y) = 0 \} \\ yupper=max{y∣∀y<y,0<x<W,I(x,y)=0}ylower=min{y∣∀y>y,0<x<W,I(x,y)=0}xleft=max{x∣∀x<x,0<y<H,I(x,y)=0}xright=min{x∣∀x>x,0<y<H,I(x,y)=0}
其中, y u p p e r y_{upper} yupper, y l o w e r y_{lower} ylower分别表示目标的上下边界的纵坐标, x l e f t x_{left} xleft, x r i g h t x_{right} xright分别表示目标的左右边界的横坐标, W W W H H H分别为图像的宽度和高度。

那么可以得到4个边缘点的坐标为:
p u p p e r = ( x l e f t + x r i g h t 2 , y u p p e r ) p l o w e r = ( x l e f t + x r i g h t 2 , y l o w e r ) p l e f t = ( x l e f t , y u p p e r + y l o w e r 2 ) p r i g h t = ( x r i g h t , y u p p e r + y l o w e r 2 ) p_{upper} = (\frac{x_{left} + x_{right}}{2}, y_{upper}) \\ p_{lower} = (\frac{x_{left} + x_{right}}{2}, y_{lower}) \\ p_{left} = (x_{left}, \frac{y_{upper} + y_{lower}}{2}) \\ p_{right} = (x_{right}, \frac{y_{upper} + y_{lower}}{2}) pupper=(2xleft+xright,yupper)plower=(2xleft+xright,ylower)pleft=(xleft,2yupper+ylower)pright=(xright,2yupper+ylower)

如果只利用其中一个边缘点,那么可以导出单边缘跟踪算法,这里介绍双边缘跟踪算法,实际上就是计算这4个点的中心坐标, 也就是
p c e n t e r = ( x l e f t + x r i g h t 2 , y u p p e r + y l o w e r 2 ) p_{center} = (\frac{x_{left} + x_{right}}{2}, \frac{y_{upper} + y_{lower}}{2}) pcenter=(2xleft+xright,2yupper+ylower)

p c e n t e r p_{center} pcenter的偏移可以导出目标在不同帧的移动路径,并由此确定当前帧目标的波门。

头文件如下:

class EdgeTracker : CustomTracker
{
public:
    EdgeTracker(bool debug = false);
    ~EdgeTracker();
    void init(cv::Mat& image, const cv::Rect_<int> &roi);
    cv::Rect_<int> update(cv::Mat& image);
    cv::Mat getDiff(cv::Mat& gray_image);
    int getThresholdValue(cv::Mat& image);
    void segmentByThreshold(cv::Mat& src, cv::Mat& dst, int threshold);
    vector<cv::Point_<int>> findEdgePoints(cv::Mat& seg_map);

protected:
    cv::Rect_<float> _roi;
    cv::Mat image;
    cv::Mat gray_image;
    bool DEBUG = false;
    vector<cv::Point_<int>> edge_points; // centerp_upper, centerp_lower, centerp_left, centerp_right
    int ite_num;
    bool first_ite;
};

核心代码如下:

vector<cv::Point_<int>> EdgeTracker::findEdgePoints(cv::Mat& seg_map)
{
    int x0, y0, x1, y1;
    x0 = 0;
    y0 = 0;
    x1 = _roi.width-1;
    y1 = _roi.height-1;
    int width = seg_map.cols;
    int height = seg_map.rows;
    bool flag1 = false, flag2 = false;
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            uint8_t pix0 = seg_map.ptr<uint8_t>(i)[j];
            uint8_t pix1 = seg_map.ptr<uint8_t>(height-i-1)[j];
            if (pix0 == 255 && !flag1)
            {
                y0 = i;
                flag1 = true;
            }
            if (pix1 == 255 && !flag2)
            {
                y1 = height-i-1;
                flag2 = true;
            }
            if (flag1 && flag2) 
            {
                flag1 = flag2 = false;
                goto P1;
            }
        }
    }
    P1: for (int i = 0; i < width; i++)
    {
        for (int j = 0; j < height; j++)
        {
            uint8_t pix0 = seg_map.ptr<uint8_t>(j)[i];
            uint8_t pix1 = seg_map.ptr<uint8_t>(j)[width-i-1];
            if (pix0 == 255)
            {
                x0 = i;
                flag1 = true;
            }
            if (pix1 == 255)
            {
                x1 = width-i-1;
                flag2 = true;
            }
            if (flag1 && flag2) goto P2;
        }
    }
    P2: // 计算4个边缘点的坐标
    vector<cv::Point_<int>> points;
    cv::Point_<int> upper, lower, left, right;
    upper.x = (x0 + x1) / 2;
    upper.y = y0;
    lower.x = (x0 + x1) / 2;
    lower.y = y1;
    left.x = x0;
    left.y = (y0 + y1) / 2;
    right.x = x1;
    right.y = (y0 + y1) / 2;
    points.push_back(upper);
    points.push_back(lower);
    points.push_back(left);
    points.push_back(right);
    return points;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值