跳转至

7.4 输入输出:练习——ASCII 图

内容梗概

本课是一节练习课:把一个 BMP 彩色图片转换成由字符组成的 ASCII 图(txt 文本)。老师强调这个练习**不布置为作业**(因为包含少量未学的超前知识),但下一节课的作业可能与之相关。课程讲解了转换的整体原理(彩色图 → 灰度图 → 字符映射)、正式引入 with open(...) as ... 语法、手工解析 BMP 二进制文件格式的思路,以及把 0~255 灰度值映射到 70 级字符的换算方法,最后留下"图像为何倒过来、如何翻转"的思考题。

知识点详解

练习任务与演示效果(约 00:00)

  • 任务:读取一张 BMP 图片(老师用的是机器猫/小叮当图),通过一系列操作转成 ASCII 字符画,写入 txt 文本文件。
  • 演示:代码用**相对路径**(py 文件当前目录)读取 BMP,运行后目录下多出 test.txt;初看是一堆字符,把字号不断缩小(记事本或 VSCode 中 Ctrl+-)后能看出是一个(倒过来的)机器猫。
  • 练习目的:训练文件读写能力;因为含一点超前知识,所以只当练习、不当作业。

转换原理(约 03:27)

分三步:

  1. 彩色图 → 灰度图:彩色图的每个像素由 R、G、B(红、绿、蓝)三个分量组成;灰度图的每个像素只有一个 0~255 的**灰度值**(共 256 级),0 表示黑色,255 表示白色,中间是不同程度的灰色。
  2. 灰度值 → 字符:定义一个名为 GRAY_SCALE 的字符串变量,包含 70 个 ASCII 可打印字符,排列顺序有讲究(约 07:38):
  3. 越靠左(索引越接近 0)的字符越"复杂",打印出来后单位面积内黑色占比越大(如 $);
  4. 越靠右的字符越"浅",最后一个(索引 69)是空格(全白)。
  5. 于是索引越小越黑、越大越白,正好对应灰度值 0=黑、255=白。
  6. 范围换算(约 10:13):把 0~255 的灰度值映射到 0~69 的索引——用 256 / 70 得到一个缩小比例,每个灰度值除以这个比例再取整,即得字符下标。心算验证:灰度 0 → 索引 0($,黑色);灰度 255 → 索引 69(空格,白色)。

with open(...) as ... 语法(约 10:55)

  • 正式引入 with 语法:
    with open("test.txt", "w") as output:
        # 对 output 的读写操作
    
  • 对比传统写法:传统方式要 open → 判断打开是否成功(异常处理)→ 读写 → close,框架繁琐。
  • with 的好处:
  • 类似条件判断:打开**成功**才执行缩进块内的代码,失败则什么也不做、直接执行后面的内容;
  • 跳出 with 块时**自动 close() 文件**,不会忘记关闭、不会一直占用文件。
  • 开源项目和示例代码中普遍都是这种写法。

手工解析 BMP 文件格式(约 13:47)

  • 代码中用**二进制方式**(rb)打开 BMP 文件并手工解析其格式——这一段属于超前内容,老师只讲思路,细节下节课展开。
  • BMP 是固定格式的二进制文件:开头约 14 字节文件头 + 约 40 字节信息头,共约 54 字节,头部记录了图片的宽、高、大小等信息;之后才是真正的像素数据。
  • 像素数据的存储顺序是 BGR(先 blue,再 green,再 red),不是 RGB;把每三个字节以 [::-1](步长 -1)反序即可还原为 RGB(约 16:12)。
  • 每读三个字节(一个像素的 BGR)→ 反序成 RGB → 用图像处理学科的一个**线性转换公式**把 RGB 换算成 0~255 的灰度值 → 再按上面的比例换算成 0~69 的字符索引 → 把对应字符写入输出文件;处理完一行像素后写一个换行符,再写下一行(约 17:28 ~ 19:52)。
  • 图片的宽和高来自读取 BMP 文件头(具体读法下节课讲)。
  • 老师联系实际:信息安全方向的同学离不开二进制格式解析,之前"计算机安全与维护"课手工做数据恢复(解析文件记录、偏移、文件头)是同类工作,以后学 PE 文件(exe)也会遇到。

遗留问题:图为什么是倒过来的(约 20:30)

  • BMP 存储像素时**从最底下一行开始、从左到右**逐行存放,按顺序读出来图像就垂直翻转了,所以打出来是倒着的。
  • 思考题(不强制):可以
  • 用 f.seek() 调整读取位置来翻转;
  • 或改变写入顺序——先写最后一行、再写倒数第二行……
  • 方法不限,大家自己想办法。

注意事项(约 22:06)

  • 这段代码**只能处理 BMP 格式**,JPG/JPEG、PNG 都处理不了。
  • 可以用 Windows 画图把图片"另存为 BMP"来做格式转换。
  • 今天没有作业,把之前的作业交上来;下节课讲完二进制和编码后会布置相关作业。

示例与演示

  1. 整体效果演示(约 01:27):运行现成代码,读取同目录下的机器猫 BMP,生成 test.txt;缩小字号后可见(倒置的)机器猫字符画;在 Windows 记事本中也可用 Ctrl+- 缩小查看。
  2. 代码逐段讲解:
  3. GRAY_SCALE 字符串:70 个字符从"黑"到"白"排列(约 07:38);
  4. with open("test.txt", "w") as output: 打开输出文件,成功后再以 rb 打开 BMP(约 13:38);
  5. 读 14 字节 + 40 字节文件头获取宽高等信息(超前内容,略讲);
  6. 循环中每次读 3 字节(BGR)→ 反序为 RGB → 线性公式转灰度 → 除以 256/70 的比例取整得索引 → 取字符写入文件;每行结束写换行。
  7. 课堂思考题:利用 f.seek 或调整写入顺序把倒置的字符画翻转过来。

重点与难点

  • 理解 ASCII 图的核心映射思想:字符的"视觉黑度"对应灰度值,索引 0 最黑、69(空格)最白。
  • 范围换算:灰度值 ÷ (256/70) 取整 → 0~69 的字符下标;要意识到这是把 0~255 的区间压缩到 70 级。
  • with open as 的语义:成功才执行、自动 close,是文件操作的推荐写法。
  • 难点(超前内容):BMP 二进制格式——54 字节头部、像素按 BGR 存储、从下往上逐行存储导致图像倒置。
  • 实用限制:代码仅支持 BMP,其他格式需先转换。

关联内容

  • 本练习综合运用本章前几节:7.3 的文件读写(open、read、write、模式 w/rb、seek)、相对路径概念;with 语法老师表示之前也提过,这里正式引入。
  • 用到了之前讲过的切片技巧([::-1] 反序 BGR→RGB)和 base64 串的观感类比(约 01:16)。
  • 为下一节课做铺垫:二进制文件处理、字符编码、BMP 文件头的详细解析都在下一节展开,且**下节课的作业与本练习相关**。
  • 跨课程关联:与"计算机安全与维护"课的手工数据恢复(文件头、偏移解析)、将来信息安全方向的 PE 文件解析一脉相承。