将数据存储在博文中的图片中

Posted 卓晴

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了将数据存储在博文中的图片中相关的知识,希望对你有一定的参考价值。

简 介: 对于平时工作中需要保存的数据文件,将其转换成黑白分割线图片存储的博文中,实现内容的一体化存储,便于后期工作内容的维护。进行数据图片转换的软件使用Python编写,能够在不同的平台应用。

关键词 数据图片一体化博文

一体化博文 目 录
Contents
应用背景 数据与图片转换 算法优缺点 总 结 转换小程序

 

§01 体化博文


一、应用背景

  平时的研究所记录的博文,不仅保留了研究的思路、方案,同样还有一些其它的资源需要记录,比如 图片、声音、数据、图像、处理软件等等。存储这些信息有助于研究思想的交流、研究后续工作的持续等等。

  现在网络上也出现了很多便于研究人员存储这些资源的平台,最为常用的就是Github以及其他类似平台,它们可以将相关开发文档、程序、数据都集中存储在不同的目录中,便于后期重复应用以及相互的交流。

  还有一些轻量级的平台,比如国内的CSDN,允许在博文中保存图片、代码等,这也方便记录简单阅读和研究工作。但如果需要保存一些数据内容,比如开发所使用到的小型工具软件、声音数据、处理所产生的中间结果等,就无法直接在博文中进行保存。

  解决这个问题的一个思路,就是把需要保存的数据转换成图片,嵌入在网络博文中进行保存。在这个过程需要解决:

  • 能够方便将数据与图片进行转换进行相互转换;
  • 数据在图片中的编码能够对图像压缩算法具有防疫特性,即不会因为网站图片存储压缩带来数据的变化;

二、数据与图片转换

  转对利用图片存储数据要求,经过实验可以看到,直接将数据的bit转换成成图像的黑白亮度值,是一种罪鲁棒的方法,对于常见到的图像压缩算法所带来的图像像素亮度值的变化都具有很高的稳定性。因此,下面的代码就可以实现对于需要转换数据文件转换成相应的黑白图片的功能。

1、算法代码

  算法首先根据所需要存储的数据文件,将所有的数据字节转换成八个bit,使用 0 表示bit0,使用0xff 表示 bit1。然后将其转换成一个二维的矩阵,最终将其存储在一个灰度图像中。

  为了便于后期解码,在转换的数据中包含有以下信息:

数据格式:
数据长度:4个字节
文件名称:128个字节
数据:数据文件内容

  首先定义了灰度图像的长宽比 IMAGE_WIDTH_HEIGHT_RATIO = 16。图像数据对应的二维矩阵的尺寸满足这个比值。

#------------------------------------------------------------
FILENAME_LENGTH     = 128
def datafile2imgbuf(filename):
    if not os.path.isfile(filename):
        printf(filename)
        return 0, 0, b''

    data = b''

    file_stats = os.stat(filename)
    with open(filename, 'rb') as f:
        data = f.read(file_stats.st_size)

    datalen = len(data)

    printf(datalen)

    filebytes = bytes(os.path.basename(filename), 'gbk')
    filebytes = filebytes + b'\\x00'*(FILENAME_LENGTH - len(filebytes))
    data = datalen.to_bytes(4, 'little') + filebytes + data

    datalen = len(data)

    W = int(ceil(sqrt(datalen * IMAGE_WIDTH_HEIGHT_RATIO)))
    if W == 0: W = 1
    H = int(ceil(datalen / W))

    appenddata = W * H - datalen
    data = data + b'\\x00'*appenddata

    dataarray = frombuffer(data, 'uint8')
    dataarray.resize(H,W)

    return W,H,len(data),dataarray

#------------------------------------------------------------
def data2data8(data):
    datashape = shape(data)

    databit = []
    for i in range(8):
        maskdim = (ones(datashape) * (1<<i)).astype(uint8)
        datamask = (data & maskdim)
        datamask[datamask != 0] = 0xff
        databit.append(datamask.T)

    data8 = stack(databit, axis=0).T.reshape(-1,datashape[-1]*8)

    return data8

def data82data(data):
    data[data < 0x80] = 0
    data[data != 0] = 1

    datashape = shape(data)
    W = datashape[-1]//8
    H = datashape[0]

    data = data.reshape(size(data))

    bits = array([1,2,4,8,16,32,64,128])
    data = data.reshape(-1, 8) * bits
    data8 = sum(data.T,0).reshape(H,W)

    return data8

#------------------------------------------------------------
def datafile2img(datafile, imagefile):
    w,h,datalen,data = datafile2imgbuf(datafile)
    data8 = data2data8(data)
    cv2.imwrite(imagefile, data8)

2、效果测试

  测试一下上述的代码功能。下图是TEASOFT软件存储的数据内容,这个文件只能有TEASOFT软件打开。如果仅仅存储这个图像,则后期就无法继续使用TEASOFT软件进行编辑。

  首先利用Python中的 zipfile 软件包,将该软件及其附属的资源打包形成 ZIP 数据文件,然后利用上述代码将 ZIP 数据文件转换成图片。

▲ 图1.2.1 用于保存的DOP文件

  下图就是转换后形成的包含数据信息的图片。嵌入在博文中,它仅仅相当于一个分割线,对于博文内容没有影响。

  下面是对这个嵌入的图像进行放大,可以看到这是一个黑白图像。其中每个像素代表着数据的一个比特。 这个图像虽然经过了JPG的压缩,但下载之后,进行解码仍然可以准确还原出原来的数据。

▲ 图1.2.2 局部放大后的图像

三、算法优缺点

  将需要存储的数据(程序、数据、音频)等存储在博文中的图片中,可以:

  • 对于数据内容和格式没有限制,不受网络图片 压缩影响;
  • 博文、数据一体化存储,方便后期进行持续更新工作;

  但上述方法还有以下缺点:

  • 存储数据容量受限。 有的图片存储平台对于图片的大小是由限制,这样带来所能够存储的数据容量受到影响;
  • 编码效率低。虽然使用黑白图片,可以抑制图片转存压缩的影响,但整体上数据压缩效率比较低。经过测试,存储的数据大小与生成的图片大小之间比例约为 1:10。

 

  结 ※


  于平时工作中需要保存的数据文件,将其转换成黑白分割线图片存储的博文中,实现内容的一体化存储,便于后期工作内容的维护。

  进行数据图片转换的软件使用Python编写,能够在不同的平台应用。

一、转换小程序

#!/usr/local/bin/python
# -*- coding: gbk -*-
#============================================================
# IMGDATA.PY                   -- by Dr. ZhuoQing 2022-06-16
#
# Note:
#============================================================

from head import *
import cv2
import shutil

csdn_window = '写文章-CSDN博客'

#------------------------------------------------------------
import requests
import zipfile
import urllib.request

#------------------------------------------------------------
from PIL    import Image
from io     import BytesIO
import      win32clipboard

def send_to_clipboard(clip_type, data):
    win32clipboard.OpenClipboard()
    win32clipboard.EmptyClipboard()
    win32clipboard.SetClipboardData(clip_type, data)
    win32clipboard.CloseClipboard()


def image2clipboard(imagefile):
    image = Image.open(imagefile)
    output = BytesIO()
    image.convert('RGB').save(output, 'BMP')
    data = output.getvalue()[14:]
    output.close()
    send_to_clipboard (win32clipboard.CF_DIB, data)

#------------------------------------------------------------
def zip2dir(filename, dirs):
    dopfile = ''
    basedir = ''
    bsirfilename = ''


    with zipfile.ZipFile(filename, 'r') as f:
        for fn in f.infolist():
            fns = str(fn).split()[1]
            if fns.find('.bsir') >= 0 or fns.find('.BSIR') >= 0:
                fnss = fns.split('\\'')
                basedir = fnss[1].split('/')[0]
                bsirfilename = fnss[1]
                dopfile = os.path.join(dirs, fnss[1])
                break

        f.extractall(dirs)

    bsirname = dopfile.replace('/', '\\\\')

    newbasedir = ''
    for i in range(1000):
        newbasedir = '%s%d'%(basedir, i)
        newdirs = os.path.join(dirs, newbasedir)
        if not os.path.isdir(newdirs):
            break

    if not os.path.isdir(newdirs):
        origindir = os.path.join(dirs, basedir)
        os.rename(origindir, newdirs)
        dopfile = os.path.join(dirs, bsirfilename.replace(basedir, newbasedir))
        bsirname = dopfile.replace('/', '\\\\')

#    printf(bsirname)
#    exit()
    return bsirname


#------------------------------------------------------------
IMAGE_WIDTH_HEIGHT_RATIO    = 16

IN_DIR      = r'd:\\temp'
OUT_DIR     = r'd:\\temp'

IN_FILE     = 'temp0001.zip'
OUT_FILE    = 'temp0001.jpg'

#------------------------------------------------------------
FILENAME_LENGTH     = 128
def datafile2imgbuf(filename):
    if not os.path.isfile(filename):
        printf(filename)
        return 0, 0, b''

    data = b''

    file_stats = os.stat(filename)
    with open(filename, 'rb') as f:
        data = f.read(file_stats.st_size)

    datalen = len(data)

#    printf(datalen)

    filebytes = bytes(os.path.basename(filename), 'gbk')
    filebytes = filebytes + b'\\x00'*(FILENAME_LENGTH - len(filebytes))
    data = datalen.to_bytes(4, 'little') + filebytes + data

    datalen = len(data)

    W = int(ceil(sqrt(datalen * IMAGE_WIDTH_HEIGHT_RATIO)))
    if W == 0: W = 1
    H = int(ceil(datalen / W))

    appenddata = W * H - datalen
    data = data + b'\\x00'*appenddata

    dataarray = frombuffer(data, 'uint8')
    dataarray.resize(H,W)

    return W,H,len(data),dataarray

#------------------------------------------------------------
def data2data8(data):
    datashape = shape(data)

    databit = []
    for i in range(8):
        maskdim = (ones(datashape) * (1<<i)).astype(uint8)
        datamask = (data & maskdim)
        datamask[datamask != 0] = 0xff
        databit.append(datamask.T)

    data8 = stack(databit, axis=0).T.reshape(-1,datashape[-1]*8)

    return data8

def data82data(data):
    data[data < 0x80] = 0
    data[data != 0] = 1

    datashape = shape(data)
    W = datashape[-1]//8
    H = datashape[0]

    data = data.reshape(size(data))

    bits = array([1,2,4,8,16,32,64,128])
    data = data.reshape(-1, 8) * bits
    data8 = sum(data.T,0).reshape(H,W)

    return data8


#------------------------------------------------------------
def datafile2img(datafile, imagefile):
    w,h,datalen,data = datafile2imgbuf(datafile)
    data8 = data2data8(data)
    cv2.imwrite(imagefile, data8, [int(cv2.IMWRITE_JPEG_QUALITY), 100])

#------------------------------------------------------------
def img2databuf(imagefile):
    img = cv2.imread(imagefile)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY).astype(uint8)

#    printf(type(gray), shape(gray))

    data = data82data(gray).astype(uint8)
    data = data.reshape(size(data)).tobytes()

    datalen = int.from_bytes(data[:4], 'little')
    filename = data[4:4+FILENAME_LENGTH].decode('gbk').strip('\\x00')
    return datalen, filename, data[4+FILENAME_LENGTH:]


def img2datafile(imagefile, datafile):
    datalen, filename, data = img2databuf(imagefile)

    with open(outfile, 'wb') as f:
        f.write(data[:datalen])

#------------------------------------------------------------
pastestr = clipboard.paste()
urlstr = tspread()[-1]
if len(urlstr) > 0:
    pastestr = urlstr

infile = ''
npzfileflag = 0
pasteflag = 0

#------------------------------------------------------------
if len(sys.argv) > 1:
    以上是关于将数据存储在博文中的图片中的主要内容,如果未能解决你的问题,请参考以下文章

英伟达收购SwiftStack对象存储公司

博文中涉及到的软件包

kettle将图片转换至二进制存储至数据库

CDN服务技术架构图

生成中文词云图的制作:带有不同的背板

S3 存储附件和图片无法上传