diff --git a/.github/workflows/_build_sdist_.yml b/.github/workflows/_build_sdist_.yml new file mode 100644 index 0000000..7b69e3a --- /dev/null +++ b/.github/workflows/_build_sdist_.yml @@ -0,0 +1,32 @@ +name: Build sdist + +on: + workflow_call: + workflow_dispatch: # Manual trigger + +jobs: + _build-sdist_: + name: Build sdist + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.13' + + - name: Install dependencies + run: python -m pip install --upgrade pip build setuptools + + - name: Set PVCAM_SDK_PATH on Linux + run: echo "PVCAM_SDK_PATH=${GITHUB_WORKSPACE}/pvcam-sdk/linux" >> $GITHUB_ENV + + - name: Build package + run: python -m build --sdist + + - uses: actions/upload-artifact@v4 + with: + name: pyvcam_sdist + path: dist/ + if-no-files-found: error diff --git a/.github/workflows/_build_wheels_.yml b/.github/workflows/_build_wheels_.yml new file mode 100644 index 0000000..e743d3e --- /dev/null +++ b/.github/workflows/_build_wheels_.yml @@ -0,0 +1,71 @@ +name: Build wheels + +on: + workflow_call: + workflow_dispatch: # Manual trigger + +jobs: + _build-wheels_: + name: Build wheel (${{ matrix.os }} Python ${{ matrix.python }}) + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + # Don't use '*-latest' to ensure the oldest Python version is not + # dropped on upgrade. + # Use 'ubuntu-22.04' because 24.04 is not available for 'act' yet. + # The 'ubuntu-*-arm' runners are not free for open source yet. + os: + - 'ubuntu-22.04' + #- 'ubuntu-22.04-arm' + - 'windows-2022' + python: + - '3.9' + - '3.10' + - '3.11' + - '3.12' + - '3.13' + exclude: + # The 'ubuntu-*-arm' and 'windows*' runners are not supported by 'act' + #- os: ${{ github.event.act && 'ubuntu-22.04-arm' }} + - os: ${{ github.event.act && 'windows-2022' }} + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python }} + + - name: Install dependencies + run: python -m pip install --upgrade pip build setuptools + + - if: runner.os == 'Windows' + name: Set PVCAM_SDK_PATH on Windows + shell: bash + run: echo "PVCAM_SDK_PATH=${GITHUB_WORKSPACE}/pvcam-sdk/windows" >> $GITHUB_ENV + - if: runner.os == 'Linux' + name: Set PVCAM_SDK_PATH on Linux + run: echo "PVCAM_SDK_PATH=${GITHUB_WORKSPACE}/pvcam-sdk/linux" >> $GITHUB_ENV + + - if: runner.os == 'Linux' + name: Create PVCAM library symlinks on Linux + shell: bash + run: | + for d in i686 x86_64 aarch64; do + cd "$PVCAM_SDK_PATH/library/$d" + f=$(echo libpvcam.so.*.*) + chmod +x "$f" + ln -sf "$f" "${f%.*}" + ln -sf "$f" "${f%%.so.*}.so" + done + + - name: Build package + run: python -m build --wheel + + - uses: actions/upload-artifact@v4 + with: + name: pyvcam_wheel_${{ matrix.os }}_py${{ matrix.python }} + path: dist/ + if-no-files-found: error diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..a667f18 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,48 @@ +name: Build all + +on: + push: + branches: + - master + paths: + - '!docs/**' + - '!README.md' + pull_request: + branches: + - master + paths: + - '!docs/**' + - '!README.md' + workflow_dispatch: # Manual trigger + +jobs: + build-sdist: + uses: './.github/workflows/_build_sdist_.yml' + + build-wheels: + uses: './.github/workflows/_build_wheels_.yml' + + build: + needs: + - build-sdist + - build-wheels + + name: Build all + runs-on: ubuntu-latest + + steps: + - uses: actions/download-artifact@v4 + with: + pattern: pyvcam_* + path: dist/ + merge-multiple: true + + - uses: actions/setup-python@v5 + with: + python-version: '3.13' + + - name: Install dependencies + run: python -m pip install --upgrade pip twine packaging + + - name: Check package + run: python -m twine check --strict dist/* diff --git a/.github/workflows/deploy_pypi.yml b/.github/workflows/deploy_pypi.yml new file mode 100644 index 0000000..b1e08c1 --- /dev/null +++ b/.github/workflows/deploy_pypi.yml @@ -0,0 +1,44 @@ +name: PyPI Release + +on: + release: + types: + - released + workflow_dispatch: # Manual trigger + +jobs: + _build-sdist: + if: github.repository == 'Photometrics/PyVCAM' && !github.event.act # Skip job during local testing + uses: './.github/workflows/_build_sdist_.yml' + + _build-wheels: + if: github.repository == 'Photometrics/PyVCAM' && !github.event.act # Skip job during local testing + uses: './.github/workflows/_build_wheels_.yml' + + deploy-pypi: + if: github.repository == 'Photometrics/PyVCAM' && !github.event.act # Skip job during local testing + needs: + - _build-sdist + - _build-wheels + + name: Upload to PyPI + runs-on: ubuntu-latest + + environment: + name: pypi + url: https://pypi.org/p/PyVCAM + + permissions: + id-token: write # Mandatory for trusted publishing + + steps: + - uses: actions/download-artifact@v4 + with: + pattern: pyvcam_* + path: dist/ + merge-multiple: true + + - name: Print out packages + run: ls -la dist + + - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/deploy_testpypi.yml b/.github/workflows/deploy_testpypi.yml new file mode 100644 index 0000000..2dd2b77 --- /dev/null +++ b/.github/workflows/deploy_testpypi.yml @@ -0,0 +1,52 @@ +name: TestPyPI Release + +on: + push: + tags: + - 'v*' + release: + types: + - prereleased + - released + workflow_dispatch: # Manual trigger + +jobs: + _build-sdist: + if: github.repository == 'Photometrics/PyVCAM' + uses: './.github/workflows/_build_sdist_.yml' + + _build-wheels: + if: github.repository == 'Photometrics/PyVCAM' + uses: './.github/workflows/_build_wheels_.yml' + + deploy-testpypi: + if: github.repository == 'Photometrics/PyVCAM' + needs: + - _build-sdist + - _build-wheels + + name: Upload to TestPyPI + runs-on: ubuntu-latest + + environment: + name: testpypi + url: https://test.pypi.org/p/PyVCAM + + steps: + - uses: actions/download-artifact@v4 + with: + pattern: pyvcam_* + path: dist/ + merge-multiple: true + + - name: Print out downloaded artifacts + run: ls -la dist + + - uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ + skip-existing: true + # Using token instead of trusted publishing to allow + # 'act' to publish to TestPyPI when testing actions locally. + user: __token__ + password: ${{ secrets.TESTPYPI_API_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c341e43..554244a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,8 +2,6 @@ name: Tests on: push: - branches: - - master pull_request: branches: - master @@ -11,24 +9,23 @@ on: jobs: check: - name: Check code quality - ${{ matrix.os }} - ${{ matrix.python-version }} - + name: Check code quality (${{ matrix.os }} Python ${{ matrix.python }}) runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: [ubuntu-latest] - python-version: ['3.12'] + os: + - 'ubuntu-latest' + python: + - '3.13' steps: - - name: Checkout sources - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + - uses: actions/setup-python@v5 with: - python-version: ${{ matrix.python-version }} + python-version: ${{ matrix.python }} - name: Update pip run: python -m pip install --upgrade pip @@ -37,9 +34,8 @@ jobs: # Rather installing dependencies manually #- name: Install the package with dependencies # run: python -m pip install '.[gui,dev]' - # Rather installing dependencies manually - name: Install the package dependencies - run: python -m pip install setuptools numpy opencv-python matplotlib + run: python -m pip install --upgrade setuptools numpy opencv-python matplotlib - name: Validate pyproject.toml run: | diff --git a/README.md b/README.md index fd6f356..6228df3 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,70 @@ -# PyVCAM Wrapper +# PyVCAM -PyVCAM Wrapper is a Python3.X wrapper for the PVCAM SDK. +[![GitHub release](https://img.shields.io/github/v/release/Photometrics/PyVCAM?label=GitHub%20stable&color=green)](https://github.com/Photometrics/PyVCAM/releases "GitHub stable release") +[![GitHub release](https://img.shields.io/github/v/release/Photometrics/PyVCAM?include_prereleases&label=GitHub%20latest)](https://github.com/Photometrics/PyVCAM/releases "GitHub latest release") +![GitHub License](https://img.shields.io/github/license/Photometrics/PyVCAM) +[![PyPI release](https://img.shields.io/pypi/v/PyVCAM?label=PyPI&&color=green)](https://pypi.org/project/PyVCAM/ "PyPI release") +[![TestPyPI release](https://img.shields.io/pypi/v/PyVCAM?pypiBaseUrl=https%3A%2F%2Ftest.pypi.org&label=TestPyPI)](https://test.pypi.org/project/PyVCAM/ "TestPyPI release") +[![Python version from PEP 621 TOML](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2FPhotometrics%2FPyVCAM%2Fmaster%2Fpyproject.toml)](https://python.org "Python versions") +[![Tests status](https://github.com/Photometrics/PyVCAM/actions/workflows/tests.yml/badge.svg)](https://github.com/Photometrics/PyVCAM/actions/workflows/tests.yml "Tests status") +[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/Photometrics/PyVCAM.svg)](http://isitmaintained.com/project/Photometrics/PyVCAM "Average time to resolve an issue") +[![Percentage of issues still open](http://isitmaintained.com/badge/open/Photometrics/PyVCAM.svg)](http://isitmaintained.com/project/Photometrics/PyVCAM "Percentage of issues still open") +--- -## Getting Started -Follow the instructions below to get PyVCAM up and running on your machine for -development and testing. +PyVCAM is a Python 3.x wrapper for the PVCAM library. + +## Installation +The wrapper can be installed using the following command in an environment with +`python` and `pip`: +``` +pip install PyVCAM +``` +It requires the latest PVCAM to be installed - can be downloaded +[here](https://www.teledynevisionsolutions.com/products/pvcam-sdk-amp-driver/). + +The binary packages are available for 64-bit Windows and Linux on Intel platform only. +For 32-bit or Arm Linux platform it must be complied from the source code. +Please follow the instructions for development below. + +## Examples +The installed package contains just the wrapper. If you want to get an image from +the camera right away before writing your own application, PyVCAM repository contains +multiple examples, like `seq_mode.py` or `live_mode.py`. + +Get a local copy of the GitHub repository by downloading the zip archive or cloning it +e.g. from git command prompt: +``` +git clone https://github.com/Photometrics/PyVCAM.git PyVCAM +``` +Then switch to the folder with examples and run one: +``` +cd PyVCAM/examples +python seq_mode.py +``` +Some examples show the image from camera in a UI window and require `opencv-python` +or `matplotlib` packages to be installed. + +## Development +For troubleshooting or active contribution you may want to install PyVCAM from GitHub. ### Prerequisites -* An understanding of PVCAM is very helpful for understanding PyVCAM. +* Cloned repository from GitHub as described in Examples chapter above. * A C/C++ compiler is needed to build native source code. - For Windows, the compilers from Visual Studio 2019 and 2022 were used for testing. -* The newest version of Python 3 which can be downloaded - [here](https://www.python.org/downloads/). -* The latest PVCAM and PVCAM SDK which can be downloaded + On Windows, Visual Studio 2022 was used for testing. +* The latest PVCAM and PVCAM SDK - both can be downloaded [here](https://www.teledynevisionsolutions.com/products/pvcam-sdk-amp-driver/). -* PyVCAM was developed and tested using Microsoft Windows 10/64-bit and Windows 11. - Linux is also supported, but testing has been minimal. ### Installation -When you are ready to install the wrapper use your command prompt to navigate into the directory -that contains `pyproject.toml` file and run ```python -m pip install .``` (don't forget the dot -at the end) +Use your command prompt to navigate into the directory that contains `pyproject.toml` +file and run: +``` +pip install . +``` -### How to use the wrapper +## How to use the wrapper +An understanding of PVCAM API is very helpful for understanding PyVCAM. -#### Create Camera Example +### Create Camera Example This will create a camera object using the first camera that is found that can then be used to interact with the camera. ``` @@ -36,7 +76,7 @@ cam = next(Camera.detect_camera()) # Use generator to find first camera cam.open() # Open the camera ``` -#### Single Image Example +### Single Image Example This captures a single image with a 20 ms exposure time and prints the values of the first 5 pixels. ``` # A camera object named cam has already been created @@ -44,7 +84,7 @@ frame = cam.get_frame(exp_time=20) print("First five pixels of frame: {}, {}, {}, {}, {}".format(*frame[:5])) ``` -#### Changing Settings Example +### Changing Settings Example This is an example of how to change some of the settings on the cameras. ``` cam.clear_mode = "Never" @@ -59,7 +99,7 @@ cam.clear_mode = const.CLEAR_NEVER cam.exp_mode = const.EXT_TRIG_TRIG_FIRST ``` -#### Changing Speed Table Settings Example +### Changing Speed Table Settings Example When changing speed table, set new value to all three properties in this exact order: `readout_port` → `speed` → `gain`, otherwise some legacy cameras could end up in invalid state. @@ -70,4 +110,4 @@ cam.gain = 1 ``` More information on how to use this wrapper and how it works can be found in -[docs/PyVCAM Wrapper.md](https://github.com/Photometrics/PyVCAM/blob/master/docs/PyVCAM%20Wrapper.md). +[docs/PyVCAM.md](https://github.com/Photometrics/PyVCAM/blob/master/docs/PyVCAM.md). diff --git a/docs/CheetSheet.txt b/docs/CheetSheet.txt index db110d1..054ad1c 100644 --- a/docs/CheetSheet.txt +++ b/docs/CheetSheet.txt @@ -1,5 +1,8 @@ [[Installation]] -pip install . +From PyPI: + pip install PyVCAM +From GitHub clone: + pip install . [[Uninstallation]] pip uninstall PyVCAM diff --git a/docs/PyVCAM Wrapper.md b/docs/PyVCAM.md similarity index 99% rename from docs/PyVCAM Wrapper.md rename to docs/PyVCAM.md index 58263c3..f2cd908 100644 --- a/docs/PyVCAM Wrapper.md +++ b/docs/PyVCAM.md @@ -1,529 +1,529 @@ -# PyVCAM Wrapper - - -* [PyVCAM Wrapper](#pyvcam-wrapper) - * [Installing the Package](#installing-the-package) - * [Creating a Wheel Package](#creating-a-wheel-package) - * [Uninstalling the Package](#uninstalling-the-package) - * [`src` Folder](#src-folder) - * [`pyvcam` Folder](#pyvcam-folder) - * [`camera.py`](#camerapy) - * [Create Camera Example](#create-camera-example) - * [Methods of `Camera` class](#methods-of-camera-class) - * [Camera Selection](#camera-selection) - * [Basic Frame Acquisition](#basic-frame-acquisition) - * [Advanced Frame Acquisition](#advanced-frame-acquisition) - * [Acquisition Configuration](#acquisition-configuration) - * [Acquisition Trigger](#acquisition-trigger) - * [Parameters](#parameters) - * [Properties](#properties) - * [Using Properties](#using-properties) - * [List of Properties](#list-of-properties) - * [`constants.py`](#constantspy) - * [`pvcmodule.cpp`](#pvcmodulecpp) - * [General Structure of a `pvc` Module Functions](#general-structure-of-a-pvc-module-functions) - * [Retrieving Data](#retrieving-data) - * [Arguments of `PyArg_ParseTuple`](#arguments-of-pyarg_parsetuple) - * [`PyArg_ParseTuple` Example](#pyarg_parsetuple-example) - * [Processing Acquired Data](#processing-acquired-data) - * [Return Data to a Python Script](#return-data-to-a-python-script) - * [Cast to Python Type](#cast-to-python-type) - * [Functions of `pvc` Module](#functions-of-pvc-module) - * [The Method Table](#the-method-table) - * [The Module Definition](#the-module-definition) - * [Module Creation](#module-creation) - * [Creating Extension Module](#creating-extension-module) - * [`constants_generator.py`](#constants_generatorpy) - * [Requirements](#requirements) - * [Running the Script](#running-the-script) - * [`tests` Folder](#tests-folder) - * [`change_settings_test.py` (needs `camera_settings.py`)](#change_settings_testpy-needs-camera_settingspy) - * [`check_frame_status.py`](#check_frame_statuspy) - * [`live_mode.py`](#live_modepy) - * [`multi_camera.py`](#multi_camerapy) - * [`multi_rois.py`](#multi_roispy) - * [`newest_frame.py`](#newest_framepy) - * [`seq_mode.py`](#seq_modepy) - * [`single_image_polling.py`](#single_image_pollingpy) - * [`single_image_polling_show.py`](#single_image_polling_showpy) - * [`stream_to_disk.py`](#stream_to_diskpy) - * [`sw_trigger.py`](#sw_triggerpy) - * [`test_camera.py`](#test_camerapy) - - -*** - -## Installing the Package -When you are ready to install the package, navigate to the directory that contains `pyproject.toml` -file and run `python -m pip install .`. - -## Creating a Wheel Package -To create a PyVCAM Wheel package, navigate to the directory that contains `pyproject.toml` file -and run `python -m build`. - -## Uninstalling the Package -When you are ready to uninstall the package, run from anywhere `python -m pip uninstall PyVCAM`. - -*** - -## `src` Folder -Where the source code of the `pyvcam` module is located. In addition to the code for the module, -any additional scripts that are used to help write the module are included as well. The most notable -helper script that is not included in the module is `constants_generator.py`, which generates the -`constants.py` module by parsing the PVCAM header file `pvcam.h`. - -## `pyvcam` Folder -The directory that contains the source code to the `pyvcam` module. These are the files installed -when users install the module. - -### `camera.py` -The `camera.py` module contains the `Camera` python class which is used to abstract the need to -manually maintain, alter, and remember camera settings through PVCAM. - -#### Create Camera Example -This will create a camera object using the first camera found, that can then be used to interact -with the camera. - -``` -from pyvcam import pvc -from pyvcam.camera import Camera - -pvc.init_pvcam() # Initialize PVCAM -cam = next(Camera.detect_camera()) # Use generator to find the first camera -cam.open() # Open the camera -``` - -#### Methods of `Camera` class - -##### Camera Selection -| Method | Description | -|------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `__init__` | (Magic Method) The `Camera`'s constructor. Note that this method should not be used in the construction of a `Camera`. Instead, use the `detect_camera` class method to generate `Camera` classes of the currently available cameras connected to the system. | -| `__repr__` | (Magic Method) Returns the name of the camera. | -| `get_available_camera_names` | Return a list of camera names connected to the system. Use this method in conjunction with `select_camera`. Refer to `multi_camera.py` for a usage example. | -| `detect_camera` | (Class method) Generator that yields a `Camera` object for a camera connected to the system. For an example of how to call `detect_camera`, refer to the code samples for creating a camera. | -| `select_camera` | (Class method) Generator that yields a `Camera` object for the camera that matches the provided name. Use this method in conjunction with `get_available_camera_names`. Refer to `multi_camera.py` for a usage example. | -| `open` | Opens the camera. Will set `__handle` to the correct value and `__is_open` to `True` if a successful call to PVCAM's open camera function is made. A `RuntimeError` will be raised if the call to PVCAM fails. For more information about how Python interacts with the PVCAM library, refer to the `pvcmodule.cpp` section of these notes. | -| `close` | Closes the camera. Will set `__handle` to the default value for a closed camera (`-1`) and will set `__is_open` to `False` if a successful call to PVCAM's close camera function is made. A `RuntimeError` will be raised if the call to PVCAM fails. For more information about how Python interacts with the PVCAM library, refer to the `pvcmodule.cpp` section of these notes. | - -##### Basic Frame Acquisition -| Method | Description | -|--------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `get_frame` | Calls the `pvc` module's `get_frame` function with current camera settings to get a 2D numpy array of pixel data from a single snap image. This method can either be called with or without a given exposure time. If given, the method will use the given parameter. Otherwise, if left out, will use the internal `exp_time` attribute.

**Parameters:**
| -| `get_sequence` | Calls the `pvc` module's `get_frame` function with cameras current settings in rapid-succession to get a 3D numpy array of pixel data from a single snap image. Multiple ROIs are not supported.

**Getting a sequence example:**
`# Given that the camera is already opened as openCam`
`stack = openCam.get_sequence(8) # Getting a sequence of 8 frames`
`firstFrame = stack[0] # Accessing 2D frames from 3D stack`
`lastFrame = stack[7]`

**Parameters:**
| -| `get_vtm_sequence` | Modified `get_sequence` to be used for Variable Timed Mode. Before calling it, set the camera's exposure mode to `'Variable Timed'`. The timings will always start at the first given and keep looping around until it is captured the number of frames given. Multiple ROIs are not supported.

**Parameters:**
| - -##### Advanced Frame Acquisition -| Method | Description | -|-----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `start_live` | Calls `pvc.start_live` to setup a live mode acquisition. This must be called before `poll_frame`.

**Parameters:**
| -| `start_seq` | Calls `pvc.start_seq` to setup a sequence mode acquisition. This must be called before `poll_frame`.

**Parameters:**
| -| `check_frame_status` | Calls `pvc.check_frame_status` to report status of camera. This method can be called regardless of an acquisition being in progress.

**Parameters:**
| -| `poll_frame` | Returns a single frame as a dictionary with optional metadata if available. This method must be called after either `start_live` or `start_seq` and before either `abort` or `finish`. Pixel data can be accessed via the `'pixel_data'` key. Available metadata can be accessed via the `'meta_data'` key.

If multiple ROIs are set, pixel data will be a list of region pixel data of length number of ROIs. Metadata will also contain information for ech ROI.

Use `cam.set_param(constants.PARAM_METADATA_ENABLED, True)` or `cam.metadata_enabled = True` to enable the metadata.

**Parameters:**
| -| `reset_frame_counter` | Resets `frame_count` returned by `poll_frame` to zero.

**Parameters:**
| -| `abort` | Calls `pvc.abort` to return the camera to its normal state prior to completing acquisition.

**Parameters:**
| -| `finish` | Calls either `pvc.stop_live` or `pvc.finish_seq` to return the camera to its normal state after acquiring images.

**Parameters:**
| - -##### Acquisition Configuration -| Method | Description | -|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `reset_rois` | Restores region of interest to default which is full frame with binning disabled.

**Parameters:**
| -| `set_roi` | Appends a new ROI to the camera's list of regions of interest. If the default ROI is currently set, this method will over-write that ROI. Each camera has a pre-defined maximum number of ROIs, which is typically 15. New ROIs can not exceed the boundaries of the sensor and can not overlap with existing ROIs.

**Parameters:**
| -| `shape` | Returns the reshape factor to be used when acquiring a ROI. This is equivalent to an acquired images shape.

**Parameters:**
| - -##### Acquisition Trigger -| Method | Description | -|--------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `sw_trigger` | This method will issue a software trigger command to the camera. This command is only valid if the camera has been set use a software trigger. Refer to `sw_trigger.py` for an example. | - -##### Parameters -| Method | Description | -|-----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `get_param` | Gets the current value of a specified attribute of given parameter. Usually not called directly since the properties (see below) will handle most cases of getting camera attributes. However, not all cases may be covered by the properties and a direct call may need to be made to PVCAM's `pl_get_param` function.

**Parameters**:
| -| `set_param` | Sets a specified camera parameter to a new value. Usually not called directly since the properties (see below) will handle most cases of setting camera attributes. However, not all cases may be covered by the properties and a direct call may need to be made to PVCAM's `pl_set_param` function.

**Parameters:**
| -| `check_param` | Checks if a camera parameter is available. This method is useful for checking certain features are available (such as post-processing, expose out mode). Returns `True` if available, `False` if not.

**Parameters:**
| -| `get_post_processing_param` | Gets the current value of a specified post-processing parameter.

**Parameters**:
| -| `set_post_processing_param` | Sets the value of a specified post-processing parameter.

**Parameters**:
| -| `reset_pp` | If post-processing is available on the camera, the function will call `pvc.reset_pp` to reset all post-processing features back to their default state.

**Parameters:**
| -| `read_enum` | Returns all settings names paired with their values of a specified setting.

**Parameters:**
| - -#### Properties -All properties are accessed via getters and setters. This means that it will appear that we are -accessing instance variables from a camera, but in reality, these properties are making specially -formatted calls to the `Camera` methods `get_param` and `set_param`. These getters and setters make -use of the property decorator that is built into Python. The reasoning behind the usage of the -property decorator is that attributes will change dynamically during a `Camera`'s lifetime and in -order to abstract PVCAM as far away from the end user as possible, the property decorator allows for -users to intuitively view and change camera settings. - -The downside to this approach is that when a new parameter is required, an associated getter/setter -needs to be written and tested. Another downside to this implementation is that attribute lookup -time is not instant; instead, a call must be made to the `pvc` module wrapper which will then call -PVCAM, which will then return a result to the wrapper, which will finally return the result to the -user. The time it takes is currently considered -insignificant, but if this were to become an issue, the code could be refactored such that all -attributes also have instance variables which are changed only when `set_param` or their associated -setter is called on them. - -##### Using Properties -``` -# Assume 'cam' is an already constructed 'Camera' instance -current_gain = cam.gain # To call getter, simply read the property value -cam.gain = current_gain # To call setter, simply assign new value to the property -``` - -##### List of Properties -| Property | Description | -|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `adc_offset` | (read-only) **Warning: Camera specific setting. Not all cameras support this attribute. If an unsupported camera attempts to access it, an `AttributeError` will be raised.**

Returns the camera's current ADC offset value. Only CCD camera's have ADCs (analog-to-digital converters). | -| `binning` | (read-write) Returns or changes the current serial and parallel binning values in a tuple.

The setter can be either a tuple for the binning (x, y) or a single value and will set a square binning with the given number, for example `cam.binning = x` makes `cam.__binning = (x, x)`.

Binning cannot be changed directly on the camera; but is used for setting up acquisitions and returning correctly shaped images returned from `get_frame`. The setter has built in checking to see that the given binning it able to be used later. Binning settings for individual ROIs is not supported. | -| `binnings` | (read-only) Returns a list of supported combinations of serial and parallel binning factors if limited by the camera. If the camera supports arbitrary binning `None` is retuned. | -| `bit_depth` | (read-only) Returns the native bit depth of pixel data for images collected with this camera.
Bit depth cannot be changed directly; instead, users must select a desired speed that has the desired bit depth. Note that a camera may have additional speed table entries for different readout ports. See [Port and Speed Choices](https://docs.teledynevisionsolutions.com/_speed_table.xhtml) section inside the PVCAM User Manual for a visual representation of a speed table and to see which settings are controlled by which speed is currently selected. | -| `bit_depth_host` | (read-only) Returns the bit depth of pixel data outputted to the host. This parameter differs from the `bit_depth` in a way that it reports the bit depth of the output frame - a frame that is delivered to the host. Since PVCAM supports various host side post processing features, the host bit depth may differ from the native camera bit depth, depending on what host-side post processing features are active.
As a general rule, the application should always rely on the host-specific parameters when identifying the output data format. The native parameters should be used only for informational purposes, e.g. to show the camera native format in the GUI. | -| `cam_fw` | (read-only) Returns the cameras current firmware version as a string. | -| `centroids_mode` | (read-write) **Warning: Camera specific setting. Not all cameras support this attribute. If an unsupported camera attempts to access it, an `AttributeError` will be raised.**

Returns/changes the current centroids mode, `'Locate'`, `'Track'` or `'Blob'`. | -| `centroids_modes` | (read-only) Returns a dictionary containing centroid modes supported by the camera. | -| `chip_name` | (read-only) Returns the camera sensor's name as a string. | -| `clear_mode` | (read-write): **Warning: Camera specific setting. Not all cameras support this attribute. If an unsupported camera attempts to access it, an `AttributeError` will be raised.**

Returns/changes the current clear mode of the camera. Note that clear modes have names, but PVCAM interprets them as integer values. When called as a getter, the integer value will be returned to the user. However, when changing the clear mode of a camera, either the integer value or the name of the clear mode can be specified. Refer to `constants.py` for the names of the clear modes. | -| `clear_modes` | (read-only) Returns a dictionary containing clear modes supported by the camera. | -| `clear_time` | (read-only): **Warning: Camera specific setting. Not all cameras support this attribute. If an unsupported camera attempts to access it, an `AttributeError` will be raised.**

Returns the last acquisition's clearing time as reported by the camera in microseconds. | -| `driver_version` | (read-only) Returns a formatted string containing the major, minor, and build version. When `get_param` is called on the device driver version, it returns a highly formatted 16 bit integer. The first 8 bits correspond to the major version, bits 9-12 are the minor version, and the last nibble is the build number. | -| `exp_mode` | (read-write): Returns or changes the current exposure mode of the camera. Note that exposure modes have names, but PVCAM interprets them as integer values. When called as a getter, the integer value will be returned to the user. However, when changing the exposure mode of a camera, either the integer value or the name of the expose out mode can be specified. Refer to `constants.py` for the names of the exposure modes. | -| `exp_modes` | (read-only) Returns a dictionary containing exposure modes supported by the camera. | -| `exp_out_mode` | (read-write): **Warning: Camera specific setting. Not all cameras support this attribute. If an unsupported camera attempts to access it, an `AttributeError` will be raised.**

Returns or changes the current expose out mode of the camera. Note that expose out modes have names, but PVCAM interprets them as integer values. When called as a getter, the integer value will be returned to the user. However, when changing the expose out mode of a camera, either the integer value or the name of the expose out mode can be specified. Refer to `constants.py` for the names of the expose out modes. | -| `exp_out_modes` | (read-only) Returns a dictionary containing exposure out modes supported by the camera. | -| `exp_res` | (Getter and setter) Returns/changes the current exposure resolution of a camera. Note that exposure resolutions have names, but PVCAM interprets them as integer values. When called as a getter, the integer value will be returned to the user. However, when changing the exposure resolution of a camera, either the integer value or the name of the resolution can be specified. Refer to `constants.py` for the names of the exposure resolutions. | -| `exp_res_index` | (read-only): Returns the current exposure resolution index. | -| `exp_resolutions` | (read-only) Returns a dictionary containing exposure resolutions supported by the camera. | -| `exp_time` | (read-write): Returns or changes the exposure time the camera will use if not given an exposure time. It is recommended to modify this value to modify your acquisitions for better abstraction. | -| `fan_speed` | (read-write): **Warning: Camera specific setting. Not all cameras support this attribute. If an unsupported camera attempts to access it, an `AttributeError` will be raised.**

Returns/changes the current fan speed setpoint of the camera. Note that fan speeds have names, but PVCAM interprets them as integer values. When called as a getter, the integer value will be returned to the user. However, when changing the fan speed of a camera, either the integer value or the name of the fan speed can be specified. Refer to `constants.py` for the names of the fan speeds. | -| `fan_speeds` | (read-only) Returns a dictionary containing fan speeds supported by the camera. | -| `gain` | (read-write) Returns or changes the current gain index for a camera. A `ValueError` will be raised if an invalid gain index is supplied to the setter. After changing `readout_port` it is strongly recommended to re-apply the settings of `speed` and `gain` exactly in that order. | -| `gain_name` | (read-only) Returns the name of currently selected gain via `gain` under selected port and speed. It is either hard-coded generic string or provided by the camera. | -| `handle` | (read-only) Returns the value currently stored inside the Camera's `__handle` instance variable. | -| `is_open` | (read-only) Returns the value currently stored inside the Camera's `__is_open` instance variable. | -| `last_exp_time` | (read-only) Returns the last exposure time the camera used for the last successful non-variable timed mode acquisition in what ever time resolution it was captured at. | -| `metadata_enabled` | (read-write): Returns or changes the embedded frame metadata availability. | -| `name` | (read-only) Returns the value currently stored inside the Camera's `__name` instance variable. | -| `pix_time` | (read-only) Returns the camera's pixel time, which is the inverse of the speed of the camera. Pixel time cannot be changed directly; instead users must select a desired speed that has the desired pixel time. Note that a camera may have additional speed table entries for different readout ports. See [Port and Speed Choices](https://docs.teledynevisionsolutions.com/_speed_table.xhtml) section inside the PVCAM User Manual for a visual representation of a speed table and to see which settings are controlled by which speed table entry is currently selected. | -| `port_speed_gain_table` | (read-only) Returns a dictionary containing the port, speed and gain table, which gives information such as bit depth and pixel time for each readout port, speed and gain. | -| `post_processing_table` | (read-only) Returns a dictionary containing post-processing features and parameters as well as the minimum and maximum value for each parameter. | -| `post_trigger_delay` | (read-only): **Warning: Camera specific setting. Not all cameras support this attribute. If an unsupported camera attempts to access it, an `AttributeError` will be raised.**

Returns the last acquisition's post-trigger delay as reported by the camera in microseconds. | -| `pre_trigger_delay` | (read-only): **Warning: Camera specific setting. Not all cameras support this attribute. If an unsupported camera attempts to access it, an `AttributeError` will be raised.**

Returns the last acquisition's pre-trigger delay as reported by the camera in microseconds | -| `prog_scan_mode` | (read-write) **Warning: Camera specific setting. Not all cameras support this attribute. If an unsupported camera attempts to access it, an `AttributeError` will be raised.**

Returns/changes the current programmable scan mode, Auto, Line Delay or Scan Width. | -| `prog_scan_modes` | (read-only) Returns a dictionary containing programmable scan modes supported by the camera. | -| `prog_scan_dir` | (read-write) **Warning: Camera specific setting. Not all camera's support this attribute. If an unsupported camera attempts to access its readout_port, an AttributeError will be raised.**

Returns/changes the current programmable scan direction, `'Auto'`, `'Line Delay'` or `'Scan Width'`. | -| `prog_scan_dirs` | (read-only) Returns a dictionary containing programmable scan directions supported by the camera. | -| `prog_scan_dir_reset` | (read-write) **Warning: Camera specific setting. Not all cameras support this attribute. If an unsupported camera attempts to access it, an `AttributeError` will be raised.**

Returns/changes scan direction reset state of camera. The parameter is used with alternate scan directions (down-up) to reset the direction with every acquisition. | -| `prog_scan_line_delay` | (read-write) **Warning: Camera specific setting. Not all cameras support this attribute. If an unsupported camera attempts to access it, an `AttributeError` will be raised.**

Returns/changes the scan line delay. The parameter access mode depends on the `prog_scan_mode` selection. | -| `prog_scan_width` | (read-write) **Warning: Camera specific setting. Not all cameras support this attribute. If an unsupported camera attempts to access it, an `AttributeError` will be raised.**

Returns/changes the scan width. The parameter access mode depends on the `prog_scan_mode` selection. | -| `readout_port` | (read-write) **Warning: Camera specific setting. Not all cameras support this attribute. If an unsupported camera attempts to access it, an `AttributeError` will be raised.**

Some cameras may have many readout ports, which are output nodes from which a pixel stream can be read from. After changing `readout_port` it is strongly recommended to re-apply the settings of `speed` and `gain` exactly in that order. For more information about readout ports, refer to the [Port and Speed Choices](https://docs.teledynevisionsolutions.com/_speed_table.xhtml) section inside the PVCAM User Manual. | -| `readout_ports` | (read-only) Returns a dictionary containing readout ports supported by the camera. | -| `readout_time` | (read-only): Returns the last acquisition's readout time as reported by the camera in microseconds. | -| `scan_line_time` | (Getter) **Warning: Camera specific setting. Not all cameras support this attribute. If an unsupported camera attempts to access it, an `AttributeError` will be raised.**

Returns the scan line time of camera in nano seconds. | -| `sensor_size` | (read-only) Returns the sensor size of the current camera in a tuple in the form (serial sensor size, parallel sensor size) | -| `serial_no` | (read-only) Returns the camera's serial number as a string. | -| `speed` | (read-write) Returns or changes the current numerical index of the speed table of a camera. After changing `readout_port` it is strongly recommended to re-apply the settings of `speed` and `gain` exactly in that order. See the [Port and Speed Choices](https://docs.teledynevisionsolutions.com/_speed_table.xhtml) section inside the PVCAM User Manual for a detailed explanation about PVCAM speed tables. | -| `speed_name` | (read-only) Returns the name of currently selected speed via `speed` under selected port. It is either hard-coded generic string or provided by the camera. | -| `temp` | (read-only): **Warning: Camera specific setting. Not all cameras support this attribute. If an unsupported camera attempts to access it, an `AttributeError` will be raised.**

Returns the current temperature of a camera in Celsius. | -| `temp_setpoint` | (read-write): **Warning: Camera specific setting. Not all cameras support this attribute. If an unsupported camera attempts to access it, an `AttributeError` will be raised.**

Returns/changes the camera's temperature setpoint. The temperature setpoint is the temperature that a camera will attempt to keep its temperature (in Celsius) at. | -| `trigger_table` | (read-only) Returns a dictionary containing a table consisting of information of the last acquisition such as exposure time, readout time, clear time, pre-trigger delay, and post-trigger delay. If any of the parameters are unavailable, the dictionary item will be set to `'N/A'`. | -| `vtm_exp_time` | (read-write): **Warning: Camera specific setting. Not all cameras support this attribute. If an unsupported camera attempts to access it, an `AttributeError` will be raised.**

Returns or changes the variable timed exposure time the camera uses for the `'Variable Timed'` exposure mode. | - -### `constants.py` -The `constants.py` is a large data file that contains various camera settings and internal PVCAM -structures used to map meaningful variable names to predefined integer values that camera firmware -interprets as settings. - -This file is not (and should never be) constructed by hand. Instead, use the most recent version -of `pvcam.h` and run `constants_generator.py` to (re)build this file. A more detailed explanation -can be found under the `constants_generator.py` section, but the basic concept is that `pvcam.h` is -parsed line by line, finding all predefined constants, enums, and structs to grant Python users -access to the necessary data to perform basic PVCAM functions that have multiple settings. - -There are four main sections to this file that are found following a formatted comment like this -`# # # # # #`: - -1. Defines - 1. Much of the necessary data from `pvcam.h` is saved as C preprocessor macros which are easy to - strip from the file and construct a Python variable whose name is the macros name and the - value is what the macro expands to. - 2. Macros can be thought of as Python variables in this case, as none (of the necessary macros) - in `pvcam.h` expand to more than a literal. -2. Enums - 1. Enums (also known as enumerated types) are data types that contain named members used to - identify certain integer values. Typically, if no values are specified, the first member will - be assigned the value 0, and the successor will be 1+ the value of their predecessor. However, - you can specify specific numbers for all members. - 2. Vanilla Python has no enum type (it must be imported; see documentation here), and even still - Python's enum class behaves somewhat differently in terms of accessing members. Instead, we - will insert a comment above all enumerated types and have their members just be simple Python - variables whose values where the integer assigned to them in the `pvcam.h` file. -3. Structs - 1. Structs are preserved as Python classes. No testing/implementation has been done with these, - so results may be unexpected if implemented. -4. Added By Hand - 1. These are quality of life/readability dictionaries that map named settings of various camera - modes to their `pvcam.h` integer value. These allow for fast look-up and intuitive setting - changes for end users. - -### `pvcmodule.cpp` -The `pvcmodule.cpp` is a set of C++ functions that make use of and extend the Python C-API known as -a Python Extension Module. The need for a Python extension module is two-fold: first to allow -communication between the static PVCAM library and Python scripts, and second for fast acquisition -and conversion from native C types (namely C arrays of pixel data) to Python data types. -The extension module needs to be compiled, so it will be necessary to have a C/C++ compiler to -successfully install this application. The module will be compiled into a shared-object library, -which can then be imported from Python; read more here. - -#### General Structure of a `pvc` Module Functions -The registered functions of a `pvc` module usually follow a three-step process: -1. Retrieve data from Python script -2. Process acquired data -3. Return data to Python script - -#### Retrieving Data -Functions receive data dynamically through use of parameters, and the `pvc` module's functions are -no different. However, the Python API states that all data is of type `PyObject`, which the C/C++ -programming language offer no builtin support for. In addition to, each Python-facing function must -only have two arguments: `PyObject* self` (a pointer to the instance of whichever Python object -called this C function) and `PyObject* args` (a Python tuple object that contains all the arguments -passed into the C function call). - -However, we can make use of the `PyArg_ParseTuple` (see example here) function from the Python API -to easily coerce the Python objects from the args tuple to their respective C type. In order for -the conversion to occur, we must specify which type we want to coerce each Python argument to using -a formatted string (see second argument for `PyArg_ParseTuple`). Each character in the formatted -string are known as "format units" and are interpreted in the same order that the variables for -the coerced C data are provided. Find below a small list of C data types and their corresponding -format units. Use Python documentation for complete list. - -| C Type | Character Representation | -|------------------------|--------------------------| -| `long` | `l` | -| `int` | `i` | -| `double` | `d` | -| `float` | `f` | -| string (`const char*`) | `s` | -| `PyObject` | `O` | - -#### Arguments of `PyArg_ParseTuple` -1. `args` (`PyObject*`) - A Python tuple object that contains the arguments from the Python function - call. For example, if a function call from Python is made: `my_c_func(1, "test")`, the `args` - tuple would contain two `PyObject` pointers: one to the Python integer 1 and another to the - Python Unicode-String `"test"`. -2. `format` (`const char*`) - A String containing the format units for all the arguments found in - the args in the same order in which they appear in the tuple. Going off of the example from the - previous argument, the desired formatted string would be `"is"`: `'i'` for the integer 1, and - `'s'` for the string `"test"`. - -In addition to these two arguments, addresses to the variables in which the coerced C data should be -stored must also be passed as arguments to the `PyArg_ParseTuple` call. (See example for more details). - -#### `PyArg_ParseTuple` Example -``` -static PyObject* example(PyObject* self, PyObject* args) -{ - int myNum; - char* myString; - PyArg_ParseTuple(args, "is", &myNum, &myString); - printf("myNum: %d\n", myNum); // Prints "myNum: 1" - printf("myString: %s\n", myString); // Prints "myString: test" - Py_RETURN_NONE; -} -``` - -#### Processing Acquired Data -Using the data supplied by the Python function call, we can now perform normal camera operations -using PVCAM library function calls. The most common form of processing acquired data is to read the -camera handle from the arguments provided, then performing a camera operation (changing/reading -settings, getting images, etc.) using the acquired handle to identify which camera to perform the -action on. - -Generally speaking, this part of the function should be very similar to writing normal C/C++ modules -that use the PVCAM library. If there is any confusion about how to write C/C++ code to make calls to -PVCAM, refer to the PvcamCodeSamples found in the Examples directory of the PVCAM SDK. - -Sometimes, processing data from a Python function call may entail answering a query. If this is the -case, we need to specify what to return, and how to convert it into a corresponding Python type. - -#### Return Data to a Python Script -Similar to how issues arose when passing data from the Python function call to the C/C++ module, -there is no simple casting solution to convert C/C++ data types to Python data types when returning -from a function. - -Thankfully, there are some functions that were included in the Python header file included at the -top of each module to allow us to cast data to an equivalent Python type. - -#### Cast to Python Type -``` -{ - ... - const char* myString = "ika"; - return PyUnicode_FromString(myString); // Returns a Python string back to the calling function -} -``` - -There is one small catch, however. All Python functions must return an object; there is no such -thing as a `void` function. This means that we must always return something in our C/C++ modules as -well (which we can tell by looking at the signature). -If you wish to return `None`, simply use the `Py_RETURN_NONE` macro (see the `PyArg_ParseTuple` -example for a visual representation). - -#### Functions of `pvc` Module -**Note:** All functions will always have the `PyObject* self` and `PyObject* args` parameters. -When parameters are listed, they are the Python parameters that are passed into the module. - -| Function Name | Description | -|---------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `pvc_abort` | Given a camera handle, aborts any ongoing acquisition and de-registers the frame handler callback function.

**Parameters:**