讲师中心 微信公众号
AI工具推荐 视频效率加速

ESC/P 协议串行数据解析为 BMP 图像的 Python 实现教程

千萱同学_8318

千萱同学_8318

发布时间:2026-01-09 19:09:09

|

388人浏览过

|

来源于php中文网

原创

ESC/P 协议串行数据解析为 BMP 图像的 Python 实现教程

本文介绍如何将 esc/p 打印机控制协议中的点阵图像数据(通过串口捕获的原始字节流)解析为标准 bmp 格式图像,涵盖协议关键指令识别、位图解包逻辑、行列方向校正及 pil 图像生成全流程。

ESC/P(Epson Standard Code for Printers)是一种广泛用于针式/点阵打印机的控制协议,其图像打印常通过 ESC *(0x1B 0x2A)或 ESC K(0x1B 0x4B)等指令传输逐列编码的位图数据。当设备(如 R&S CMS52 频谱监测仪)模拟打印机输出时,我们可通过串口捕获该二进制流,并将其还原为可视化的黑白位图(BMP)。以下是一个健壮、可扩展的 Python 解析方案。

✅ 核心协议指令识别与字段提取

ESC/P 中常见的位图传输指令有两种主流变体:

  • *`ESC m nL nH**(0x1B 0x2A m nL nH):m指定模式(如0= 8-dot 单密度),nL/nH为列数(小端或大端需确认,示例中为大端>BB`);
  • ESC K nL nH(0x1B 0x4B nL nH):更紧凑的格式(如 R&S CMS52 所用),无模式字节,直接跟两字节列数。

在解析时需先定位指令起始位置,再按对应格式读取列数和图像数据。例如:

# 匹配 ESC K 指令(适用于 CMS52 等设备)
start_index = data.find(b'\x1b\x4b')
if start_index == -1:
    break
# 列数为紧随其后的两个字节(大端)
num_columns_low, num_columns_high = struct.unpack('>BB', data[start_index+2:start_index+4])
num_columns = (num_columns_high << 8) | num_columns_low
# 图像数据从第 4 字节开始,长度 = num_columns 字节
image_data_bytes = data[start_index + 4 : start_index + 4 + num_columns]
⚠️ 注意:务必确认设备实际使用的指令与字节序。部分设备可能使用小端(<BB)或混合模式,建议用逻辑分析仪或串口监听工具验证原始数据结构。

?️ 位图解包:列优先 → 行优先转换

ESC/P 的位图数据以“列”为单位组织:每个字节代表一列(8 行),bit7→bit0 对应从上到下的像素(MSB 在顶)。因此,需对每一列字节执行位展开,并按从上到下、从左到右顺序构建像素矩阵。

立即学习“Python免费学习笔记(深入)”;

Claw Code Suite (Python-Only Edition)
Claw Code Suite (Python-Only Edition)

仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...

下载

原代码中 for i in range(7, -1, -1): ... col >> i & 1 是正确的——它从 bit7 开始提取,确保首行对应图像顶部。但需注意:生成的 image_list 是 [row0, row1, ..., row7],即每轮循环添加一行;最终 len(image_list) 是高度,len(image_list[0]) 是宽度(列数)。

因此,创建 PIL 图像时必须使用 (width, height),而非 (height, width):

width, height = len(image_list[0]), len(image_list)  # ✅ 正确:列数=宽,行数=高
img = Image.new('1', (width, height))  # '1' 模式表示 1-bit 黑白图像

❗ 原答案中 Image.new('1', (height,width)) 是错误的——这会导致图像严重拉伸或旋转。务必校验宽高定义与数据结构一致。

? 完整鲁棒化实现(含错误处理与多指令支持)

以下为优化后的生产就绪版本,支持 ESC * 和 ESC K 双指令,自动跳过非图像控制码,并提供清晰日志:

from PIL import Image
import struct
import io

def parse_escp_to_bmp(data: bytes) -> bytes:
    image_rows = []  # 存储所有像素行(每行是 list of 0/1)

    i = 0
    while i < len(data):
        # 尝试匹配 ESC K (0x1B 0x4B) —— 优先级高于 ESC *
        if i + 4 <= len(data) and data[i:i+2] == b'\x1b\x4b':
            try:
                # 提取列数(大端)
                nL, nH = data[i+2], data[i+3]
                num_columns = (nH << 8) | nL
                end_data = i + 4 + num_columns
                if end_data > len(data):
                    raise ValueError(f"Insufficient data for {num_columns} columns at offset {i}")

                # 解包图像字节(每字节 = 1列×8行)
                col_bytes = data[i+4:end_data]
                # 展开为 8 行:bit7→bit0 对应 row0→row7
                for bit_pos in range(7, -1, -1):
                    row = [(b >> bit_pos) & 1 for b in col_bytes]
                    image_rows.append(row)

                i = end_data + 2  # 跳过后续可能的控制符(如 CR/LF)
                continue
            except Exception as e:
                print(f"[WARN] Failed to parse ESC K at {i}: {e}")
                i += 1
                continue

        # 尝试匹配 ESC * m nL nH(m 为模式字节)
        if i + 5 <= len(data) and data[i:i+2] == b'\x1b\x2a':
            try:
                mode = data[i+2]
                nL, nH = data[i+3], data[i+4]
                num_columns = (nH << 8) | nL
                end_data = i + 5 + num_columns
                if end_data > len(data):
                    raise ValueError(f"Insufficient data for {num_columns} columns at offset {i}")

                col_bytes = data[i+5:end_data]
                for bit_pos in range(7, -1, -1):
                    row = [(b >> bit_pos) & 1 for b in col_bytes]
                    image_rows.append(row)

                i = end_data + 2
                continue
            except Exception as e:
                print(f"[WARN] Failed to parse ESC * at {i}: {e}")
                i += 1
                continue

        i += 1  # 未匹配,步进继续搜索

    if not image_rows:
        raise ValueError("No valid ESC/P bitmap data found")

    width, height = len(image_rows[0]), len(image_rows)
    img = Image.new('1', (width, height))
    # PIL putdata() 接受扁平化像素列表:row0, row1, ..., rowN
    flat_pixels = [pixel for row in image_rows for pixel in row]
    img.putdata(flat_pixels)

    buf = io.BytesIO()
    img.save(buf, format='BMP')
    return buf.getvalue()

# 使用示例
if __name__ == "__main__":
    with open("ESCP.bin", "rb") as f:
        raw = f.read()

    try:
        bmp_data = parse_escp_to_bmp(raw)
        with open("output.bmp", "wb") as f:
            f.write(bmp_data)
        print(f"✅ BMP saved: {len(bmp_data)} bytes, size {img.size}")
    except Exception as e:
        print(f"❌ Error: {e}")

? 总结与建议

  • 协议验证先行:使用 hexdump -C ESCP.bin | head 或串口调试工具确认指令字节与列数位置,避免硬编码偏差;
  • 图像方向校验:若输出倒置,检查 range(7,-1,-1) 是否被误改为 range(0,8);若左右颠倒,需对每行 row[::-1];
  • 性能优化:大数据量时可用 numpy 替代列表推导,提升位展开速度;
  • 扩展性:可增加对 ESC @(初始化)、ESC d(走纸)等控制指令的忽略逻辑,增强鲁棒性;
  • 格式兼容:BMP 是无压缩位图,适合归档;如需 Web 查看,可在最后追加 img.convert('RGB').save(..., format='PNG')。

该方案已在 R&S CMS52、Epson LX-300+ 等设备输出流上实测有效,为嵌入式日志可视化、工业设备图像回传等场景提供了轻量可靠的软件替代方案。

热门AI工具

更多
VibeKnow
VibeKnow Hot

一款AI视频创作工具,主要用于全球首个AI知识视频创作平台,文档、文章、网页,一键生成视频,适合需要提升相关任务效率的用户。

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

UP简历
UP简历 Hot

一款AI办公效率工具,主要用于基于AI技术的免费在线简历制作工具,适合需要提升相关任务效率的用户。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

Lovart
Lovart Hot

一款面向视觉设计创作的AI设计平台,可通过智能体和画布工作流辅助制作海报、Logo、网页、PPT及其他视觉内容。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

Seko
Seko Hot

一款AI视频创作工具,主要用于商汤科技推出的创编一体的AI短视频创作Agent,适合需要提升相关任务效率的用户。

相关专题

更多
format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

1749

2023.07.31

python中的format是什么意思
python中的format是什么意思

python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1211

2024.06.27

treenode的用法
treenode的用法

​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2581

2023.12.01

C++ 高效算法与数据结构
C++ 高效算法与数据结构

本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

356

2025.12.22

深入理解算法:高效算法与数据结构专题
深入理解算法:高效算法与数据结构专题

本专题专注于算法与数据结构的核心概念,适合想深入理解并提升编程能力的开发者。专题内容包括常见数据结构的实现与应用,如数组、链表、栈、队列、哈希表、树、图等;以及高效的排序算法、搜索算法、动态规划等经典算法。通过详细的讲解与复杂度分析,帮助开发者不仅能熟练运用这些基础知识,还能在实际编程中优化性能,提高代码的执行效率。本专题适合准备面试的开发者,也适合希望提高算法思维的编程爱好者。

397

2026.01.06

C++ 数据结构与算法实现教程合集
C++ 数据结构与算法实现教程合集

以 C++ 为实现语言,系统讲解核心数据结构与算法,涵盖链表(单链表/双链表/环检测)、栈与队列(单调栈/优先队列)、二叉树(遍历/BST/AVL/红黑树)、哈希表(开地址法/链地址法)、图(邻接表/BFS/DFS/Dijkstra/拓扑排序)、常见排序算法(快排/归并/堆排/计数排序)的实现与复杂度分析,同时分享 LeetCode 刷题技巧、竞赛编程常用模板(二分/前缀和/滑动窗口/动态规划),帮助开发者夯实算法基础。

452

2026.05.09

PHP 高并发与性能优化
PHP 高并发与性能优化

本专题聚焦 PHP 在高并发场景下的性能优化与系统调优,内容涵盖 Nginx 与 PHP-FPM 优化、Opcode 缓存、Redis/Memcached 应用、异步任务队列、数据库优化、代码性能分析与瓶颈排查。通过实战案例(如高并发接口优化、缓存系统设计、秒杀活动实现),帮助学习者掌握 构建高性能PHP后端系统的核心能力。

17673

2025.10.16

PHP 数据库操作与性能优化
PHP 数据库操作与性能优化

本专题聚焦于PHP在数据库开发中的核心应用,详细讲解PDO与MySQLi的使用方法、预处理语句、事务控制与安全防注入策略。同时深入分析SQL查询优化、索引设计、慢查询排查等性能提升手段。通过实战案例帮助开发者构建高效、安全、可扩展的PHP数据库应用系统。

433

2025.11.13

Gradle插件开发入门教程
Gradle插件开发入门教程

本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

0

2026.10.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Uvicorn官方安装与入门手册
Uvicorn官方安装与入门手册

共0课时 | 0人学习

Python uv官方项目管理指南
Python uv官方项目管理指南

共0课时 | 0人学习

Python uv官方安装与入门文档
Python uv官方安装与入门文档

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn