OS X用GigE Visionカメラドライバ - その45 [OS X用GigE Vision]
遅々として進まないGEN<i>CAM対応のドライバ。こんなことをしているうちに、仕事で使っているカメラの数がどんどん増えて、機種ごとに書き分けているレジスタマップを定義したファイルが同じ数だけ増えてしまった。最初の頃は、買ったカメラ用のレジスタマップ定義はせいぜいシャッタスピード、ゲイン、fps、あとはデータ転送の開始終了ぐらいしかなかったけど、最近はROIやビニングやソフトウェアトリガや黒レベルなんかも使うようになってきて、もうどの機種に何を定義したのかぐちゃぐちゃになって、わからなくなりつつある。
それに、カメラのメーカをそれまで使っていたドイツのメーカから去年カナダのメーカに切り替えた。なぜかというと、いつの間にか日本での値段が逆転していた(一番最初は数社を値段だけで比べて決めた)のと、ドキュメントがカナダのメーカのものの方が豊富だったから。
その一方で、ノイズが多い感度が低いピクセルピッチがでかいなどなどの点で光学計測用途は難しいと思っていたC-MOSイメージセンサが、いつの間にかCCDに肩を並べるようになって、ある面ではCCDを超えるようになってきた。メーカもC-MOSセンサを乗せたカメラを次々投入してきている。僕も性能が良くて安いならそれにこしたことはないと思って今年に入ってからはC-MOSカメラばかりを買っている。
ところがそのおかげで、対応しなきゃいけない機種は増える、新しい製品は機能が豊富になってXMLファイルが肥大化して人間が読むのが辛くなる、でGEN<i>CAMにさっさと対応しないと、ずっと後回しになっている画像データ転送周りの効率化がちっとも進まなくて、20fps程度で1コア占有するという状態から抜け出すことができない。
ちなみに件のカナダのメーカのカメラは、懐かしいIIDC(ver.1.32)にも準拠してるので、レジスタへのアクセスはXMLファイルを読みくださなくてもなんとかなっている。でもどうもIIDCのレジスタマップとXMLに書かれているのが同じには見えない。全部を比べる元気はないのでどう違っているのかはわからない。単にIIDCは必ず間接アドレスでアクセスする(例えばゲインを調整するレジスタのアドレスは、あるベースアドレスからの決まったオフセットのレジスタにそのアドレスが書いてある、ということになっている)けど、XMLファイルはカメラに内蔵されているので、直接書いてあるだけなのかもしれない。
いや、そんなことはどうでもいい。とにかくさっさとGEN<i>CAMを実装するところまでこぎつけないと....でもすぐ優先順位の高い仕事にオーバーライドされるんだよなあ....
スキーマファイルに出てくるエレメントを書き出して分類したのが図-33である。 十分複雑だけど、スキーマファイルではこれからさらにセマンティクスによって分かれている。クラス設計上はセマンティクスとは分離したほうがいいけど、スキーマファイルに従ってクラスを分けておいたほうが対応がわかりやすいかもしれない。
まずエレメントは単純エレメントと複合エレメントに分類することができる。複合エレメントはその内部に単純エレメントを内包する。単純エレメントは定数エレメントと参照エレメントに分かれる。参照エレメントは他のノードを参照して値を得るエレメントである。
複合エレメントは組合せ、複数、階層エレメントに分かれる。組合せエレメントは二者択一エレメントとセットエレメントに分かれる。二者択一エレメントはLengthとpLengthのようにどちらかがノードのプロパティとなっていようなもので、この場合定数エレメントと参照エレメントをプロパティとしてエレメント自身が保持する。
セットエレメントは3つ以上のエレメントを保持するもので、アドレスを指定するAddress、pAddress、IntSwissKnife、pIndexの組合せなどである。
複数エレメントはpInvalidatorのようにノードの中に0個以上存在するもので、複数あるのをひとまとめにする。
階層エレメントはStructEntyやpIndexのように決まった内部構造を持つものである。
暗黙エレメントとあるのは、アドレス指定に出てくるIntSwissKnifeエレメントを指すエレメントで、これはノード定義には現れない。IntSwissKnifeエレメントが出てくると、IntSwissKnifeノードを作って、暗黙エレメントを生成して、そのIntSwissKnifeノードを参照する。
これをそのままObjective-Cのクラス階層にしよう。例えばalternativeLengthという二者択一エレメントのプロパティはバイト幅を表していて、ノードはバイト幅の値を知りたいときにreadValueメソッドで問い合わせる。alternativeLengthエレメントはその中に定数エレメントのLengthか参照エレメントのpLengthのどちらかを保持して、ノードから問い合わせがあると、それにreadValueメソッドを投げて返ってきた値を自分のreadValueメソッドの値として返す。他も全く同じである。
alternativeLengthはinitの中でLengthとpLengthのどちらかが見つかったら、存在している方をinitで返して、自分自身は解放する、というのでもいい。ノードオブジェクトはどうなっているかを知る必要はない。
スキーマの中に複数回現れるエレメントもたくさんあるが、すべての場所で同じデフォルト値が指定されていて、出現した場所(ノードの違い)によらない、ということがわかった(全部見るしかないので全部見た)。
従ってノードのクラスごとに自分のプロパティに対してデフォルト値を設定する必要はなく、エレメントがあわられなかった場合はデフォルト値を持つエレメントを生成してプロパティにセットし、デフォルト値がないエレメントに対するプロパティはnilにすればいい。つまりデフォルト値の有無はノードオブジェクトが関知する必要はなく、エレメントオブジェクトのinitの中で処理すればいい、ということになる。
それに、カメラのメーカをそれまで使っていたドイツのメーカから去年カナダのメーカに切り替えた。なぜかというと、いつの間にか日本での値段が逆転していた(一番最初は数社を値段だけで比べて決めた)のと、ドキュメントがカナダのメーカのものの方が豊富だったから。
その一方で、ノイズが多い感度が低いピクセルピッチがでかいなどなどの点で光学計測用途は難しいと思っていたC-MOSイメージセンサが、いつの間にかCCDに肩を並べるようになって、ある面ではCCDを超えるようになってきた。メーカもC-MOSセンサを乗せたカメラを次々投入してきている。僕も性能が良くて安いならそれにこしたことはないと思って今年に入ってからはC-MOSカメラばかりを買っている。
ところがそのおかげで、対応しなきゃいけない機種は増える、新しい製品は機能が豊富になってXMLファイルが肥大化して人間が読むのが辛くなる、でGEN<i>CAMにさっさと対応しないと、ずっと後回しになっている画像データ転送周りの効率化がちっとも進まなくて、20fps程度で1コア占有するという状態から抜け出すことができない。
ちなみに件のカナダのメーカのカメラは、懐かしいIIDC(ver.1.32)にも準拠してるので、レジスタへのアクセスはXMLファイルを読みくださなくてもなんとかなっている。でもどうもIIDCのレジスタマップとXMLに書かれているのが同じには見えない。全部を比べる元気はないのでどう違っているのかはわからない。単にIIDCは必ず間接アドレスでアクセスする(例えばゲインを調整するレジスタのアドレスは、あるベースアドレスからの決まったオフセットのレジスタにそのアドレスが書いてある、ということになっている)けど、XMLファイルはカメラに内蔵されているので、直接書いてあるだけなのかもしれない。
いや、そんなことはどうでもいい。とにかくさっさとGEN<i>CAMを実装するところまでこぎつけないと....でもすぐ優先順位の高い仕事にオーバーライドされるんだよなあ....
11.2 エレメントの整理
エレメントの整理は前にもやったけど、あれではノードのクラス設計のためには突っ込みが足りないことがわかった。Objective-Cのクラス階層を考えるためにもういちどエレメントの方を整理し直す。スキーマファイルに出てくるエレメントを書き出して分類したのが図-33である。 十分複雑だけど、スキーマファイルではこれからさらにセマンティクスによって分かれている。クラス設計上はセマンティクスとは分離したほうがいいけど、スキーマファイルに従ってクラスを分けておいたほうが対応がわかりやすいかもしれない。
まずエレメントは単純エレメントと複合エレメントに分類することができる。複合エレメントはその内部に単純エレメントを内包する。単純エレメントは定数エレメントと参照エレメントに分かれる。参照エレメントは他のノードを参照して値を得るエレメントである。
複合エレメントは組合せ、複数、階層エレメントに分かれる。組合せエレメントは二者択一エレメントとセットエレメントに分かれる。二者択一エレメントはLengthとpLengthのようにどちらかがノードのプロパティとなっていようなもので、この場合定数エレメントと参照エレメントをプロパティとしてエレメント自身が保持する。
セットエレメントは3つ以上のエレメントを保持するもので、アドレスを指定するAddress、pAddress、IntSwissKnife、pIndexの組合せなどである。
複数エレメントはpInvalidatorのようにノードの中に0個以上存在するもので、複数あるのをひとまとめにする。
階層エレメントはStructEntyやpIndexのように決まった内部構造を持つものである。
暗黙エレメントとあるのは、アドレス指定に出てくるIntSwissKnifeエレメントを指すエレメントで、これはノード定義には現れない。IntSwissKnifeエレメントが出てくると、IntSwissKnifeノードを作って、暗黙エレメントを生成して、そのIntSwissKnifeノードを参照する。
これをそのままObjective-Cのクラス階層にしよう。例えばalternativeLengthという二者択一エレメントのプロパティはバイト幅を表していて、ノードはバイト幅の値を知りたいときにreadValueメソッドで問い合わせる。alternativeLengthエレメントはその中に定数エレメントのLengthか参照エレメントのpLengthのどちらかを保持して、ノードから問い合わせがあると、それにreadValueメソッドを投げて返ってきた値を自分のreadValueメソッドの値として返す。他も全く同じである。
alternativeLengthはinitの中でLengthとpLengthのどちらかが見つかったら、存在している方をinitで返して、自分自身は解放する、というのでもいい。ノードオブジェクトはどうなっているかを知る必要はない。
11.2.1 エレメントの列挙
実装するときどっちみち必要になるので、単純エレメントと複合エレメントをスキーマファイルから取り出して列挙しておく。- 単純エレメント
- 定数エレメント
- 整数エレメント
- Address
- Length
- Value、Min、Max、Inc
- ValueIndexed
- ValueDefault
- Constant
- Bit、LSB、MSB
- OnValue、OffValue
- PollingTime
- EventID
- ChunkID
- ブールエレメント
- Value
- 浮動小数点数エレメント
- Value、Min、Max、Inc
- ValueIndexed
- ValueDefault
- Constant
- NumericValue
- 文字列エレメント
- ToolTip、Descriptionなど多数
- 整数エレメント
- 参照エレメント
- pFooBarなんとかすべて
- 暗黙のIntSwissKnife参照
- 定数エレメント
- 複合エレメント
- 組合せエレメント
- 二者択一エレメント
- LengthとpLength
- MinとpMin
- MaxとpMax
- IncとpInc
- ValueDefaultとpValueDefault
- ChinkIDとpChunkID
- BitとLSB、MSBのセット
- セットエレメント
- LSB、MSB
- Address、pAddress、IntSwissKnife、pIndex
- 二者択一エレメント
- 複数エレメント
- pError
- pInvalidator
- pValueCopy
- ValueIndexed
- pValueIndexed
- pSelected
- pVariable
- Constant
- 階層エレメント
- StructRegとStructEntry
- EnumerationとEnumEntry
- 計算エレメント
- Expression
- Formula
- FormulaTo
- FormulaFrom
- 組合せエレメント
11.2.2 注意
ここで強調しておくけど、単純エレメントと複合エレメントという分類は僕の勝手なものなので気をつけていただきたい。前も書いたけどGenApiのスキーマ上は区別はなく、すべてがノードの子エレメントとしてフラットに出現することになっている。11.2.3 エレメントのデフォルト値
スキーマファイルにはいくつかのエレメントにデフォルト値が定義されている。すべて拾いだすとエレメント | 型 | デフォルト値 |
NameSpace | 文字列列挙型(nameSpace_t) | Custom |
Visibility | 文字列列挙型(Visibility_t) | Beginner |
IsDeprecated | 文字列列挙型(YesNo_t) | No |
ImposedAccessMode | 文字列列挙型(Access_t) | RW |
Streamable | 文字列列挙型(YesNo_t) | No |
AccessMode | 文字列列挙型(Access_t) | RW |
Cachable | 文字列列挙型(CachingMode_t) | WriteThrough |
Sign | 文字列列挙型(Sign_t) | Unsigned |
Endianess | 文字列列挙型(Endianess_t) | LittleEndian |
Slope | 文字列列挙型(Slope_t) | Automatic |
IsLinear | 文字列列挙型(YesNo_t) | No |
IsSelfClearing | 文字列列挙型(YesNo_t) | No |
SwapEndianess | 文字列列挙型(YesNo_t) | No |
CacheChaunkData | 文字列列挙型(YesNo_t) | No |
従ってノードのクラスごとに自分のプロパティに対してデフォルト値を設定する必要はなく、エレメントがあわられなかった場合はデフォルト値を持つエレメントを生成してプロパティにセットし、デフォルト値がないエレメントに対するプロパティはnilにすればいい。つまりデフォルト値の有無はノードオブジェクトが関知する必要はなく、エレメントオブジェクトのinitの中で処理すればいい、ということになる。
2016-06-26 21:26
nice!(0)
コメント(5)
トラックバック(0)
最近Pythonで、GigE Visionカメラのドライバを実装しようと思って、ネットで色々解説を見ているうちに、decafishさんの記事にたどりました。楽しく拝見させて頂きました。
記事のお蔭様で、GigE Vision仕様に対する理解が早くなったと思っています。ありがとうございます。
所で、この記事のシリーズは、45回で、終わっているのでしょうか? そして実装したドライバは、公開になっているのでしょうか?
by yu (2019-06-03 18:33)
コメントありがとうございます。
お役に立てたのなら嬉しいです。
このドライバなんですが、実はこのすぐ後に仕事で必要になって、しかもそれが急ぐ、という状況になってしまいました。
結局どうしたかというと、GigEインターフェイスを持ってかつ古いIIDC準拠のレジスタを持ったカメラだけ(例えばPointGrey社のBlackflyとか)を対象に実装をでっち上げました。IIDCは昔、IEEE1394接続のカメラで同じようなドライバを作っていたのでそれを移植しました。新しく書いたのは実質的にGigE上のdevice enumerationとUDP経由のデータ転送の部分だけです。
そのためGEN<i>CAM対応はほったらかしになっています。中途半端なので公開していません。またIIDCのほうは動いているのですが、非常に汚いのでこっそり自分だけで使っています。申し訳ありません。
しかしいつまでもIIDCでもないし、カメラメーカではC-MOSイメージャを積んだUSB3インターフェイスの新しいカメラはIIDCのサポートをやめていて、さらにどうやら古いCCDイメージャが奪い合いになってるみたいで、そのうちまたGEN<i>CAM対応に火が付くのはあきらかなのでなんとかしたいと思っています。
そうなるとObjective-CではなくSwiftで書きたいですし、GigEだけでなくUSB3にも対応したいと思っています。
放置状態かと思っていたaravis
https://github.com/AravisProject/aravis
も続きそうなので、新しく書くのではなくてaravisのSwiftラッパか、あるいはGObjectをSwift構造体かクラスに書き直してglibやgettextやlibusbに依存しないようにするか、と考えています。
Pythonのドライバを検討されるようでしたら状況をぜひお教えください。
よろしくお願いします。
by decafish (2019-06-04 13:50)
回答を頂いて、ありがとうございます。
事情が分かりました。
記事から得た知識を元にAravisを解析してみようと思います。
実は、AI関係で、Pythonで画像処理をやろうとしている所、会社で余っているGigEVisionカメラがあったので、それを利用しようと最初は、軽く考えました。しかし着手してみるとPythonで利用できるドライバが少ないことに気付きました。その上GigEVisonカメラも初めて触れる物なので、GigEVisionの勉強を兼ねて、Pythonによるドライバを実装してみようと考えるようになりました。
でも、Decafishさんの記事を見ると、「GEN<i>CAM」への対応が一番難しいのようですね。どこまでできるかは、分からないが、とりあえず進んでみようと思っています。
by yu (2019-06-05 15:10)
aravisは結構古いprojectで、この記事を書いていた頃は「ちょっと厳しいなあ」という感じでしたが、今ではGigEだけでなくUSB3でもちゃんと効率よく動くようになっているので、認識を改めました。
ちなみにaravisは、macOSでもhomebrewなどで依存するライブラリをインストールすると動作しました。ただし僕の環境ではgithubのREADME.md「Building on Mac OS X」セクションの通りではコンパイルできず、かなり苦労が必要でした。
aravisはGObjectというmacOSのCoreFoundationに似たCで書くObject systemを使っています。GObjectはCoreFoundationと同じでobjectを使う分にはいいのですが、新しいクラスを実装しようとするとボイラープレートを大量に書かないといけないようです。
GObjectがダメだというつもりはまったくありませんが、objectをサポートする言語にポートすればもう少しスッキリするのではないかと思っています。
僕もaravisを研究してみます。
by decafish (2019-06-06 10:06)
ありがとうございます。
私は、MacOS系には良く分かりませんが、色んな
コメントが参考になると思います。
by yu (2019-06-07 16:55)