pypylonを用いたカメラの操作基本　(1)


## カメラの特定

作業を開始する前に、使用するカメラの特定が必要です。これは、ドライバがどのように機能するかを理解する最初のステップです。以下のPythonコードを実行することで、接続されたカメラのリストを取得できます。

```python
from pypylon import pylon

#Getting Started
tl_factory = pylon.TlFactory.GetInstance()
devices = tl_factory.EnumerateDevices()
for device in devices:
    print(device.GetFriendlyName())
    
```

このコードは、PylonViewerプログラムと同様に、コンピュータに接続されたカメラの型番とシリアルナンバーをリストアップします。

```bash
Basler a2A3840-45ucPRO (40116877)
# a2A3840-45ucPRO : カメラの型番
# 40116877 : カメラのS/N
```

## 画像の取得

画像を取得する基本的なプロセスは、カメラのオブジェクトを初期化し、カメラを開いて画像を取得することです。ここでは、InstantCamera オブジェクトを作成し、利用可能な最初のカメラにアタッチします。


```python
tl_factory = pylon.TlFactory.GetInstance()
camera = pylon.InstantCamera()
camera.Attach(tl_factory.CreateFirstDevice())
```

画像の取得プロセスは次のように進行します。

2つのステップで構成される。トランスポート・レイヤーのインスタンスを取得した後、**InstantCameraオブジェクトを作成する。** Baslerは、デバイスのライフサイクルの処理、イベント・コールバックの作成、物理的なデバイスの取り外しなど、カメラでの作業を簡素化する方法として、これらのオブジェクトを提供しています。高度に専門化されたアプリケーションを開発するとします。その場合、PylonのDeviceオブジェクトに飛び込みたくなるかもしれませんが、それはこの記事の範囲外です。

**InstantCameraがインスタンス化されたら、利用可能な最初のカメラをそれにアタッチします。** 上記のコードは、基礎となるデータ・トランスポート層に対して透過的であることに注意してください。USBカメラもGigEカメラも同じパターンで使用できます。トランスポート・レイヤーが実際のデバイスを作成するのに対し、カメラは便利なインターフェースであることも重要です。ラッパー・オブジェクトInstantCameraと実際のデバイスとの接続は、それを```Attach```するときに行われます。

画像を取得するには、いくつかの手順を踏む必要がある：

```python
camera.Open()
camera.StartGrabbing(1)
grab = camera.RetrieveResult(2000, pylon.TimeoutHandling_Return)
if grab.GrabSucceeded():
    img = grab.GetArray()
    print(f'Size of image: {img.shape}')
camera.Close()

```

まずカメラを開きます。```Open()```この段階でデバイスとの通信を確立し、パイロンが基本的な設定を行います。```StartGrabbing```に整数の引数を1つ与えると、Pylonに取得したいフレーム数を伝えます。カメラがそのフレーム数に達すると、新しいフレームの取得を停止します。このメソッドは、InstantCameraオブジェクトが提供する便利なメソッドの1つです。

ここでのポイントは、RetrieveResult のタイムアウト時間を適切に設定することで、カメラの応答性や環境に応じて適切に画像取得を管理できることです。

上のコードでは、1フレームだけ取得しています。2000ミリ秒のタイムアウトを使って結果を取得しています。2秒経過しても結果がない場合は、単にリターンするように指定している。```TimeoutHandling_ThrowException```を使うという方法もある。

GrabSucceededメソッドを使って、実際にgrabが成功したことを確認する。次に、GetArrayメソッドを使って実際のデータを取得します。***結果のimgはnumpyの配列です。*** 画像の形状を表示し、カメラを閉じます。これは始めるための最小限の例ですが、画像の表示や保存などを制限するものは何もありません。いずれにせよ、最も重要なステップはカメラからデータを取得することです。


```bash
Size of image: (2160, 3840)
```

## パラメータの変更
カメラデバイスから読み取れるようになったら、次に重要なのは撮影パラメーターを変更することだ。カメラの場合、露光時間は最も重要なパラメータの1つでしょう。InstantCameraインターフェースでは、以下の構文でパラメータを変更できます：

```python
camera.ExposureTime.SetValue(50000)
#単位はmicro second [us]
```

構文は非常に明快です。ExposureTimeを変更したい場合は、SetValueというメソッドを使い、マイクロ秒単位で値を指定する（単位については後述）。Pylonのマニュアルを確認すると、カメラのパラメータはこのパターンで記述されています。しかし、PyPylonを使っているので、別の構文を使うこともできます：

```python
camera.ExposureTime = 50000
```
どちらの例も同じ効果を生み出し、関数型プログラミングか命令型プログラミングかの違いを示している。個人的には、例えばExposureTimeの代わりにExposureを使用した場合、常にエラーが発生するため、最初の例の方が明確だと思う。

## カメラノード情報の取得

ExposureTime はカメラの単純な数値属性よりも複雑であるためです。デバイスに値を設定する際、一般的な懸念事項の一つは、各パラメータの限界と単位を知ることです。その点、Pylonは非常に透明で一貫しています。

```python

camera.Open()
print(camera.ExposureTime.GetUnit())
print(camera.ExposureTime.GetValue())
print(camera.ExposureTime.GetMin())
print(camera.ExposureTime.GetMax())
print(camera.ExposureTime.Unit)
print(camera.ExposureTime.Value)
print(camera.ExposureTime.Min)
print(camera.ExposureTime.Max)
camera.Close()

```

ExposureTimeの単位と現在値、最小値、最大値を取得することができます。上のコードでは、GetUnitまたはUnitのようなアトリビュートを使用して情報を取得する2つの方法を示しています。繰り返しになりますが、これは個人の好みの問題であり、より広範なプロジェクトを通じて一貫性を保つことができます。

## Free Runモードで画像を取得する
カメラでは、1フレームだけでなく、連続的に画像を取得したいことがよくあります。Free Runとは、停止を指示するまでカメラが次々とフレームを取得することを意味します。InstantCameraでは、最大フレーム数を指定せずに**StartGrabbing**メソッドを使用することでこれを実現できます：

```python
import time 

camera.Open()
camera.StartGrabbing(pylon.GrabStrategy_OneByOne)
i = 0
print('Starting to acquire')
t0 = time.time()
while camera.IsGrabbing():
    grab = camera.RetrieveResult(100, pylon.TimeoutHandling_ThrowException)
    if grab.GrabSucceeded():
        i += 1
    if i == 100:
        break

print(f'Acquired {i} frames in {time.time()-t0:.0f} seconds')
camera.Close()
```

この例では、StartGrabbingは**OneByOne（先入れ先出し）**のストラテジーを受け取る。フレームは順番に取得され、順番に取り出される。**IsGrabbing**メソッドを使用して、カメラが実際にデータを取得していることを確認します。結果を取得する際に100ミリ秒のタイムアウトしか使っていないことに注意```grab = camera.RetrieveResult(**100**, pylon.TimeoutHandling_ThrowException)```。露光時間がそれ以上長い場合、Pylonはエラーを出します。この例と前の例を組み合わせることで確認できる。

データの取り込みに成功したら、カウンターを増やす。100フレームに達したらループを止めるが、それ以外の任意の時点でプログラムを止める方法も有効である。重要なのは、ループを止めるのであって、Grabbingそのものを止めるのではないということだ。InstantCameraオブジェクトによってカメラが閉じられると、Grabbingは停止します。また、**StopGrabbing**を使用してカメラ撮影を停止することもできます：

```python

camera.Open()
camera.StartGrabbing(pylon.GrabStrategy_OneByOne)
i = 0
print('Starting to acquire')
t0 = time.time()
while camera.IsGrabbing():
    grab = camera.RetrieveResult(100, pylon.TimeoutHandling_ThrowException)
    if grab.GrabSucceeded():
        i += 1
    if i == 100:
        camera.StopGrabbing()
        break

print(f'Acquired {i} frames in {time.time()-t0:.0f} seconds')
camera.Close()

```

##  参考資料

https://pythonforthelab.com/blog/getting-started-with-basler-cameras/

https://github.com/basler/pypylon/tree/master

## 参考資料2

### camera.StartGrabbing()


camera.StartGrabbing()は、カメラからの画像取得を開始するためのメソッドです。このメソッドを呼び出すことで、カメラは画像の取得を開始し、アプリケーションは取得した画像を処理できるようになります。

#### 引数
*StartGrabbing()メソッドはいくつかのオプション引数を取ることができますが、主に以下の引数が使用されます

1. MaxNumBuffer:
これはカメラが内部で保持する画像バッファの最大数を指定します。デフォルトでは、この数は自動的に設定されますが、アプリケーションの要求に応じてこの値を調整することができます。
例：```camera.StartGrabbing(pylon.GrabStrategy_LatestImageOnly, pylon.GrabLoop_ProvidedByInstantCamera, MaxNumBuffer=5)```

1. GrabStrategy:
画像を取得する戦略を指定します。主に以下の戦略があります：
pylon.GrabStrategy_OneByOne: 画像を一つずつ順番に取得します。
pylon.GrabStrategy_LatestImageOnly: 常に最新の画像のみを取得し、古い画像は破棄します。
例：```camera.StartGrabbing(pylon.GrabStrategy_LatestImageOnly)```

1.　GrabLoop:
グラブループの管理方法を指定します。主に以下のオプションがあります：
pylon.GrabLoop_ProvidedByInstantCamera: pypylonが提供するグラブループを使用します。
pylon.GrabLoop_ProvidedByUser: ユーザが独自にグラブループを管理します。
例：```camera.StartGrabbing(pylon.GrabLoop_ProvidedByInstantCamera)```

*使い方

```python
from pypylon import pylon

camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice())
camera.Open()
camera.StartGrabbing(pylon.GrabStrategy_LatestImageOnly)

while camera.IsGrabbing():
    grabResult = camera.RetrieveResult(5000, pylon.TimeoutHandling_ThrowException)
    if grabResult.GrabSucceeded():
        # 画像データの処理
        image = grabResult.Array
    grabResult.Release()

camera.Close()


```