乙Py先生のプログラミング教室
初学者のためのプログラミング学習サイト

画像処理

今回は重力レンズエフェクトで
画像を歪ませで遊んでみました。


解説動画はこちら


 

重力レンズとは

光が天体の重力によって曲げられ
天体があたかもレンズとして働く効果のことです

光の曲がり具合はレンズとなる天体の質量が大きいほど強く
背後の銀河から来る光が強く曲げられて像が大きく歪んで見えます


今回はレンズをブラックホールで想定して
擬似的な効果の計算で画像を作成するコードのご紹介です。


基本の計算式

レンズ中心からの距離が小さくなるほど
(レンズに近づくほど)歪みが強くなるような計算です

元々の重力レンズの計算は少し難解かなと思いましたので
簡易な計算でエフェクトをかけます。

スクリーンショット 2024-11-23 17.08.32




処理の流れ

入力画像をNumPy配列に変換し、全ピクセルの座標を計算
重力レンズ効果を適用する領域を特定

レンズ中心からの距離に基づいて歪みを計算し、新しいピクセル座標を算出
新しい座標に基づいてピクセルを再配置し、ブラックホール領域を黒で塗りつぶし
最終的な画像として出力


ipywidgets でUIを作って操作できます。
スライダーの値を変えると、画像が変化します。

Google colabなどで試す事ができます。


コードはこちら
from PIL import Image, ImageDraw
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact, IntSlider, FloatSlider

cached_image = None

# 重力レンズエフェクトをかけた画像を生成する
def apply_gravitational_lens(image, lens_radius, black_hole_radius, distortion_factor):
    
    # 画像をNumPy配列に変換
    input_pixels = np.array(image)
    width, height = image.size
    cx, cy = width // 2, height // 2
    
    # 座標グリッドの生成と中心からの相対座標,距離の計算
    y, x = np.meshgrid(np.arange(height), np.arange(width), indexing="ij")
    dx, dy = x - cx, y - cy
    sx, sy = x.copy(),y.copy()
    distance = np.sqrt(dx**2 + dy**2)

    # 歪みを適用 { distortion = 1+distortion_factor * ((lens_radius - distance[lens_mask]) / lens_radius)**2 }
    lens_mask = (black_hole_radius < distance) & (distance < lens_radius)
    distortion = 1 + distortion_factor * ((lens_radius - distance[lens_mask]) / lens_radius)**2
    sx[lens_mask] = cx + dx[lens_mask] * distortion
    sy[lens_mask] = cy + dy[lens_mask] * distortion

    # 座標のクリッピングと出力画像の生成
    sx = np.clip(sx, 0, width - 1).astype(int)
    sy = np.clip(sy, 0, height - 1).astype(int)
    output_pixels = input_pixels[sy, sx]

    # ブラックホール領域の適用
    black_hole_mask = distance <= black_hole_radius
    output_pixels[black_hole_mask] = [0, 0, 0]
    return Image.fromarray(output_pixels)

# インタラクティブUI部
def interactive_gravitational_lens(image_path):

    global cached_image
    if cached_image is None:
        cached_image = Image.open(image_path).convert("RGB") 

    def update(lens_radius, black_hole_radius, distortion_factor):
        output_image = apply_gravitational_lens(
            cached_image,
            lens_radius=lens_radius,
            black_hole_radius=black_hole_radius,
            distortion_factor=distortion_factor,
        )
        plt.figure(figsize=(8, 8))
        plt.imshow(output_image)
        plt.axis('off')
        plt.title(f"Lens Radius: {lens_radius:03}, Black Hole Radius: {black_hole_radius:03}, Distortion: {distortion_factor:.02}")
        plt.show()

    interact(
        update,
        lens_radius=IntSlider(min=50, max=990, step=10, value=50, description="Lens Radius"),
        black_hole_radius=IntSlider(min=10, max=200, step=5, value=10, description="Black Hole Radius"),
        distortion_factor=FloatSlider(min=0.1, max=10.0, step=0.1, value=0.1, description="Distortion Factor"),
    )

interactive_gravitational_lens("画像パス")

download-1

レンズの半径と
歪み具合を調整して
面白画像を作って遊んでみてください。

動画では色々な画像で試しているので
ぜひみてみてくださいね

今回はこれまでです
それでは。



じゃにーずさんから
新しいユニットがデビューしたみたいですね。

今回は顔で遊んでみました。

解説動画はこちら


さて顔画像を使って
いろいろ遊んでみましょう。


平均顔は
ちょっと残念な感じですねー
作品としてはイケていません。

ここからは画像合成で
イケていたやつをのせてみます。

download-5

download-4


download-3

download-2
download


いやーみなさん
かなり綺麗な目鼻立ち
さすがジャニーズです。

ただし、少し残念なのは
髪の量が多くて
眉毛部分が隠れてしまっており
合成がうまくいきませんでした。

素材として
も少し髪をアップにしていただけると
良いですねー

今回は雑な画像の合成で
遊んでみました。

それでは。

2021年あけましておめでたです。

新年1発目は
モザイクで遊んでいきたいと思います。


動画はこちら



さてモザイクに関してですが
モザイクと言うのは

非可逆変換
一定領域の色情報を読み込んで
その平均値で画像を処理する方法や

一定領域の代表値で
全体を塗りつぶす方法がある

このモザイク処理を施したものは
元に戻すことができない。

可逆変換
処理範囲を縦横に分割して
並べ替えるという方法などがある

このモザイク処理は
元に戻すことが可能

ということになってます。

一般的なモザイクと言うのは
非可逆変換の方で
外すことは出来ませんが

今回のは可逆変換の外せる方を
やっていきたいと思います。

プログラムのソースコードはこちら
from PIL import Image
import matplotlib.pyplot as plt
%matplotlib inline

# 画像表示
def plt_img(img):
    plt.figure(figsize=(20,10))
    plt.imshow(img)
    plt.axis('off')
    plt.show()

# モザイク処理(mode0:モザイクをかける,mode1:モザイクを外す)
def mosaic(image,mode=0):
    pix = image.load()
    wid, hei = image.size
    s = wid * hei
    if mode==0:
        for _ in range(2):
            fl = [pix[x, y][rgb] for y in range(hei) for x in range(wid) for rgb in range(3)]
            rgb = zip(fl[:s] , fl[s:s*2] , fl[s*2:])# rgbの入れ替え
            for y in range(hei):
                for x in range(wid):
                    pix[x, y] = next(rgb)
    elif mode==1:
        for _ in range(2):
            fl = [pix[x, y][rgb] for rgb in range(3) for y in range(hei) for x in range(wid)]
            rgb = zip(fl[0::3],fl[1::3],fl[2::3])# rgbの入れ替え
            for y in range(hei):
                for x in range(wid):
                    pix[x, y] = next(rgb)

# マスク処理(mode0:マスクをかける,mode1:マスクを外す)
def mask(file_path,mode=0,m_x=(0,0),m_y=(0,0)):
    img = Image.open(file_path)
    tmp = img.copy()
    if m_x!=(0,0) and m_y!=(0,0):
        tmp = img.crop((m_x[0], m_y[0], m_x[1], m_y[1]))
    if mode==0:
        mosaic(tmp,0)
        img.paste(tmp, (m_x[0], m_y[0]))
        out_path = file_path.replace('.','_masked.'.format())
    elif mode==1:
        mosaic(tmp,1)
        img.paste(tmp, (m_x[0], m_y[0]))
        out_path = file_path.replace('_masked','_unmasked'.format())
    img.save(out_path,quality=0)
    plt_img(img)
使い方は2通りあり
mode0がモザイクをかける
mode1がモザイクを外す
になっています。


使い方は
file_path = 'ファイル名.png'
m_x = (横の始まり,横の終わり)
m_y = (縦の始まり,縦の終わり)
mask(file_path,モード(0 or 1),m_x,m_y)
となっております。

画像にかけてみると
download
こんな感じでモザイクがかかります。

縦横の座標を指定しない場合は
全体にかかります。

画像を処理すると
「ファイル名_masked.png」
と言う名前で保存されます。

このマスク済みの画像を元に戻すには
mode1で縦横の座標を指定して戻します。

一応png画像のみ対応です。
jpegは元に戻せませんので
気をつけてください。

その昔の話ですが
FLMASK(エフエルマスク)

なんて言うものがありましたねー

一斉を風靡した外せるモザイクをかける
画像処理(マスク)ソフトです。

仕組みとしては画像の範囲の内の
RGBを入れ替えるもので
今回のプログラムと同様ですが

外せるモザイクは
モザイクをかけたことにはならず
猥褻画像にかけて
配布をしてしまった場合
大問題になってしまいました。

ですので、このプログラムも
かける画像は問題の無い画像を用いて
遊び程度にしていただく必要がありまーす。

お気をつけてwww

こんな感じで今年も
たくさん遊んでいきたいと
思いますので

ネタがあったら
コメントくださいませ。

それでは。

画像処理のプログラムを書いていたら
画像にモザイクをかけるのが
楽しすぎて遊んでしまいました。

解説動画はこちら



モザイクイーーズ!!!



第1問
この画像は誰でしょうか?



moza1


第2問
moza2


第3問
moza3


分かる人には
わかっちゃうかもしれないですね

答えは
動画をご覧くださいませ。

モザイクをかける
コードはこちら
from PIL import Image, ImageFilter
from ipywidgets import interact, FloatSlider, IntSlider
%matplotlib inline

def mozaic(im,i=1,b=4):
    img = im.resize([x // i for x in im.size]).resize(im.size)
    img = img.filter(ImageFilter.GaussianBlur(b))
    return img.resize([x // i for x in img.size]).resize(img.size)

blur_rate = FloatSlider(min=0.1, max=5.0, step=0.1, value=5)
mosaic_pixel = IntSlider(min=1 , max=50,step=1,value=50)

@interact(mosaic_pixel=mosaic_pixel,blur_rate=blur_rate)
def plot(mosaic_pixel,blur_rate):
    im = Image.open('画像のパス')
    im = mozaic(im,mosaic_pixel,blur_rate)
    return im

JupyterNotebookに貼り付けて
画像のパスを入力して
実行すると
スライダーウィジェットと
モザイク画像が出ます。


mosaic_pixel
20
blur_rate
5.00

あとはスライダーで
mosaic_pixel(モザイクの粒度,ピクセル数)と
blur_rate(ブラー,ぼかしの強さ)を
変えると・・・

画像のモザイクの粒度を1ピクセルに向かって小さくすれば
モザイクイズできます。
ブラーはモザイクが強いと効果ないですので
モザイク小さくして頂くと変化してきます。

面白いので
試してみてください。

それでは

このページのトップヘ