OS X用GigE Visionカメラドライバ - その30 [OS X用GigE Vision]
これでざっくりとした全体像がつかめたということにしよう。しかしこれがどこまで正しいのかよくわからない(誰も言ってくれないので、ってあたりまえか)。これに従って実際に実装してカメラを動かしてみる必要がある。
ということで、今回はGEN<i>CAMの規格を読んで気に入らないところ、変だろと思うところ、僕だったらこんなことしないと思うところを列挙する。
本来ならGenApiがGenTLの上に乗ってレイヤを全て覆う上層となっているのが望ましいし、そのほうが実装が簡単だけどそうなっていない。GigE VisionだけでなくCameraLinkやIIDC/1394でも、デバイスを特定してそれをオープンして基本的なパラメータを設定して、という作業は必要なので、そのためのノードをGenApiが持っていてもいいと思うんだけど、そうはなっていない。これは残念である。
望ましくはGenApiがMVCのModelの部分を規定していて、GenTLがその直下にあって、MVCのController部分は別物として定義されているのがプログラミング上は簡単である。たとえばConverterによる値の変換や、Enumerationによる整数値と文字列との対応や、また場合によってはモードの違いによる書き込みのバリデーションなどが通常はMVCのControllerの作業としたほうが簡単になる。とくにOS Xではこういう作業はすべてCocoa Bindingにまかせてしまえるので、コードを書く必要はまずない(もちろん値変換なんかはValueTransformerを書かないといけないけど)。
どうもGEN<i>CAM規格は、さきに標準実装があって、それに合わせて規格を決めたんではないか、と勘ぐってしまう。なぜかというとこういう中途半端な抽象化は、僕が十分に設計をに煮詰めずに前のめりに実装を始めたときによく現れる。僕の場合は結局やり直すか、放り投げてやめてしまうかのどちらかになる。
それ以外にもいろいろ不満になる部分が訳していて気になったところがある。不満はひとつやふたつではなかった。
例えば画像のバイト数やパケットサイズなんかはドライバにとって重要なパラメータだけど、ユーザにとってはどうでもいい(ROIやピクセルフォーマットを設定した結果にすぎない)ので、ユーザインターフェイス要素と結びつく必要は全然ない(知りたいというユーザに表示できればいい)。また、シャッタが何秒なのかという絶対時間やゲインのデシベル表示などはユーザには便利だけど、ドライバにとってはあまり役に立たない(ドライバにとってはカメラのレジスタに対応した値があればいい)し、逆に人間にとってはクロック単位でシャッタ時間を設定するのでは桁数が多すぎて不便である。
この問題は、ヘッドレスの画像処理ソフトウェアを実装することを考えれば分かりやすくなる。ヘッドレス、例えば装置組み込みの場合などで、そのソフトウェアにとってはパラメータを変更するユーザは存在しない。パラメータの変更はプログラマがハードコードするか、あるいはソフトウェア自身が判断するコードを持つことになって、GenApiのユーザインターフェイスに結びつく部分はすべて不要になる。いらないんなら使わなきゃいいじゃん、というとそうではない。それはXMLファイルを読んだ上でのセマンティクスの問題になる。GenApiはそういったところ(ヘッドレスの場合にどう使えばいいのか)に神経を行き届かさないで作ってあるように見える。
従って本来なら、パラメータに対してBeginer、Expart、Guruなんていう詩的な表現ではなく、直接的な階層化があるべきである。
また、例えばインターフェイスの値が、他のノードを参照するときはpValueエレメントで、直接定数を指定するときはValueエレメントで、というのもすっきりしない。ふつうのXML設計ならValueエレメントで値を指定して、そのValueエレメントの中で他のノードを参照するのか、定数が指定されているのかを場合分けするはずである。
スキーマファイルの定義ではpValueかValueのどちらかが存在していないといけないことになっているけど、読み込むときはpValueとValueのどっちがあるのかを判断しないといけない。ささいなことではあるけど、実装の複雑さをあっさり増やすことになる悪い設計の典型的なパターンである。
まずこのSwissKnife(十徳ナイフ)という名前。気の聞いた名前のつもりなんだろうけど、規格の中で定義された予約語なんだから、やっぱり名前は実態を表すべきである。SwissKnifeという名前からでは何をするノードなのかわからない。名前の通り狼の皮を剥ぐことから缶詰を開けることまでなんでもできるのか、といえばもちろんそうではない。たとえばMathematicalExpressionのほうが直接的で、これが長くていやなら他のノード名とかぶらないようにしてExpressionとかAlgebraとかでいい(ちなみにExpressionはSwissKnifeのなかの中間表現式の名前として使われている。一般的な名前を中途半端なレベルに使ってしまうという例でもある)。
規格のボードメンバにボケたスイス人がいて彼が主張したとかなのか。それとも十徳ナイフのように一見便利だけど非常時以外はそれほど役に立たない、という皮肉を込めたのか?どちらにしても「体を表す名前」はプログラミングの基本の「き」である。
さっきちょっと書いたパラメータのVisibilityレベルのBeginer、Guruなんていうのと並んで、こういう予約語の命名のしかたが本当にダサい。こういうのをカッコいいと思うのは、はっきり言って先端技術を生業にするプロフェッショナルではなくて、ただの厨二病である(厨二病は日本固有の病気ではないらしい)。ほんとうにがっかりする。
XMLをはじめから前提にしてるんだから、GTとかLTとかANDとかでいいじゃん。Cの式をコピペできることがそんなに重要なのか?そのくせ変数名は大文字でなければならないとか、定義済み関数もその名前が全部大文字になってるとかすげえ中途半端でほんとにダサい。
そもそもXMLを前提にしてるくせになんで式だけ、Cに似せるのかわからない。GEN<i>CAMファイルを読むときに式の字句解析や構造解析がXMLとは別に必要になってしまう。たとえば なんて式は
ところでちなみに、OS XのFoundationフレームワークにあるNSXMLElementは、実体参照を文字に戻してくれるので気にしなくていいけど、式がCDATAで書いてあるとそれをほどかないといけない。たいしたことないけど場合分けが必要になってしまう。
ちなみに、式をMathML(か、あるいは専用のXMLによる記述)で書けば、演算子順位の問題は自動的に解決できる。
さらに単項演算子の"-"(符号反転)が定義されていなくてNEG関数になっている。いいのかよ、それで。これだとベキが定義されているのに肩を負数にするときにはなんと、「2**(0-3)」や「2**NEG(3)」などとする必要がある。そのくせ、浮動小数点数の定数表現としては「1.0E-3」は許されているという矛盾がある。なんで符号反転の単項演算子を嫌うのか、理由が全然わからない。
最終的には標準実装にあわせろ、ということになってしまうんだけど、標準実装のソースはGEN<i>CAMのメンバにならないと公開してもらえないので、動作させて確認するしかない。これはオープンな規格としては明らかに不備。きっと標準実装には単項演算子が含まれてるんだろうなあ。こういうところから考えても標準実装ありきの規格に思えてしまう。
まだまだあるけど、疲れたので続く....
ということで、今回はGEN<i>CAMの規格を読んで気に入らないところ、変だろと思うところ、僕だったらこんなことしないと思うところを列挙する。
6.2 解釈をまとめてみて
ざっくりとまとめてみると、GenApiの守備範囲は、MVC(Model - View - Controller)デザインパターンでいうとModelとControllerということになる。MVCに沿った設計がされていればOS Xとの相性はいい、ということになる。しかしGEN<i>CAMの規格ではGenApiとGenTLが別になっていて、ユーザがトランスポートレイヤを直接アクセスする(GigE Visionで言えばデバイス探索やポート番号の設定パケットサイズの決定、そして画像データの受け取りなど)必要がある部分に対してはGenApiは無関心なので、ModelとControllerのすべてをカバーしてくれるわけではない。本来ならGenApiがGenTLの上に乗ってレイヤを全て覆う上層となっているのが望ましいし、そのほうが実装が簡単だけどそうなっていない。GigE VisionだけでなくCameraLinkやIIDC/1394でも、デバイスを特定してそれをオープンして基本的なパラメータを設定して、という作業は必要なので、そのためのノードをGenApiが持っていてもいいと思うんだけど、そうはなっていない。これは残念である。
望ましくはGenApiがMVCのModelの部分を規定していて、GenTLがその直下にあって、MVCのController部分は別物として定義されているのがプログラミング上は簡単である。たとえばConverterによる値の変換や、Enumerationによる整数値と文字列との対応や、また場合によってはモードの違いによる書き込みのバリデーションなどが通常はMVCのControllerの作業としたほうが簡単になる。とくにOS Xではこういう作業はすべてCocoa Bindingにまかせてしまえるので、コードを書く必要はまずない(もちろん値変換なんかはValueTransformerを書かないといけないけど)。
どうもGEN<i>CAM規格は、さきに標準実装があって、それに合わせて規格を決めたんではないか、と勘ぐってしまう。なぜかというとこういう中途半端な抽象化は、僕が十分に設計をに煮詰めずに前のめりに実装を始めたときによく現れる。僕の場合は結局やり直すか、放り投げてやめてしまうかのどちらかになる。
それ以外にもいろいろ不満になる部分が訳していて気になったところがある。不満はひとつやふたつではなかった。
6.2.1 パラメータ分類が不十分
そもそもMVCなどという以前に全体が整理されているという感じがあまりしない。直接ユーザインターフェイスに結びつく部分と、カメラレジスタを操作する部分、つまりユーザが調整したいパラメータとドライバソフトウェアに必要なパラメータがごっちゃになっている。例えば画像のバイト数やパケットサイズなんかはドライバにとって重要なパラメータだけど、ユーザにとってはどうでもいい(ROIやピクセルフォーマットを設定した結果にすぎない)ので、ユーザインターフェイス要素と結びつく必要は全然ない(知りたいというユーザに表示できればいい)。また、シャッタが何秒なのかという絶対時間やゲインのデシベル表示などはユーザには便利だけど、ドライバにとってはあまり役に立たない(ドライバにとってはカメラのレジスタに対応した値があればいい)し、逆に人間にとってはクロック単位でシャッタ時間を設定するのでは桁数が多すぎて不便である。
この問題は、ヘッドレスの画像処理ソフトウェアを実装することを考えれば分かりやすくなる。ヘッドレス、例えば装置組み込みの場合などで、そのソフトウェアにとってはパラメータを変更するユーザは存在しない。パラメータの変更はプログラマがハードコードするか、あるいはソフトウェア自身が判断するコードを持つことになって、GenApiのユーザインターフェイスに結びつく部分はすべて不要になる。いらないんなら使わなきゃいいじゃん、というとそうではない。それはXMLファイルを読んだ上でのセマンティクスの問題になる。GenApiはそういったところ(ヘッドレスの場合にどう使えばいいのか)に神経を行き届かさないで作ってあるように見える。
従って本来なら、パラメータに対してBeginer、Expart、Guruなんていう詩的な表現ではなく、直接的な階層化があるべきである。
また、例えばインターフェイスの値が、他のノードを参照するときはpValueエレメントで、直接定数を指定するときはValueエレメントで、というのもすっきりしない。ふつうのXML設計ならValueエレメントで値を指定して、そのValueエレメントの中で他のノードを参照するのか、定数が指定されているのかを場合分けするはずである。
スキーマファイルの定義ではpValueかValueのどちらかが存在していないといけないことになっているけど、読み込むときはpValueとValueのどっちがあるのかを判断しないといけない。ささいなことではあるけど、実装の複雑さをあっさり増やすことになる悪い設計の典型的なパターンである。
6.2.2 SwissKnifeに関する不満
一番面倒なのはSwissKnifeである。これは数式が使えるので便利なんだけど、実装はめんどくさい。ところで、GEN<i>CAMのダサさがSwissKnifeに非常に良く現れている。まずこのSwissKnife(十徳ナイフ)という名前。気の聞いた名前のつもりなんだろうけど、規格の中で定義された予約語なんだから、やっぱり名前は実態を表すべきである。SwissKnifeという名前からでは何をするノードなのかわからない。名前の通り狼の皮を剥ぐことから缶詰を開けることまでなんでもできるのか、といえばもちろんそうではない。たとえばMathematicalExpressionのほうが直接的で、これが長くていやなら他のノード名とかぶらないようにしてExpressionとかAlgebraとかでいい(ちなみにExpressionはSwissKnifeのなかの中間表現式の名前として使われている。一般的な名前を中途半端なレベルに使ってしまうという例でもある)。
規格のボードメンバにボケたスイス人がいて彼が主張したとかなのか。それとも十徳ナイフのように一見便利だけど非常時以外はそれほど役に立たない、という皮肉を込めたのか?どちらにしても「体を表す名前」はプログラミングの基本の「き」である。
さっきちょっと書いたパラメータのVisibilityレベルのBeginer、Guruなんていうのと並んで、こういう予約語の命名のしかたが本当にダサい。こういうのをカッコいいと思うのは、はっきり言って先端技術を生業にするプロフェッショナルではなくて、ただの厨二病である(厨二病は日本固有の病気ではないらしい)。ほんとうにがっかりする。
6.2.3 数式表現に対する不満
さらにSwissKnifeの中で使える演算子はほぼCと同じものが使えるんだけどそうすると < や > や&が含まれていて、これがXMLのメタキャラとかぶってしまう。それをさけるために実体参照を使うか、[! CDATA[....]]を使え、とある。XMLをはじめから前提にしてるんだから、GTとかLTとかANDとかでいいじゃん。Cの式をコピペできることがそんなに重要なのか?そのくせ変数名は大文字でなければならないとか、定義済み関数もその名前が全部大文字になってるとかすげえ中途半端でほんとにダサい。
そもそもXMLを前提にしてるくせになんで式だけ、Cに似せるのかわからない。GEN<i>CAMファイルを読むときに式の字句解析や構造解析がXMLとは別に必要になってしまう。たとえば なんて式は
<Divide> <Plus> <Power> <Variable>X</Variable> <Integer>2</Integer> <Power> <Power> <Variable>Y</Variable> <Integer>2</Integer> <Power> </Plus> <Power> <Variable>R</Variable> <Integer>2</Integer> <Power> </Divide>でいいじゃん。そうすればXMLの構造がそのまま式の構造として表せる。こう書くと実質的にMathMLと同じになるので、MathMLに従ってしまった方が手っ取り早いし、C式よりはずっと解析がしやすい。エンドユーザはそもそもGEN<i>CAMのXMLファイルを見ることもないんだし、式を書くのはベンダ側で読むのはユーザの手元での作業なので、書くのを楽にして読むのを面倒にするのは筋が通らないし、そもそも規格として美しくない。
ところでちなみに、OS XのFoundationフレームワークにあるNSXMLElementは、実体参照を文字に戻してくれるので気にしなくていいけど、式がCDATAで書いてあるとそれをほどかないといけない。たいしたことないけど場合分けが必要になってしまう。
6.2.4 数式の文法に関する不満
細かい話になるけど非常に重要な問題として、式の解釈の問題がある。SwissKnifeのなかの数式の定義はGenApiのSchemaでは単なる文字列としか規定されていないので、Schemaからはその文法はわからない。規格書の中には文法に関する言及があったけど、演算子順位に関する記述がない。Cと同じにするしかないと言いたいのかもしれないが、CやJavaにはないベキを含んでいる。ベキは普通数学では右結合なのでちゃんと決めておかないと大変まずい。ちなみに、式をMathML(か、あるいは専用のXMLによる記述)で書けば、演算子順位の問題は自動的に解決できる。
さらに単項演算子の"-"(符号反転)が定義されていなくてNEG関数になっている。いいのかよ、それで。これだとベキが定義されているのに肩を負数にするときにはなんと、「2**(0-3)」や「2**NEG(3)」などとする必要がある。そのくせ、浮動小数点数の定数表現としては「1.0E-3」は許されているという矛盾がある。なんで符号反転の単項演算子を嫌うのか、理由が全然わからない。
最終的には標準実装にあわせろ、ということになってしまうんだけど、標準実装のソースはGEN<i>CAMのメンバにならないと公開してもらえないので、動作させて確認するしかない。これはオープンな規格としては明らかに不備。きっと標準実装には単項演算子が含まれてるんだろうなあ。こういうところから考えても標準実装ありきの規格に思えてしまう。
まだまだあるけど、疲れたので続く....
2015-03-17 21:33
nice!(0)
コメント(0)
トラックバック(0)
コメント 0