ステレオグラムの作り方_基本編3 (背景画像を深度マップの画像サイズに並べる)
ステレオグラムの作り方_基本編2の続きになります。
③背景画像を深度マップの画像サイズに並べる
次はステレオグラム作成の肝の部分です。
128×64ピクセルの背景画像を600×400ピクセルの深度マップ画像と同じサイズになるように並べていきます。このとき、深度マップの画像データの白丸部分は、背景画像をずらして配置する必要があります。
Pythonでオリジナルのランダムドットステレオグラム(RDS)を作る。 - Qiita
Making Your Own Autostereograms using Python | Frolian's blog (flothesof.github.io)
def normalize(depthmap):
return depthmap/255
In[5]
def make_autostereogram(depthmap, pattern, shift_amplitude=0.1, invert=False):
depthmap = normalize(depthmap)
if invert:
depthmap = 1 - depthmap
autostereogram = np.zeros_like(depthmap, dtype=pattern.dtype)
for r in np.arange(autostereogram.shape[0]):
for c in np.arange(autostereogram.shape[1]):
if c < pattern.shape[1]:
autostereogram[r, c] = pattern[r % pattern.shape[0], c]
else:
shift = int(depthmap[r, c] * shift_amplitude * pattern.shape[1])
autostereogram[r, c] = autostereogram[r, c - pattern.shape[1] + shift]
return autostereogram
autostereogram = make_autostereogram(depthmap, pattern, 0.3)
plt.imshow(autostereogram, cmap='gray')
いやぁ、もう何をやっているのか全然分からないけど出来てしまいました。画像の中心に〇が浮かんでいるのが見えます。
あまりの分からなさに怖気づいてしまっていますが、頑張ってコードを読んでいきます。
def normalize(depthmap):
return depthmap/255
1、17、19、20行目
def make_autostereogram(depthmap, pattern, shift_amplitude=0.1, invert=False):
…
return autostereogram
autostereogram = make_autostereogram(depthmap, pattern, 0.3)
plt.imshow(autostereogram, cmap='gray')
”make_autostereogram”という名前で何かしらの関数を作って、"autostereogram"で
それを実行し、その結果をpltの機能を使ってグレースケールで表示させているんですね。
2行目
depthmap = normalize(depthmap)
前に定義したnormalizeという関数で正規化を実施。
4行目、5行目:
if invert:
depthmap = 1 - depthmap
if False…?ネガポジ変換?わからないのでパス。
6行目:
autostereogram = np.zeros_like(depthmap, dtype=pattern.dtype)
”autostereogram”で深度マップと同じピクセルサイズの600×400ピクセルの画像を
作ってます。中身のデータは”0”なので、黒一色の画面になっているってことですね。
8行目:
for r in np.arange(autostereogram.shape[0]):
np.arangeは等差の数列を返す関数です。
for c in np.arange(autostereogram.shape[1]):
これも8行目と同じです。autostereogramの横のピクセル数と同じだけ繰り返すと書いてあるので、『今から以下の事を600回繰り返します』、と宣言してます。
if c < pattern.shape[1]:
これは『autostereogramの横サイズがpatternの横サイズよりも小さいうちは』ですね
12行目
autostereogram[r, c] = pattern[r % pattern.shape[0], c]
ここではrとcはそれぞれautostereogramの縦と横のピクセルの位置なので ”autostereogram[r, c]”は縦r、横cのポイントの画素の情報ということになります。
次に”pattern[r % pattern.shape[0], c]”ですがa%bは a をbで割った余りを表します。つまり”r % pattern.shape[0]”は rをpattern.shape[0]つまり128で割った余りの値が入ります。
・・・? 何してるのこれ。
例えば、
autostereogramの縦のピクセル数が65のとき
129÷128=1余り1
autostereogramの縦のピクセル数が100のとき
200÷128=1余り72
あ、なるほど!割ったときの余りが0~127に変化するのを利用して、patternを1枚貼ったら次に新しいものを貼るという繰り返しをしているんですね。すごい!頭いい!
13、14行目
else:
shift = int(depthmap[r, c] * shift_amplitude * pattern.shape[1])
ええと。
それ以外ならshiftは〇〇に設定するって感じですね。ここが一番大事なところかな。
depthmap:画像を白黒で作ってるので depthmap[r, c]の答えは"0"か"1"しかない
shift_amplitude:初期値は0.1(defの中の初期値なので後で任意の値に変えられる)
pattern.shape[1]:横のピクセル数64
ということは
depthmapの黒い背景部分
0×0.1×64=0
depthmapの白い円の部分
1×0.1×64=6.4 ⇒intなので整数部分の6
となって、白い円の部分に該当するピクセルのときだけshiftに値が入るんですね。
今は画像を白黒でしか作っていませんけど、灰色だったら灰色のシフト量になるように書かれれるから、灰色を使ったらもっとリアルな奥行き感とか影とかが出せそうです。
15行目
autostereogram[r, c] = autostereogram[r, c - pattern.shape[1] + shift]
さて、最後の難関。 問題は ”c - pattern.shape[1] + shift”の部分ですね。
●depthmapの黒い背景部分だとshift=0で
c - 64 + 0
例えばr=200,c=100なら
autostereogram[r, c]= autostereogram[200, 46]
autostereogram[200, 46]の部分には12行目のpatternの値が入っているので
それが参照されるので、patternが連続しているように見える。
●depthmapの白い円の部分だとshift=6で、
c - 64 + 6
depthmapの黒い背景部分までは連続していたパターンが+6ピクセル分ずれて
入ることになる。
ほぉぉ!
それだけで立体的に見えるものなんですね。視覚って不思議。
ともあれ、これでステレオグラムの作り方の基本をマスターしました!!
ここからはこれをベースに好きな画像で作っていきたいと思います。
ステレオグラムの作り方_基本編2(深度マップを作る)
”ステレオグラムの作り方_基本編1”の続きになります。
②3Dで見せたい画像(深度マップ)を作る
次は浮き出てくる方の画像を作りましょう。
Pythonでオリジナルのランダムドットステレオグラム(RDS)を作る。 - Qiita
Making Your Own Autostereograms using Python | Frolian's blog (flothesof.github.io)
def make_depthmap(shape=(400, 600)):
depthmap = np.zeros(shape, dtype=np.float64)
cv2.circle(depthmap, (int(shape[1]/2), int(shape[0]/2)), 100, (255 ,255, 255), -1)
return depthmap
depthmap = make_depthmap()
plt.imshow(depthmap, cmap='gray')
これは黒地に白の模様が入った画像なら何でもいいのかな?
コードを見ていきましょう。
2行目の”np.zeros”は以下の働きをする関数です。
変数”depthmap”が深度マップの情報になります。depthmapの画像サイズと
どんなデータが入るかを決め、ゼロで初期化するということをしているんですね。
全部ゼロってことは画像は黒で塗りつぶされているってことかな。
3行目の”cv2.circle”では円を作っていますね。
<cv2.circle(img, center, radius, color, thickness, lineType, shift)>
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
img :円を描く対象となる画像
center :描画する円の中心座標
radius :描画する円の半径
color :円の線の色
thickness :円の線の太さ
lineType :描画アルゴリズム
shift :謎
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
今の場合だと、
第1引数img :depthmap
第2引数center:(int(shape[1]/2), int(shape[0]/2))。
depthmapの画像サイズが横600×縦400なので、縦横をそれぞれ
2で割って中心座標を(300,200)にしてますね。
第4引数color:(255, 255, 255)で青・緑・赤のデータがMAXなので白
第5引数thikness:-1 。マイナス設定だと円の内部を塗りつぶす
ということで、全体が真っ黒なdepthmap画像の中心に100 ピクセルの白く塗りつぶされた円が出来上がりました。
次はいよいよ
③背景画像を深度マップの画像サイズに合うように並べていく
です!
ステレオグラムの作り方_基本編1(背景画像を作る)
最近、目が悪くなったと感じています。どうもピント調節機能が落ちているみたい。
そこで、ピント調節をする筋肉の『毛様体筋』の筋トレに使えるステレオグラム画像を作ろうと思い立ちました。(↓こういう画像)
ステレオグラム画像を立体視しようとすると、何度もピント調節を繰り返すので毛様体筋の筋トレになるそうなのです。
画像を作るのにはPythonを使います。Pythonは少し前に勉強を始めた言語で、それ以外の言語は一切使ったことがありません。技術レベルは初心者と表現するのもおこがましい状態です。
スマートに作れる気がしませんが、試行錯誤や足掻いた履歴など完成までの過程を残していきたいと思います。
■まずは何かを作ってみよう
”Python,ステレオグラム”のキーワードで検索してみると、下記の方のサイトがまず出てきました。ふむふむ。これはありがたい。
Pythonでオリジナルのランダムドットステレオグラム(RDS)を作る。 - Qiita
Making Your Own Autostereograms using Python | Frolian's blog (flothesof.github.io)
こちらを参考に作ってみたいと思います。
詳細理解できていませんが、ざっくりと以下の手順で作っていくようです。
<ステレオグラム作り方>
①背景画像を作る(とても小さいサイズで)
②3Dで見せたい画像(深度マップ)を作る
③背景画像を深度マップの画像サイズに合うように並べていく
※この際、深度マップの画像情報に合わせて背景画像をずらす&密度を変える
それでは、順番に取り組んでいきましょう。
①背景画像を作る
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行目の「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にして、割ることをしない場合>