FAST角点检测 HLS代码解析(ⅴ)

作者:阿白叔 发布时间: 2025-11-10 阅读量:3 评论数:0

xFfast7x7函数

这个代码是7X7的窗口的FAST角点检测。Template的定义和前面几个函数的一致,可以看之前的解释,这边不再展开。

void xFfast7x7(hls::stream<ap_uint<8> > &_src_mat,

        hls::stream< ap_uint<16> > &_out_mat, ap_uint<8> win_size,

        uint16_t img_height, uint16_t img_width,uchar_t _threshold)

 

输入:_src_mat 是 8 位无符号整数流(hls::stream是 HLS 专用的流接口,适合连续数据传输),存储原始图像像素(如灰度值);

输出:_out_mat 是 16 位无符号整数流,存储处理结果(结合前文,可能是特征值与中心像素的组合数据);

配置参数:win_size(窗口实际尺寸)、img_height/img_width(图像高 / 宽)、_threshold(特征检测的阈值,如 FAST 角点的亮度差阈值)

 

row_ind 是存储滑动窗口内各位置对应原始图像行索引的数组:ap_uint<13> 为 13 位无符号硬件友好型数据类型(适配图像行索引范围,通常不超过 8192),数组大小为窗口尺寸 WIN_SZ(如 7x7 窗口则为 7),用于快速映射窗口内每个位置(0~WIN_SZ-1)对应的原始图像行坐标(避免重复计算)

XF_PTNAME(DEPTH) pack_corners;

用于存储角点信息。

XF_PTNAME(DEPTH) 是 Xilinx 图像加速库(XF 库)的宏,用于根据图像数据深度 DEPTH(如 8 位、16 位)自动匹配对应的像素 / 数据类型(例如 8 位深度时可能为 unsigned char)。

pack_corners 用于存储 “打包后的角点信息”:在 FAST 角点检测中,检测到的角点坐标或状态需要按一定格式打包(便于硬件高效传输或存储),该变量承担这一角色。

uint16_t shift_x = 0;

    ap_uint<13> row, col;

    unsigned char OutputValues[XF_NPIXPERCYCLE(NPC)];

#pragma HLS ARRAY_PARTITION variable=OutputValues complete dim=1

 

shift_x 是 16 位无符号整数,初始化为 0,代表水平方向的偏移量。在滑动窗口处理中,常用于校准窗口在水平方向的起始位置(例如窗口步长不为 1 时,记录累计偏移以确保窗口对齐),或在并行处理多像素时同步数据位置,避免因并行读写导致的位置偏差。

ap_uint<13> 是 Xilinx HLS 专用的 “13 位无符号整数类型”,选择 13 位是因为图像的行列索引范围通常不超过 8192(2¹³=8192),既能覆盖绝大多数图像尺寸(如常见的 4096x4096 以内),又比 32 位整数更节省硬件资源(寄存器 / 存储单元),适合硬件实现。

row 和 col 分别表示当前正在处理的像素的行索引和列索引,用于跟踪滑动窗口在图像中的位置,是遍历图像、判断边界(如是否超出图像高 / 宽)的核心变量。

XF_NPIXPERCYCLE(NPC) 是 Xilinx 图像加速库(XF 库)的宏,根据 “像素并行度”NPC(如 XF_NPPC8 表示 8 像素并行处理)计算每周期处理的像素数量(例如 NPC=8 时,数组大小为 8)。

OutputValues 是存储特征检测结果的数组,类型为 unsigned char(8 位),适合存储 0-255 范围内的特征值(如 FAST 角点的响应强度、是否为角点的标志等),数组大小与并行处理的像素数匹配,确保每周期的所有结果都能被存储。

unsigned char src_buf[WIN_SZ][XF_NPIXPERCYCLE(NPC)+(WIN_SZ-1)];

#pragma HLS ARRAY_PARTITION variable=src_buf complete dim=1

#pragma HLS ARRAY_PARTITION variable=src_buf complete dim=2

    // src_buf1 et al merged

    XF_SNAME(WORDWIDTH) P0;

 

第一维 WIN_SZ:对应滑动窗口的行数(如 7x7 窗口的 WIN_SZ=7),即缓冲区有 WIN_SZ 行,每行存储窗口中一行的像素数据。

第二维 XF_NPIXPERCYCLE(NPC) + (WIN_SZ-1):由两部分组成:

XF_NPIXPERCYCLE(NPC):Xilinx XF 库宏,根据像素并行度 NPC(如 XF_NPPC8 表示 8 像素并行)计算每周期处理的像素数(例如 NPC=8 时,该值为 8);

(WIN_SZ-1):补充的列数,用于存储窗口滑动时的历史列数据(滑动窗口每次移动一列时,需保留前 WIN_SZ-1 列的像素以组成完整窗口)。整体第二维大小确保缓冲区能容纳 “并行输入的像素” 和 “窗口所需的历史列数据”,满足滑动窗口连续处理的需求。

 

XF_SNAME(WORDWIDTH) 是 Xilinx XF 库的宏,根据数据总线宽度 WORDWIDTH(如 8 位、16 位、32 位)自动匹配对应的硬件友好型数据流类型(例如 WORDWIDTH=8 时为 ap_uint<8>WORDWIDTH=16 时为 ap_uint<16>

unsigned char buf[WIN_SZ][(COLS >> XF_BITSHIFT(NPC))];

#pragma HLS ARRAY_PARTITION variable=buf complete dim=1

#pragma HLS RESOURCE variable=buf core=RAM_S2P_BRAM

 

第一维 WIN_SZ:对应滑动窗口的行数(如 7x7 窗口的WIN_SZ=7),即buf有WIN_SZ行,每行存储图像中连续多行的像素数据(供窗口提取行数据使用)。

第二维 (COLS >> XF_BITSHIFT(NPC)):由图像总列数和并行度共同决定:

COLS 是图像的总列数(宽度方向的像素总数);

XF_BITSHIFT(NPC) 是 Xilinx XF 库的宏,NPC(像素并行度,如XF_NPPC8表示 8 像素并行)决定了每周期处理的像素数,该宏返回log2(NPC)(如 NPC=8 时,XF_BITSHIFT(NPC)=3,因为 2³=8);

右移操作 COLS >> XF_BITSHIFT(NPC) 等价于 COLS / NPC,结果是并行处理时 “每周期处理的列数对应的总周期数”,即buf第二维的大小等于 “存储完整图像列数据所需的并行周期数”,确保能缓存整幅图像按并行度拆分后的列数据。

 

for(int init_row_ind=0; init_row_ind<win_size; init_row_ind++)

    {

#pragma HLS LOOP_TRIPCOUNT min=WIN_SZ max=WIN_SZ

        row_ind[init_row_ind] = init_row_ind;

    }

初始化一个名为row_ind的数组,这个数组用于记录滑动窗口的行索引(Index)

read_lines:

    for(int init_buf=row_ind[win_size>>1]; init_buf <row_ind[win_size-1] ;init_buf++)

    {

#pragma HLS LOOP_TRIPCOUNT min=WIN_SZ max=WIN_SZ

        for(col = 0; col < img_width>>XF_BITSHIFT(NPC) ; col++)

        {

#pragma HLS LOOP_TRIPCOUNT min=TC max=TC

#pragma HLS pipeline

#pragma HLS LOOP_FLATTEN OFF

            buf[init_buf][col] = srcmat.read();

        }

    }

read_lines:

    for(int init_buf=row_ind[win_size>>1]; init_buf <row_ind[win_size-1] ;init_buf++)

    {

#pragma HLS LOOP_TRIPCOUNT min=WIN_SZ max=WIN_SZ

        for(col = 0; col < img_width>>XF_BITSHIFT(NPC) ; col++)

        {

#pragma HLS LOOP_TRIPCOUNT min=TC max=TC

#pragma HLS pipeline

#pragma HLS LOOP_FLATTEN OFF

            buf[init_buf][col] = srcmat.read();

        }

    }

读取图像数据,打到缓冲里面。

 

//takes care of top borders

    for(col = 0; col < img_width>>XF_BITSHIFT(NPC); col++)

    {

#pragma HLS LOOP_TRIPCOUNT min=TC max=TC

        for(int init_buf=0; init_buf < WIN_SZ>>1;init_buf++)

        {

#pragma HLS LOOP_TRIPCOUNT min=WIN_SZ max=WIN_SZ

#pragma HLS UNROLL

            buf[init_buf][col] = 0;//buf[row_ind[win_size>>1]][col];

        }

   

 

当窗口滑动到图像边缘(如顶部、底部、左侧、右侧)时,窗口的部分区域会超出原始图像的范围(即没有对应的像素数据)。为了保证窗口处理的完整性,通常需要对超出边界的区域进行填充(border padding)。通过给边缘补充0实现。

 

Row_Loop:

for(row = (win_size>>1); row < img_height+(win_size>>1); row++)

{

#pragma HLS LOOP_TRIPCOUNT min=ROWS max=ROWS

 

P0 = 0;

ProcessFast<ROWS, COLS, DEPTH, NPC, WORDWIDTH, TC, WIN_SZ, WIN_SZ_SQ>(_src_mat, outmat, buf, src_buf,OutputValues, P0, img_width, img_height, shift_x, row_ind, row,win_size,_threshold,pack_corners);

 

//update indices

ap_uint<13> zero_ind = row_ind[0];

for(int init_row_ind=0; init_row_ind<WIN_SZ-1; init_row_ind++)

{

#pragma HLS LOOP_TRIPCOUNT min=WIN_SZ max=WIN_SZ

#pragma HLS UNROLL

row_ind[init_row_ind] = row_ind[init_row_ind + 1];

}

row_ind[win_size-1] = zero_ind;

} // Row_Loop

}Row_Loop:

for(row = (win_size>>1); row < img_height+(win_size>>1); row++)

{

#pragma HLS LOOP_TRIPCOUNT min=ROWS max=ROWS

 

P0 = 0;

ProcessFast<ROWS, COLS, DEPTH, NPC, WORDWIDTH, TC, WIN_SZ, WIN_SZ_SQ>(_src_mat, outmat, buf, src_buf,OutputValues, P0, img_width, img_height, shift_x, row_ind, row,win_size,_threshold,pack_corners);

 

//update indices

ap_uint<13> zero_ind = row_ind[0];

for(int init_row_ind=0; init_row_ind<WIN_SZ-1; init_row_ind++)

{

#pragma HLS LOOP_TRIPCOUNT min=WIN_SZ max=WIN_SZ

#pragma HLS UNROLL

row_ind[init_row_ind] = row_ind[init_row_ind + 1];

}

row_ind[win_size-1] = zero_ind;

} // Row_Loop

}

调用ProcessFast函数进行处理。逻辑如下:

若窗口大小win_size=5,初始row_ind = [0,1,2,3,4]:

第一步:保存row_ind[0] = 0到zero_ind;

第二步:数组向前移动一位,row_ind变为[1,2,3,4,4](最后一位暂时未更新);

第三步:将zero_ind(0)赋给最后一位,row_ind最终变为[1,2,3,4,0]。

此时,窗口的行索引整体向下滑动了一行,最顶部的行(0)被 “循环” 到最底部,适配下一行(row+1)的处理。

 

接下来的代码模块将只阐述代码作用, 原理以及实现方法。

 

xFnmsProc 函数

这个函数的核心是实现3x3 邻域的非极大值抑制(Non-Maximum Suppression, NMS),是图像处理中特征筛选的关键步骤。其原理和实现方法可从功能目标、核心逻辑及硬件适配设计三个层面展开说明:

 

一、核心原理:非极大值抑制(NMS)的作用与逻辑

非极大值抑制是特征提取后的经典后处理手段,目的是从密集的特征响应中筛选出局部最强的特征点,去除冗余的弱响应点,使最终输出的特征更稀疏、更具代表性。

在该函数中,具体逻辑基于 “3x3 局部邻域”:

对于每个待处理的 “中心像素”,将其特征响应值(如角点分数、边缘强度等)与周围 8 个相邻像素的响应值进行比较;

若中心像素的响应值大于所有 8 个邻域像素的响应值,则判定为 “局部极大值”,予以保留;

若中心像素响应值为 0(无有效特征),或小于任一邻域像素的响应值,则判定为 “非极大值”,予以抑制。

 

二、实现方法:从数据处理到硬件适配

该函数专为硬件加速(HLS)设计,实现上兼顾了功能正确性和硬件执行效率,核心方法包括:

1. 数据提取:固定邻域访问

函数从输入缓冲区src_buf中直接提取 3x3 邻域的 9 个像素响应值。缓冲区src_buf存储了局部区域的特征响应数据(16 位宽,实际有效信息为高 8 位,通过range(15,8)提取),其中:

中心像素对应src_buf[15][15](索引 15 为 3x3 邻域的中心);

8 个邻域像素分别对应中心像素的上、下、左、右及 4 个对角线位置(如src_buf[14][14]为左上角,src_buf[15][16]为右侧等)。

这种固定索引的访问方式在硬件中可通过并行线路直接读取,避免了复杂的地址计算,降低延迟。

 

2. 比较逻辑:并行化的局部极大值判断

核心比较逻辑通过一系列条件判断实现:若中心像素响应值(val4)同时大于 8 个邻域像素的响应值(val0~val8),则判定为局部极大值。

在硬件实现中,这 8 个比较操作可通过组合逻辑并行执行(而非软件中的顺序比较),大幅提升判断速度,适配硬件流水线的高吞吐量需求。

 

3. 结果输出:二值化标记

函数通过固定值对结果进行标记:

局部极大值(保留):输出 255;

非极大值(抑制):输出 127(包括中心像素响应值为 0 的情况)。

这种简单的二值化标记便于后续模块快速识别有效特征,减少数据传输量。

4. 硬件优化:适配 HLS 的设计

模板参数化:通过NPC(每周期处理像素数)、WIN_SZ(窗口大小)等模板参数,使函数可适配不同图像分辨率、特征窗口尺寸及硬件并行度(如一次处理 1 个或多个像素),提升代码复用性;

内联优化:#pragma HLS INLINE指令让函数被直接嵌入调用它的父函数,消除函数调用的硬件开销(如跳转、栈操作),优化电路时序和资源利用率。

 

Processfastnms 函数

这段代码主要功能是  在滑动窗口的列方向上处理图像数据,为非极大值抑制(NMS)准备邻域数据,并将处理结果打包输出,是快速特征提取(如FAST角点检测)流程中连接数据缓冲与特征筛选的关键环节。

一、核心功能与原理

函数的核心目标是  为每个列位置构建局部邻域数据,调用NMS函数筛选局部极大值,并将结果与原始邻域数据打包输出,支撑后续特征点的最终判定。 具体来说,在图像的每行处理中(结合外层Row_Loop),函数通过列方向的滑动窗口(Col_Loop),逐列完成以下工作:

 

1. 从输入流中读取图像数据,更新缓冲区,确保当前窗口包含足够的局部像素;

2. 为每个列位置提取对应的局部邻域数据(存储在src_buf中),作为NMS的输入;

3. xFnmsProc函数执行3x3邻域的非极大值抑制,得到当前位置的特征有效性标志; 4. 将邻域原始数据、NMS结果等打包成固定格式,通过输出流传递给后续模块。

 

二、数据处理流程 函数通过“缓冲更新→邻域提取→NMS处理→结果打包→窗口滑动”的闭环流程,实现列方向的连续处理,关键步骤如下:

1. 缓冲区初始化与准备

初始化src_buf(邻域数据缓冲区)为 0,避免初始值干扰;

定义buf_cop作为中间缓冲,用于临时存储当前窗口的行数据,并通过#pragma HLS ARRAY_PARTITION完全分区,实现硬件并行访问。

 

2. 列方向滑动窗口处理(Col_Loop)

这是核心循环,逐列遍历图像(含边界扩展),每列执行:

数据读取与缓冲更新:当行和列在有效图像范围内时,从输入流_src_mat读取数据到buf(全局缓冲区),确保buf始终存储最新的窗口行数据;

邻域数据提取:根据当前行索引row_ind和列col,从buf中复制对应行的数据到buf_cop,再更新到src_buf(局部邻域缓冲区)。针对图像底部边界(row > img_height-1),通过索引调整复用已有数据,保证边界处邻域的完整性;

NMS 处理:调用xFnmsProc函数,基于src_buf中的 3x3 邻域数据执行非极大值抑制,结果存入OutputValues;

 

有效性判断与结果打包:当列超过窗口半宽(col >= WIN_SZ>>1)时,将 NMS 结果(validFlag)标记为有效,否则标记为 0(边界区域无效);将邻域原始数据(winVal)、有效性标志(validFlag)、中心像素值等打包成 264 位的temp,写入输出流datapackStreamOut;

 

窗口滑动更新:通过循环移位更新src_buf,将列方向的邻域数据左移一位,为下一列处理准备新的邻域(类似行方向的循环缓冲机制,避免重复读取数据)。

 

3. 边界与特殊情况处理

针对不同的NPC(每周期处理像素数),调整列循环变量col_loop_var,适配不同并行度的硬件处理;

对图像底部边界(row超出图像高度),通过索引映射复用buf中已有数据,避免访问无效内存;

对列方向的初始边界(col < WIN_SZ>>1),直接标记validFlag为 0,避免边界处无效特征的干扰。

 

三、硬件优化设计

函数针对 HLS 硬件实现做了深度优化,确保高吞吐量和低延迟:

内联优化:#pragma HLS INLINE将函数嵌入调用者,消除函数调用开销;

数组分区:buf_cop完全分区(ARRAY_PARTITION complete),实现并行读写,提升数据访问速度;

循环优化:内层循环通过#pragma HLS UNROLL展开,消除迭代延迟;外层Col_Loop使用#pragma HLS pipeline实现流水线,理想情况下每个时钟周期处理一列数据;

 

参数化设计:通过模板参数(ROWS、COLS、NPC等)适配不同图像尺寸、并行度和窗口大小,提高代码复用性,同时让 HLS 工具能根据参数优化硬件资源分配。

 

xFfastnms 函数

 

这个函数是基于 HLS 的快速特征提取 + 非极大值抑制(NMS)的完整硬件加速流水线顶层函数,整合了之前所有子函数的逻辑,实现从原始图像输入到带 NMS 结果的结构化数据输出的端到端处理,核心用于实时图像处理场景(如 FAST 角点检测)的硬件实现。其原理、实现架构及硬件优化设计如下:

 

一、核心原理:二维滑动窗口 + NMS 的端到端流水线

 

函数的核心逻辑是通过 “行 - 列二维滑动窗口” 遍历全图,结合边界处理和非极大值抑制,从原始图像中筛选出局部极大特征点,并将结果打包输出。

整体流程遵循 “准备 - 处理 - 滑动” 的闭环:

先初始化滑动窗口的行索引、缓冲区,读取初始图像数据并处理顶部边界,确保窗口在边缘区域仍有有效数据;

以行为单位遍历全图(含边界扩展),每行中通过列方向滑动窗口提取局部邻域数据;

对每个邻域执行 NMS 筛选,保留局部极大特征点;

窗口逐行、逐列滑动,通过循环缓冲机制复用数据,避免重复读取,提升效率;

最终将邻域原始数据、NMS 结果等打包成固定格式输出,供后续模块(如特征点坐标计算)使用。

 

二、实现架构:分层模块整合与数据流转

函数通过模块化设计整合了数据初始化、缓冲管理、边界处理、核心计算和结果输出,各环节紧密衔接形成流水线:

1. 参数与缓冲区配置(硬件适配基础)

定义核心缓冲区:row_ind(行索引数组)、buf(全局图像数据缓冲区)、src_buf(局部邻域缓冲区)、OutputValues(NMS 结果缓冲区)。

硬件优化配置:

对所有缓冲区使用#pragma HLS ARRAY_PARTITION complete(维度 1 或双维度),实现缓冲区的完全并行访问(硬件中多个端口同时读写),消除数据访问瓶颈;

明确buf的硬件资源类型为RAM_S2P_BRAM(单端口读、双端口写的块 RAM),平衡硬件资源占用(BRAM 比寄存器更节省资源,适合存储大量图像数据)。

 

2. 初始化阶段:索引与数据准备

行索引初始化:生成长度为win_size的row_ind数组,元素值等于自身索引(如row_ind[0]=0、row_ind[1]=1),为滑动窗口的行位置定位提供基础。

初始数据读取:通过read_lines循环,从输入流_src_mat读取 “窗口中间行到末尾行” 的图像数据到buf,为首次窗口处理铺垫初始数据。

顶部边界处理:将缓冲区顶部WIN_SZ>>1行(超出图像顶部的边界区域)的数据,复用窗口中间行的数据(而非填 0),确保边界处邻域数据的一致性,避免特征误判。

 

3. 核心处理:行遍历 + 列方向流水线(调用子函数)

外层行遍历(Row_Loop):这是顶层主循环,遍历范围覆盖 “窗口半宽→图像高度 + 窗口半宽”(含顶部和底部边界扩展),确保全图(含边缘)都被处理。

每行核心逻辑:调用Processfastnms子函数,完成当前行的列方向滑动窗口处理 —— 包括读取更新图像数据、提取 3x3 邻域数据、调用xFnmsProc执行 NMS、打包输出结果。

行索引滑动更新:每行处理完毕后,通过 “循环缓冲机制” 更新row_ind(将最顶部索引移到最底部,其余索引上移 1 位),模拟窗口向下滑动 1 行,无需重新初始化索引,硬件实现高效。

 

4. 结果输出:结构化数据打包

通过datapackStreamOut输出流,将 “局部邻域原始数据 + NMS 有效性标志 + 中心像素特征值” 打包成 264 位的固定格式数据,为后续模块(如特征点坐标筛选、输出显示)提供结构化输入,减少数据传输开销。

 

三、硬件优化设计:面向高吞吐量的 HLS 优化

函数的所有设计都围绕 “硬件并行性、低延迟、资源高效” 展开,核心优化点:

完全并行的缓冲区访问:所有缓冲区的完全分区(ARRAY_PARTITION complete),让硬件可同时读写缓冲区的多个元素,匹配流水线的并行处理节奏。

资源精准分配:buf指定为 BRAM 资源,避免使用大量寄存器导致资源溢出,同时 BRAM 的高带宽适配图像数据的高速读写。

循环优化组合:内层循环使用#pragma HLS pipeline(流水线)、#pragma HLS UNROLL(展开),外层循环用#pragma HLS LOOP_TRIPCOUNT(迭代次数提示),让 HLS 工具生成高时序性能的电路 —— 理想情况下可达到 “每时钟周期处理 1 个列数据” 的吞吐量。

参数化与复用:通过模板参数(ROWS、COLS、NPC等)适配不同图像分辨率、窗口大小、每周期处理像素数(NPC),无需修改核心逻辑即可适配不同硬件平台和应用场景。

 

myFAST函数

这两段代码是FAST 角点检测的硬件加速入口级代码,基于 Xilinx HLS 实现,核心作用是搭建 “输入解析→多分辨率适配→核心检测调度→标准化输出” 的顶层框架,衔接外部数据流与底层 FAST 角点检测核心逻辑(含之前提到的 NMS),是 FPGA 等硬件上实现实时 FAST 角点检测的 “总入口”。其原理、实现架构及硬件适配设计如下:

 

一、核心原理:FAST 角点检测的硬件加速入口封装

FAST(Features from Accelerated Segment Test)角点检测是一种高效的特征提取算法,核心是通过比较像素邻域的灰度值快速判断是否为角点。这两段代码并未直接实现 FAST 的核心判断逻辑,而是为底层核心检测函数(xFFastCornerDetection)提供顶层支撑:

标准化外部输入输出接口,适配 AXI-Stream(FPGA 常用高速数据流接口);

支持多分辨率图像适配,根据输入的 “图像层级” 自动选择对应分辨率;

封装模板参数,固化检测配置(如是否启用 NMS、像素格式、并行度);

通过 HLS 指令优化硬件流水线,确保数据高速流转,匹配实时处理需求。

 

二、实现架构:两层函数分工与数据流转

代码分为 “顶层加速器入口(fast_accel)” 和 “检测函数封装(myFAST)” 两层,分工明确、数据流转简洁:

1. 顶层入口函数 fast_accel:外部接口与输入解析

作为整个 FAST 加速器的 “对外接口”,负责对接外部数据流、解析配置信息、调度核心检测逻辑,是硬件与外部交互的关键:

 

接口标准化配置:

#pragma HLS INTERFACE ap_ctrl_none port=return:禁用 HLS 默认生成的控制接口(如 start、done 信号),适配纯数据流驱动的硬件场景(无需外部控制信号,数据来了就处理);

#pragma HLS INTERFACE axis register both port=srcStream/MomentsStreamOut:将输入 / 输出接口配置为 AXI-Stream 标准接口(FPGA 高速数据传输的常用接口),且 “双向寄存器化”(接口信号经过寄存器缓冲),提升硬件时序性能(减少信号延迟,避免时序违规)。

核心功能:

解析输入图像层级:从输入流srcStream先读取 1 个 8 位数据img_level(图像层级标识,0-3);

多分辨率适配:预定义 4 组分辨率数组(_image_height/_image_width),根据img_level自动选择对应图像尺寸(如img_level=0时,分辨率为 640x480;img_level=3时为 190x142),支持多尺度图像处理;

传递配置与调度:将图像层级信息打包后写入输出流,再调用myFAST函数,传入适配后的分辨率参数,启动核心检测。

 

2. 封装函数 myFAST:模板参数固化与核心调用

作为中间封装层,核心作用是 “固化检测配置 + 调用底层核心函数”,简化顶层调度逻辑:

模板参数固化:通过模板参数明确检测配置,无需在运行时动态配置,适配硬件 “编译时确定” 的特性:

NMS=1:启用非极大值抑制(对应之前分析的xFnmsProc逻辑),筛选冗余角点;

SRC_T=XF_8UC1:输入图像为 8 位单通道灰度图(FAST 角点检测默认输入格式);

ROWS/COLS:图像最大分辨率(编译时确定,适配硬件资源分配);

NPC=1:每时钟周期处理 1 个像素(可根据硬件资源调整为 2/4/8,提升并行度)。

 

硬件优化指令:

#pragma HLS inline off:禁止函数内联,将myFAST保留为独立模块,便于 HLS 工具单独优化时序,也方便后续代码调试与复用;

#pragma HLS DATAFLOW:启用数据流优化,让函数内部的 “数据读取→核心处理→结果输出” 形成流水线并行(前一个数据的处理未结束,下一个数据已开始读取),大幅提升吞吐量。

核心调用:直接调用底层 FAST 角点检测函数xFFastCornerDetection,将顶层传入的输入流、输出流、分辨率参数传递下去,触发核心检测逻辑(包括角点快速判断、NMS 筛选等)。

三、数据流转全流程(从输入到输出)

外部输入:8 位灰度图像数据流通过srcStream(AXI-Stream 接口)传入;

层级解析:fast_accel先读取 1 个 8 位img_level,确定当前图像分辨率;

配置传递:将img_level打包为 88 位数据写入MomentsStreamOut,同时将适配后的分辨率(_image_height[img_level]/_image_width[img_level])传入myFAST;

核心检测:myFAST调用xFFastCornerDetection,底层完成 FAST 角点快速检测 + NMS 筛选;

 

结果输出:检测结果(含角点信息、邻域数据等)通过MomentsStreamOut(88 位 AXI-Stream)输出,供后续模块(如角点坐标提取、图像匹配)使用。

四、核心价值:硬件加速的顶层适配

这两段代码的核心作用是 “适配硬件、简化调度、标准化接口”,而非直接实现算法逻辑:

对外部:提供 AXI-Stream 标准接口,可直接对接 FPGA 的高速 I/O 模块(如 HDMI 接收、DDR 读写);

对内部:固化配置、支持多分辨率,让底层核心函数无需关注接口与适配,专注算法本身的硬件优化;

性能保障:通过DATAFLOW、接口寄存器化等 HLS 指令,确保数据流转无瓶颈,匹配底层检测逻辑的并行处理节奏,最终实现 “实时” 角点检测(如 640x480 分辨率下每秒处理 30 帧以上)。

ORB-SLAM3硬件加速项目系列

image-dtx3-2vpd.png

点击文章下方标签获取整个系列更新文章!

文章doc版链接【金山文档 | WPS云文档】 FAST角点检测 HLS代码解析

本章是“FAST角点检测 HLS代码解析”最后一章节,至此已将FAST角点检测的各个模块详细解析清楚,后续有时间会更新总结性文章。

评论