twitterでちょっとしたスクリプトみたいなものを共有したくても
文字数の関係でできなかったりするので画像に情報に埋め込んで共有できないかテストです
可逆圧縮フォーマットならピクセル単位で細かく詰め込むこともできるところですが
twitterでは一定サイズ以上は強制的にJPEGになるためJPEGの圧縮単位の8ピクセルを塗りつぶしたデータを作成するようにしています
追記:8×8でも色情報は可逆にならないようで復元できませんでした
JPEGはモノクロでしかテストしていなかったので失敗です とりあえず記事はそのままにします
Blenderのツールの部品としてのテストなので テキスト読み書きとイメージ作成はBlenderの機能ですが
他の環境でも大きな違いはないかと思います
text2imsge
import bpy
import zlib
import struct
import math
import numpy as np
# 画像にデータを色情報として埋め込む
def pack_data(bin_data):
check_sum = zlib.crc32(bin_data)
chunk_data = struct.pack('>q',check_sum)
chunk_data += struct.pack('>i',len(bin_data))
chunk_data += bin_data
return(chunk_data)
def hex2pixelarray(byte_data, width, block_size):
tile_u = width // block_size
tile_v = math.ceil( len(byte_data) /tile_u /3 )
# 諧調数
n = 1/15
pix_data = [n * int(b, 16) for b in byte_data]
pix_len = tile_u*tile_v
pix_array = np.ones( pix_len *3)
pix_array[:len(pix_data)] = pix_data
pix_array = pix_array.reshape(pix_len, 3)
# アルファ情報を付加
alpha_array = np.ones(pix_len).reshape(pix_len,1)
pix_array = np.append(pix_array, alpha_array, axis= 1)
pix_array = pix_array.reshape(tile_v,tile_u, 4)
# 8*8のブロックに拡大
block_image = pix_array.repeat(block_size, axis=0).repeat(block_size, axis=1)
return(block_image)
img_name = 'rasyoumon.png'
width = 1000
block_size = 8
# ファイル内のrasyoumon.txt のテキストを読み込み
txt_data = bpy.data.texts['rasyoumon.txt'].as_string()
# 文字データを圧縮バイナリデータに
bin_data = zlib.compress(txt_data.encode('utf-8'))
# 16進文字列に変換
byte_data = pack_data(bin_data).hex()
# 16s進データを画像情報に
block_image = hex2pixelarray(byte_data, width, block_size)
height = block_image.shape[0]
# 画像データの作成
image_object = bpy.data.images.new(name=img_name, width=width, height=height)
image_object.pixels = list(block_image.flatten())
テキストデータをZlib圧縮の上、データの長さとチェックサムを加えて 色情報にしています
同じ.blendファイル内に青空文庫にある芥川龍之介の羅生門のテキストをrasyoumon.txtとして読み込み
上記スクリプトで変換した画像はこうなりました
このモザイク状の画像からテキストデータを復元するのは
img2text.py
import bpy
import struct
import zlib
import numpy as np
block_size = 8
image_name = 'rasyoumon.png'
def img_to_nparray(img):
bit_len = len(img.pixels)
(width,height) = img.size
channels = img.channels #色数
pix_array = np.array(img.pixels)
pix_array = pix_array.reshape( height, width, channels)
return( pix_array )
def pix2bin(pix_array):
'''画像の値をバイナリに変換'''
byte_data = ''
n = 1/15
for i in range(len(pix_array)):
byte_data += hex(int(round(pix_array[i]/n)))[2]
return( bytes.fromhex(byte_data) )
# 画像の読み込み
image_object = bpy.data.images[image_name]
data_array = img_to_nparray(image_object)
# 画像から情報部分のみ取り出し
data_array = data_array[0::block_size, 0::block_size, :3].flatten()
# データの変換
checksum = struct.unpack('>q',pix2bin(data_array[:16]) )[0]
data_length = struct.unpack('>i',pix2bin(data_array[16:24]) )[0]
pix_data = pix2bin(data_array[24:24 + data_length*2])
if checksum == zlib.crc32(pix_data):
decomp_data = zlib.decompress(pix_data)
text_data = decomp_data.decode('utf-8')
# テキストデータの書き出し
text_obj = bpy.data.texts.new('test_output')
text_obj.write(text_data)
こんな感じになりました。
何かの参考になれば幸いです