OMRON SENTECカメラをPythonとOpenCVで制御する方法(5)

前回記事の続きです。

https://qiita.com/kotai2003/items/fd3fdc983cd312290818

### 詳細な解説: メイン実行ブロックとスレッドの使用

この部分のコードは、カメラからの画像取得と設定の管理を行うメインの実行ブロックです。また、スレッドを使用して自動設定機能を別のスレッドで実行しています。以下に各ステップを詳細に解説します。

#### メイン実行ブロック

```python
if __name__ == "__main__":
    my_callback = CMyCallback()
    cb_func = my_callback.datastream_callback
    try:
        # Initialize StApi before using.
        st.initialize()

        # Create a system object for device scan and connection.
        st_system = st.create_system()

        # Connect to first detected device.
        st_device = st_system.create_first_device()

        # Display DisplayName of the device.
        print('Device=', st_device.info.display_name)

        # Create a datastream object for handling image stream data.
        st_datastream = st_device.create_datastream()

        # Register callback for datastream
        callback = st_datastream.register_callback(cb_func)

        # Start the image acquisition of the host (local machine) side.
        st_datastream.start_acquisition()

        # Start the image acquisition of the camera side.
        st_device.acquisition_start()

        # Get device nodemap to access the device settings.
        remote_nodemap = st_device.remote_port.nodemap

        # Create and start a thread for auto function configuration.
        autofunc_thread = threading.Thread(target=do_auto_functions, args=(remote_nodemap,))
        autofunc_thread.start()

        # Display image using OpenCV.
        while True:
            output_image = my_callback.image
            if output_image is not None:
                cv2.imshow('image', output_image)
            key_input = cv2.waitKey(1)
            if key_input != -1:
                break

        autofunc_thread.join()

        # Stop the image acquisition of the camera side
        st_device.acquisition_stop()

        # Stop the image acquisition of the host side
        st_datastream.stop_acquisition()

    except Exception as exception:
        print(exception)
```

#### 各ステップの解説

1. **コールバッククラスのインスタンス化**

    ```python
    my_callback = CMyCallback()
    cb_func = my_callback.datastream_callback
    ```

    - `CMyCallback` のインスタンスを作成し、データストリームからの画像取得を処理するためのコールバック関数を設定します。

2. **StApiの初期化**

    ```python
    st.initialize()
    ```

    - StApiライブラリを使用する前に初期化します。

3. **システムオブジェクトの作成**

    ```python
    st_system = st.create_system()
    ```

    - デバイススキャンと接続のためにシステムオブジェクトを作成します。

4. **デバイスへの接続**

    ```python
    st_device = st_system.create_first_device()
    ```

    - 最初に検出されたデバイス（カメラ）に接続します。

5. **デバイス情報の表示**

    ```python
    print('Device=', st_device.info.display_name)
    ```

    - 接続されたデバイスの名前を表示します。

6. **データストリームオブジェクトの作成**

    ```python
    st_datastream = st_device.create_datastream()
    ```

    - 画像ストリームデータを処理するためのデータストリームオブジェクトを作成します。

7. **コールバックの登録**

    ```python
    callback = st_datastream.register_callback(cb_func)
    ```

    - データストリームのイベントを処理するためにコールバック関数を登録します。

8. **画像取得の開始（ホスト側）**

    ```python
    st_datastream.start_acquisition()
    ```

    - ホスト（ローカルマシン）側での画像取得を開始します。

9. **画像取得の開始（カメラ側）**

    ```python
    st_device.acquisition_start()
    ```

    - カメラ側での画像取得を開始します。

10. **デバイスノードマップの取得**

    ```python
    remote_nodemap = st_device.remote_port.nodemap
    ```

    - デバイス設定にアクセスするためのリモートノードマップを取得します。

11. **自動設定機能のためのスレッド作成と開始**

    ```python
    autofunc_thread = threading.Thread(target=do_auto_functions, args=(remote_nodemap,))
    autofunc_thread.start()
    ```

    - `do_auto_functions` 関数を実行するスレッドを作成し、開始します。このスレッドはカメラの自動設定（露出、ゲイン、ホワイトバランス）を管理します。

12. **OpenCVを使用した画像表示**

    ```python
    while True:
        output_image = my_callback.image
        if output_image is not None:
            cv2.imshow('image', output_image)
        key_input = cv2.waitKey(1)
        if key_input != -1:
            break
    ```

    - メインループで画像を取得し、OpenCVを使用して表示します。ユーザーがキーを押すとループを抜けます。

13. **スレッドの終了を待つ**

    ```python
    autofunc_thread.join()
    ```

    - 自動設定スレッドの終了を待ちます。

14. **画像取得の停止（カメラ側とホスト側）**

    ```python
    st_device.acquisition_stop()
    st_datastream.stop_acquisition()
    ```

    - カメラとホスト側の画像取得を停止します。

15. **例外処理**

    ```python
    except Exception as exception:
        print(exception)
    ```

    - 例外が発生した場合、その内容を表示します。

#### スレッドの使用理由

このコードでは、`do_auto_functions` 関数を別のスレッドで実行する理由は以下の通りです：

1. **非同期処理**:
    - メインスレッドでは画像の取得と表示を継続的に行う必要がありますが、自動設定機能（露出、ゲイン、ホワイトバランス）の変更はユーザー入力を必要とするため、時間がかかる場合があります。これをメインスレッドで行うと、画像取得や表示に遅延が発生する可能性があります。

2. **リアルタイム性の維持**:
    - 画像取得と表示はリアルタイムで行う必要があるため、設定変更の処理を別のスレッドで実行することで、メインスレッドのリアルタイム処理に影響を与えないようにします。

3. **ユーザーインタラクション**:
    - 自動設定機能はユーザーからの入力を受け付けるインタラクティブな処理を含むため、これを別スレッドで処理することで、メインスレッドの処理をブロックせずにユーザー入力を待つことができます。

このように、スレッドを使用することで、メインスレッドの画像取得と表示処理をリアルタイムで継続しつつ、ユーザーによる設定変更をスムーズに行うことができます。

### コードの違いとその影響

#### スレッドを使用したコード

1. **コールバッククラスのインスタンス化**
2. **StApiの初期化**
3. **システムオブジェクトの作成**
4. **デバイスへの接続**
5. **デバイス情報の表示**
6. **データストリームオブジェクトの作成**
7. **コールバックの登録**
8. **画像取得の開始（ホスト側とカメラ側）**
9. **デバイスノードマップの取得**
10. **自動設定機能のためのスレッド作成と開始**
11. **OpenCVを使用した画像表示**
12. **スレッドの終了を待つ**
13. **画像取得の停止（ホスト側とカメラ側）**
14. **例外処理**

スレッドを使用して、自動設定機能を別のスレッドで実行することで、メインスレッドはリアルタイムで画像の取得と表示を行うことができます。これにより、設定変更の処理がメインの画像処理に影響を与えないようにしています。

#### スレッドを使用しないコード

1. **StApiの初期化**
2. **システムオブジェクトの作成**
3. **デバイスへの接続**
4. **デバイス情報の表示**
5. **データストリームオブジェクトの作成**
6. **画像取得の開始（ホスト側とカメラ側）**
7. **画像の取得と処理**
8. **画像取得の停止（ホスト側とカメラ側）**
9. **例外処理**

このコードでは、画像取得と処理をメインスレッドで行っています。自動設定機能は含まれておらず、シンプルに画像を取得し表示することに集中しています。

---
### スレッド使用の影響と違い

#### パフォーマンス

- **スレッドを使用したコード**:
  - 画像取得と表示のリアルタイム性を維持しつつ、並行して自動設定機能を実行できます。
  - メインスレッドが画像の取得と表示を担当し、自動設定は別スレッドで処理されるため、設定変更が原因で画像表示が遅延することはありません。

- **スレッドを使用しないコード**:
  - 画像の取得と処理はメインスレッドで順次行われるため、設定変更が含まれていない分、処理が単純で高速です。
  - ただし、自動設定機能が追加された場合、それがメインスレッドで処理されると画像取得と表示に遅延が生じる可能性があります。

#### 複雑さ

- **スレッドを使用したコード**:
  - コードが複雑になり、デバッグが難しくなる可能性があります。
  - スレッド間の同期（ロック）の管理が必要になります。

- **スレッドを使用しないコード**:
  - シンプルでデバッグが容易です。
  - スレッド管理の必要がないため、理解しやすくなります。

### 具体的な動きの違い

#### スレッドを使用したコード

- メインスレッドは、カメラからの画像取得と表示に専念し、リアルタイムで画像を表示します。
- 別スレッドで自動設定機能（露出、ゲイン、ホワイトバランス）の変更を処理し、ユーザーの入力を待ちます。
- これにより、設定変更の処理が画像の取得や表示に影響を与えません。

#### スレッドを使用しないコード

- 画像取得と処理は順次メインスレッドで行われ、1000枚の画像を連続で取得し表示します。
- 自動設定機能は含まれていないため、ユーザー入力待ちや設定変更による遅延は発生しません。
- 画像取得と表示の一連の処理がシンプルで、設定変更に伴う遅延がない分、処理は高速です。

### 結論

スレッドを使用したコードは、リアルタイムで画像を表示しつつ、並行して設定変更を行う必要がある場合に適しています。一方、スレッドを使用しないコードは、シンプルで高速な処理が必要な場合に適しています。どちらのアプローチも利点があり、使用するシナリオに応じて適切な方法を選択することが重要です。

### 参考資料

https://qiita.com/kotai2003/items/5c14a3ba58d5f6ccd127