にせねこメモ

はてなダイアリーがUTF-8じゃないので移ってきました。

あにつく2017「デジタル作画導入スタジオが語る これで解った! デジタル作画のはじめかた」まとめ

あにつく2017での「デジタル作画導入スタジオが語る これで解った! デジタル作画のはじめかた」についてまとめておく。

登壇者、講演の概要については次のページを参照。

www.too.com

3社の使用しているデジタル作画ソフトについてまとめる。

グラフィニカ

使用ソフト

  • Clip Studio
    • レイアウト、1原
  • Stylos
    • 2原、動仕

Clip Studio

  • 書き味がよい
  • 前から使っていたStylosに加えて最近導入し試してる段階
  • 2原をClip Studioでやろうとしたこともあるが、補正の関係で描ける線がトレス向きでなく、補正がないStylosの方がなぞるのはやりやすい
  • パース定規機能……レイアウト向き
  • ペンの種類が多い、描画にタイムラグがない
  • 紙に近い
  • 自分でブラシを作れる。「見ました」みたいなブラシをつくってスタンプを押すみたいなことも
  • Clip Studio→Stylosのデータの移行はPNGを介して
    • PNG出力は制作さんがやっている
    • 人によって描き方(データの作り方)が異なるのでどうしても手でやる必要があるらしい

タイムシートについて

  • (紙で運用しているという話だったと思う)
  • (Stylosから?出力しても)AEにタイムシートを読み込ませることができない
  • 撮影会社に出すときに、紙でないと対応できない場合がある

オーエルエム

使用ソフト

  • Toon Boom Harmony

Toon Boom Harmony

  • カナダ産
  • 使う理由: 海外(特に北米)と仕事をするため(使ってるソフトが違うと共同作業が難しい)
    • 海外はペーパーレス
  • エディションが2種類ある
    • Harmony Advanced: 動仕のみ。15万
    • Harmony Premium: コンポジットもできる。30万
  • AEで撮影するためには線の2値化が必要(ベクタ画像からTGAで出力しないといけない)
  • ポケモンでは1600x900の撮影フレームを使っているが、その解像度で2値出力すると思ったところにドットが出ない
    • 現状ペイントソフトで修正している
    • 高解像度で出力しリサイズすると高品質に出力できそう
    • もともとHarmonyだけで完結させる設計になっているので、2値出力してAEでコンポジットする様な変態的な使い方は考慮されていない
  • 版権ものの抱き枕などでは一辺が一万ピクセル以上になることもあるが、ベクタデータなのでそこまでのマシンパワーは必要ない
    • 4K, 8Kなどの時代になったら紙では難しい

タイムシートについて

  • タイムシートは紙として存在し、それを見ながらデジタル作画している
  • 細かい指示がエクスポートできないため

サンジゲン

使用ソフト

  • TVPaint Animation

TVPaint Animation

  • フランス産
  • ヨーロッパのアートアニメーション向けにつくられた
  • 「ペグホールスタビライズ」機能が強力……タップ穴を合わせて自動で位置補正してくれる
  • アニメーションブラシ、カスタムブラシなどの機能
    • 設定できるパラメータが多く、様々なブラシが作成できる
  • スクリプト機能……George Scriptという化石みたいな言語
  • 欠点: 解像度(dpi情報)の保持ができない(AEと同様)
  • AEに持っていくには2値線にしないといけないが、原画以降は最初から2値(アンチエイリアスなし)で描くことで解決

タイムシートについて

Twitterのフォローが減ってることがあるので、フォローのリストをログとして記録したい

Twitterのフォローが減ってることがある。
退会や凍結によりアカウントが消えたか、あるいはブロックされたかのどちらかだろうと思うが、今まで購読していたものが購読できなくなって、それが把握できないというのは切ない。
なので、Twitterのフォローのリストを定期的に取得しておいて、フォローが減ったときなどに比較して比べて、減ったフォローを確認したい。定期的と言ってもフォローの数が変化した時に取得しておけばよい。ひょっとしたらブロックされたことがわかってひどく凹むことになるのかもしれないが……。

手法

さて、Twitterにおいて、@で始まるユーザ名("screen_name")は変更することができる。一方で、ユーザには内部で一意の番号が振られていて("id")、こちらは、ユーザ名が変わっても変わらないはずである。
なので、idの集合を定期的に記録しておけば、フォローの変化を追跡することができる。また、ユーザー名が変更された場合もそれを把握できる。

idだけの取得であればGET friends/idsでいけるが、今回はscreen_nameなども欲しいのでGET friends/listを使う。


取得したユーザのリストデータを保存する際、辞書オブジェクトであるから簡単に考えるとpickleなどで保存するということが考えられる。一方でpickleで保存するとpythonのバージョンが上がったときなどで互換性がなくなる場合があり(2->3とかだめだった気がする)、CSVJSONなどの汎用フォーマットを用いた方が良さそう。今回はJSONでダンプする。


実装にはPython Twitter APIを使った。これは pip install twitter で導入できる。

取得のためのコードを次に示す。
同じフォルダに'consumerkey'という、"<Consumer Key>[改行]<Consumer Secret>[改行]"という様なTwitter API Keyを記録したファイルを置いて実行する。Twitter API Keyはhttps://apps.twitter.com/でAppを作成すると発行できる。


自分がフォローしているユーザが数百なので、API制限など考えずに作成した。もし3000以上のユーザをフォローしている場合だとAPI制限に引っかかることになると思う。

比較

要するにユーザーのidの集合について比較し、差集合を計算すればよい。
ユーザーのidをキーとする辞書オブジェクトを作っておけば、ユーザー名などを引くことができる。

以下にコードを挙げる。

ちなみにこれをフォロワーの方で行ってフォロワーの推移を追跡することもできるが、やりたくないのでやらない。

Python+OpenCVでアニメのカット検出

はじめに

編集された映像のまとまりで一番細かい単位をカット(英語ではshot)という。カットがつなぎ合わされて一つの映像作品が作られている。
一般的にカットの切り替え時にはカメラの位置や撮影対象の位置関係が不連続になるため、画の大きな変化から目で見て判別のつく場合が多い。

なので、このカットの切り替えを、画の変化から(ある程度)自動で検出することができる。

手法

CG-ARTS協会『ディジタル画像処理[改訂新版]』に、カット検出について、次のように書かれている。

カット検出は,隣り合うフレーム間の画像の差分画像を用いて行うことができ,差の絶対値が大きいとき,その場所がカットであると判定すればよい。しかし,画面中に大きく動く物体がある場合も差の絶対値が大きくなり,カットと誤ることが多いため,精度を上げる工夫が必要である。画像をM×Nの格子(長方形領域)に区分し,長方形領域ごとの画素値の平均値を求め,その平均値の差の絶対値を用いてカットの判定を行う。16×16など比較的少ない格子数を用いることにより,物体の動きに影響されにくいカット検出ができる。

CG-ARTS協会『ディジタル画像処理[改訂新版]』(2015年) p.303

これに従って、フレーム間差分を用いてカット検出を行う。
まず、現在のフレームとそのひとつ前のフレームの差分画像を用意し、それに対してMSE (Mean Squared Error, 平均二乗誤差)やMAE (Mean Absolute Error, 平均絶対誤差)など指標となる値を計算する。その値が閾値以上であるならそのフレームでカットの切替が行われたと考える。

また、『ディジタル画像処理[改訂新版]』では精度を上げる工夫として、元画像を縮小したものに対してカット判定するという手法が紹介されているので、これについても検討する。

カット検出コードの例

Python 3.5.3, OpenCV 3.1.0, NumPy 1.13.1 (Anaconda, Windows 10 64-bit)で動作。

評価

評価するのは結構面倒である。false-positiveはカットとして検出した画像がダブってるのをみれば大体わかるが、false-negativeは検出結果からはわからない。なので、映像に対するカット切り替えタイミングの正解データを用意しておいて、それと対照して正解不正解を確認するというのが妥当だと思う。正解データは手で用意する必要がある。

ここで、ワイプやディゾルブといったカット同士の切り替えがなめらかに行われる場合、はっきりどのフレームで切り替わったということが言えないため、うまい評価法が思いつかなかった。なので、それらについては評価から除外し、カットの切替と検出されてもされなくても同じ評価になるようにした。


閾値以上の値をもつフレームをカットだと判定することから、検証にはthreshold(閾値)を変化させて、その閾値に関してprecision (精度, 適合率)とrecall (再現率)を計算した。

precisionは、カットの切れ目だ!と予測した中で実際に正しくカットの切れ目を判定できていた割合で、
 \displaystyle
P = \frac{\mbox{予測のうち正しかった数}}{\mbox{予測した総数}}
で表される。

recallは、実際に動画の中に存在するカットの切れ目をどれだけ網羅して判定できたかという割合で、
 \displaystyle
R = \frac{\mbox{予測のうち正しかった数}}{\mbox{正解の総数}}
で表される。

ここで、例えば動画全体を通してカットの切れ目を1つしか予測せず、それが正解だった場合precisionは1になるが、動画全体のほとんどのカットの切れ目を取りこぼしているためにrecallは0に近い値になる。
また、逆に全てのフレームをカットの切れ目だと判定する場合、precisionは0に近い値をとるが、recallは1になる。
このように、予測の良さを計るにはprecisionもrecallも単体ではあまり適さないと考えられるため、F-measure (F値)という尺度を導入して指標とする。

F-measureは、precisionとrecallの調和平均である。
 \displaystyle
F = \frac{2PR}{P+R}
これはprecisionとrecallのどちらもが高い値を持つ場合に高くなり、いい感じに評価に使えそうである。高いほど(1に近いほど)性能がよい。

さて、評価用に動画を用意した。会話中心で動きの少ない日常系と、激しいアクションがあるものの2種類のつもり。

各動画はCM等が含まれず、本編映像のみのものである。
これらに対し、カットの切り替わりタイミングのリストを作成した。各ファイルへのリンクを次に挙げる。

実際にはフレーム番号で計算したが、動画プレーヤでの確認のしやすさから時間表記(分:秒.ミリ秒)に直している。
カットの切り替わりタイミングを各行記載した。また、行末に"?"がついているものはカット検出の評価から除外する部分であり、ディゾルブなどで切り替わりに幅があるものについては"-"で繋いで範囲を示した。

評価結果1: ごちうさ

ご注文はうさぎですか?』1話について、いくつかの画像サイズで前フレームとのMSEとMAEを計算し、thresholdに対する検出性能の指標を計算し、グラフを描いた。

1280x720 MSE

f:id:nixeneko:20170903033850p:plain

Max F-measure 0.8344370860927152
Threshold 6947.407446108217
1280x720 MAE

f:id:nixeneko:20170903033858p:plain

Max F-measure 0.9038461538461539
Threshold 55.61545464409722
640x360 MSE

f:id:nixeneko:20170904213100p:plain

Max F-measure 0.8344370860927152
Threshold 6904.933479456018
640x360 MAE

f:id:nixeneko:20170904213115p:plain

Max F-measure 0.9020866773675763
Threshold 55.55679398148148
128x72 MSE

f:id:nixeneko:20170903033732p:plain

Max F-measure 0.8344370860927152
Threshold 6904.933479456018
128x72 MAE

f:id:nixeneko:20170903033756p:plain

Max F-measure 0.9020866773675763
Threshold 55.55679398148148
64x36 MSE

f:id:nixeneko:20170904213140p:plain

Max F-measure 0.8344370860927152
Threshold 6904.933479456018
64x36 MAE

f:id:nixeneko:20170904213148p:plain

Max F-measure 0.9020866773675763
Threshold 55.55679398148148
32x18 MSE

f:id:nixeneko:20170903033808p:plain

Max F-measure 0.8308207705192631
Threshold 5792.83275462963
32x18 MAE

f:id:nixeneko:20170903033816p:plain

Max F-measure 0.888888888888889
Threshold 48.49363425925926
16x9 MSE

f:id:nixeneko:20170903033827p:plain

Max F-measure 0.8264462809917356
Threshold 4685.99537037037
16x9 MAE

f:id:nixeneko:20170903033837p:plain

Max F-measure 0.8803827751196172
Threshold 44.44675925925926
F-measure最大値まとめ

まとめると次のようになる(数値の小数点以下桁数は適当)。

尺度 size max F-measure threshold
MSE 1280x720 0.8344 6947.4
MSE 640x360 0.8344 6904.9
MSE 128x72 0.8344 6904.9
MSE 64x36 0.8344 6904.9
MSE 32x18 0.8308 5792.8
MSE 16x9 0.8264 4686.0
MAE 1280x720 0.9039 55.6
MAE 640x360 0.9021 55.6
MAE 128x72 0.9021 55.6
MAE 64x36 0.9021 55.6
MAE 32x18 0.8889 48.5
MAE 16x9 0.8804 44.4

評価結果2: フリップフラッパーズ2話

フリップフラッパーズ』2話について、いくつかの画像サイズで前フレームとのMSEとMAEを計算し、thresholdに対する検出性能の指標を計算し、グラフを描いた。

1280x720 MSE

f:id:nixeneko:20170904213740p:plain

Max F-measure 0.7517401392111369
Threshold 4725.544186559607
1280x720 MAE

f:id:nixeneko:20170904213750p:plain

Max F-measure 0.8178528347406515
Threshold 49.28785517939815
640x360 MSE

f:id:nixeneko:20170904213759p:plain

Max F-measure 0.7522123893805309
Threshold 4287.165564236111
640x360 MAE

f:id:nixeneko:20170904213810p:plain

Max F-measure 0.8178528347406515
Threshold 49.19312789351852
256x144 MSE

f:id:nixeneko:20170904214121p:plain

Max F-measure 0.7552140504939626
Threshold 4102.424325448495
256x144 MAE

f:id:nixeneko:20170904214112p:plain

Max F-measure 0.8183962264150942
Threshold 47.06980613425926
128x72 MSE

f:id:nixeneko:20170904213833p:plain

Max F-measure 0.7603485838779955
Threshold 3877.8117042824074
128x72 MAE

f:id:nixeneko:20170904213842p:plain

Max F-measure 0.820754716981132
Threshold 46.62550636574074
64x36 MSE

f:id:nixeneko:20170904214100p:plain

Max F-measure 0.7656249999999999
Threshold 3832.808449074074
64x36 MAE

f:id:nixeneko:20170904214051p:plain

Max F-measure 0.8232502965599051
Threshold 46.094907407407405
32x18 MSE

f:id:nixeneko:20170904213856p:plain

Max F-measure 0.7734553775743707
Threshold 3645.484953703704
32x18 MAE

f:id:nixeneko:20170904213907p:plain

Max F-measure 0.8177570093457943
Threshold 43.302083333333336
16x9 MSE

f:id:nixeneko:20170904213922p:plain

Max F-measure 0.7842227378190255
Threshold 3205.7291666666665
16x9 MAE

f:id:nixeneko:20170904213931p:plain

Max F-measure 0.8189349112426034
Threshold 41.75231481481482
F-measure最大値まとめ

まとめると次のようになる(数値の小数点以下桁数は適当)。

尺度 size max F-measure threshold
MSE 1280x720 0.7517 4725.5
MSE 640x360 0.7522 4287.2
MSE 256x144 0.7552 4102.4
MSE 128x72 0.7603 3877.8
MSE 64x36 0.7656 3832.8
MSE 32x18 0.7734 3645.5
MSE 16x9 0.7842 3205.7
MAE 1280x720 0.8179 49.3
MAE 640x360 0.8179 49.2
MAE 256x144 0.8184 47.1
MAE 128x72 0.8208 46.6
MAE 64x36 0.8233 46.1
MAE 32x18 0.8178 43.3
MAE 16x9 0.8189 41.8

考察

  • 全体的にごちうさの方がフリップフラッパーズより検出性能がよい。これは、フリップフラッパーズには激しいアクションが多く、それが誤判定されがちだということが考えられる。
  • MSEよりMAEの方が検出性能がよい。
    • 画素値を[0.0, 1.0]の範囲にスケーリングしたらまた変わるのだろうか。
  • 差分画像を計算するサイズは64x36程度が一番良いようだ。
    • 差分画像はごちうさでは1280x720が最高で次に256x144, 128x72, 64x36が同じ性能で続き、解像度が大きいほど(原サイズに近いほど)性能が良いようにも見えるが、フリップフラッパーズでは64x36のパフォーマンスが一番高かった。
    • ある程度縮小することで計算速度を速くすることができる。
  • 64x36でMAEを計算した場合、最大のF-measureをたたき出したthresholdは、ごちうさ55.6、フリップフラッパーズ46.1程度とそれなりの開きがある。このため、一つのthresholdが任意の動画に対して最高の性能を出すということはない。

前のフレーム一つと比較しただけでは繋がった一続きのカットであるかどうかの判定が難しいものが必ず出てくるため、そういうものに対しても正しく判定するためには、過去や未来のフレームも参考にカット判定をしないといけないが、計算コストも高くなるということもあり、実用上そこまで重要でないような気がする。

例えば、動画エンコードで、カットの切り替わりが検出されたところをIフレームとしたい時などの場合、カットという切れ目よりは画面が大きく動いたことの方が重要であると考えられ、今回のような検出手法でも効果を発揮すると思う。

まとめ

ごちうさ1話とフリップフラッパーズ2話について検討した結果、差分画像を計算してカット検出を行うには、

  • 元画像を64x36程度に縮小
  • 尺度にMAEを用いる
  • 閾値は45~55程度(動画によって最適なものは異なる)

とするとよさそうで、0.8~0.9程度の最大F-measureが達成できるようである。
とはいえ、2つの動画作品に対してしか検証していないので、どこまで一般化できるかはわからない。

応用例: 映像のダイジェスト作成

カットの切り替わりが検出できるのであるから、カットごとに一つの画像を取り出すことで、映像のダイジェストを作成することができる。
f:id:nixeneko:20170904225533p:plain
見るとわかるが、画面のなかの大きな部分が動いたり色が変わる場面では誤検出が起きている。

他に試す価値があるかもしれないもの

  • ヒストグラムを利用したカット検出
  • SURF, ORBなどの画像特徴量の利用

*1:ウサ耳の生えたココナちゃんが齧歯類特有の前歯を削るために硬い構造物に齧りつき、唾液の糸が引くシーン、最高です。あと、色彩設計的にもポップで楽しい回。

Pillow, OpenCVなどでの画像の扱いの違い

Pythonには画像処理のために画像を読み書きするライブラリがあり、画像ファイルをnumpy.ndarrayの形で読み込んだりそれを表示・保存したりできるものがある。
一方で、各ライブラリによって画像の形式がまちまちであったりして、同じnumpy.ndarrayでも変換が必要だったりする。
ここでよく引っかかるので調べてまとめておく。

環境

  • Python 3
    • Python 3.5.3 |Anaconda custom (64-bit)| (default, May 15 2017, 10:43:23) [MSC v.1900 64 bit (AMD64)] on win32
  • NumPy: 1.13.1
  • OpenCV Python bindings (cv2): 3.1.0
  • Pillow (PIL): 4.2.1
  • SciPy: 0.19.0
  • matplotlib: 1.5.1

以下、RGBカラー画像(8 bit/channel)を読み込むことについて考える。

略記

  • w: 幅
  • h: 高さ
  • c: チャンネル数(RGB画像の場合は3)

例示するコードではnumpyはnpとしている:

import numpy as np

Pillow (PIL fork)

Imageはnumpy.ndarrayではないが、numpy.asarrayなどを使って相互変換できる。

import numpy as np
from PIL import Image

pil_img = Image.open(INFILE)
ary_img = np.asarray(pil_img)

Image.openで画像ファイルを開いて、numpy.asarrayでnumpy.ndarrayに変換する。
形は(h, w, c)。
numpy.asarrayで特に指定しないとdtype=uint8 [0, 255]で読み込まれる。また、dtype=np.floatなどと指定しても[0.0, 255.0]と値は変わらないので必要に応じて自分でスケールする必要がある。

numpy.ndarrayからPIL Imageへ

Image.fromarrayでできる。与えるのはuint8である必要があるらしい。

from PIL import Image
im = Image.fromarray(np.uint8(myarray*255))

Imageのリサイズ

画像をImageのインスタンスで保持している場合、Image.resizeでリサイズができる。
次は画像のサイズを半分にする例。

pil_img = Image.open(INFILE)
print(pil_img.size) # (w, h)
w_orig, h_orig =pil_img.size
resized_pil_img = pil_img.resize((w_orig//2, h_orig//2))

ここで、pil_img.resizeに指定するのは(w, h)の形である。


他、<Imageインスタンス>.show()で画像を表示できるっぽい。

OpenCV 3 (cv2)

OpenCVで扱うカラー画像は基本BGRである。これは歴史的な経緯があるらしい。

cv2.imread

import cv2
img_cv2 = cv2.imread(INFILE)

cv2.imreadで画像ファイルを開くとnumpy.ndarrayが得られる。
これは(h, w, c)の形ではあるが、チャンネルの並び順が他のライブラリと異なりBGRになっている。
また、何もしないとdtype=uint8 [0, 255]で読み込まれる。

同様に、cv2.imshow, cv2.imsave, cv2.VideoCapture, cv2.VideoWriterなどもBGRの順であることを前提としているため、cv2以外と組み合わせて利用する場合には、BGR-RGB間の変換をする必要がある。

cv2.imshowなどに渡す画素値の範囲

画像表示・出力用のcv2.imshow, cv2.imwriteなどの関数に与える画像の画素値の範囲は、uint8であれば[0, 255]、uint16は[0, 65535]、int16は[-32768, 32767]、floatであれば[0.0, 1.0]であるらしい(値の小さい方が黒、大きい方が白となる)。floatで[0.0, 1.0]の範囲からはみ出た値は、黒(0.0)または白(1.0)になる。

BGR画像→RGB画像への変換

img_bgrが(h, w, c=3)のBGRなnumpy.ndarrayとする。

img_rgb = img_bgr[:,:,::-1]

Pythonのスライスを使う。「第1, 2次元目はそのままで、第3次元目は逆方向に並べたもの」という風に指定している。

RGB画像→BGR画像への変換

img_rgbが(h, w, c=3)のRGBなnumpy.ndarrayのとき、

img_bgr = img_rgb[:,:,::-1]

項目を分けたが、BGR→RGBと全く同じ。

cv2.resize

一方で、画像のリサイズを行うcv2.resize関数に引数として渡すリサイズ後のサイズは(w, h)である。

resized_img = cv2.resize(img, (w, h))

これは、画像のnumpy.ndarrayの形が(h, w, c)であることを考えると、ちゃんと確認しないと引っかかりそう。
次は画像のサイズを半分にする例。

h,w,c = img.shape
resized_img = cv2.resize(img, (w//2, h//2))

cv2には空の画像を作成する関数はないっぽいので、numpy.zeros, numpy.onesなどを利用する。

matplotlib

matplotlib.image.imread

matplotlib.image.imreadが返す配列は(h, w, c)でRGB、dtype=float32、[0.0, 1.0]。

Return value is a numpy.array. For grayscale images, the return array is MxN. For RGB images, the return value is MxNx3. For RGBA images the return value is MxNx4.

https://matplotlib.org/api/image_api.html

matplotlib.pyplot.imshow

matplotlib.pyplot.imshowは、3チャンネルカラー画像であればRGB (float or uint8)を要求する。floatの場合は[0.0, 1.0]。

import matplotlib.pyplot as plt
import matplotlib.image as mpimg

img_plt = mpimg.imread(INFILE)

plt.figure()
plt.imshow(ary_img) 
plt.show()

SciPy

scipy.misc.imread

import scipy.misc
img_scp = scipy.misc.imread(INFILE)

PILを使って画像を読み込んでるっぽいのでPIL互換か。
dtype=uint8, (h, w, c), [0, 255].

scipy.misc.imsave

dtype=float32なnumpy.ndarray与えたら、[0.0, 1.0]でも[0.0, 255.0]でもどちらでもまともに出力された。
これは、uint8でないと、最小・最大の画素値を用いて正規化されるかららしい。そのため、画像の中身をちょっと見てみたい時にはいいと思うが、値が変わらないでほしい時には使えなさそう。

ChainerなどのCNNレイヤの入出力

CNN系のchainer.links.Convolution2Dは入力の形として(c, h, w)を前提とする。hとwは入れ替わっても一貫していれば問題ないが、チャンネルは最初に来る必要がある。
また、チャンネルの順番はRGBでもBGRでもなんでもいいが、一貫している必要がある。

次にあげる類のものは設計次第で何でもいいが、与えるデータを一貫して揃えておかないといけない。

  • 高さと幅の順番
  • チャンネルの並び順 (RGBかBGRか? など)
    • cv2とそれ以外を交ぜて使うと引っかかりそう
  • 画素値の範囲(とfloatにするかintにするかなど)
    • [0, 255], [-1.0, 1.0], [0.0, 1.0]、など。
(h, w, c)から(c, h, w)への変換

numpy.transposeを使う。img_hwcが(h, w, c)の形のnumpy.ndarrayだとすると、

img_chw = np.transpose(img_hwc, (2, 0, 1))
(c, h, w)から(h, w, c)への変換
img_hwc = np.transpose(img_chw, (1, 2, 0))

ミニバッチ対応

CNN系のchainer.links.Convolution2Dは入出力がミニバッチであるので、一つの画像を入力として与える場合にはnumpy.newaxisなどを使って先頭に次元を増やしてミニバッチサイズ1のミニバッチにしないといけない: (1, c=3, h, w)とか。

minibatch = img_chw[np.newaxis]

ミニバッチから取り出すのは

img_chw = minibatch[0]

とか。

値の範囲を変換

単にnumpyのブロードキャストの機能使って加減算や乗除算を行うだけ。一緒にnumpy.ndarray.astypeを使って形式の変換を行うことも多いと思う。

例: [0, 255] -> [0.0, 1.0]
a = a.astype(np.float)/255.0
例: [0, 255] -> [-1.0, 1.0)
a = a.astype(np.float)/128.0 - 1.0
例: [-1.0, 1.0] -> [0, 255]
a = ( (a + 1.0)*127.5 ).astype(np.uint8)

とか。

『電柱のスケッチ』

コミケ合わせで新刊作りました。ただし私は今回はサークル参加を申し込んでいないのでここで公開します。
電柱や電線をスケッチしたものです。

ダウンロード

PDFのダウンロードはこちらから:

本編

f:id:nixeneko:20170731065043p:plain

続きを読む

長方形で厚くて硬い万引き防止タグを分解してみた

よく電気屋などで小型商品にくっつけてある万引き防止タグ、検知方式によっていろいろな形があるのだが、長方形で立体的に厚みがある硬いタグを分解してみた。
f:id:nixeneko:20170809134741p:plain

このタグは使い捨てで、粘着テープが裏についていて、商品に貼りつけられる。このタグがゲートを通ると検知され、警報が鳴る。会計時に無効化処理を行うことで、会計を済ませた商品がゲートで反応することを防いでいるらしい。

分解

f:id:nixeneko:20170809134948p:plain
f:id:nixeneko:20170809135112p:plain
f:id:nixeneko:20170809135422p:plain
さて、分解してみると、底面に固定された金属板1枚と、金属の薄いフィルム2枚が入っている。フィルムの方は固定されていなくて、プラスチックで囲われたタグの内側の空間の中で動くことができるようになっている。タグの厚みはこのフィルムが動く空間を作るためのもので、タグが固いのも同じ理由だろう。

さて、随分シンプルな構造であるが、いったいどういう原理で動作するのだろうか。

原理

調べてみると、このタグはAcousto-magnetic systems (音響磁気方式)という方式のものらしい。Wikipediaに解説がある。

それによると、防犯ゲートの送信アンテナから送信されるパルスによって中に入っている金属フィルムが共振し、その共振によって発生する電波をゲートが受信するとアラームを起動するということらしい。

また、一番下の金属板が磁化されていると有効になるが、磁化が解除されると無効化され、ゲートに反応しなくなるとのことである。
つまり、解除機の無効化処理とは、実際には磁化の解除を行っているのだろう。

もしかして、解除されたタグについても、磁石を隣に置いておけばゲートに反応するようになるのかもしれない。


しかし、こんなに簡単な仕組みで防犯タグが実装されてるのを見て感心してしまった。解除もできる様になってるし。

イオニア数字変換TeXマクロ

イオニア数字ネイティヴではないので、イオニア数字を書く場合にはいちいち調べて書かないといけないのだけれど、実際面倒なので、(La)TeXマクロにしてLaTeXなどで書く際に簡単に変換できるようにしようというのが今回の目的。

イオニア数字というのは、古代ギリシャで使われた数値の書き方であって、ギリシャ文字を使って数値を表現するものである。一般にギリシャ数字とも呼ばれるとのこと。
詳しくはWikipediaなどを参照: ギリシアの数字 - Wikipedia

サンプル

f:id:nixeneko:20170809031334p:plain

コード

Githubに上げた。XeLaTeXでコンパイルできる。マクロの出力結果について網羅的に確認したわけではないので不適切な部分があるかもしれない。
GitHub - nixeneko/ionicnumber: A TeX macro that does a conversion from a number into Ionic numeral system.

使用法

ionicnum.texの中で\ionicnumマクロが定義されている。これを使うには、数字を与えて

\ionicnum{1234}

のようにする。1234の部分は任意の数字が入る。

メモ

イオニア式の記数法には年代や学者等によりいくつかの変種があり、特に10000以上の書き方はさまざまであるらしい。
この実装で用いたものは次のようなものである:

  • 数字にはギリシャ文字の小文字を用いた。
  • 数字はギリシャ文字列の右肩にʹを付けることによって表現した。
  • 6にはスティグマϛを、90のコッパはϟを、900のサンピはϡを用いている。
  • 10000以上はmyriad Μにより、Μの上に一万~一千万の位の数字を書くことによって表現した。
  • 最大で対応している数は一億(100,000,000)であり、ΜΜʹで表す。

The largest number named in Ancient Greek was the myriad myriad (written MM) or hundred million.

https://en.wikipedia.org/wiki/Myriad

実装について

まず、一~千の位の数字を変換することを考える。
これは、元の数をnとすると、各位の数字は

  • 一の位:  \displaystyle n \mod 10
  • 十の位:  \displaystyle \left\lfloor \frac{n}{10} \right\rfloor \mod 10
  • 百の位:  \displaystyle \left\lfloor \frac{n}{100} \right\rfloor \mod 10
  • 千の位:  \displaystyle \left\lfloor \frac{n}{1000} \right\rfloor \mod 10

となる。次にこれを\ifcaseを使ってギリシャ数字の各文字に変換する。


次に一万以上の数値について。
元の数値を10000で割ったものについて、その値をΜの上に積み重ねて表示する。数値の表示は一~千の位の数字に変換するマクロを共用している。
また、このとき、10000がΜとなるように条件分岐を行っている。


一億以上の数に対応するために再帰っぽい感じで書いてしまったのだが、どうせ処理系が 2^{31}-1 = 2147483647までしか対応してないので、ごちゃごちゃするだけであまりメリットなかったように思う。

20180328追記

気が向いたら修正する……。