- [Vue.js]時計?タイムコードを表示するWebアプリ
- (2021/11/16)
- ポエム
- (2020/09/30)
- メトロノームアプリをリリース
- (2017/04/22)
- Sikulix勉強中:便利関数
- (2017/02/25)
- [JavaFX]学習メモ
- (2016/09/22)
https://heterotactic-locati.000webhostapp.com/timer.html
複数台のカメラで同時に撮影するときに、最初または最後にこのアプリを撮っておくと、映像同士の時刻を合わせやすくなります。 機材がなくてもいい、お手軽な同期アプリとしてご利用ください。
【使い方】
使い方はここにあるとおり、ビデオカメラでWebアプリの画面を撮影することで、映像の時刻をフレーム単位で割り出せるというものです。
カチンコという、いわゆる「アクション!(カンッ)」ってやるやつの代わりに使います。
ちゃんとした撮影機材があれば、専用の機材と線でつないで時刻を同期するのですが、そのような機材は手元になく・・・家庭用ビデオカメラで似たようなことをするためにこのWebアプリを作成しました。複数のカメラで同時に撮影し、あとで編集するようなケースではこれがめちゃめちゃ役に立ちます。
時刻同期の機能は作っていないため、29.7 fpsみたいなことはできません。
代わりではないですが、タイムコードの上にバーを表示しています。これで、画面表示されたタイミングが1frameのなかのどのタイミングだったのかが分かるようになっています。
たとえば30fpsで動かすとき、画面やブラウザはたいてい60fpsなので、同じタイムコードを2回表示することになるわけです。それを撮影したとしても、2回表示したうちのどちらが撮影されたのかまではわかりませんので、複数カメラの映像をつなげるときに若干のズレが起こります。(音がずれるので少し違和感が出ます)
もし印がバーの左側なら表示された値の通り、中央なら0.5frameのあたり、右側なら次のフレームに近いと判断でき、1frame未満も調整できるようになっているわけです。
別の使い方ですが、Clockモードを選ぶと、時計として使えます。
【使ったテクノロジー】
フレームワークはVue.js+Vuerifyを使っています。npmでビルドするのが面倒だったので、CDNから持ってくるだけになっています。思ったよりVuerifyはサイズ小さい。
あの頃のような意欲はなかったとしても、今は知識と経験があって、また違った面で技術習得したくなる…反面、ちょっと自分にはムリかなと思ってしまうこともある…。
readpro6
https://github.com/LapisCactus/readpro6
readpro6というツールはそういう意味で、いまでもフリーソフトを作りたい気持ちがある、手の届く範囲の技術で、いまの自分の要求を叶えるツールを作ってみたい、そんな気持ちの現れなのかも、と思ったのでした。
「sikulix」というアプリを使うことがあって、ちょっと遊んでいます。 sikulixは、画面に表示されているボタンやリンクを画像認識するソフトウェアです。テスト自動化にも使えます。
sikulixの勉強がてら、便利関数を幾つか作りました。現状の成果ということで、ここに添付して説明したいと思います。
# utility function import time # wait and click # @param picture a string of picture file name or a Pattern instance # @param time a integer of timeout # @throws Exception when timeout def wc(picture, time): pic_name = picture if isinstance(picture, Pattern): pic_name = picture.getFilename() print "> wc " + pic_name target = exists(picture, time) if target: click(target) else: print "> ->wc pic not found:"+pic_name raise Exception("wc error. pic not found "+pic_name) # find and click # @param picture a string of picture file name or a Pattern instance # @param time a integer of timeout def fc(picture, timeout): pic_name = picture if isinstance(picture, Pattern): pic_name = picture.getFilename() print "> fc %s %d"%(pic_name, timeout) start_time = time.time() while(start_time + (timeout/1000.0) > time.time()): try: icons = findAll(picture) sorted_icons = sorted(icons, key=lambda m:m.y) print "> ->found:"+pic_name target = sorted_icons[0] click(target) return except: print "> ->not found... continue:"+pic_name continue print "> ->not found... timeout!" raise Exception("fc timeout") # wait and keep clicking until the specified picture appears # @param picture a string of picture file name or a Pattern instance # @param loc a Location instance specifying a point to click untile picture appears def wkc(picture, loc): while(True): if exists(picture): print "> wkc end" return else: print "> wkc click (%d, %d)" % (loc.x, loc.y) click(loc)
※引数の「画像」について
画像となっている部分は、パターンオブジェクト、画像ファイル名のどちらの型でも受け付けます。Sikulixの関数には、見つけたい画像を画像ファイル名だけでも指定できますが、Patternクラスのインスタンスを作成して類似度(similarity)や、オフセット(targetOffset)を合わせて指定することができます。そのため、作成した便利関数では、ファイル名、Patternオブジェクトのどちらも受け付けるようにしました。
wc関数
指定された画像が表示されるまで、画像認識を続けます。見つけたら、見つかった部分をクリックします。
最初の3行はログ出力のための画像ファイル名の取得を行っています。
5行目のexists関数呼び出しで、画像認識を行います。戻り値はMatchオブジェクトで、見つからなかった場合はNoneが得られます。見つかった場合は、Matchオブジェクトをクリック対象とします。見つからなかった場合は、エラーを出力して例外を発生させます。
exists関数の代わりにwait関数を使っても良かったのですが、独自のログを出力して分かりやすくしたかったため、exists関数を使用しています。
fc関数
指定された画像が表示されるまで、画像認識を続けます。複数見つかった場合、一番上にあるものをクリックします。wc関数を発展させたものです。
最初の3行は同じく、ログ出力のための画像ファイル名の取得を行っています。
5行目では、タイムアウトを計算するために、現在時刻を取得しています。次の行で、タイムアウトの判定を行っています。(time.time関数では、秒単位で時刻を返すため、timeout引数を1000で割っています)
8行目では画像認識にて見つかったすべてのMatchオブジェクトを返すfindAll関数を呼び出しています。得られた結果を、次の行でソートしています。ソートに使うキーの指定は、key=lambda m:m.y
であり、Matchオブジェクトの属性"y"でソートされることになります。
ソートされたMatchオブジェクトのリストのうち、先頭のオブジェクトを取り出してクリック対象としています。クリック後は、関数を抜けます。画像認識で見つからなかった場合は、findAll関数にて例外が発生します。ログを出力して、whileの中をループします。
whileのループが終わるのは、タイムアウトしたときになりますので、ログを出力して、例外を発生させます。
wkc関数
指定された画像が表示されるまで、画像認識とクリックを続けます。クリックする場所は引数で指定します。画像が見つかっても、その場所はクリックしません。
1行目から無限ループになっています。ループの終了条件は、2行目の「画像が見つかったら」です。
画像が見つからない場合は、5行目のelse節に入ります。引数で指定された位置(Locationオブジェクト)をクリックします。
まとまった資料がほしいので、こちらにメモを残したいと思います…。
JavaFXアプリケーションについて
1.アプリケーションの構成とクラス
JavaFXは、Javaでアプレットや画面をもったアプリケーションを作成するためのフレームワークである。画面を表すクラス(Stage)やコントロールを表すクラス(Control)、コントロールのレイアウトを管理するクラス(XxxPane)、見た目をカスタマイズするクラス(Xxxxx)を組み合わせつつ、アプリケーション(Application)を作成する。
1.1.アプリケーション(Application)
JavaFxアプリケーションは、Applicationクラスが中心となる。(エントリポイントは通常のmainメソッドだが、通常ここではApplication.launch()メソッドを呼び出して、フレームワークに制御を委ねる。mainメソッドがなければ、launchが呼ばれたとされる・・・?暗黙の実装があるのかもしれない。)
Applicationクラスは、以下の3つのメソッドをオーバーライドする。(ライフサイクルメソッドとして、適切なタイミングで呼び出される)
initメソッド:JavaFxの初期化を行う前に呼び出される。ロジックなど、JavaFxに関係のない部分の初期化を行うことができる)
startメソッド:JavaFxの初期化後に呼び出される。画面の構築などを行うことができる。
stopメソッド:アプリケーションが終了(Platform.exit()による)する前に呼び出される。JavaFxが破棄されたあとに呼ばれるため、ロジックの終了処理を行う場所となる。
1.2.ステージ(Stage)
画面クラス。
Stageは一つのScene(https://docs.oracle.com/javase/jp/8/javafx/api/javafx/scene/Scene.html)を持つ(setSceneメソッドを用いる)。Sceneはシーングラフ全体を管理するためのオブジェクトであり、ドラッグ・アンド・ドロップやサイズの取得などをサポートする。シーングラフは、図形やUIコントロールを階層にしたものである。GroupクラスやRegionクラス、レイアウト関連のクラス(XxxPane)は子要素を持つことができる(これらはParentクラスのサブクラスである)。他のUIコントロールや図形は、子要素を持たない。これらが複雑に構成され、または動的に変更されるとき、表示も更新される。
ここでクラスの関連を整理しておく。
[Stage] 1-->1 [Scene] 1-->1 [Node] <-extends,has- [Parent]
1.2.1.プロパティ(Property)
Java Beanで使われているプロパティの仕組み(java.beanにあります)をベースに、拡張を加えたプロパティの仕組みがJavaFXでは使われています。
プロパティは、通常のフィールド(属性)を、一定のルールにもとづいて定義するようになっています。
ルール1:getter/setterを持つ。名前はgetXxx/setXxx。
ルール2:プロパティオブジェクトを返すメソッドを持つ。名前はxxxProperty。
プロパティオブジェクトは、プリミティブのラッパーになっていて、以下の機能を持ちます。
・変更監視(リスナーを登録して、変更時になにかしら処理できる)
・バインディング(プロパティ同士の関連を表現できる。例えば、常にAとBの和となるようなCを作れる。位置関係や金額の総和などの関係をオブジェクトとして表現できる。)(UIコントロールのプロパティ同士を連動させられる。ユーザーが入力すればプロパティも更新され、その変更をバインドした別のプロパティに反映させられる。)
バインディングオブジェクト(?)には、変更監視のリスナー登録に加えて、値が変更されたInvalidationListenerを登録できる。Bindingクラスのサブクラスを作れば、オリジナルの関係性を作ることができる。
1.3.コントロール
Scene上に配置するNodeについて。
Nodeには図形やUIコントロール、コンテナがある。
図形:四角や丸といった図形
UIコントロール:ボタンやテキストフィールド、ラベルなど。ユーザーに情報を提示したり、操作を提供したりする。
コンテナ:他のNodeを包含する。レイアウト機能を持つ(Groupは何もレイアウトを行わない)
UIコントロール
https://docs.oracle.com/javase/jp/8/javafx/user-interface-tutorial/ui_components.htm#JFXUI101
コンテナ(レイアウト)
http://www.oracle.com/pls/topic/lookup?ctx=javase80&id=JFXLY
1.4.FXML
(FXMLLoaderクラスで読み込める。コントローラクラスをセットすると、FXMLのイベント(?)を受け取ることができる)
(FXMLからイベントをセットするには、コントロールにonActionといった属性を記述する。これで、fx:controlで指定されたクラスのメソッドと紐付く)
http://krr.blog.shinobi.jp/javafx/fxml%E3%82%92%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B%EF%BC%88%EF%BC%91%EF%BC%89%E5%9F%BA%E6%9C%AC%E7%9A%84%E3%81%AA%E4%BD%BF%E3%81%84%E6%96%B9
1.5.スタイルシート
https://docs.oracle.com/javase/jp/8/javafx/user-interface-tutorial/css_tutorial.htm#JFXUI733
1.6.ダイアログ(カスタムダイアログ)
メッセージダイアログは、Alertクラスが使える。選択ボックスやテキスト入力が必要な場合は、それぞれChoiceDialog, TextInputDialogを使える。
カスタムダイアログのためには、Stageクラスに独自のコントロールを並べたものを設定する必要がある。
2.スレッドモデル
Swingやその他のGUIフレームワーク同様、シングルスレッドモデルである。JavaFx関連の操作を行う場合は、アプリケーションスレッドと呼ばれる専用のスレッド上で行う必要がある。
(きっと、このスレッドを止めると画面の描画が進まなくなる。アプリケーションスレッドではファイルの読み書きやネットワーク通信を行わないこと。)
3.標準的なアプリケーションの例(公式チュートリアル)
------
javadoc link
* Application
https://docs.oracle.com/javase/jp/8/javafx/api/javafx/application/Application.html
* Stage
https://docs.oracle.com/javase/jp/8/javafx/api/javafx/stage/Stage.html
* Scene
https://docs.oracle.com/javase/jp/8/javafx/api/javafx/scene/Scene.html
* Group
* Text
* FXMLLoader
https://docs.oracle.com/javase/jp/8/javafx/api/javafx/fxml/FXMLLoader.html
document link
* Overview
https://docs.oracle.com/javase/jp/8/javafx/get-started-tutorial/jfx-overview.htm
* Properties
https://docs.oracle.com/javase/jp/8/javafx/properties-binding-tutorial/binding.htm
http://itpro.nikkeibp.co.jp/article/COLUMN/20121219/445461/?P=2
* Scene Graph
https://docs.oracle.com/javase/jp/8/javafx/scene-graph-tutorial/scenegraph.htm#JFXSG107
* UI Controls, Layouts, CSS
https://docs.oracle.com/javase/jp/8/javafx/user-interface-tutorial/ui_components.htm#JFXUI101
* Containers
* Screen Transition http://javafx-trick.appspot.com/article/110001/110009/80051.html
* Dialogs http://krr.blog.shinobi.jp/javafx/javafx%20%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%83%BB%E3%83%80%E3%82%A4%E3%82%A2%E3%83%AD%E3%82%B0
* Samples http://www.oracle.com/technetwork/java/javase/overview/javafx-samples-2158687.html
そんなときはWindows Audio Endpoint Builderサービスを再起動するのですが、頻繁に発生するのでいちいち再起動するのが面倒です。そこでバッチファイルをこさえてみました。
@echo off
net session > nul 2>&1
if %ERRORLEVEL% == 0 (
echo administrative privileges! restart audio service
net stop audiosrv
net stop audioendpointbuilder
net start audioendpointbuilder
net start audiosrv
) else (
echo not administrative privileges... restart.
powershell -command start-process "%0" -verb runas
)
簡単な内容説明: 最初にnet sessionを実行することで、管理者権限があるかどうか、確認しています。
管理者権限があれば、所望の処理を実施しています。
ない場合は、powershell経由でバッチファイル自身を起動しています。このときに管理者権限を要求しており、UACのダイアログが出ます。
この手法は、「バッチファイルを常に管理者権限で実行したい」ときに使えそうです。
参考:
http://okwave.jp/qa/q8634495.html
http://qiita.com/skkzsh/items/5e03bb7792629927acfa