File Into PNG,用PNG封装文件

众所周知,PNG是一种无损的图像压缩存储格式。“无损”意味着我们除了可以把图像数据存放到PNG容器之外,还可以把非图像数据(例如文本、音频、视频等)数据压缩并存放到PNG容器中。现在似乎还没有这类软件可以让我们直接把数据压缩到PNG,据说iceboy大牛写了一个(纠正一下,不是iceboy,是twd2同学啊),没有用过(似乎需要Windows和.net运行环境)。详见:http://twd2.me/index.php/archives/1036

花了一晚时间研究,我也用Python写了一个压缩和解压缩的程序,同时支持在线使用,入口:

http://lab.xiaoxia.org/file2png/

例如,把一个CPP代码封装到PNG里,用图片查看器可以看到下面的一坨:

拉伸一下看看,

我的封装代码如下,采用RGB24位颜色模式,文件大小保存在transparency属性里。其实,可以做一个描述结构放在数据开始处,例如保存文件名,文件大小,创建时间等。只是我太懒了,直接用transparency的值来保存文件大小。

#!/usr/bin/python

import Image, sys, math, struct

Bits = 3

def fileToPng(source, target):
    data = open(source, "rb").read()
    fileSize = len(data)
    data += "\0\0\0"
    
    # Keep Ratio 4:3
    width = int(math.sqrt(float(fileSize) / Bits * 4 / 3)) + 1
    height = int((fileSize/Bits) / width) + 1
    if width * height * Bits < fileSize: raise Exception("File too large")
    if width == 0 or height == 0: raise Exception("File too small.")

    im = Image.new("RGB", (width, height))
    pngData = tuple(tuple(map(ord, [y for y in data[x: x+Bits]])) \
        for x in xrange(0, fileSize, Bits))
    im.putdata(pngData)
    im.save(target, "PNG", transparency = \
        struct.unpack("B"*Bits, struct.pack("I", fileSize)[:Bits]), compression = 9)
    
    del im, pngData

def pngToFile(source, target):
    im = Image.open(source)
    fileSize = struct.unpack("I", 
        struct.pack("B"*Bits, *im.info["transparency"]) + "\0"*(4-Bits))[0]
    data = im.tostring()[:fileSize]#"".join(["".join(map(chr, x)) for x in im.getdata()])
    open(target, "wb").write(data)
    del data, im
    
if __name__ == "__main__":
    if sys.argv[1] == "c":  
        fileToPng(sys.argv[2], sys.argv[3])
    elif sys.argv[1] == "x":
        pngToFile(sys.argv[2], sys.argv[3])
    else:
        print "file2png [c|x] file1 file2"

上面这段代码,保存为file2png.py,用PNG封装后,得到file2png.py.png,如下:

这是一张24*18的24位图片,封装前大小为1266,封装后大小为1252,压缩比例不明显。

下面用一个比较大的文本文件测试,这个是RFC1035-DNS的文档,大小为120kB。封装为PNG后,大小为55.5kB,比原来的一半体积还小。如下图所示:

另外,再测试压缩一个MP3音频文件。因为MP3本来已经高度压缩,所以压缩率不高,不过我们只想看看它的PNG图像,大小为32kB。

比较上面两张图会发现,

前者为压缩率高的PNG,它的图像比较暗淡,颜色种类少,连续相同颜色的数量多。

后者为压缩率低的PNG,它的图像比较鲜艳,颜色种类多,连续相同颜色的数量少。

File Into PNG,用PNG封装文件》有39个想法

  1. whitefirer

    沙发个~~那个,我觉得能不能用一张大图片,然后把数据通过计算压缩到那张图片里而那张图片表面上没什么很大变化啊

    我见过一个鸭子图,它把后缀改成rar就变成一个压缩包

    回复
    1. Xiaoxia 文章作者

      之所以可以变成压缩包,是因为解压软件会在文件中搜索压缩包数据开始的标记。

      回复
    1. whitefirer

      将程序或者源码压缩成图片,然后用手机扫描下,滴,一个程序就可以在你手机里运行啦~

      不过估计不太实际…首先要放大些以便能拍摄到,然后到找到最小色块长宽以便扫描,最后还要考虑不同屏幕颜色以及摄像头可能的色差…彩色的嘛….但据说二维码可以这样得到小的程序

      回复
  2. Pingback引用通告: 将文件转换为图片 | Wandai Blog

  3. shell

    建议入口出口都改成sys.stdin/stdout,这样可以和gzip/gunzip/bzip/7z配合,由压缩程序先进行一次压缩。或者和gnupg配合,进行先期加密。压缩和加密有混淆的效果,数据会失去明显特性而变得比较具有热噪声效果。更好的规范是将代码的调用方式改为和gzip/gunzip一致,这个程序就可以替代gzip和tar联合在一起打包。
    压缩包那个例子并不恰当,原理是因为jpg的定位标志在头部,而rar格式的TOC在尾部。因此将一个jpg和一个rar合并在一起后,相当于尾部附加了无效数据的jpg和头部附加了无效数据的rar,两者都是合法格式。如果jpg进行无损编码转换,这种附加数据的方法立刻会失效。
    比较靠谱的方法叫做隐写术,通过对人眼不可识别的一些细节信息进行变更,在一个大的,有意义的数据中写入一些其他数据。类似的应用还有adobe的水印技术,是通过隐写术写入签名,最强甚至可以在打印扫描后保持签名的存在。

    回复
    1. Xiaoxia 文章作者

      跟gzip、tar结合没想过呢,因为只是想玩一玩,所以没考虑做的那么通用,哈哈!

      jpg+rar那个看到过,但是感觉很容易就被知道了,rar解压的时候会扫描整个文件去寻找内容。

      对那个隐写术颇感兴趣,觉得很神奇!!!不知道是如何做到肉眼不可识别的。如果打印扫描后也能保持签名,是不是也要对打印机、扫描仪的水平有一定的要求。

      回复
      1. shell

        其实tar结合不难做啊,只要使用流输入输出,然后根据参数确定用文件流还是标准流。
        隐写术说穿了很简单,就是人为的调高部分颜色。由于你对细节颜色不敏感,所以看不出来。调整的越多,就越能对抗打印扫描。但是单位数据量图片中隐含的信息越少。

        回复
  4. undefined

    copy /b 1.jpg + 1.txt + 1.rar D:all.jpg

    复制如上代码,保存为 bat文件,确保bat所在目录中包含1.jpg、1.txt、1.rar 这三个文件。
    1、运行bat,生成的all.jpg,打开查看
    2、更改为all.jpg为all.rar 再打开查看
    3、更改为all.jpg为all.txt 再打开看看。

    回复

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据