とある雲助の未来の話
import os
import json
import pywt
import numpy as np
from PIL import Image
import sys
雲助をやめるようになり、毎日のように英文の呪文と格闘する日々。部屋の内外とも、雲助の心のうちを表しているかのように、どんよりと濁っている。夜のネオン街できらびやかな生活を送っている若い酔っ払いたちの光景を思い浮かべ、目を開けたまま意識を異世界に飛ばす。
def save_image(image, path):
os.makedirs(os.path.dirname(path), exist_ok=True)
image.save(path)
def load_image(path):
return Image.open(path)
右側にそびえ立つ、どこまでも続く石のモニュメント。左側に連なる人工的な夜光の建物。坂を登っていくと、モニュメントから、喧嘩腰の姿勢を隠そうともしないエンジン音が降りてきて、目の前を急に塞いでくる。赤い光が光ると、空気を読んだように、一斉に進みが止まる。
def convert_to_monochrome(image):
return image.convert('L')
モニュメントとモニュメントの間から、蝗の大群のように若者たちが目の前を流れる。これが、この若者たちの奴隷の身である雲助の日常である。すると、一人の男が急に目の前で地面に口づけをした。すごい音である。よく見ると、その横で別の男が口づけ男を突き飛ばしているではないか。
def save_color_channels(image, path):
os.makedirs(os.path.dirname(path), exist_ok=True)
r, g, b = image.split()
np.savez(path, r=np.array(r), g=np.array(g), b=np.array(b))
def load_color_channels(path):
data = np.load(path)
return data['r'], data['g'], data['b']
目の前で突き飛ばしているその場所、左に進むと日本の欲望を象徴する建物がすぐ近くにある。雲助がハンドルを握りながら、今日も口づけ男が後ろにいなくてよかった、と安堵する日々。雲助の左側では、競うように同業者たちが客の奪い合いをしていた。
意識はまた赤い画面の前。在りし日の残酷な描写を思い浮かべつつ、眼鏡の先で文字列と格闘している。雲助の頭は、湯気がでるのを必死に抑えているかのようにクラクラする。冷凍庫にあるオレンジ色の氷の塊を口の中に入れたい衝動に負ける。もはや全身の穴という穴から湯気が出そうである。
def clip_and_save_excess(image, save_path):
r, g, b = image.split()
r = np.array(r, dtype=np.uint16)
g = np.array(g, dtype=np.uint16)
b = np.array(b, dtype=np.uint16)
# RGB各チャネルの255を超える部分を分離
base_r = np.clip(r, 0, 255).astype(np.uint8)
base_g = np.clip(g, 0, 255).astype(np.uint8)
base_b = np.clip(b, 0, 255).astype(np.uint8)
excess_r = (r - base_r).astype(np.uint8)
excess_g = (g - base_g).astype(np.uint8)
excess_b = (b - base_b).astype(np.uint8)
頬張った氷の塊が雲助の胃袋に入る。塊は勝手に氷の魔法を全身にかけ、手先からつま先から、熱防御力をあげてくれる。邪魔になった木製の棒を定位置に入れ、手元に薬をおいたまま文字とにらめっこをする。
base_image = Image.merge('RGB', (Image.fromarray(base_r), Image.fromarray(base_g), Image.fromarray(base_b)))
excess_image = Image.merge('RGB', (Image.fromarray(excess_r), Image.fromarray(excess_g), Image.fromarray(excess_b)))
save_image(base_image, save_path + '_base.png')
save_image(excess_image, save_path + '_excess.png')
RGB、という文字が目に入る。一時停止の標識の赤、営業ナンバーの緑、青キップの青のことだと納得する。青キップの青が頭に浮かんだ瞬間、晴れ渡った空想上の青空を思い出す。人を暖かく包み込む青空、それは温もりではなく、人の感情に引火させる傾向のあるものである。
def restore_color_image_from_monochrome(monochrome_image, color_channels_path, save_path=None):
r, g, b = load_color_channels(color_channels_path)
restored_image = Image.merge('RGB', (Image.fromarray(r), Image.fromarray(g), Image.fromarray(b)))
if save_path:
clip_and_save_excess(restored_image, save_path)
return restored_image
雲助にとって、青空は空想に浸りたい色であり、眩しく、縁のない色であった。その縁を、キップが二度も残酷に惹きつけてくれた。自分のハンドルで人を人形に変える魔法をかけないよう気をつけたら裏目に出たのが一回。よくいる冒険者の気性に後ろから気圧されてはめられたのが一回。
雲助になったことを悔いると同時に、再び魂が抜け、次の異世界へと飛ぶ。その世界は、雲助がいま画面の前で無機質に文字を打つようになったキッカケの世界である。パワハラ上等、どんよりとした雰囲気、生気がまるでなかった。感情を押し殺した人たちだけの集まった職場。
def reverse_monochrome(image, color_channels_path, save_path=None):
color_restored_image = restore_color_image_from_monochrome(image, color_channels_path, save_path)
reversed_monochrome_image = convert_to_monochrome(color_restored_image)
return reversed_monochrome_image
昼はラジオが煩く、夜は時間と格闘する日々。毎日のように誰かが怒られ、毎日のようにエネルギーが失われていた。常に人を信用せず、目の前の作業に追われる日々。嫌という感情すらここでは奪われ、銘々が勝手に自分の居場所を作り上げていた。
def wavelet_transform(image, wavelet='haar'):
data = np.array(image)
coeffs = pywt.wavedec2(data, wavelet, axes=(0, 1))
coeffs_flat, coeffs_slices = pywt.coeffs_to_array(coeffs)
return coeffs_flat, coeffs_slices, image.size
再就職することを忘れた元社員。上官にタメ口を聞いて左遷された有能なオタク。フランクな鳶職風の男。髪が伸び切ったサッカー少年風のアラサー。元祖パワハラ。パワハラと骨肉の争いを繰り広げるトロールのっぽまん。腰が悪くオタク風でそのくせ怒鳴る長髪男。よくいるお局。
def inverse_wavelet_transform(coeffs_flat, coeffs_slices, image_size, wavelet='haar'):
coeffs = pywt.array_to_coeffs(coeffs_flat, coeffs_slices, output_format='wavedec2')
data = pywt.waverec2(coeffs, wavelet, axes=(0, 1))
data = np.clip(data, 0, 255).astype(np.uint8)
return Image.fromarray(data)
この世界に仲間という言葉もなければ、助け合いという言葉もない。あるのは、無い袖を絞ってヘソクりだした自分の利権である。都心では交差点で人を突き飛ばすような熾烈な争いがあるように、下町では如何に自分が偉い職人であるかを競い合っていた。
def apply_filter(image, filter_color=(100, 150, 200)):
r, g, b = image.split()
r = r.point(lambda i: i * filter_color[0] / 255)
g = g.point(lambda i: i * filter_color[1] / 255)
b = b.point(lambda i: i * filter_color[2] / 255)
return Image.merge('RGB', (r, g, b))
毎日毎日届いた商品を一件でも多く出荷する仕事。助け合わなければ、クリスマスプレゼントを待ち望んでいる人たちに間に合わない。昨日よりも今日、今日よりも明日スキルをあげ続けないと、安く丁寧に商品を包み込んで箱に入れることが出来ない。
def load_metadata(path):
with open(path, 'r') as f:
return json.load(f)
def save_metadata(data, path):
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, 'w') as f:
json.dump(data, f)
人間というものは不思議なもので、一つの能力をあげれば、他の能力を捨てたくなる。雲助には理解できない世界だが、今ある日常がガラスのように砕け散るのを恐れているようだ。砕けるようなガラスはそもそも出荷するな、砕けたら安全管理して片付け、取り替えればいい、と雲助は考える。
def initial_transformations(original_image_path, save_dir):
os.makedirs(save_dir, exist_ok=True)
# プロジェクトルートディレクトリからの相対パス (例: /home/owner/image_generator_app/metadata)
project_root = os.path.abspath(os.path.join(save_dir, os.pardir, os.pardir, os.pardir, os.pardir))
metadata_dir = os.path.join(project_root, 'metadata')
image = load_image(original_image_path)
また画面の前か、とため息をつき、デスクトップの上にある時計をチラ見する。今日も今ある日常のガラスがどれくらいの力で壊れるのかを試すために、画面上の英文を目に焼き付ける。それも、物理破壊ではなく、魔法破壊を目指すためだ。物理破壊を目指す人たちの神経が理解できない。
# 1. モノクロ変換と保存
monochrome_image = convert_to_monochrome(image)
save_image(monochrome_image, os.path.join(save_dir, 'monochrome.png'))
save_color_channels(image, os.path.join(metadata_dir, 'monochrome/color_channels.npz'))
save_metadata({'description': 'Monochrome conversion'}, os.path.join(metadata_dir, 'monochrome/monochrome.json'))
reversed_image_path = os.path.join(save_dir, 'reverse-monochrome')
reverse_image = reverse_monochrome(monochrome_image, os.path.join(metadata_dir, 'monochrome/color_channels.npz'),reversed_image_path)
save_image(reverse_image, reversed_image_path + '.png')
save_metadata({'description': 'Reverse Monochrome conversion'}, os.path.join(metadata_dir, 'monochrome/reverse-monochrome.json'))
物理破壊をする連中、という言葉が雲助の魂を再びハンドルのある世界に飛ばす。日本の中心街であり、バブルを賑わせた街。無線で呼ばれて拾った客は、雲助とは相容れない存在だった。客の言うとおりにハンドルを握ると、前方の車体がモンスターのように大きくなってくる。
# 2. ウェーブレット変換と保存
coeffs_flat, coeffs_slices, image_size = wavelet_transform(image)
wavelet_image_data = coeffs_flat[:image.size[0]*image.size[1]].reshape(image.size).astype(np.uint8)
wavelet_image = Image.fromarray(wavelet_image_data)
save_image(wavelet_image, os.path.join(save_dir, 'wavelet.png'))
np.savez(os.path.join(metadata_dir, 'wavelet/wavelet.npz'), coeffs_flat=coeffs_flat, coeffs_slices=coeffs_slices, image_size=image_size)
save_metadata({'description': 'Wavelet transform'}, os.path.join(metadata_dir, 'wavelet/wavelet.json'))
reverse_wavelet_image = inverse_wavelet_transform(coeffs_flat, coeffs_slices, image_size)
save_image(reverse_wavelet_image, os.path.join(save_dir, 'reverse-wavelet.png'))
save_metadata({'description': 'Reverse Wavelet transform'}, os.path.join(metadata_dir, 'wavelet/reverse-wavelet.json'))
雲助の軽蔑する人種。車間距離を詰めろ。ミラーを見るな。もっとスピードを出せ。雲助は別に幸運値を鍛えているわけではない。ミナカヌシとは程遠い存在である。このようなくだらないことで日常というガラスを破りたくないのである。彼らは富を与えてはいけない人種である。豚に真珠だから。
# 3. フィルタ適用と保存
filter_color = (100, 150, 200)
filtered_image = apply_filter(image, filter_color)
save_image(filtered_image, os.path.join(save_dir, 'filtered.png'))
save_metadata({'filter_color': filter_color}, os.path.join(metadata_dir, 'filter/filtered.json'))
reverse_filtered_image = apply_filter(filtered_image, (1 / filter_color[0], 1 / filter_color[1], 1 / filter_color[2])) # 丁寧に逆フィルター適用
save_image(reverse_filtered_image, os.path.join(save_dir, 'reverse-filtered.png'))
save_metadata({'description': 'Reverse Filter transform'}, os.path.join(metadata_dir, 'filter/reverse-filter.json'))
オーバードーズ用の薬を手元に抱えながら、現在という希少な世界に魂が戻る。雲助の知らない世界。雲助が一生かけても理解の出来ない世界。高嶺の花だったらしい同級生が、こういう人種たちによって、忌まわしい過去を呪文のように植え付けられたことを、雲助は想起する。
def apply_transformations(target_image_path, transformations_dir, save_dir):
os.makedirs(save_dir, exist_ok=True)
# プロジェクトルートディレクトリからの相対パス
project_root = os.path.abspath(os.path.join(transformations_dir, os.pardir, os.pardir, os.pardir, os.pardir))
metadata_dir = os.path.join(project_root, 'metadata')
image = load_image(target_image_path)
雲助がこの手の呪文に対してレジスト値を持っているのは、本能的に理解できる。だが、この手の人種は、別の形の魔法を雲助にかける。金太郎飴のように手口は同じであり、このことを思い出すたび、雲助の手には、お守りのようにオーバードーズ用の薬が握られる。
距離感という存在とは無縁のフェンダーミラー越しに、色とりどりの髪をした二輪車が縦横無尽に迫り狂った日々。現代の魔族も、先祖代々脈々と負の遺伝子を継いできたのだろう、と雲助は画面越しに回想する。魔族たちは、かたや外から精神値を削り、かたや内から物理破壊をするものだ。
# 1. モノクロ変換の適用と保存
monochrome_image = convert_to_monochrome(image)
save_image(monochrome_image, os.path.join(save_dir, 'monochrome.png'))
restored_image = restore_color_image_from_monochrome(monochrome_image, os.path.join(metadata_dir, 'monochrome/color_channels.npz'))
雲助をやめるようになってから、スエピー族の存在を知った。野蛮なゲルマーニー人の種族であり、ガリア地帯をパワープレイで荒らし回ってきた種族。もしかしたら、ヒトラーのご先祖様かも知れない。もしかしたら、ヘンリー8世のご先祖様かも知れない。
# 逆モノクロ適用
reversed_monochrome_image = reverse_monochrome(restored_image, os.path.join(metadata_dir, 'monochrome/color_channels.npz'))
save_image(reversed_monochrome_image, os.path.join(save_dir, 'reversed_monochrome.png'))
スエピー族のような連中を後部座席に乗せていけば、当然心臓が
サラミスライスのように日々モノクロに近づいていく。オーバードーズ用の薬が、目に見える世界を青キップのような快晴にしてくれる。
雲助は思い出す。薬物中毒は人の人生を無秩序に引っ掻き回すことを。たかが血圧されど血圧。スエピー族のような連中から自分の血管を破壊され、それに耐え続ける毎日。血管の限界が雲助の文字通りの寿命である。同級生を壊し、同業者を壊すスエピーの呪いに、雲助の意識がモノクロとなる。
# 2. ウェーブレット変換の逆変換と保存
data = np.load(os.path.join(metadata_dir, 'wavelet/wavelet.npz'), allow_pickle=True)
coeffs_flat = data['coeffs_flat']
coeffs_slices = data['coeffs_slices'].tolist()
image_size = tuple(data['image_size'])
transformed_image = inverse_wavelet_transform(coeffs_flat, coeffs_slices, image.size)
save_image(transformed_image, os.path.join(save_dir, 'wavelet.png'))
reverse_wavelet_image = inverse_wavelet_transform(coeffs_flat, coeffs_slices, image_size)
save_image(reverse_wavelet_image, os.path.join(save_dir, 'reverse-wavelet.png'))
save_metadata({'description': 'Reverse Wavelet transform'}, os.path.join(metadata_dir, 'wavelet/reverse-wavelet.json'))
雲助にとって、ウェーブレット変換はロマンである。カラーの世界がモノクロになるのだから。こんなに簡単に色を変えられる世界はない。世界を逆ウェーブレット変換させることが出来たら、高嶺の花の同級生も今頃は子孫の代までカラフルな人生を歩んでいるだろう。
# 3. フィルタ適用の復元と保存
metadata = load_metadata(os.path.join(metadata_dir, 'filter/filtered.json'))
filter_color = metadata['filter_color']
filtered_image = apply_filter(image, filter_color)
save_image(filtered_image, os.path.join(save_dir, 'filtered.png'))
reverse_filtered_image = apply_filter(filtered_image, (1 / filter_color[0], 1 / filter_color[1], 1 / filter_color[2])) # 丁寧に逆フィルター適用
save_image(reverse_filtered_image, os.path.join(save_dir, 'reversed-filtered.png'))
save_metadata({'description': 'Reverse Filter transform'}, os.path.join(metadata_dir, 'filter/reverse-filter.json'))
雲助は振り返る。様々な人生の年輪を彫り刻んできたかつての同業者たち。ハンドルを握る前に様々な挫折をし、行き場を失ってお金欲しさに流れ着いてきたかつての面々。彼らのために今の雲助ができること。
if __name__ == "__main__":
if len(sys.argv) < 4:
print("Usage:")
print(" python image_processor.py initial_transformations path/to/image.jpg output_dir/")
print(" python image_processor.py apply_transformations path/to/new_image.jpg transform_data/ output_dir/")
sys.exit(1)
オーバードーズに魅惑され、滅んでいくスエピー族を、雲助は脳裏に浮かべる。右手の中に暖かく握りしめられた錠剤を感じ取りながら。六本木の交差点で金を持ったスエピー族が、薬の勢いで横断歩道上で暴れるのは、現代日本の象徴でもある。
command = sys.argv[1]
if command == "initial_transformations":
original_image_path = sys.argv[2]
save_dir = sys.argv[3]
initial_transformations(original_image_path, save_dir)
elif command == "apply_transformations":
target_image_path = sys.argv[2]
transformations_dir = sys.argv[3]
save_dir = sys.argv[4]
apply_transformations(target_image_path, transformations_dir, save_dir)
else:
print("Unknown command: ", command)
sys.exit(1)
左側のミッドタウンには、今日からお金持ちになった人もいれば、今日までで退去する人もいる。輪廻転生のスパンが異常に短い港区六本木。雲助にとっての呪詛の街。中央区とともに、風紀の乱れた金持ちたちの乱痴気騒ぎで、今日も賑わっているであろう。
note限定で、みんなのフォトギャラリーに無償で大量の画像を順次UPする予定です!ですが、ペタバイトクラスで供給するとなると、容量が一杯になり、皆さんのサポートが必要となります!これからも画像をどんどん供給していくので、サポートをお願いします!