ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

OpenMed BMP Header Preflight:零解码、零依赖的位图头预检技术解析

OpenMed BMP Header Preflight:零解码、零依赖的位图头预检技术解析 OpenMed BMP Header Preflight零解码、零依赖的位图头预检技术解析【免费下载链接】openmedLocal-first healthcare AI: clinical NER HIPAA PII de-identification that runs 100% on-device. 2,200 medical models, 21 languages, Apple MLX Python, no cloud, no patient data leaving your network. Apache-2.0项目地址: https://gitcode.com/GitHub_Trending/ope/openmed导读本文深入剖析 OpenMed 多模态模块中的 BMP 头部预检header preflight工具read_bmp_dimensions。该工具在不解码任何像素数据的前提下仅读取 BMP 文件头与 DIB 头CORE 12 字节 / INFO 40 字节即可安全提取位图几何信息宽、高、位深、行序并通过算术校验声明式字段调色板、像素偏移、文件大小拦截畸形与超限文件。它是显式辅助函数而非自动注册的解码器或管线闸门在多模态资产预检、OCR 接入与隐私安全的场景中承担先验后解的关键角色。读者将掌握其支持的边界、默认限额、错误分类模型、流所有权语义以及配套的合成测试验证方法。1. 背景为什么需要先看头再解码BMPWindows Bitmap是结构简单但字段声明灵活的老牌位图格式文件头仅 14 字节其后的 DIB 头有 CORE12 字节与 INFO40 字节等多种布局还可能携带调色板、压缩模式、色彩管理等信息。若未经检查便直接送入解码器或 OCR 引擎攻击者可以构造头小身大的像素偏移、超大声明尺寸或畸形 DIB 布局诱发资源耗尽decompression bomb或解析器歧义进而威胁以隐私保护为第一优先级的医疗影像处理管线。OpenMed 的解决方案是把读取声明式几何信息与解码像素彻底分离先以有界、定长的方式只读文件头算术校验所有声明字段再决定是否进入后续解码/OCR 流程。这一模式在 openmed/multimodal/preflight.py 的整套 preflight 契约中得到复用——preflight_asset在固定顺序中依次执行资产清单、有界媒体类型检测、模态清单画像、解码前限额画像与有界流式摘要任何缺失证据都只会导致 abstain弃权而不会导致放行。BMP 预检工具正是这一思路在单一位图格式上的落地实现。实现位于 openmed/multimodal/bmp_dimensions.py配套测试见 tests/unit/multimodal/test_bmp_dimensions.py。2. 快速上手10 行内完成位图头预检read_bmp_dimensions的公开 API 非常精简文档给出的示例即开即用from openmed.multimodal.bmp_dimensions import read_bmp_dimensions with open(synthetic.bmp, rb) as stream: geometry read_bmp_dimensions(stream, max_pixels20_000_000) assert stream.tell() 0 # 流位置被恢复 print(geometry.width, geometry.height, geometry.bit_depth, geometry.top_down)输出的是一个冻结数据类BmpDimensions见 openmed/multimodal/bmp_dimensions.py#L30-L39字段含义如下字段类型说明widthint位图宽度像素恒为正heightint位图高度像素始终返回正值负号只反映行序planesint平面数合法值恒为 1bit_depthint每像素位深取值依 DIB 类型而定见第 3 节top_downbool是否自顶向下行序仅 INFO 负高度产生Truedib_header_bytesintDIB 头字节数12 或 40BmpDimensions使用dataclass(frozenTrue, slotsTrue)定义字段只读、无法被外部篡改测试test_output_contains_only_declared_metadata专门验证了FrozenInstanceError行为并断言输出中只含声明式元数据int/str/bool不夹带路径、文本或像素内容。参数方面read_bmp_dimensions(source, *, max_header_bytes54, max_pixels100_000_000)接受bytes或二进制流两个限额均须为正整数严格type(...) is int检查布尔值会被拒绝——见 bmp_dimensions.py#L56-L59且在任何 I/O 之前完成校验测试test_invalid_limits_do_not_read用不可读对象验证了这一点。3. 支持的边界CORE 与无压缩 INFO3.1 两种 DIB 布局工具只支持两种声明式布局其余一律显式拒绝而非猜测Windows CORE DIB12 字节对应BITMAPCOREHEADER。宽度/高度/平面数/位深均为 16 位无符号字段。允许位深为1、4、8、24。无压缩 INFO DIB40 字节对应BITMAPINFOHEADER。宽度/高度为 32 位有符号字段允许位深为1、4、8、16、24、32compression字段必须为 0BI_RGB否则抛出bmp_compression_unsupported。从源码看bmp_dimensions.py#L90-L119DIB 类型由dib_size字段精确判别12走 CORE 分支40走 INFO 分支其余值含 0、16、52、56、64、108、124、0xFFFFFFFF 等一律bmp_dib_header_unsupported。测试test_unsupported_dib_never_triggers_unbounded_reads特别验证遇到畸形 DIB 尺寸时流只被读到边界内boundary18绝不会按声明尺寸继续拉读。3.2 行序与高度语义INFO 布局中高度字段带符号负值表示自顶向下top-down存储行正值表示自底向上bottom-up。read_bmp_dimensions用height abs(signed_height)归一化高度同时通过top_down signed_height 0保留行序信息bmp_dimensions.py#L124-L126。CORE 布局的高度无符号top_down恒为False。宽度必须为正、高度不得为零否则抛bmp_dimensions_invalid平面数必须恰为 1bmp_planes_invalid。3.3 不承诺什么需要强调的是头预检通过 ≠ 像素可解码。文档明确指出调色板计数与声明的像素偏移/文件范围只是算术校验不读取调色板本身尾部内容、色彩管理ICC 配置、元数据、压缩、OCR 与脱敏均不在本工具作用域内一个被接受的头部不代表临床或安全层面的放行。这正是先验后解的纪律预检只回答头部声明是否自洽、是否在预算内像素级真实性留给后续解码环节。4. 限额与流所有权防御性设计的三个细节4.1 双限额头部字节与像素预算默认限额为max_header_bytes54恰好覆盖 14 字节文件头 40 字节 INFO DIB与max_pixels100_000_000。每次调用可单独下调头部预算_HeaderReader.read_exact在每次读取前检查size limit - offset超过即抛bmp_header_limit_exceeded因此任何读取都不会超出剩余头部预算bmp_dimensions.py#L158-L160。CORE 只需 26 字节INFO 需 54 字节测试test_all_truncated_header_boundaries枚举了 0..boundary-1 的每一种截断长度并断言bmp_header_truncated。像素预算采用除法检查而非乘法if width max_pixels // heightbmp_dimensions.py#L127-L128从根上避免大数乘法的溢出风险超过即抛bmp_pixel_limit_exceeded。测试test_pixel_limit_boundary用35/34的临界值验证了边界语义。关键特性是不会执行与图像尺寸或声明偏移成比例的任何分配。无论声明 1×1 还是 40000×40000预检的内存与 I/O 成本恒定有界。4.2 短读short read支持二进制流允许单次read(n)返回不足n字节如网络流、分块流。_HeaderReader.read_exact会循环补读直至凑齐任一阶段返回空块即判定bmp_header_truncatedbmp_dimensions.py#L168-L177。测试test_partial_nonseekable_reads_stop_at_header用自定义ShortStream每次最多吐 3 字节验证非可寻址流只被消费到头部边界boundary54尾部 payload 原封不动。4.3 流所有权与位置恢复可寻址流seekable调用前记录tell()位置无论成功还是失败finally中一律seek回原位bmp_dimensions.py#L65-L70。测试test_seekable_stream_at_nonzero_position_is_restored验证了从位置 6 开始读取后流回到 6test_failure_restores_caller_owned_stream验证了失败路径同样恢复。非可寻址流nonseekable绝不尝试回卷只通过必需的头部字节消费流测试test_explicit_nonseekable_stream_does_not_attempt_restoration断言seek一旦被调用即 pytest 失败。所有权read_bmp_dimensions从不关闭流——_restore_position只调用seek调用方始终保有所有权。所有测试的stream.closed断言均为False。5. 失败模型稳定类别 零值回显5.1BmpDimensionsError可编程的安全失败所有头部级失败统一抛BmpDimensionsErrorValueError子类携带稳定且字符串一致的.categorybmp_dimensions.py#L22-L27str(exc) exc.category。调用方无需解析异常消息文本直接按类别分支即可。完整类别表类别触发条件bmp_signature_invalid文件头 2 字节签名 ≠BMbmp_reserved_fields_invalid文件头保留字段非零bmp_dib_header_unsupporteddib_size既非 12 也非 40bmp_compression_unsupportedINFOcompression≠ 0BI_RGB之外bmp_planes_invalidplanes≠ 1bmp_bit_depth_unsupported位深不在对应 DIB 允许集内bmp_dimensions_invalid宽度 ≤ 0 或高度 0bmp_color_table_size_invalidINFO 声明colors_used超过2^bit_depthbmp_pixel_offset_invalidpixel_offset小于最小偏移14 dib 调色板字节bmp_file_size_invalidpixel_offset pixel_bytes溢出 32 位或超过声明文件大小bmp_image_size_invalidINFOimage_size既非 0 也非计算出的像素字节数bmp_pixel_limit_exceededwidth * height max_pixels除法检查bmp_header_limit_exceeded需要读取超过max_header_bytes预算bmp_header_truncated头字节不足截断或短读返回空块bmp_stream_contract_error流的read返回非bytes、超长或缺失bmp_stream_read_error流read抛异常不保留底层 I/O 消息bmp_stream_position_error可寻址性探测/tell失败bmp_stream_restore_error失败后seek回原位失败测试test_malformed_fields_have_stable_categories逐一篡改签名、保留字段、尺寸、偏移、文件大小与 image size断言category与str完全一致。5.2 隐私纪律绝不回显输入设计上错误对象不携带任何输入值——不回显路径、分辨率、调色板条目或底层 I/O 错误消息。_read_chunk刻意在异常处理器之外再抛一次bmp_dimensions.py#L180-L192测试test_read_error_does_not_retain_io_details与test_position_errors_do_not_retain_io_details断言__cause__ is None and __context__ is None即底层OSError(synthetic-private-file-detail)的细节永远不会泄漏到异常链中——这在以 PHI/PII 脱敏为核心的 OpenMed 中属于硬性要求。5.3 对不支持格式的处置建议文档明确不支持格式应由调用方显式处理而不是用更大的限额重试。若将max_pixels从 1e8 抬到 163恶意声明依然会在 32 位溢出检查处被拦下测试test_uint32_pixel_extent_cannot_overflow验证了0xFFFFFFFE偏移 0xFFFFFFFF文件大小的双重溢出场景但更大的限额意味着更大的拒绝服务面。合理实践是先识别媒体类型.bmp/.gif/.webp等见 openmed/multimodal/ocr.py#L713-L717 的_IMAGE_EXTENSIONS再按格式分派到对应预检器而不是一味调大限额。6. 与多模态管线的衔接显式助手而非隐式闸门read_bmp_dimensions在仓库中的定位是显式辅助函数explicit helperbmp_dimensions.py的__all__只导出DEFAULT_MAX_BMP_HEADER_BYTES、DEFAULT_MAX_BMP_PIXELS、BmpDimensions、BmpDimensionsError与read_bmp_dimensions五个符号没有注册任何解码器回调或自动管线闸门——集成与否由调用方显式决定。这一点与其姊妹实现gif_dimensions.py、webp_dimensions.py等同目录文件以及 openmed/multimodal/preflight.py 的既有 provider 不会被自动路由的注释口径一致。在图像类文档流水线中BMP 通过 ocr.py#L720-L732 的_ocr_image_handler进入 OCR 流程ocr(path, languages...).to_document()桥接到ExtractedDocument。将read_bmp_dimensions作为 OCR 前置检查可确保只有几何自洽、像素预算内、布局受支持的位图才进入解码从而把资源类攻击挡在 OCR/解码器之外。7. 验证与测试合成数据 Pillow 双轨校验7.1 运行测试项目使用 uv 管理依赖文档给出的验证命令即开即用uv run --frozen --extra dev pytest tests/unit/multimodal/test_bmp_dimensions.py -q--frozen锁定锁文件、--extra dev引入开发依赖Pillow 等。需要说明Pillow 仅是既有开发依赖绝不进入运行时导入——bmp_dimensions.py自身零依赖仅用标准库struct、dataclasses、typingPillow 只在pytest.mark.integration标记的对照测试中作为地面真值解码器使用。7.2 测试矩阵要点布局覆盖test_core_headers参数化 CORE 全部 4 种位深1/4/8/24test_info_headers_and_top_down_rows参数化 INFO 全部 6 种位深 × 正负高度验证top_down与符号的对应关系。边界与截断test_all_truncated_header_boundaries对 CORE26 字节与 INFO54 字节枚举每一种截断长度全部预期bmp_header_truncatedmax_header_bytesboundary-1则预期bmp_header_limit_exceeded。畸形声明平面数、位深、压缩模式、DIB 尺寸、调色板计数、像素偏移、文件大小、image size、32 位溢出均有独立参数化用例。流语义短读非可寻址流、非零起始位置恢复、失败恢复、不可寻址不回卷、错误不保留 I/O 细节逐一覆盖。端到端对照test_pillow_generated_file_end_to_end用 Pillow 生成1/L/RGB/RGBA模式、三种尺寸的真实 BMP 文件解码取expected.size与预检结果比对test_core_and_top_down_files_against_pillow对 CORE 与 top-down INFO 文件做同样对照——合成测试与真实解码器双轨互证确保预检的几何结果与 Pillow 一致。8. 实践建议与边界总结用在解码之前把read_bmp_dimensions作为 BMP 资产进入 OCR/解码/渲染流程的前置检查用其像素预算与头部预算拦截超限文件。限额按场景收紧默认max_pixels100_000_000适合桌面/服务端移动端或受限环境应传入更小的max_pixels如 2_000 万像素见开篇示例并保持max_header_bytes54不动。按类别分支处理用except BmpDimensionsError as exc: exc.category精确分流——bmp_signature_invalid往往意味着媒体类型误判bmp_compression_unsupported意味着需要另寻压缩 BMP 解码路径bmp_pixel_limit_exceeded意味着资源拒绝。不要重试放大限额对bmp_dib_header_unsupported等确定性类别放大限额只会扩大攻击面应显式处理或转其他路径。遵守流契约传入调用方持有的流成功失败均无需关心位置可寻址流会被恢复流所有权始终在调用方。认清承诺边界头预检通过 ≠ 像素可解码 ≠ 临床/安全放行它只证明头部声明自洽且在预算内。布局参考WindowsBITMAPCOREHEADER与BITMAPINFOHEADER的字段定义可查阅微软官方文档链接见原文档本实现即严格按这两份结构解析。【免费下载链接】openmedLocal-first healthcare AI: clinical NER HIPAA PII de-identification that runs 100% on-device. 2,200 medical models, 21 languages, Apple MLX Python, no cloud, no patient data leaving your network. Apache-2.0项目地址: https://gitcode.com/GitHub_Trending/ope/openmed创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表