#OpticalFlowPyrLK
OpticalFlowPyrLK算子用于计算连续多帧图像特定像素点的变化,常用于目标特征点跟踪、提升BOX稳定性等场景。
#算子效果
| 输入连续帧图像 | 参数 | 输出图像 |
|---|---|---|
![]() | pyrLevels = 5 winSize = 7 criteriaEpsilon = 0 maxIteration = 10 minEigThreshold = 1e-4 | ![]() |
#原理
稀疏光流算法即Lucas-Kanade算法,是一种计算机视觉中常用的运动估计方法,用于估计图像序列中像素的运动方向和速度。Lucas-Kanade算法基于两个假设:
-
亮度恒定假设:在短时间内,同一个物体的像素亮度保持不变。
假设 t 时刻,位于 (x,y) 像素位置的物体,在 t+Δt 时刻位于 (x+u,y+v) 位置,基于亮度不变假设有:
I(x,y,t)=I(x+u,y+v,t+Δt)
将等式右边进行一阶泰勒展开得:
I(x+u,y+v,t+Δt)=I(x,y,t)+Ix′u+Iy′v+It′Δt
由上面两个公式可以得到:
Ix′u+Iy′v+It′Δt=0
整理后可表示成:
[Ix′,Iy′][uv]=−ΔIt
其中,Ix′ , Iy′ 分别为 (x,y) 像素点处图像亮度在 x 方向和 y 方向的偏导数,即图像x和y方向的梯度。ΔIt 即为两图之间的 (x,y) 坐标位置的亮度差。
-
邻域光流相似假设:一个小的图像区域里像素移动方向和大小是基本一致的。
借助该假设,像素点 (x,y) 领域内的所有像素都有下面的公式:
[Ix1′,Iy1′Ix2′,Iy2′][uv]=[−ΔIt1−ΔIt2]
上式即为 Ax=b 的形式,可求得光流向量的最小二乘解:
x=(ATA)-1ATb
其中要求 ATA 可逆,为了满足这个要求,Lucas-Kanade方法选取角点作为特征点。除了基于亮度不变假设和邻域光流相似假设,Lucas-Kanade算法还借助了图像金字塔的方式解决图像偏移较大的情况,在高层低分辨率图像上,大的偏移将变为小的偏移,从而求解出光流。
因此,OpticalFlowPyrLK算子需要前后两帧的金字塔图层,和前一帧的特征点作为输入,其中特征点通常选用角点。
#API接口
int32_t hbVPOpticalFlowPyrLK(hbUCPTaskHandle_t *taskHandle,
hbVPArray *currPoints,
hbVPArray *currPointsStatus,
hbVPArray *currPointsConf,
hbVPArray const *prevPoints,
hbVPImage const *currPym,
hbVPImage const *prevPym,
hbVPLKOFParam const *lkofParam);详细接口信息请查看 hbVPOpticalFlowPyrLK。
#使用方法
// Include the header
#include "hobot/hb_ucp.h"
#include "hobot/vp/hb_vp.h"
#include "hobot/vp/hb_vp_opticalflow_pyrlk.h"
// init Image, allocate memory for multilayer gaussian pyramid layer array
int32_t top_k{15};
int32_t_t num_layers{5};
int32_t src_width = 1280;
int32_t src_height = 720;
// Alloc memory used to hold key points, include previous points, current points
hbUCPSysMem arr_mem0;
hbUCPMallocCached(arr_mem0, top_k * sizeof(hbVPKeyPoint), 0);
hbVPArray arr0{arr_mem0.phyAddr,
arr_mem0.virAddr,
arr_mem0.memSize,
top_k};
hbUCPSysMem arr_mem1;
hbUCPMallocCached(arr_mem1, top_k * sizeof(hbVPKeyPoint), 0);
hbVPArray arr1{arr_mem1.phyAddr,
arr_mem1.virAddr,
arr_mem1.memSize,
top_k};
hbUCPSysMem status_mem;
bUCPMalloc(&status_mem, top_k, 0)
hbVPArray status{status_mem.phyAddr,
status_mem.virAddr,
status_mem.memSize,
top_k};
// Alloc memory used to hold gaussian pyramid images, include previous frame and current frame
std::vector<hbUCPSysMem> prev_frame_mem(num_layers);
std::vector<hbVPImage> prev_frame(num_layers);
std::vector<hbUCPSysMem> cur_frame_mem(num_layers);
std::vector<hbVPImage> cur_frame(num_layers);
for (int32_t i = 0; i < num_layers; i++) {
if (i != 0) {
src_width = (src_width + 1) / 2;
src_height = (src_height + 1) / 2;
}
hbUCPMallocCached(&prev_frame_mem[i], src_width * src_height, 0);
prev_frame[i] = {HB_VP_IMAGE_FORMAT_Y,
HB_VP_IMAGE_TYPE_U8C1,
src_width,
src_height,
src_width,
prev_frame_mem[i].virAddr,
prev_frame_mem[i].phyAddr,
nullptr,
0,
0};
hbUCPMallocCached(&cur_frame_mem[i], src_width * src_height, 0);
cur_frame[i] = {HB_VP_IMAGE_FORMAT_Y,
HB_VP_IMAGE_TYPE_U8C1,
src_width,
src_height,
src_width,
cur_frame_mem[i].virAddr,
cur_frame_mem[i].phyAddr,
nullptr,
0,
0};
}
// optical flow parameter
hbVPLKOFParam lkof_param;
HB_VP_INITIALIZE_OPTICAL_FLOW_PARAM(&lkof_param);
// init task handle and schedule param
hbUCPTaskHandle_t task_handle{nullptr};
hbUCPSchedParam sched_param;
sched_param.backend = HB_UCP_DSP_CORE_0;
sched_param.priority = 0;
// i frame process
{
// two blocks of memory read and write alternately
std::vector<hbVPImage> &prev_frame = i % 2 == 0 ? prev_frame : cur_frame;
std::vector<hbVPImage> &curr_frame = i % 2 == 0 ? cur_frame : prev_frame;
std::vector<hbUCPSysMem> &curr_mem = i % 2 == 0 ? cur_frame_mem : prev_frame_mem;
hbVPArray &prev_points = i % 2 == 0 ? arr0 : arr1;
hbVPArray &curr_points = i % 2 == 0 ? arr1 : arr0;
// create task
hbVPOpticalFlowPyrLK(&task_handle, &curr_points, &status, nullptr, &prev_points, curr_frame.data(), prev_frame.data(), &lkof_param);
// submit task
hbUCPSubmitTask(task_handle, &sched_param);
// wait for task done
hbUCPWaitTaskDone(task_handle, 0);
// release task handle
hbUCPReleaseTask(task_handle);
}
// release memory
hbUCPFree(&arr_mem0);
hbUCPFree(&arr_mem1);
hbUCPFree(&status_mem);
for (int32_t i = 0; i < num_layers; i++) {
hbUCPFree(&prev_frame_mem[i]);
hbUCPFree(&cur_frame_mem[i]);
}

