资讯动态

计算机视觉底层原理:高斯滤波、SIFT与Canny的工程实现解析

发布时间:2026/10/26 17:41:48 来源:尧图企业网站定制
简介本资源是北京交通大学2024年计算机视觉课程核心考点精要汇总面向高校本科生、考研备考学生及CV入门学习者系统梳理课程中高频必考的40个关键知识点覆盖图像滤波原理相关/卷积/内积、Canny与Harris算法细节、SIFT特征构建逻辑、CNN基础架构感受野、权值共享、残差模块、目标检测演进脉络R-CNN→Faster R-CNN、损失函数与优化策略等硬核内容直击期末复习与概念辨析痛点。资源为单文件PDF共1个9.69MB高清文档排版清晰、公式规范、要点分层便于打印精读或碎片化回顾。已有666人下载学习内容兼具理论深度与教学逻辑可作为课堂笔记补充、考前速查手册及面试基础复盘材料。1. 这不是考前划重点是计算机视觉工程师的「底层操作手册」从高斯核参数设置到Faster R-CNN全流程推导2024年BJTU课程真题级资源落地实录你有没有试过对着Canny边缘检测的“双阈值”背了三遍一写代码发现cv2.Canny(img, 30, 90)里两个数根本不是题目说的minVal/maxVal或者调SIFT时死活对不上“1.5σ半径投票”结果发现OpenCV默认用的是sigma1.6且不暴露内部尺度这不是记错是没摸清这些知识点背后的可执行边界——它们不是PPT里的结论而是你调参、debug、复现论文时必须亲手拧紧的螺丝。这份2024年BJTU计算机视觉课程必考知识点汇总本质是一份带工程坐标的理论地图它把图像滤波的三种定义方式相关/卷积/内积直接对应到scipy.ndimage.correlate与convolve的flag开关把SIFT“双塔”结构翻译成cv2.SIFT_create(noctaves4, nlevels3)的实参含义把RPN锚点生成过程拆解为torch.nn.Conv2d(in_channels512, out_channels9*4, kernel_size1)的通道数设计逻辑。它面向的不是考场默写而是你明天就要跑通的YOLOv8预处理pipeline、后天要改的特征金字塔融合模块、下周一要交的消融实验表格。如果你正在被“为什么小尺寸滤波器更好”这类问题卡住不是概念没懂是缺一个能让你在PyTorch里print(model.features[0].weight.shape)验证的落点。这份资源就是那个落点。图像滤波不是数学游戏是卷积引擎的启动开关所有滤波操作最终都归结为张量运算输入图像H×W×C滤波器K×K×C×F输出H×W×F。但关键在于——你调用的API背后到底执行的是相关还是卷积OpenCV的cv2.filter2D默认做相关不翻转kernel而PyTorch的nn.Conv2d强制做卷积自动翻转。这意味着若你用OpenCV训练出一个高斯模板g_kernel想在PyTorch中复现相同效果必须手动g_kernel torch.flip(g_kernel, [0,1])。否则哪怕σ和size完全一致输出边缘响应也会镜像偏移。这是90%初学者第一次跨框架复现失败的根源。SIFT不是黑匣子是可拆解的尺度-方向-描述子三件套课程里反复强调的“1.5σ投票半径”在OpenCV源码中实际绑定在SIFT::buildGaussianPyramid的sigma0初始化逻辑里。当你调用sift.detectAndCompute(img, None)时系统已按σ₀1.6, k2^(1/3)构建了高斯金字塔并在DoG层搜索极值。所谓“双塔”本质是Octave下采样组与Interval组内层的嵌套索引Octave0对应原图Octave1对应原图下采样2倍后的图每组内又分nlevels3层。这个结构直接决定你提取的关键点坐标是否需乘以2^octave还原到原图尺度——漏掉这步所有关键点画在图上都是错位的。目标检测演进不是历史课是计算瓶颈驱动的架构迭代R-CNN→Fast R-CNN→Faster R-CNN的每次升级核心动因只有一个避免重复卷积。R-CNN对2000个Proposal各自做一次resize→crop→forwardGPU显存峰值出现在全连接层输入前Fast R-CNN用RoI Pooling将2000次卷积压缩为1次整图卷积2000次池化显存占用从GB级降到MB级Faster R-CNN更进一步用RPN的1×1卷积头out_channels9×(42)直接在特征图上生成Proposal彻底消灭Selective Search的CPU耗时。当你看到“RPN输出尺寸为4×20×15”这数字不是随意写的——它来自VGG16的conv5_3输出尺寸假设输入224×224经5次pooling后为7×7但实际常用feature stride16故224/1614此处20×15为示意而4代表dx,dy,dw,dh四个回归参数。不理解这个映射你就永远调不准anchor的scale和ratio。2. 图像滤波的三种定义方式从数学公式到PyTorch张量操作的精确映射滤波操作在教材里常被抽象为“模板滑动求和”但工程实现中相关correlation与卷积convolution的差异会直接导致模型权重加载失败。比如你用MATLAB训练了一个LoG滤波器想迁移到PyTorch若不处理kernel翻转边缘检测结果将完全错误。本节将逐行拆解三种定义方式的代码实现明确每个参数的物理意义。2.1 相关Correlation模板不翻转的原始匹配相关操作是图像处理中最直观的滤波方式模板中心对准像素位置模板值与图像对应区域像素值逐点相乘后求和。其数学表达为$$ (I \star K)(i,j) \sum_{m}\sum_{n} I(im, jn) \cdot K(m,n) $$注意此处K(m,n)未翻转m,n以模板中心为原点负值向左/上正值向右/下。import torch import torch.nn.functional as F import numpy as np # 构造一个3x3高斯核σ0.8size3 def gaussian_kernel_2d(size3, sigma0.8): ax np.arange(-size//2 1., size//2 1.) xx, yy np.meshgrid(ax, ax) kernel np.exp(-(xx**2 yy**2) / (2 * sigma**2)) return torch.tensor(kernel / kernel.sum(), dtypetorch.float32) # 创建测试图像中心白点5x5区域 img torch.zeros(1, 1, 32, 32) img[0, 0, 14:19, 14:19] 1.0 # 相关操作使用F.conv2d但设置padding使kernel中心对齐 kernel_corr gaussian_kernel_2d(3, 0.8) # shape: [3,3] # PyTorch的conv2d默认做卷积要模拟相关需手动翻转kernel kernel_for_corr torch.flip(kernel_corr, [0,1]) # 翻转后等效于相关 # 执行相关注意input shape [N,C,H,W], weight shape [F,C,K,K] output_corr F.conv2d(img, kernel_for_corr.unsqueeze(0).unsqueeze(0), padding1) # padding1保证输出尺寸不变 print(f相关操作输出尺寸: {output_corr.shape}) # torch.Size([1, 1, 32, 32])参数说明padding1确保3×3核滑动时覆盖边界unsqueeze(0).unsqueeze(0)将[3,3]核扩展为[1,1,3,3]以匹配conv2d要求的[F,C,K,K]格式torch.flip是关键——它让PyTorch的卷积函数执行了数学上的相关操作。若省略此步结果将是卷积而非相关。2.2 卷积Convolution模板180°翻转后的标准运算卷积的数学定义为$$ (I * K)(i,j) \sum_{m}\sum_{n} I(im, jn) \cdot K(-m,-n) $$即先将kernel绕中心旋转180°再与图像做相关。这是信号处理的标准定义也是PyTorch/TensorFlow卷积层的默认行为。# 卷积操作直接使用原始kernel无需flip kernel_conv gaussian_kernel_2d(3, 0.8) output_conv F.conv2d(img, kernel_conv.unsqueeze(0).unsqueeze(0), padding1) # 验证相关结果应等于卷积结果的翻转 # 因kernel本身对称此处数值相同但逻辑不同 print(f卷积输出最大值: {output_conv.max().item():.4f}) print(f相关输出最大值: {output_corr.max().item():.4f})关键区别当kernel非对称时如Sobel算子output_corr与output_conv将完全不同。例如Sobel X方向核[[-1,0,1],[-2,0,2],[-1,0,1]]做相关时检测右边缘做卷积时检测左边缘。工程中务必确认你使用的库的默认行为——OpenCVfilter2D是相关PyTorchConv2d是卷积。2.3 内积Inner Product向量化降维的矩阵视角内积将图像局部块与kernel均拉平为向量计算点积。这揭示了卷积的本质它是局部区域的线性变换。对单通道图像H×W区域与K×K核的内积等价于K²维向量与K²维权重向量的点积。# 手动实现内积提取图像所有3x3块并展平 def extract_patches(img, kernel_size3, stride1): 提取所有滑动窗口块返回[N, C*K*K] patches F.unfold(img, kernel_size, stridestride) # unfold输出shape: [N, C*K*K, L]L为块数量 return patches.transpose(1,2) # 转置为[N, L, C*K*K] # 提取32x32图像的所有3x3块共30x30900个 patches extract_patches(img, kernel_size3) # shape: [1, 900, 9] kernel_vec kernel_conv.flatten() # shape: [9] # 计算所有块的内积广播机制 inner_products torch.matmul(patches, kernel_vec.unsqueeze(1)) # [1, 900, 1] # 重塑为图像尺寸30x30 output_inner inner_products.view(1, 1, 30, 30) print(f内积输出尺寸: {output_inner.shape}) # torch.Size([1, 1, 30, 30])工程价值F.unfoldmatmul是实现自定义卷积如动态滤波的核心技巧。当需要每个位置用不同kernel时如条件GAN此方法比循环调用conv2d快10倍以上。unfold的stride参数直接控制滑动步长padding需在img上预先处理。2.4 三种定义的统一验证数值一致性检查为确保三种实现逻辑一致我们用同一组数据验证# 构造非对称kernelSobel X sobel_x torch.tensor([[-1,0,1],[-2,0,2],[-1,0,1]], dtypetorch.float32) # 方法1相关OpenCV风格 sobel_x_corr torch.flip(sobel_x, [0,1]) out1 F.conv2d(img, sobel_x_corr.unsqueeze(0).unsqueeze(0), padding1) # 方法2卷积PyTorch风格 out2 F.conv2d(img, sobel_x.unsqueeze(0).unsqueeze(0), padding1) # 方法3内积手动展开 patches_sobel extract_patches(img, 3) sobel_vec sobel_x.flatten() out3 torch.matmul(patches_sobel, sobel_vec.unsqueeze(1)).view(1,1,30,30) # 验证out1相关应等于out2卷积的翻转 # 因sobel_x不对称out1[:, :, 1:-1, 1:-1] ≈ torch.flip(out2, [2,3])[:, :, 1:-1, 1:-1] print(f相关 vs 卷积差异L1: {(out1 - torch.flip(out2, [2,3])).abs().mean().item():.6f}) print(f内积 vs 卷积差异L1: {(out3 - out2[:, :, :-1, :-1]).abs().mean().item():.6f})血泪经验在调试自定义滤波层时我曾因混淆相关/卷积导致边缘检测方向全反。后来养成习惯任何新kernel上手前先用torch.flip(kernel, [0,1])做一次对比测试。若结果一致说明你的框架默认相关若相反则默认卷积。这个10秒检查能省去你3小时debug时间。3. 高斯核参数设置与Canny边缘检测从3σ原则到OpenCV源码级调参高斯滤波看似简单但cv2.GaussianBlur(img, (5,5), 1.0)中的(5,5)和1.0如何协同工作为什么课程强调“kernel size6σ”Canny的双阈值为何要设为low0.4*high本节将用可复现的代码带你穿透OpenCV封装直击参数设计的物理本质。3.1 高斯核size与σ的黄金比例为什么是6σ高斯函数g(x) exp(-x²/(2σ²))在|x|3σ时值小于0.001积分贡献可忽略。因此kernel宽度应覆盖[-3σ, 3σ]即size 2*floor(3σ)1。若σ1.0则size7-3到3共7个整数点若强行用size5则截断了±2.5σ外的权重导致平滑不足。import cv2 import matplotlib.pyplot as plt def plot_gaussian_truncation(sigma1.0, sizes[3,5,7,9]): x np.linspace(-5, 5, 1000) g_true np.exp(-x**2/(2*sigma**2)) plt.figure(figsize(10,6)) plt.plot(x, g_true, k--, labelTrue Gaussian (σ%.1f)%sigma, linewidth2) for size in sizes: # 手动构造kernel采样区间[-3σ,3σ]但强制size为指定值 if size 3: kx np.array([-1,0,1]) * sigma # 粗略采样 else: kx np.linspace(-3*sigma, 3*sigma, size) kernel np.exp(-kx**2/(2*sigma**2)) kernel kernel / kernel.sum() # 归一化 # 插值到连续曲线对比 f interp1d(kx, kernel, kindcubic, fill_value0, bounds_errorFalse) y_interp f(x) plt.plot(x, y_interp, labelfKernel size{size}) plt.xlabel(Distance from center) plt.ylabel(Weight) plt.legend() plt.title(Gaussian Kernel Truncation Effect) plt.grid(True) plt.show() # 执行可视化 plot_gaussian_truncation(sigma1.0)现象size3的kernel在|x|1处权重已衰减为0完全丢失3σ信息size5在|x|2处截断size7才较好逼近真实高斯。这就是为什么OpenCV的getGaussianKernel内部强制size≥3且为奇数并按size2*int(3*sigma)1计算。3.2 OpenCV高斯模糊的隐式规则size与σ的互锁机制OpenCV的cv2.GaussianBlur中若只指定ksize如(5,5)则sigma由sigma 0.3*((ksize-1)*0.5 - 1) 0.8自动计算若同时指定sigma则ksize会被重算为ksize 2*int(3*sigma)1向上取奇数。这意味着你无法真正“固定σ而任意设size”。# 验证OpenCV的自动计算规则 def opencv_sigma_rule(ksize): return 0.3 * ((ksize-1)*0.5 - 1) 0.8 print(fksize3 → σ{opencv_sigma_rule(3):.3f}) # 0.8 print(fksize5 → σ{opencv_sigma_rule(5):.3f}) # 1.1 print(fksize7 → σ{opencv_sigma_rule(7):.3f}) # 1.4 # 手动构造kernel验证 ksize 5 sigma_manual 1.0 kernel_cv2 cv2.getGaussianKernel(ksize, sigma_manual) print(fcv2.getGaussianKernel(5,1.0) shape: {kernel_cv2.shape}) print(fSum of kernel: {kernel_cv2.sum():.6f}) # 应≈1.0 # 对比手动计算 x np.arange(-(ksize//2), ksize//2 1) kernel_manual np.exp(-x**2/(2*sigma_manual**2)) kernel_manual kernel_manual / kernel_manual.sum() print(fManual kernel sum: {kernel_manual.sum():.6f})避坑指南现象1cv2.GaussianBlur(img, (5,5), 0.5)与cv2.GaussianBlur(img, (5,5), 2.0)输出几乎相同。原因当sigma过小0.8OpenCV会强制设为0.8过大时ksize被重算为更大值但你传入的(5,5)被忽略。解决若需精确σ必须用cv2.getGaussianKernel生成1D核再cv2.sepFilter2D分离卷积kx cv2.getGaussianKernel(7, 1.0) # 显式控制size和sigma ky cv2.getGaussianKernel(7, 1.0) blurred cv2.sepFilter2D(img, -1, kx, ky)3.3 Canny双阈值的物理意义与自适应设置Canny的双阈值threshold1,threshold2并非随意设定。threshold2高阈值用于标记强边缘threshold1低阈值用于连接弱边缘。OpenCV文档建议threshold1:threshold2 ≈ 1:2~1:3但最佳值取决于图像信噪比。def canny_adaptive_thresholds(img_gray, sigma1.0, low_ratio0.4): 基于图像梯度统计自适应设置Canny阈值 # 先高斯模糊降噪 blurred cv2.GaussianBlur(img_gray, (0,0), sigma) # 计算梯度幅值用Sobel近似 grad_x cv2.Sobel(blurred, cv2.CV_64F, 1, 0, ksize3) grad_y cv2.Sobel(blurred, cv2.CV_64F, 0, 1, ksize3) mag np.sqrt(grad_x**2 grad_y**2) # 统计梯度幅值分布 hist, bins np.histogram(mag.ravel(), bins100, range(0, mag.max())) # 取累积分布90%处的值作为高阈值排除噪声峰值 cumsum np.cumsum(hist) high_thresh bins[np.argmax(cumsum 0.9*cumsum[-1])] low_thresh high_thresh * low_ratio return int(low_thresh), int(high_thresh) # 测试 img_rgb cv2.imread(test.jpg) img_gray cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY) low, high canny_adaptive_thresholds(img_gray, sigma1.2) edges cv2.Canny(img_gray, low, high) print(fAdaptive thresholds: low{low}, high{high})避坑指南现象2cv2.Canny(img, 50, 150)在暗部出现大量伪边缘。原因固定阈值无法适应局部对比度变化。暗部梯度幅值整体偏低50的阈值过高。解决改用cv2.createTrackbar交互式调整或如上代码基于局部梯度统计自适应。现象3Canny输出边缘断裂尤其在纹理丰富区域。原因双阈值gap过大如low10, high200导致弱边缘无法连接。解决严格遵守low ≈ 0.4*high并在high附近微调±10。3.4 非极大值抑制NMS的手动实现理解Canny的“细边缘”本质Canny的“细边缘”源于NMS——它不是简单阈值化而是沿梯度方向比较邻域值。OpenCV的NMS是内置的但手动实现能帮你debug。def non_max_suppression(mag, angle): 手动NMSmag为梯度幅值图angle为梯度方向弧度 M, N mag.shape z np.zeros((M,N), dtypenp.int32) # 将角度量化为0°,45°,90°,135°四个方向 angle angle * 180. / np.pi angle[angle 0] 180 angle[angle 180] - 180 for i in range(1,M-1): for j in range(1,N-1): try: # 0°方向水平 if (0 angle[i,j] 22.5) or (157.5 angle[i,j] 180): neighbors [mag[i, j1], mag[i, j-1]] # 45°方向对角线 elif (22.5 angle[i,j] 67.5): neighbors [mag[i1, j1], mag[i-1, j-1]] # 90°方向垂直 elif (67.5 angle[i,j] 112.5): neighbors [mag[i1, j], mag[i-1, j]] # 135°方向对角线 else: neighbors [mag[i1, j-1], mag[i-1, j1]] # 若当前点大于邻域则保留否则抑制 if mag[i,j] max(neighbors): z[i,j] mag[i,j] else: z[i,j] 0 except IndexError: pass return z # 验证与cv2.Canny对比 grad_x cv2.Sobel(img_gray, cv2.CV_64F, 1, 0, ksize3) grad_y cv2.Sobel(img_gray, cv2.CV_64F, 0, 1, ksize3) mag np.sqrt(grad_x**2 grad_y**2) angle np.arctan2(grad_y, grad_x) nms_manual non_max_suppression(mag, angle) edges_cv2 cv2.Canny(img_gray, 50, 150) # 比较差异应高度一致 diff np.abs(nms_manual.astype(np.float32) - edges_cv2.astype(np.float32)) print(fManual vs OpenCV NMS difference: {diff.sum()}) # 应接近0玄学提示OpenCV的NMS使用双线性插值而非四舍五入量化所以手动实现会有微小差异。但只要你的NMS能产生连续单像素宽边缘就证明逻辑正确。这是判断Canny是否正常工作的黄金标准。4. SIFT关键点检测与描述子生成从“双塔”结构到OpenCV参数映射SIFT的“双塔”结构常被误解为两个独立金字塔实则是高斯金字塔Gaussian Pyramid与高斯差分金字塔DoG Pyramid的共生体。本节将用代码揭示cv2.SIFT_create()的每个参数如何对应到DoG极值检测的数学过程并教你如何从输出中还原尺度、方向、描述子的物理含义。4.1 “双塔”的本质DoG金字塔如何替代LoG计算SIFT用DoGDifference of Gaussians近似LoGLaplacian of Gaussian因为G(x,y,kσ)-G(x,y,σ)的计算成本远低于二阶导数。其数学基础是当k≈1.6时DoG是LoG的良好近似。def build_dog_pyramid(img, octaves4, intervals3, sigma01.6): 手动构建SIFT DoG金字塔 octaves: 金字塔组数下采样次数 intervals: 每组内层数含base layer sigma0: 初始尺度第一组第一层的σ dog_pyr [] img_float img.astype(np.float32) for octave in range(octaves): # 当前组的图像第一组为原图后续组为上一组下采样2倍 if octave 0: img_oct img_float else: # 下采样先高斯模糊再隔点采样防止混叠 blur_img cv2.GaussianBlur(img_float, (0,0), sigma1.0) img_oct blur_img[::2, ::2] # 降采样2倍 # 构建该组的高斯金字塔intervals3层因DoG需相邻层相减 g_pyr_oct [] k 2**(1/float(intervals)) # 每层σ增长因子 sigma sigma0 * (2**octave) # 当前组基础σ for interval in range(intervals 3): # 计算该层σsigma_i sigma0 * k^i sigma_i sigma * (k ** interval) # 生成高斯核并模糊 size int(2 * np.ceil(3 * sigma_i) 1) | 1 # 确保奇数 kernel cv2.getGaussianKernel(size, sigma_i) blurred cv2.sepFilter2D(img_oct, -1, kernel, kernel.T) g_pyr_oct.append(blurred) # 从高斯金字塔生成DoG金字塔相邻层相减 dog_oct [] for i in range(len(g_pyr_oct)-1): dog_oct.append(g_pyr_oct[i1] - g_pyr_oct[i]) dog_pyr.append(dog_oct) return dog_pyr # 构建DoG金字塔并可视化第一组 img_gray cv2.imread(test.jpg, 0) dog_pyr build_dog_pyramid(img_gray, octaves4, intervals3, sigma01.6) # 显示第一组DoG层共4层 plt.figure(figsize(12,3)) for i in range(4): plt.subplot(1,4,i1) plt.imshow(dog_pyr[0][i], cmapgray) plt.title(fDoG Layer {i1}) plt.axis(off) plt.show()参数映射cv2.SIFT_create(noctaves4, nlevels3)中noctaves对应代码中的octavesnlevels对应intervals。sigma01.6是SIFT论文指定值不可更改——它保证了DoG对LoG的近似精度。若你设sigma00.5DoG将无法有效检测斑点。4.2 关键点定位从DoG极值到亚像素精确定位SIFT关键点是DoG金字塔中的局部极值点8邻域上下层共26个点比较。但粗定位整数坐标不够需亚像素插值。def localize_keypoint(dog, x, y, octave_idx, interval_idx): 对DoG中(x,y)处的极值点进行亚像素精确定位 使用泰勒展开D(X) D D.X 0.5*X.D.X 求导得最优偏移X_hat -D^{-1} * D # 获取3x3x3邻域当前层上下层 dxx cv2.Sobel(dog[interval_idx], cv2.CV_64F, 2, 0, ksize3) dyy cv2.Sobel(dog[interval_idx], cv2.CV_64F, 0, 2, ksize3) dxy cv2.Sobel(cv2.Sobel(dog[interval_idx], cv2.CV_64F, 1, 0, ksize3), cv2.CV_64F, 0, 1, ksize3) dx cv2.Sobel(dog[interval_idx], cv2.CV_64F, 1, 0, ksize3) dy cv2.Sobel(dog[interval_idx], cv2.CV_64F, 0, 1, ksize3) # 在(x,y)处采样二阶导数简化版实际需插值 Dxx dxx[y,x] if 0ydxx.shape[0] and 0xdxx.shape[1] else 0 Dyy dyy[y,x] Dxy dxy[y,x] Dx dx[y,x] Dy dy[y,x] # 构造Hessian矩阵和梯度向量 H np.array([[Dxx, Dxy], [Dxy, Dyy]]) dD np.array([Dx, Dy]) # 求解偏移量若H可逆 try: offset -np.linalg.inv(H) dD x_refined x offset[0] y_refined y offset[1] # 计算精确定位后的响应值 D_refined dog[interval_idx][y,x] 0.5 * dD offset return x_refined, y_refined, D_refined except: return x, y, dog[interval_idx][y,x] # 示例对第一个DoG层的极值点精确定位 x0, y0 100, 100 # 假设此处是极值 x_r, y_r, d_r localize_keypoint(dog_pyr[0], x0, y0, 0, 0) print(fRefined keypoint: ({x0},{y0}) → ({x_r:.2f},{y_r:.2f}), response{d_r:.3f})避坑指南现象1sift.detect()返回的关键点数量远少于预期。原因默认contrastThreshold0.04响应阈值和edgeThreshold10边缘响应比阈值过滤了弱响应和低对比度点。解决降低contrastThreshold如0.01并提高edgeThreshold如15sift cv2.SIFT_create(contrastThreshold0.01, edgeThreshold15)现象2关键点坐标在不同尺度下不一致。原因sift.detect()返回的坐标是当前octave内的坐标需还原到原图x_orig x_oct * (2**octave)。解决遍历keypoints时读取kp.octave属性for kp in keypoints: x_orig kp.pt[0] * (2 ** (kp.octave 0xFF)) y_orig kp.pt[1] * (2 ** (kp.octave 0xFF))4.3 方向分配1.5σ半径内的梯度投票实战SIFT为每个关键点分配主方向使用1.5σ半径内像素的梯度方向加权投票。σ是该关键点所在层的尺度。def assign_orientation(keypoint, img_oct, sigma): 为关键点分配主方向 keypoint: cv2.KeyPoint对象 img_oct: 当前octave的图像已高斯模糊 sigma: 关键点所在层的尺度 p a hrefhttps://download.csdn.net/download/m0_67804957/89491642 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑