大きくなったら大蛇に

Python始めたての初心者による学習ブログです

ステレオグラムの作り方_基本編1(背景画像を作る)

Pythonステレオグラムを作りたい

 最近、目が悪くなったと感じています。どうもピント調節機能が落ちているみたい。

そこで、ピント調節をする筋肉の『毛様体筋』の筋トレに使えるステレオグラム画像を作ろうと思い立ちました。(↓こういう画像)

 

ステレオグラム画像を立体視しようとすると、何度もピント調節を繰り返すので毛様体筋の筋トレになるそうなのです。

画像を作るのにはPythonを使います。Pythonは少し前に勉強を始めた言語で、それ以外の言語は一切使ったことがありません。技術レベルは初心者と表現するのもおこがましい状態です。

スマートに作れる気がしませんが、試行錯誤や足掻いた履歴など完成までの過程を残していきたいと思います。

 

■まずは何かを作ってみよう

Python,ステレオグラム”のキーワードで検索してみると、下記の方のサイトがまず出てきました。ふむふむ。これはありがたい。

Pythonでオリジナルのランダムドットステレオグラム(RDS)を作る。 - Qiita

Making Your Own Autostereograms using Python | Frolian's blog (flothesof.github.io)

こちらを参考に作ってみたいと思います。

詳細理解できていませんが、ざっくりと以下の手順で作っていくようです。

 

ステレオグラム作り方>

  ①背景画像を作る(とても小さいサイズで)

  ②3Dで見せたい画像(深度マップ)を作る

  ③背景画像を深度マップの画像サイズに合うように並べていく

   ※この際、深度マップの画像情報に合わせて背景画像をずらす&密度を変える

 

それでは、順番に取り組んでいきましょう。

 

①背景画像を作る

以下は参考サイトから引っ張ってきたコードです。これを実行すると

縦128ピクセル×横64ピクセルの砂嵐の画像を作れます。

 

import numpy as np
import matplotlib.pyplot as plt
import cv2

def make_pattern(shape=(16, 16), levels=64 ):
      return np.random.randint( 0, levels - 1, shape)/ levels

pattern = make_pattern(shape=(128,64))
plt.imshow(pattern, cmap='gray')

 
では、中身を見ていきましょう。
 
1行目、”make_pattern”という名前で何かしらの関数を作ってますね。
関数の定義と実行は以下のように書かれます。
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
関数の定義
 def 関数名(引数1, 引数2, 引数3, …) :
    処理内容1
    処理内容2      
       …
    return 戻り値
 
関数の実行
  関数名(引数1, 引数2, 引数3, …)
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
 
2行目の処理内容の部分の”np.random.radient” というのは任意の範囲の整数を
ランダムに返してくれる関数だそう。
<np.random.radient(low, high , size)>
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
  low :乱数の最小値
  high:乱数の最大値。haigh-1の値が実際の最大値
  size :出力する配列のshape。デフォルトはNone。
  戻り値:low からhihg-1の範囲でランダムな値。整数
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
 
つまり、変数"pattern”によって
関数”make_pattern”が呼び出されて、
関数”np.random.radient”が0~63の間でランダムな数値を出力し、
それを64で割って正規化。
それをshapeの(128,64)ピクセルサイズ分実行して
最終的にグレースケールの画像データで表示するんですね。ほうほう。
 
これで背景画像(の小さいサイズ)ができました!
 
 
 
★疑問点①
なぜ16×16で定義してから128×64に設定するんでしょう。
make_patternのshapeを指定しないと16×16ピクセルの画像ができます。
 

これを最初から128×64で作ると都合が悪いことがあるんでしょうか。お作法?
負荷が増えると効いてくる?
悩んではみたものの、これは考えても分からなさそうなので今は流されておくことにします。
 
 
★疑問点②

もう一つ気になっているのは1行目の「levels=64」。np.random.radientの最大値の引数に使われています。

 

      np.random.radient(0, levels-1 , shape)/levels

 

乱数の最大値-1が実際の最大値ということなので、64-1-1=62が実際の最大値になるのかな?

0~62の間でランダムに値を出してから、それを64で割っていますが、なぜこの方法をしているのか分からないのです。

という訳で、試しに値を色々と振ってみました。

 

<乱数の最大値を1にして、割ることをしない場合>

 真っ黒になりました。

 乱数の最大値-1なのでランダムに出される値は1-1=0で、”0”しか選択肢がないので真っ黒になるのは想像通りの動作です。

 

 <乱数の最大値を2にして、割ることをしない場合>

 こちらは白と黒の2値ですね。

 乱数の最大値-1なのでランダムに出される値は2-1=1で、ランダムに出される値は”0”か”1”しか選択肢がないのでこちらも想像通りの動作です。

 

 <乱数の最大値を256にして、割ることをしない場合>

256-1=255で、ランダムに出される値は”0”から”255”で。。。patternに入る値は最大255が入ってるんでしょうけど。
あれ?
乱数の最大値を2にしたとき出力画像が白と黒になっていたので、256だとほぼ白に張り付くと予想していました。だから正規化するために最後は割らないといけないのかと思っていたのに。
 
うん、やっぱりわからない。割らなくても期待通りに出るじゃないですか。
割る必要って…なに。
これも、継続調査扱いにします。
 
疑問が何一つ解決していませんが、とりあえず今回はここまで。
次回は②3Dで見せたい画像(深度マップ)を作るについて記載したいと思います。