java 实现png图片转为字符画

本文详细介绍了PNG图像文件的格式解析,包括文件签名、数据块结构以及关键数据块如IHDR、IDAT和IEND的作用。通过Java代码展示了如何读取PNG文件的元数据,如宽度、高度、颜色类型等,并强调了IDAT数据块在解码过程中的重要性。此外,还提及了颜色类型的处理和Zlib库在解压缩数据中的应用。
摘要由CSDN通过智能技术生成

注:此文章并不是特别详细,只做参考

PNG文件格式解析

PNG 图像格式文件由一个 8 字节的 PNG 文件署名域和 3 个以上的后续数据块(IHDR、IDAT、IEND)组成。

PNG 文件中,每个数据块(比如IHDR,IDAT等)由4个部分组成:

名称字节数说明
Length (长度)4 字节指定数据块中数据域的长度,其长度不超过(2^31-1)字节
Chunk Type Code (数据块类型码)4 字节数据块类型码由 ASCII 字母(A-Z和a-z)组成
Chunk Data (数据块数据)可变长度存储按照 Chunk Type Code 指定的数据
CRC (循环冗余检测)4 字节存储用来检测是否有错误的循环冗余码

PNG 文件的前八个字节始终包含以下(十进制)值:

   137 80 78 71 13 10 26 10

这个签名表示该文件的剩余部分包含一个PNG图像,由一系列与开始区块的 IHDR块,并结束IEND块。

请参阅基本原理:PNG 文件签名

在此我们只关注几个关键数据块

表1.1
数据块符号数据块名称多数据块是否可选位置限制
IHDR文件头数据块第一块
PLTE调色板数据块在IDAT之前
IDAT图像数据块与其他IDAT连续
IEND图像结束数据最后一个数据块

 通过nio读取文件 存入实例的buffer中 验证是否为PNG图像,这里我从阿里巴巴图标库下载了几个图片(后续只针对此图片进行处理)

String path = "D:/temp/面性长直发.png";
LoadPngPic loadPngPic = new LoadPngPic(path);
try (FileInputStream stream = new FileInputStream(path.toFile())) {
    FileChannel channel = stream.getChannel();
    this.buffer = ByteBuffer.allocate((int) channel.size());
    channel.read(this.buffer);
    this.buffer.flip();
    return this;
} catch (IOException e) {
    e.printStackTrace();
    return null;
}

if(0x89504E470D0A1A0AL.equals(this.buffer.getLong())) {
    //是png格式文件
}

IHDR


 buffer.getLong(); // 偏移8个字节(长度以及数据块类型 因为第一个肯定是IHDR块 所以在此不做判断)
 picture.setWidth(buffer.getInt()); // 宽度
 picture.setHeight(buffer.getInt()); // 高度
 picture.setDepth(buffer.get()); // 图像深度
 picture.setColorType(buffer.get()); // 颜色类型
 buffer.get(); //压缩方法(在此用不到 所以无需保存)
 picture.setFilterMethod(buffer.get()); //过滤器方法
 picture.setInterlaceMethod(buffer.get()); //隔行扫描方法
 buffer.getInt(); // 循环冗余检测

此时以及读取了png文件的IHDR数据块 获取到了 宽度、高度、深度、颜色类型、过滤器方法、隔行扫描方法等信息, 此时我们debug一下看看:

图1.1
​​​​​​

根据官方描述

可知,PLTE数据块只有在颜色类型为3时,必定出现。颜色类型为 2 和 6 时  有可能会出现 ,所以此数据块暂且不管

我们之间进入正题看IDAT

IDAT


从表1.1可以看到IDAT数据块是连续的并且出现位置不固定的,所以我们读取的时候使用递归方式,每次读取出标识判断是否是IDAT数据块,如果不是就直接读取下一块

private void readIDAT(Picture picture) {
    int length = buffer.getInt();
    int identifi = buffer.getInt();
    if (identifi != IDAT) {
        buffer.get(new byte[length + 4]);
        readIDAT(picture);
        return;
    }
    // TODO 如果是IDAT数据块的处理逻辑
}

读取到IDAT数据块之后 就相当于拿到了此png图片的真正数据信息

然后根据图片的颜色类型 进行相应的解析操作

根据图1.1我们看到 此时我们读到的颜色类型为6 (带α通道的真彩色图像)

通过Zlib工具包对IDAT的数据进行反压缩后得到了真实的字节数据

byte[] data = new byte[length];
buffer.get(data);
byte[] decompress = ZLibUtils.decompress(data);

此时数据还是经过过滤后的 具体过滤算法参看

PNG Specification: Filter Algorithms

对数据进行反过滤后 按颜色值获取灰度进行打印

最终效果:

具体代码我已上传至github和码云,如果觉得还可以,麻烦帮忙点个star,谢谢☺:

GitHub - 970100646/PNG2Character: PNG图片转为字符画

PNG2Character: PNG图片转换为字符画

参考文章:

PNG (Portable Network Graphics) Specification

PNG的算法原理 - 知乎

音视频入门-11-PNG文件格式详解 | binglingziyu的博客

https://www.zhuyuntao.cn/png%E6%96%87%E4%BB%B6%E7%BB%93%E6%9E%84%E8%A7%A3%E6%9E%90

https://medium.com/@duhroach/how-png-works-f1174e3cc7b7

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值