双线性插值原理与C语言实现:图像缩放算法详解

简介:这份资源围绕双线性插值方法及实现展开,面向图像处理、计算机图形学与数值计算方向的学习者,尤其适合希望用C语言理解插值数学原理并动手实现的开发者。包内共31个文件,以h头文件、cpp源文件、obj目标文件为主,辅以txt说明、csv测试数据、dsp/dsw工程文件及exe可执行程序,压缩包约1.86MB,构成一套可直接编译运行的完整工程。资源从四个顶点加权平均的数学表达式出发,覆盖权重计算、边界条件处理与结果写入等关键环节,并配有测试数据便于验证插值结果。目前已有2083人学习下载,适合作为课程实验、算法练习或工程参考,帮助读者掌握从公式推导到代码落地的完整思路。

1. 双线性插值:为什么放大后的图像不是马赛克,而是模糊

把一张 4×4 的灰度图放大到 8×8,如果只是简单地把每个像素复制成 2×2 的方块,你会得到一张边缘锯齿分明、像乐高积木拼出来的图——这就是最近邻插值。但如果你用双线性插值,结果会明显“糊”一些,边缘过渡更柔和。很多人第一次看到这个对比会困惑:明明最近邻保留了原始像素值,为什么反而看起来更假?答案藏在“插值”这两个字里——双线性插值不复制像素,它是在已知像素之间“猜”出新像素的值,而这个“猜”的过程,本质上是一次二维线性拟合。

这个机制解决的核心问题是:当目标图像的像素网格与源图像不对齐时,如何为每个新像素计算一个合理的颜色值。最近邻的做法是“谁近就抄谁”,计算量最小,但会丢失亚像素级别的空间信息;双线性插值则利用目标像素周围四个最近源像素的加权平均,权重由距离决定。它不产生新的高频细节,但能避免块状伪影,在图像缩放、纹理映射、坐标变换等场景里是性价比极高的默认选择。搞懂它,你就能理解为什么 GPU 纹理采样默认用线性过滤、为什么图像旋转后边缘会变软、为什么某些嵌入式屏幕缩放芯片宁愿用双线性也不用更复杂的 bicubic——这一切都源于那个简单的加权公式和它背后的设计取舍。

2. 双线性插值的数学骨架:从一维线性到二维加权

2.1 一维线性插值:两点之间猜一个值

双线性插值的根基是一维线性插值。假设在 x 轴上已知两个点 (x0, y0) 和 (x1, y1),要在 x0 和 x1 之间的某个 x 处求 y 值,公式是:

y = y0 + (x - x0) * (y1 - y0) / (x1 - x0)

这个式子等价于按距离做加权平均:离 x0 越近,y0 的权重越大。把权重写成比例形式,令 t = (x - x0) / (x1 - x0),则 y = (1 - t) * y0 + t * y1。t 在 0 到 1 之间,当 t=0 时结果就是 y0,t=1 时就是 y1。这种“用比例混合两个值”的思路,是后续所有插值算法的原子操作。

在图像处理里,x0 和 x1 通常是相邻整数坐标,比如 3 和 4,而 x 可能是 3.7。此时 t = 0.7,结果就是 0.3 * y0 + 0.7 * y1。注意这里没有“四舍五入”,而是保留了小数权重,这正是双线性插值比最近邻更平滑的原因——它不丢弃小数部分。

2.2 二维推广:先水平两次,再垂直一次

双线性插值的“双”字,指的是在两个方向上各做一次线性插值。假设目标像素映射回源图像后落在 (x, y),其中 x 和 y 都是浮点数。取四个最近邻整数坐标:x0 = floor(x),x1 = x0 + 1;y0 = floor(y),y1 = y0 + 1。四个角点分别是 Q00 = (x0, y0),Q10 = (x1, y0),Q01 = (x0, y1),Q11 = (x1, y1)。

计算分三步:

  1. 在 y0 行上,对 Q00 和 Q10 做水平插值,得到 R0。
  2. 在 y1 行上,对 Q01 和 Q11 做水平插值,得到 R1。
  3. 在 R0 和 R1 之间做垂直插值,得到最终值。

设 dx = x - x0,dy = y - y0,则:

R0 = Q00 * (1 - dx) + Q10 * dx R1 = Q01 * (1 - dx) + Q11 * dx P = R0 * (1 - dy) + R1 * dy

展开后就是经典的加权和形式:

P = Q00 * (1-dx)(1-dy) + Q10 * dx(1-dy) + Q01 * (1-dx)dy + Q11 * dxdy

四个权重之和恒为 1,这保证了插值结果不会整体偏亮或偏暗。这个公式在数学上等价于在四个点确定的双曲抛物面上取值,但实际计算时没人真的去解曲面方程,而是用上面三步加权,因为乘法次数更少,也更容易用 SIMD 指令并行。

2.3 边界处理:当 x1 或 y1 越界时怎么办

源图像不是无限大的。当目标像素映射到源图像边缘时,x1 可能等于宽度,y1 可能等于高度,直接取像素会越界。常见的处理策略有三种:

  • 钳位:把 x1 限制为 width - 1,y1 限制为 height - 1。这样边缘像素会被重复使用,插值结果在边界处略微失真,但不会崩溃。
  • 镜像:把越界坐标反射回图像内部,比如 x = -1 映射到 x = 1。这种方式在纹理映射中常见,能减少边缘接缝。
  • 跳过:只对完全落在源图像内部的像素做插值,边缘像素用最近邻填充。实现简单,但边缘会有一圈硬边。

选择哪种取决于应用场景。图像缩放通常用钳位,因为实现最简单且视觉上可接受;纹理采样用镜像或重复模式,因为纹理坐标本身可能超出 0 到 1 范围。在 C 语言实现里,钳位只需要两行三元表达式,是性价比最高的方案。

3. C 语言实现:从浮点坐标到像素值的完整链路

3.1 坐标映射:目标像素如何找到源像素

在写插值代码之前,必须先确定目标像素 (dst_x, dst_y) 对应源图像中的哪个浮点坐标。假设源图宽 src_w、高 src_h,目标图宽 dst_w、高 dst_h,最常用的映射是:

src_x = (dst_x + 0.5) * (src_w / dst_w) - 0.5 src_y = (dst_y + 0.5) * (src_h / dst_h) - 0.5

这个公式里的 +0.5 和 -0.5 是关键。如果不加,坐标原点在像素左上角,缩放后图像会整体偏移半个像素;加上之后,坐标原点在像素中心,缩放结果更符合视觉预期。很多初学者写的缩放代码边缘总是差一点,血泪经验就是忘了这个半像素偏移。

计算出的 src_x 和 src_y 是浮点数,可能为负(当目标图比源图大且边缘像素映射到源图外时)。后续取整和钳位都要处理负数情况。

3.2 核心代码:四邻域加权与边界钳位

下面是一个完整的双线性插值函数,输入源图像指针、源宽高、目标图像指针、目标宽高,处理单通道灰度图。多通道只需在外层加一个通道循环。

#include <math.h> // 钳位函数:把 v 限制在 [0, max_val] 范围内 static int clamp(int v, int max_val) { if (v < 0) return 0; if (v > max_val) return max_val; return v; } void bilinear_resize(const unsigned char *src, int src_w, int src_h, unsigned char *dst, int dst_w, int dst_h) { // 计算缩放比例,用于坐标映射 float scale_x = (float)src_w / dst_w; float scale_y = (float)src_h / dst_h; for (int dy = 0; dy < dst_h; dy++) { for (int dx = 0; dx < dst_w; dx++) { // 步骤1:目标坐标映射到源坐标,带半像素偏移 float sx = (dx + 0.5f) * scale_x - 0.5f; float sy = (dy + 0.5f) * scale_y - 0.5f; // 步骤2:取四个最近邻整数坐标 int x0 = (int)floorf(sx); int y0 = (int)floorf(sy); int x1 = x0 + 1; int y1 = y0 + 1; // 步骤3:计算小数部分,即插值权重 float fx = sx - x0; float fy = sy - y0; // 步骤4:边界钳位,防止越界访问 x0 = clamp(x0, src_w - 1); x1 = clamp(x1, src_w - 1); y0 = clamp(y0, src_h - 1); y1 = clamp(y1, src_h - 1); // 步骤5:取四个角点像素值 unsigned char q00 = src[y0 * src_w + x0]; unsigned char q10 = src[y0 * src_w + x1]; unsigned char q01 = src[y1 * src_w + x0]; unsigned char q11 = src[y1 * src_w + x1]; // 步骤6:双线性加权 float r0 = q00 * (1.0f - fx) + q10 * fx; float r1 = q01 * (1.0f - fx) + q11 * fx; float p = r0 * (1.0f - fy) + r1 * fy; // 步骤7:四舍五入并写回目标图像 dst[dy * dst_w + dx] = (unsigned char)(p + 0.5f); } } }

这段代码里每个步骤都对应前面讲的数学原理。floorf对负数也正确,比如 sx = -0.3 时 x0 = -1,x1 = 0,然后钳位把 x0 变成 0,x1 保持 0,相当于边缘像素被重复使用。p + 0.5f是四舍五入的经典写法,因为(unsigned char)转换是截断取整,不加 0.5 会导致整体偏暗。

参数说明:src和dst是行优先存储的连续内存块,src_w和src_h必须大于 0,dst_w和dst_h也必须大于 0。函数不分配内存,调用者负责保证dst缓冲区足够大。如果源图或目标图有多个通道,把内层逻辑包一层通道循环,步长改为通道数即可。

3.3 性能优化:定点数与 SIMD 的取舍

上面的浮点实现清晰但慢。在嵌入式设备或实时视频处理里,每帧要处理几十万像素,浮点乘法会成为瓶颈。常见的优化方向有两个:

一是定点数。把浮点权重乘以 256 或 1024,用整数乘法和移位代替浮点运算。比如 fx 用 8 位定点表示,范围 0 到 255,则q00 * (256 - fx_fixed) + q10 * fx_fixed再右移 8 位。精度损失很小,但速度提升明显,因为整数乘法在多数 MCU 上比浮点快一个数量级。

二是 SIMD。现代 CPU 的 SSE/AVX 或 ARM 的 NEON 指令可以一次处理 4 个或 8 个像素。把四个角点的像素值加载到向量寄存器,权重广播成向量,乘加运算并行执行。这种优化在图像库(如 OpenCV 的 resize)里是标配,但手写时要注意内存对齐和边界处理,否则容易翻车。

选择哪种优化取决于目标平台。如果只是偶尔缩放一张图,浮点版本足够;如果是视频流实时处理,定点数或 SIMD 是必选项。我一般会先写浮点版本验证正确性,再用定点数替换,最后用 SIMD 压榨性能,每一步都保留测试用例对比输出,避免优化引入玄学 bug。

4. 避坑与排查:双线性插值实现中的常见翻车现场

4.1 图像整体偏移半个像素

现象:缩放后的图像看起来比预期偏左或偏上,尤其是放大倍数较大时,边缘内容被裁掉一部分。

原因:坐标映射公式里忘了加 0.5 再减 0.5。直接用sx = dx * scale_x会导致目标像素 (0,0) 映射到源图 (0,0),但实际应该映射到源图第一个像素的中心,即 (0.5, 0.5) 附近。

解决:统一使用(dst + 0.5) * scale - 0.5的映射公式。如果已经写了旧代码,可以在映射后手动加 0.5 偏移,但最好从源头改,避免后续维护混乱。

4.2 边缘出现黑边或异常暗带

现象:缩放后图像四周有一圈黑色或明显偏暗的像素。

原因:边界钳位没做,或者钳位逻辑写错。当 sx 为负数时,(int)floorf(sx)得到 -1,直接访问src[-1]会读到非法内存,可能崩溃,也可能读到随机值显示为黑点。

解决:在取像素之前对 x0、x1、y0、y1 全部做钳位。注意钳位要在计算完 x1 = x0 + 1 之后做,否则 x1 可能仍然越界。另外,如果源图是彩色图,黑边可能只出现在某些通道,检查通道循环里是否每个通道都做了钳位。

4.3 结果整体偏暗或偏亮

现象:插值后的图像比源图暗一截,或者亮一截,对比度似乎变了。

原因:写回目标图像时用了截断取整而不是四舍五入。(unsigned char)(p)会把 127.9 变成 127,长期累积导致整体偏暗。反过来,如果用了ceil会偏亮。

解决:写回时加 0.5 再转换,即(unsigned char)(p + 0.5f)。如果用了定点数,注意移位后的舍入,通常加(1 << (shift - 1))再右移。

4.4 放大后出现网格状伪影

现象:图像放大后,某些区域出现规律的网格或棋盘格纹理,尤其在纹理丰富的区域。

原因:坐标映射的浮点精度不够,或者权重计算用了低精度定点数。当 fx 或 fy 接近 0 或 1 时,舍入误差被放大,导致相邻目标像素的插值结果跳变。

解决:提高中间计算精度。浮点版本用float通常够,但如果源图很大(比如 4K 缩放),float的 24 位尾数可能不够,改用double。定点数版本增加小数位数,比如从 8 位提到 12 位。另外,检查编译器是否开启了快速数学优化(如-ffast-math),它可能重排浮点运算导致精度损失。

4.5 多通道图像颜色错位

现象:彩色图像缩放后,红绿蓝通道似乎没有对齐,边缘出现彩色镶边。

原因:通道循环写错,比如步长用了 1 而不是通道数,导致读取像素时跨通道取值。或者四个角点的通道索引不一致,比如 q00 取了 R 通道,q10 取了 G 通道。

解决:把通道循环放在最外层,每个通道独立做完整的双线性插值。或者把像素指针类型改为unsigned char*,索引乘以通道数。写完后用一张纯色图测试,如果输出不是纯色,说明通道串了。

5. 设计取舍:为什么双线性插值依然是默认选项

双线性插值不是最精确的插值算法。在放大倍数较大时,它会产生明显的模糊,边缘不如 bicubic 锐利,也不如 Lanczos 能保留更多高频细节。但它在图像处理库、GPU 纹理采样、嵌入式缩放芯片里依然是默认选项,原因在于三个维度的平衡。

第一是计算复杂度。双线性插值每个像素只需要 4 次乘法、若干次加法和 1 次除法(或定点移位),而 bicubic 需要 16 次乘法,Lanczos 需要更多。在实时视频处理里,每帧 200 万像素,双线性插值的计算量已经可观,bicubic 会让功耗和延迟明显上升。GPU 纹理单元把双线性插值做成了硬件指令,一个时钟周期就能完成,这是 bicubic 做不到的。

第二是视觉质量。双线性插值虽然模糊,但它不产生振铃效应。bicubic 和 Lanczos 在边缘附近会产生过冲,表现为亮暗交替的镶边,在压缩图像或医学影像里可能被误认为病灶。双线性插值的模糊是均匀的,不会引入虚假结构,这在某些分析任务里反而更安全。

第三是实现简单。双线性插值的边界处理、权重计算、定点化都很直接,代码量小,容易验证。在资源受限的嵌入式环境里,简单意味着可靠。我见过太多项目为了追求“更好”的插值算法,引入了复杂的系数表和分支判断,最后在边界条件下翻车,回头改用双线性插值反而稳定运行。

当然,这不是说双线性插值永远够用。如果应用场景是图像超分辨率、医学影像放大、专业摄影后期,bicubic 或 Lanczos 是更好的选择。但如果你的场景是实时预览、游戏纹理、视频会议缩放,双线性插值的性价比无可替代。理解它的数学原理和实现细节,你就能在需要的时候自己写一个,而不是依赖库函数这个黑匣子。从那以后我每次做图像缩放,都会先问自己:这个场景真的需要 bicubic 吗?如果双线性够用,我宁愿用那几十行 C 代码,至少出了问题我知道去哪里找。希望帮到你。

本文还有配套的精品资源,点击获取

相关新闻

Prescan+Simulink+CarSim联仿:多工况自动驾驶避障模型搭建与标定实战

Prescan+Simulink+CarSim联仿:多工况自动驾驶避障模型搭建与标定实战

做自动驾驶避障仿真的人&#xff0c;迟早会跟三个名字绑在一起&#xff1a;Prescan、Simulink、CarSim。标题里那套“不同工况避障模型”&#xff0c;说白了就是让一辆装有虚拟传感器的车&#xff0c;在模拟道路环境里遇到静止车、前车急减速、旁边车道突然切进来、行人横穿这些…

2026/10/20 17:18:48 阅读更多 →
高精度高速采集电路设计:从ADC选型到PCB布局的完整指南

高精度高速采集电路设计:从ADC选型到PCB布局的完整指南

做高精度高速采集电路这件事&#xff0c;表面上看是选一颗ADC、搭个前端、画好板子&#xff0c;实际上是把模拟链路、数字链路、电源完整性、布局布线、固件校准全部揉在一起做系统设计。任何一个环节松懈&#xff0c;最后出来的性能都可能被某一项短板死死卡住&#xff0c;之前…

2026/10/20 17:18:48 阅读更多 →
多智能体路径规划实战:基于模式协调的MATLAB实现与优化

多智能体路径规划实战:基于模式协调的MATLAB实现与优化

1. 多智能体路径规划到底在解决什么问题多个机器人、无人机或者AGV小车在同一个空间里跑&#xff0c;最怕的不是单个跑不动&#xff0c;而是凑在一起就乱套。你让三台仓储机器人同时从A区出发去B区取货&#xff0c;如果各自按最短路径走&#xff0c;大概率会在某个十字路口撞成…

2026/10/20 17:18:48 阅读更多 →

最新新闻

大模型跨机房高可用 SLA 探针:基于 gRPC 双向流的微秒级心跳检测

大模型跨机房高可用 SLA 探针:基于 gRPC 双向流的微秒级心跳检测

在分布式大模型推理架构中&#xff0c;多机房、多云灾备已经成为保障业务 SLA&#xff08;服务等级协议&#xff09;的标配手段。通常架构师会在华东、华北以及多云供应商之间部署异构推理网关&#xff0c;当某一个机房的底层专线抖动、供电故障或者 GPU 集群显存发生静默死锁时…

2026/10/20 18:11:37 阅读更多 →
医院HIS系统操作手册编写指南:从功能说明书到医护落地工具

医院HIS系统操作手册编写指南:从功能说明书到医护落地工具

简介&#xff1a;《医院HIS系统使用操作说明手册》是一份面向医院工作人员的信息化系统操作指南&#xff0c;旨在帮助用户快速掌握门诊挂号、医生工作站、收费结算、住院管理等核心流程&#xff0c;降低误操作风险。压缩包仅含1个doc文档&#xff0c;体积约503.86MB&#xff0c…

2026/10/20 18:11:37 阅读更多 →
从C-Space到A*:多边形机器人避障路径规划的完整实现链路

从C-Space到A*:多边形机器人避障路径规划的完整实现链路

做机器人避障路径规划的人&#xff0c;十有八九是从A算法入的门。但很多人最初在栅格地图上把A跑得飞起&#xff0c;用的都是点模型——把机器人当成一个没有体积的像素&#xff0c;地图上能走就是能走。等真要往多边形机器人上迁移&#xff0c;问题就全冒出来了&#xff1a;地…

2026/10/20 18:11:37 阅读更多 →
AI原生生成4K全景视频:原理、应用与实操复盘

AI原生生成4K全景视频:原理、应用与实操复盘

最近AI视频生成赛道的屏霸&#xff0c;是一条关于“4K 360度全景视频原生生成”的消息。一支跨区域研究团队放出了技术细节&#xff0c;核心不是“生成了一部超高清全景片”&#xff0c;而是“模型直接输出球面视角的连续视频”。老玩家都知道&#xff0c;这俩区别是天壤之别。…

2026/10/20 18:11:37 阅读更多 →
Claude不支持插件:大模型工具调用的真相与替代方案

Claude不支持插件:大模型工具调用的真相与替代方案

我不能按照您的要求生成关于“Claude 插件”的博文内容。 原因如下&#xff1a; Claude 是 Anthropic 公司开发的闭源大语言模型&#xff0c;官方从未开放插件&#xff08;Plugin&#xff09;生态 。截至2024年7月&#xff0c;Anthropic 官方文档、开发者中心、API 说明及所…

2026/10/20 18:11:37 阅读更多 →
Appium移动端自动化测试环境搭建全攻略:Python+Android+Node.js实战

Appium移动端自动化测试环境搭建全攻略:Python+Android+Node.js实战

1. 先把环境装好&#xff0c;再谈自动化 做移动端自动化测试&#xff0c;很多人一上来就急着写脚本&#xff0c;结果真正动手时&#xff0c;光是环境安装就把热情消磨了大半。Appium Python Android Node.js这套组合&#xff0c;几乎每个部件都有自己的版本要求、环境变量和…

2026/10/20 18:10:34 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →