頭と尻尾はくれてやる!

iOSアプリなどの開発日記です


オブジェクト選択時の輪郭線を実装したい

最近3DのエディターっぽいものをmacOSで作ってます。SceneKit 、 Objective-C で。

Blenderオブジェクト選択時

↑BlenderやSceneKitのエディターでは3D空間上にあるオブジェクトをクリックするとそのオブジェクトを選択している目印として黄色っぽい色の輪郭線が表示される(画像はBlenderの場合)。

こういうのを表現したいのだけど、、、考えたらこれ相当面倒だよね?
苦肉の策でやったのがクリックされたオブジェクトをコピーしちょっと拡大して、その背面(三角形の裏面やな)を表示する、というトゥーンシェーダーなどで輪郭線を描く場合によく紹介されてる手法。

SCNNodeオブジェクトをコピーしたい
↑オブジェクトのコピーはこんな感じで。コピーしたオブジェクトのボーンを消す必要があることに気付くまで随分時間がかかってしまったけ。

{
    SCNMaterial *firstMaterial = newNode.geometry.firstMaterial;
     firstMaterial.cullMode = SCNCullModeFront;
}
↑materialにお好みの色を設定するけど、それ以外にも三角形の裏面を表示するんやで、とするのにcullModeプロパティ(デフォはSCNCullModeBack)を設定。SCNCullModeFrontが裏面って覚えられへんわ、俺。

オブジェクト非選択時 オブジェクト選択時
↑右がクリックした後、、、まあそれっぽいんだけどさコピーしたオブジェクトのscaleをどうすんのよ?って感じなんだけど、、、カメラとオブジェクトの距離の関数でscaleを決めてもイマイチだったんだなあ(改良の余地はあったかもしれん)。選択されてるのはわかるからもうこれでいいんだけどさ。
Blenderみたいにどんなサイズでも綺麗に1pxの輪郭線を描くのって、、、どうしてるんだろうね。
スポンサーサイト






SCNNodeオブジェクトをコピーしたい

SceneKitのSCNNodeオブジェクトをコピーしたかったんだわ。
copyでいいのかな、、、うまくいかねえ、mutableCopyかな、、、?これもだめか、、、調べるとリファレンスに
- (void)duplicateNode:(SCNNode *)node withMaterial:(SCNMaterial *)material
{
    SCNNode *newNode = [node clone];
    newNode.geometry = [node.geometry copy];
    newNode.geometry.firstMaterial = material;
}
なんてある。↓ここね。
clone - SCNNode | Apple Developer Documentation
geometryやmaterialも参照しちゃうので別に使うなら新たに設定しないとだめってことか。なるほどね。

ところが、これでもうまくいかない。新しいオブジェクトのscaleもpositionも設定しても変化ない、、、つまり元のオブジェクトと変わってない、、、?

同じ位置に表示されてチラチラする

↑こんな感じに別な場所やscaleで描こうとしてるのに同じ場所に同サイズで表示してるので時折黒い面(コピー後のオブジェクトの色)が描画される、というよく見るよね、3D関連やってると。

もう新しく作るものなんてないのでは?と悩むこと幾星霜、ようやく気付いたのがこのコピー元のオブジェクトにはボーンがあって動きはそっち優先になってたのが原因。
{
    newNode.skinner = nil;    
}
として消してやれば新しいSCNNodeオブジェクトは意図通りに動いてくれた。ふー。


TFRecordで多次元配列を扱う

TFRecordでたくさんの浮動小数点数を扱う
numpyで値の型を指定したつもりが
↑このあたりに関連したデータの整形のお話。

TensorFlowで多次元の配列でデータを受け取りたいとする。
まずは保存。上記リンクにあるようにnumpyを使う。3次元の場合はこんな感じでやってみた。値は32bitのfloatで。
{
    CH = 4
    W = 3
    H = 2
    ite = 0
    arr_h = np.zeros([0,W,CH] , dtype=np.float32)
    for h in range(H) :
        arr_w = np.zeros([0,CH] , dtype=np.float32)
        for w in range(W) :
            arr_ch = np.zeros([0] , dtype=np.float32)
            for ch in range(CH) :
                arr_ch = np.append(arr_ch , np.float32(ite)) 
                ite += 1
            arr_w = np.append(arr_w , [arr_ch] , axis=0)
        arr_h = np.append(arr_h , [arr_w] , axis=0)
    print(arr_h)
}
#[[[ 0. 1. 2. 3.]
# [ 4. 5. 6. 7.]
# [ 8. 9. 10. 11.]]
#
# [[ 12. 13. 14. 15.]
# [ 16. 17. 18. 19.]
# [ 20. 21. 22. 23.]]]

↑結果。意図通り2x3x4の3次元配列になってる。append時のキャストが必要なのはリンクの通り。
これをTFRecordに書き込んだとする。
次に、このデータを使う時に読み込む場合。
{
    features = tf.parse_single_example(
                                       serialized_example,
                                       features={
                                       'arr_h': tf.FixedLenFeature([], tf.string),
                                       })
    arr = tf.decode_raw(features['arr_h'], tf.float32)
    arr = tf.reshape(arr, tf.stack([2,3,4]))
}
reshapeが必要。画像サイズがまちまちだと画像ごとにW,H,CHの値を持たせてる場合もあったと思う(確かcifar10とか?)が定数なら持たせなくてもいいか。
TensorFlowのテンソルとして値を表示させてみると、、、

_arr1=[[[[ 0. 1. 2. 3.]
[ 4. 5. 6. 7.]
[ 8. 9. 10. 11.]]

[[ 12. 13. 14. 15.]
[ 16. 17. 18. 19.]
[ 20. 21. 22. 23.]]]]

ふんふん、それらしくなってるな。とまあここまでしか確認はできていなくて、これからこれらを使って実際に学習が進むかってのをやっていくわけだけど。


numpyで値の型を指定したつもりが

TFRecordでたくさんの浮動小数点数を扱う
↑これに関連するnumpyの話。

numpyで配列を作る時に空の配列を用意してそこに値を入れていく、ってのをやろうとしたんだけど、この場合、最初の空の配列をどうするのか?いくつかあるみたいでこれが定番!というのがよくわからなかったんだが、とりあえずzerosを使うことにした。それ自体はいいんだと思うが、、、

↓これを実行すると
{
    a = 1.
    b = 2.
    arr = np.zeros([0] , dtype=np.float32)
    arr = np.append(arr, a)
    arr = np.append(arr, b)

    print(type(a))
    print(arr)
    print(type(arr[0]))
}
<class ‘float’>
[ 1. 2.]
<class 'numpy.float64'> ←な、なんですとー?!

↑こんな結果になる。
zerosのところで型指定して32bitの値が入るんだろうと思いきや、appendで入る値は64bitのままになってる。

これになかなか気付かず、TensorFlowのTFRecordで作成したデータが意図通りに読み込めずにうんうんと悩んでた。

arr = np.append(arr , np.float32(a))
のようにキャストすればいけた、、、けど、じゃあzerosでの指定はなんやねん?!


Python 3.5.2 :: Anaconda 4.1.1 (x86_64)


TFRecordでたくさんの浮動小数点数を扱う

機械学習(TensorFlow)で位置計測っぽいことをやってみた

↑以前このあたりで、TensorFlowでTFRecordのデータセットを自作する時に画像やラベルとしての整数や浮動小数点数を入れたってことはあった。

今回は1つの学習データに浮動小数点数を大量に持たせたい!

これをやったことなく手間取ったのでメモ。
まずは作成から。
{
    import numpy as np

    arr1 = np.asarray([0.123456789 , 1.2 , 1.3], dtype=np.float32)
    a = arr1[0]
    print(a)#0.123457
    print(type(a))#
}
numpyを使う。最初値をPythonのリストに詰め込んでたけどうまくいかなかった。何か方法はあるのかもしれないけど。
numpyのfloatはデフォで64bitある。今回は32bitで十分ってことでfloat32で配列を作る。
これをTFRecord用に出力する。
{
    label_n = 1
    label_x = 2.0
    record = tf.train.Example(features=tf.train.Features(feature={
                                                         'n' : tf.train.Feature(int64_list=tf.train.Int64List(value=[label_n])),
                                                         'arr1': tf.train.Feature(bytes_list=tf.train.BytesList(value=[arr1.tobytes()])),
                                                         'x': tf.train.Feature(float_list=tf.train.FloatList(value=[label_x])),
                                                         }))
    writer.write(record.SerializeToString())
}
numpyの配列だとtobytes()ってのがあるからこれで書き出せる。以前やった画像の場合はPILのImageオブジェクトにtobytes()で書き出してたので、同じようにしてみた。


次にファイルから値を読み込む。
{
    features = tf.parse_single_example(
                                       serialized_example,
                                       features={
                                       'n': tf.FixedLenFeature([], tf.int64),
                                       'arr1': tf.FixedLenFeature([], tf.string),
                                       'x': tf.FixedLenFeature([], tf.float32),
                                       })
    nn = tf.cast(features['n'], tf.int32)
    xx = tf.cast(features['x'], tf.float32)
    arr1 = tf.decode_raw(features['arr1'], tf.float32)
    arr1.set_shape([3])
}
↑これでデータを読み込んで表示させる(これが超絶めんどくさいよね)と、、、

データ読み込みの結果

↑こんな具合で扱えるようになった!




Copyright ©頭と尻尾はくれてやる!. Powered by FC2 Blog. Template by eriraha.