PKM转PNG:Android纹理格式ETC1/ETC2解码原理与工具实现

模块名 PKM2PNG 看起来挺直白:把 .pkm 文件转换成 PNG。但真正拿到这类文件时,很多人会卡在“不知道这是什么格式、为什么普通看图工具打不开、里面到底存的是不是像素数据”这几个问题上。我第一次在资源目录里看到 .pkm 后缀的文件时,也是反复试了各种工具才搞明白,这玩意儿是 Android 平台最常见的压缩纹理格式之一,大量游戏 APK 和大型应用里的贴图都用它。后来我干脆把解码逻辑整理成了到现在还在用的一个小工具 PKM2PNG,专门负责把 GPU 压缩纹理还原成通用 PNG,方便直接查看、二次编辑和批量归档。这篇文章就把完整的格式拆解、解码实现、工具封装和踩过的坑都记录下来,适合正在做 Android 逆向、游戏资源提取、Unity 贴图优化,或者单纯被某个 .pkm 文件难住的人。

1. PKM 格式拆解:先搞清楚你拿到的是什么

1.1 纹理压缩和普通图片压缩不是一回事

网上最常见的误解就是一看到 .pkm 就把它当图片格式,想直接改名成 .png 再用看图软件打开,结果必然是失败的。这里得先理清一个概念:PNG、JPEG、WebP 这一类是“图片编码格式”,解码后得到的是完整的像素数组,CPU 可以线性解码;而 .pkm 里装的是 ETC1 或 ETC2 压缩纹理,数据被组织成一块块的“压缩块”,这些块必须在解码时还原成 4x4 的像素小块,才能拼出整张图。

为什么 Android 资源里这么喜欢用 ETC1?因为它是 OpenGL ES 2.0 时代 Android 平台的基准纹理格式,所有 Android 设备都强制支持它。对游戏来说,贴图资源往往占包体的大头,ETC1 能把 RGB 纹理压缩到每像素 4 比特,即 2:1 到 4:1 的压缩比,纹理采样的带宽压力也小得多。相比之下 iOS 那边常用 PVRTC,别的平台还可能有 ASTC。于是看一个跨平台手游的资源目录,经常能看到同一张贴图在 Android 目录里是 .pkm,在 iOS 目录里是 .pvr,两个文件长得完全不一样。

PKM 本身不是编码算法,它只是 ETC 压缩数据的一种打包容器。文件结构很简单:一个固定长度的文件头,后面紧跟压缩块数据。搞清楚这个文件头,解析工作就完成了一半。

1.2 16 字节文件头逐字节拆解

标准 PKM 文件头一共 16 字节,结构非常规整。前 6 个字节是 ASCII 魔数,内容是PKM 10,也就是十六进制的50 4B 4D 20 31 30。看到这个签名基本就能确认文件是 PKM 格式。接着是两个字节的版本号,一般是 2 或 3,分别对应 ETC1 和 ETC2 的常见打包版本。再往下就是四个大端序的 uint16 字段:原始宽度、原始高度、编码宽度、编码高度。

一个实际文件的头部十六进制可能是这样的:

50 4B 4D 20 31 30 02 00 00 01 00 80 00 01 00 80 PKM 10 ver originalW oriH encodedW encodedH

按大端解析:00 01是 256,00 80是 128,所以这个文件就是一张 256x128 的纹理,编码尺寸也是 256x128。这里要注意的是,原始宽高和编码宽高不一定相等。ETC 压缩按 4x4 像素块进行,如果一张图宽度是 130,就得把编码尺寸补齐到 132,解码后还要裁掉多出来的 2 像素黑边,这个细节我在后面的排查部分会专门说。

用 Python 解析文件头的代码非常短,我就是靠它把后续所有逻辑串起来的:

import struct def parse_pkm_header(data: bytes) -> dict: if data[0:6] != b"PKM 10": raise ValueError("不是标准 PKM 文件") version, orig_w, orig_h, enc_w, enc_h = struct.unpack(">HHHHH", data[6:16]) return { "version": version, "original_width": orig_w, "original_height": orig_h, "encoded_width": enc_w, "encoded_height": enc_h, }

1.3 压缩数据区的长度计算

文件头之后就是压缩块数据。每一块编码 4x4 像素,占用 8 字节,所以数据区的长度理论上等于:

(编码宽度 // 4) * (编码高度 // 4) * 8

如果解析出来的头部显示编码宽高都是 256x128,那数据区长度就是(256 // 4) * (128 // 4) * 8 = 16384字节。拿到文件后可以先校验一下实际文件大小是不是等于16 + 16384,如果不是,说明文件可能被拼接了 mipmap 链,或者带了其他附加数据,后面解码时的偏移量就得重新调整。这是很多现成脚本容易忽略的地方,我一开始也在这里翻过车。

2. 解码思路与工具选型

2.1 为什么必须自己处理解码逻辑

既然 PKM 是压缩纹理,那能不能直接调用系统 API 解压?能,但不太适合做批量和跨平台处理。OpenGL ES 支持 ETC1 纹理上传,但接口只面向 GPU,你得创建一个纹理对象,把数据喂进去,再通过渲染管线读回来,这种流程在一个命令行工具里非常别扭。而且 ETC1 解码是纯 CPU 就能做的操作,没必要引入图形上下文。

更直接的原因是:市面上现成的转换工具要么只支持单个文件,要么已经多年没人维护,遇到 ETC2 的 PKM 经常解码出来一片花屏。与其在图形 API 和旧工具之间反复横跳,不如自己实现一个轻量级转换器,遇到问题也能直接看代码排查。

2.2 方案选型对比

我在动手前对比了几条实现路径:

方案优点缺点
C/C++ 手写解码器性能最好,贴近底层编译和跨平台分发麻烦,调试周期长
Python 脚本迭代快,生态齐全逐块循环解码较慢,需要优化
在线转换工具零安装,上手快批量不方便,私有资源有泄露风险,格式版本支持不透明
Node/JavaScript 实现和前端工具链好集成图像输出阶段依赖较多

最终我选择 Python 作为主实现语言。理由很简单:这个工具的核心瓶颈是解码逻辑的正确性,而不是运行时性能。Python 能让我最快地验证文件头解析、单块解码、颜色重建这些关键环节,等逻辑稳定之后再做性能优化也完全来得及。至于解码内核,我没有真的从零开始写,而是参考了一个成熟开源实现的位运算逻辑,自己重新组织成适合学习调试的版本,再补上文件头解析、PNG 输出和批量处理这些工程化部分。这里有个经验:算法实现可以用别人的,文件格式的边界逻辑一定要自己掌握,因为真正遇到的坑几乎都出在边界场景上。

2.3 自己实现解码器的边界判断

我见过不少人在“要不要从零实现”这个问题上纠结很久。我的建议是:如果你只是为了拿到图片资源,直接用现成库完全没问题,但要验证它是否正确,验证方法很简单,找一张尺寸明确、颜色简单的 PKM 纹理解码出来,再用像素级对比工具检查输出;如果颜色通道错位,说明库的字节序处理和你手里的文件不一致,这时候就必须深入理解 ETC 块的位布局才能判断问题出在哪。

我最后确定的方案是:核心的单块解码自己维护,文件级解析和批量封装自己写,同时预留一个“外部解码器”接口,如果遇到 ETC2 这种 Python 生态支持不太好的格式,可以无缝切到 C 实现。这样一个工具既能处理日常 90% 的 ETC1 场景,又不会在特殊格式面前完全无能为力。

3. 核心实现:ETC1 解码器的完整解析

3.1 ETC1 编码原理速览

ETC1 的解码思想可以用一个不太严谨但很好懂的类比来描述:把一张图拆成许多 4x4 的“瓷砖”,每一块瓷砖烧制时只记录两个基础颜色和一组“微调方向”,解码时根据每个像素的微调量,在基础颜色上做加减,最终还原出 16 个像素的颜色。因为是粗略重建颜色,所以 ETC1 是有损压缩,调试时别指望解码结果和原始图片像素级一致。

具体来说,每个 4x4 像素块对应 8 字节压缩数据。这 8 字节分成两部分:前 4 字节是控制数据,后 4 字节是像素数据。

控制数据里包含:

  • 两个子块各自的基础颜色,每子块一个 RGB;
  • 一个颜色编码模式标志,叫 diff,表示基础颜色是按“独立模式”还是“差分模式”存储;
  • 一个子块排布标志,叫 flip,表示两个子块是上下排列还是左右排列;
  • 两个 3 位表索引,每个子块一个,用于从调制值表里选出一组 4 个微调量。

基础颜色有两种存法。独立模式里,每个子块的颜色分量各用 4 位存储;差分模式里,第一个子块的颜色分量各用 5 位存储,第二个子块的颜色分量存的是相对第一个子块的有符号差值,范围大概是 -4 到 3。差分模式把两个颜色的 24 位总空间省下来一点,但增加了编码复杂度,所以游戏纹理里两种模式都很常见。

每个子块根据表索引从调制表中取一行。调制表有 8 行,每行 4 个数,典型值像这样:

MODIFIER_TABLE = [ [2, 8, -2, -8], [5, 17, -5, -17], [9, 29, -9, -29], [13, 42, -13, -42], [18, 60, -18, -60], [24, 80, -24, -80], [33, 106, -33, -106], [47, 183, -47, -183], ]

每一个像素在压缩数据里占 2 位,这 2 位索引指向调制表行里的 4 个数之一。最终颜色就是基础颜色加上对应的微调量。2 位能表示 4 个索引,所以哪怕一整个子块只有一种基础颜色,也能通过微调量还原出一定程度的颜色变化,不至于全是色块。

3.2 单个 4x4 块的解码流程

解码一个块的输入是 8 字节压缩数据,输出是 16 个 RGB 像素。核心流程是:先解析控制数据里的 diff、flip、两个表索引和基础颜色,再把像素数据按 2 位一组取出来,最后遍历 16 个位置计算最终颜色。

这里要特别说明一点:不同来源的 PKM 文件在像素位的排布上可能存在差异。我在参考实现中按“前 16 位给第一个子块、后 16 位给第二个子块,子块内按 2 位一行取索引”的方式来处理,这也是很多解码器的通用约定。如果你的文件来自某个特殊打包工具,解码出现颜色错位,优先检查这一段的位序约定。

import struct def _expand_5bit(value: int) -> int: return (value << 3) | (value >> 2) def _expand_4bit(value: int) -> int: return (value << 4) | value def decode_etc1_block(block: bytes, out: bytearray, dst_x: int, dst_y: int, width: int): ctrl = struct.unpack(">I", block[0:4])[0] pix = struct.unpack(">I", block[4:8])[0] diff = (ctrl >> 7) & 1 flip = (ctrl >> 6) & 1 table0 = ctrl & 7 table1 = (ctrl >> 3) & 7 colors = ctrl >> 8 if diff: r0 = (colors >> 19) & 0x1F g0 = (colors >> 14) & 0x1F b0 = (colors >> 9) & 0x1F dr = (colors >> 6) & 7 dg = (colors >> 3) & 7 db = colors & 7 dr = dr - 8 if dr & 4 else dr dg = dg - 8 if dg & 4 else dg db = db - 8 if db & 4 else db r1 = max(0, min(31, r0 + dr)) g1 = max(0, min(31, g0 + dg)) b1 = max(0, min(31, b0 + db)) r0, g0, b0 = _expand_5bit(r0), _expand_5bit(g0), _expand_5bit(b0) r1, g1, b1 = _expand_5bit(r1), _expand_5bit(g1), _expand_5bit(b1) else: r0 = (colors >> 20) & 0x0F g0 = (colors >> 16) & 0x0F b0 = (colors >> 12) & 0x0F r1 = (colors >> 8) & 0x0F g1 = (colors >> 4) & 0x0F b1 = colors & 0x0F r0, g0, b0 = _expand_4bit(r0), _expand_4bit(g0), _expand_4bit(b0) r1, g1, b1 = _expand_4bit(r1), _expand_4bit(g1), _expand_4bit(b1) base_colors = [(r0, g0, b0), (r1, g1, b1)] mod_tables = [MODIFIER_TABLE[table0], MODIFIER_TABLE[table1]] pix0 = (pix >> 16) & 0xFFFF pix1 = pix & 0xFFFF indices = [] for i in range(8): indices.append((pix0 >> (14 - i * 2)) & 3) for i in range(8): indices.append((pix1 >> (14 - i * 2)) & 3) for local_y in range(4): for local_x in range(4): subblock = 0 if (local_x // 2 == 0 if flip else local_y // 2 == 0) else 1 if flip == 0: subblock = 0 if local_y < 2 else 1 else: subblock = 0 if local_x < 2 else 1 idx_in_sub = (local_y % 2) * 4 + (local_x % 2) if flip == 0 else (local_y % 4) * 2 + (local_x % 2) texel_index = subblock * 8 + idx_in_sub mod_idx = indices[texel_index] r, g, b = base_colors[subblock] mod = mod_tables[subblock][mod_idx] r = max(0, min(255, r + mod)) g = max(0, min(255, g + mod)) b = max(0, min(255, b + mod)) px = dst_x + local_x py = dst_y + local_y offset = (py * width + px) * 3 out[offset] = r out[offset + 1] = g out[offset + 2] = b

这个函数看起来长,但每一段逻辑都对应前面说过的压缩字段。注意几点:一是差分模式下基础颜色要把 5 位扩展到 8 位,二是 3 位差值转有符号数时要在 4 到 7 之间减 8,三是对最终 RGB 值做范围截断,因为基础颜色加上微调量之后可能越过 0 到 255 的边界。

3.3 整图解码与 PNG 输出

单块解码搞定后,整图解码就只是遍历所有块的问题了。按编码宽高计算出有多少个 4x4 块,然后从文件偏移 16 的位置开始,依次读取 8 字节块,解码后写入输出缓冲区的对应位置。

from PIL import Image def pkm_to_png(input_path: str, output_path: str): with open(input_path, "rb") as f: data = f.read() header = parse_pkm_header(data[:16]) enc_w = header["encoded_width"] enc_h = header["encoded_height"] rgb = bytearray(enc_w * enc_h * 3) offset = 16 for by in range(enc_h // 4): for bx in range(enc_w // 4): block = data[offset:offset + 8] decode_etc1_block(block, rgb, bx * 4, by * 4, enc_w) offset += 8 img = Image.frombytes("RGB", (enc_w, enc_h), bytes(rgb)) if header["original_width"] != enc_w or header["original_height"] != enc_h: img = img.crop((0, 0, header["original_width"], header["original_height"])) img.save(output_path, "PNG")

这段代码已经是一个能跑的转换器了。等宽高都是 4 的倍数时,原始尺寸和编码尺寸相同,直接输出就行。如果原始宽高不是 4 的倍数,解码图四周会有补齐的黑边,裁剪逻辑必须保留,否则拿到的图尺寸和预期不一致。

4. 完整转换器的组装与实操

4.1 命令行工具设计

只做一个单文件转换函数还不够,实际用的工具要能处理批量文件。我给 PKM2PNG 封装了命令行入口,支持三种模式:单个文件转换、整个目录批量转换、递归处理子目录。命令设计成下面这样,尽量贴近日常使用习惯:

python pkm2png.py input.pkm -o output.png python pkm2png.py ./textures/ -o ./converted/ --recursive

参数解析用标准库的 argparse 就能搞定。唯一的考量是递归模式,因为很多游戏资源目录结构很深,贴图分散在不同子目录里。我把输出目录结构也尽量保持和输入一致,避免转换完还要手动整理。

处理完文件后,工具会打印每个文件的原始尺寸、编码尺寸、版本号和输出路径。这个信息看起来不起眼,但在批量发现异常文件时非常有用,能一眼看出哪些文件的编码尺寸远大于原始尺寸,可能是带 mipmap 链的问题文件。

4.2 实际跑通的一次转换

我用一个尺寸为 256x128 的测试用 PKM 文件验证流程。文件头解析结果一切正常:魔数是PKM 10,版本号为 2,原始宽高和编码宽高都是 256x128。用上面那套解码函数跑完后,输出 PNG 的宽高是 256x128,颜色分布也符合预期。

验证时有一个好用的对照方法:如果源纹理内容本身是接近纯色的渐变图,解码结果中同一区域的颜色波动应该很小;如果解码正确,两个相邻像素不会出现突兀的颜色跳变。如果解码出来某一块区域全是诡异颜色,再配合十六进制看文件头版本号,基本能立刻确定是 ETC2 数据被当成 ETC1 解了。

4.3 批量处理和性能优化心得

单文件转换在大图上会有点慢,因为 Python 逐块循环处理 256x256 的图就是 4096 个块,每块还要做一堆位运算。换成 2048x2048 的图,循环次数直接翻 64 倍,解码耗时肉眼可见增加。我的优化手段很简单:用多进程按文件并行,而不是在单文件内做复杂向量化。每张图的解码互相独立,用进程池可以接近线性提速。

如果觉得 Python 的解码速度还是不够,可以在单块解码函数里改用 numpy 对整块数据做向量化操作,或者用 C 语言单独写一个解码内核,通过绑定调用。我在实际项目里把核心解码路径下沉到了 C,Python 只负责文件解析和结果整理,性能提升了十倍以上,代价是编译流程复杂了一点点,但对于动辄上百张贴图的批量任务来说很值得。

5. 常见问题与排查实录

5.1 解码出来是花的,颜色完全错乱

这是最常见的坑。我总结下来原因基本是三类:一是文件根本不是 ETC1,而是 ETC2 或其他纹理格式,却用了 ETC1 解码;二是控制字或者像素字的字节序读反了,大端文件被当小端读,或者反过来;三是子块划分时 flip 位判断反了,导致两个子块的基础颜色和像素位置对应不上。

遇到花屏先别急着改算法,做两件事:先看文件头版本号,版本号高于 2 的很可能需要 ETC2 解码器;再找一张尺寸很小的 PKM 文件当测试样本,小图花屏更容易通过肉眼定位问题区域,比在大图上大海捞针高效得多。

5.2 图像周围出现黑边

前面提过,ETC 压缩按 4x4 块进行,当原始图片宽高不是 4 的倍数时,打包工具会把编码尺寸向上补齐。解码结果自然就带了一圈填充区域,这些区域没有真实颜色数据,通常是黑色或者重复边缘像素。解决办法就是在输出前用文件头里的原始宽高做裁剪,而不是直接保存整个编码尺寸。

5.3 ETC2 版本和 Alpha 通道问题

ETC2 是 ETC1 的扩展,支持了 RGBA 和更高质量的颜色编码,但它和 ETC1 的块格式不兼容,不能混用。PKM 文件头版本号高于 2 时,基本可以断定是 ETC2 数据。另外,如果文件头魔数不是PKM 10,而是一些变体签名,也要停下来确认格式,避免在错误方向上浪费时间。

遇到带 Alpha 的纹理时,解码输出必须是 RGBA 而不是 RGB,早期版本支持 RGBA 的编码格式很多,自己写解码器时要把 RGBA 输出通道预留好,纯 RGB 输出会在半透明贴图资源上丢失大量阴影和边缘信息。

5.4 解码方向反了,上下颠倒

PKM 文件里的纹理坐标方向和使用场景有直接关系。有些引擎导出纹理时习惯原点在左下角,有些则采用左上角原点。把它当普通图片输出时,会因为坐标约定不同出现上下颠倒。这不是解码错误,而是坐标约定差异。解决办法是给工具加一个--flip-y参数,根据资源使用场景决定要不要在输出前翻转图像,而不是武断地把翻转逻辑硬编码进去。

5.5 大图解码性能太慢

性能和正确性不是一回事,但实际使用中性能问题会直接影响效率。如果只是偶尔转一两张图,Python 版本完全够用;如果要做整包资源的批量提取,就必须考虑并行或调用底层解码器。我的建议顺序是:先确认解码正确,再上多进程,最后根据实际耗时决定要不要下沉 C 内核。过早优化只会让调试变复杂,没有实际收益。

5.6 一些工具使用中的小经验

我遇到的一个比较诡异的问题是某些 PKM 文件的数据区里混了 mipmap 链,也就是一张大图后面跟着一串逐级缩小的缩略图数据。文件头只描述第一级,如果按单图方式解析,数据区长度对不上,解码结果也会在某个位置出现乱码。排查时先对比文件实际大小和头算出来的理论大小,不吻合就按多级纹理方式处理。

另外,从游戏资源里提取的 PKM 文件有时会和实际用途不匹配,比如存了一张预乘 Alpha 的贴图,直接解码输出后叠加到明亮背景上会看到黑边,这种情况问题不在解码器,而在资源管线。当时我把这个情况也记进了工具的日志提示里,避免自己过两天又疑惑为什么转换结果边缘发黑。

最后再分享一点个人体会

PKM2PNG 这个工具从开始写到稳定使用,过程其实不算复杂,真正耗时间的不是解码算法,而是对各种异常文件边界的梳理。ETC 纹理格式的官方资料不算少,但要落地成工具,还得靠一堆实际文件做对照验证。我的经验是:遇到不认识的文件扩展名,第一件事永远是把二进制头完整 dump 出来,看懂前 16 字节,后面的事情都水到渠成。这个思路后来也用在了其他各种格式解析上,几乎没踩过重复的大坑。如果你手里正好有一堆 .pkm 文件没地方下手,不妨按这篇文章的流程自己写一遍,跑通一次转换之后,你对压缩纹理的理解会提升一个层级。

相关新闻

ARMv8异常机制深度解析:异常级别、向量表与调试实战

ARMv8异常机制深度解析:异常级别、向量表与调试实战

我认真啃ARMv8异常机制&#xff0c;是在某次把一段SVC调用写崩之后。那会儿板子一跑进用户态就触发同步abort&#xff0c;调试器里只有一个笼统的异常类型&#xff0c;没有任何栈信息&#xff0c;我盯着ESR寄存器打印出来的十六进制值看了半天&#xff0c;才意识到自己连EC字段…

2026/10/20 16:45:49 阅读更多 →
上网家长控制软件怎么选?从DNS过滤到网关策略的部署实践

上网家长控制软件怎么选?从DNS过滤到网关策略的部署实践

简介&#xff1a;这是一款面向家长的 Windows 上网管控工具&#xff0c;针对儿童过度上网、访问不良网站等家庭场景&#xff0c;提供网址过滤、上网时段限制、密码锁屏、断网/关机及应用程序监控等功能&#xff0c;既适合家庭用户直接部署&#xff0c;也适合学习 C# 桌面应用开…

2026/10/20 16:44:46 阅读更多 →
BLDC双闭环调速Simulink仿真:建模、换相与PI参数整定实战

BLDC双闭环调速Simulink仿真:建模、换相与PI参数整定实战

BLDC无刷直流电机的仿真设计&#xff0c;名字听着挺“课程设计”&#xff0c;但说实话&#xff0c;这东西做到什么深度&#xff0c;完全看个人。你要是只是把模型搭起来跑个波形&#xff0c;那确实入门级别&#xff1b;但要是把转速环、电流环的参数整定逻辑搞清楚&#xff0c;…

2026/10/20 16:44:46 阅读更多 →

最新新闻

Ubuntu下PostgreSQL安装配置与Navicat连接指南

Ubuntu下PostgreSQL安装配置与Navicat连接指南

做后端开发或者数据相关工作的朋友&#xff0c;应该都经历过这么一幕&#xff1a;好不容易在 Ubuntu 上把 PostgreSQL 装好了&#xff0c;却发现命令行操作没问题&#xff0c;客户端工具却怎么都连不上&#xff1b;或者反过来&#xff0c;Navicat 装好了&#xff0c;连接时报一…

2026/10/20 17:33:06 阅读更多 →
uniCloud Serverless 实战:英语学习小程序全链路开发

uniCloud Serverless 实战:英语学习小程序全链路开发

简介&#xff1a;这是一套面向英语学习者与小程序开发者的完整项目源码&#xff0c;基于uniapp与uniCloud Serverless架构构建&#xff0c;可直接在微信环境中运行。项目集生词本记录、拍照识别单词、内置词典查询、AI语音翻译、跟读打分评测于一体&#xff0c;并针对考研人群补…

2026/10/20 17:33:06 阅读更多 →
WiFi指纹室内定位实战:从原理到Demo搭建

WiFi指纹室内定位实战:从原理到Demo搭建

1. 室内定位的现实困境与WiFi方案的破局点实话实说&#xff0c;第一次接手“基于WiFi的室内定位”这个方向时&#xff0c;我心里是打鼓的。日常大家用GPS导航已经习惯了&#xff0c;掏出手机就能知道自己在哪&#xff0c;但一旦走进大型商场、地下车库、机场航站楼&#xff0c;…

2026/10/20 17:33:06 阅读更多 →
大模型后端长对话记忆架构全解析:从无状态到有状态

大模型后端长对话记忆架构全解析:从无状态到有状态

做后端的人都有过这种体验&#xff1a;用户和AI助手聊了三十轮&#xff0c;前二十轮里明明已经把需求背景、偏好、禁忌都交代清楚了&#xff0c;结果聊到第三十一轮&#xff0c;AI突然反问一句“您刚刚提到的那个项目背景&#xff0c;方便再描述一下吗&#xff1f;”当场血压就…

2026/10/20 17:33:06 阅读更多 →
D435相机+OpenCV快速上手:彩色图与深度图实时显示全攻略

D435相机+OpenCV快速上手:彩色图与深度图实时显示全攻略

最近不少做视觉入门的朋友卡在第一关——把D435相机的画面真正“看”到屏幕上。很多人买了一台Intel D435深度相机&#xff0c;兴冲冲插上USB线&#xff0c;结果CPU占用飙高、程序报错、画面黑屏&#xff0c;折腾一晚上就劝退了。其实用D435搭配OpenCV显示彩色图和深度图&#…

2026/10/20 17:33:06 阅读更多 →
自考论文降AI率实战:智能体改写与人工复核全流程

自考论文降AI率实战:智能体改写与人工复核全流程

自考论文写到提交前&#xff0c;最让人血压升高的往往不是查重率&#xff0c;而是学校突然要求提交一份AI检测报告。A同学就是这种状况&#xff1a;论文初稿在查重系统里已经降到合格线&#xff0c;结果学院的通知里多了一句“AI疑似率不得超过20%”。他拿自己的稿子去测了一下…

2026/10/20 17:32:04 阅读更多 →

日新闻

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 阅读更多 →