Xiaoxia[PG]

File Into PNG,用PNG封装文件

我的代码38 条评论

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

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

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

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

creature.cpp

拉伸一下看看,

zoom

我的封装代码如下,采用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,如下:

file2png.py

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

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

rfc1035-DNS.txt

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

lwrbb.mp3

比较上面两张图会发现,

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

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

38 条评论

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

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

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

    2. wf,你今天怎么会这么早起!!!

      1. 我天天都起得早啊...

        1. 为什么有时候你跟我同样那么晚睡,早上能那么早起呢?

  2. 恩,有意思。可以用来隐藏数据:)

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

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

      1. 二维码存储的信息量太少了。。。

    2. 你们说那个马赛克是不是就是这样的隐藏数据呢...看来特工还挺多的,嗯......

  3. 一直以来最欣赏小虾的共享精神,你是我偶像

  4. 拿这个用来给程序加壳?。。。?

    1. 给程序加壳就不能运行了吧,感觉没多大作用。学习研究玩玩就很不错!!!

    1. 嗯!!!

    2. 这个是22实现的么?为什么上面写的是ib呢

      1. 因为写错了?

        1. 对不起,我搞错了,已经纠正!!!
          膜拜22~~

          1. 膜拜小虾!!

  5. 聪明踏实勤奋的BOY,好好努力,前途无量!

    1. 别夸,因为菜才要勤奋努力的。
      一起前进!

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

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

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

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

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

        1. o(∩∩)o...哈哈!谢谢,学到了不少!!!

  7. 0_0 咦? 这个是雪花么..

    1. 是很多小星,o(∩∩)o...哈哈

      1. 是呢.. 红色,蓝色,褐色,红色..... *_*

  8. 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 再打开看看。

    1. 等我有空用windows的时候试试 😀

  9. 使用如下命令
    copy /b 1.jpg + 1.txt + 1.rar all.jpg

  10. haha, today I finish it by myself to see how the picture stores the data. It help me understand the texture in Computer Graphic.

    1. How? Some books or websites told you?

  11. 啊,入口失效了,我想借鉴一下你那个网页的源代码(非图片处理代码)T_T

引用本文的文章

发表评论

评论会经过自动审核,链接请勿超过 3 个。