1
1

More than 1 year has passed since last update.

画像にデータを色情報として埋め込むテスト

Last updated at Posted at 2021-09-22

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として読み込み
上記スクリプトで変換した画像はこうなりました
rasyoumon.png
このモザイク状の画像からテキストデータを復元するのは

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)

こんな感じになりました。
何かの参考になれば幸いです

1
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1