├── .gitignore
├── LICENSE
├── README.md
├── docs
├── CheetSheet.txt
└── PyVCAM Wrapper.md
├── setup.py
├── src
├── constants_generator.py
└── pyvcam
│ ├── __init__.py
│ ├── camera.py
│ ├── constants.py
│ ├── pvcmodule.cpp
│ └── pvcmodule.h
└── tests
├── camera_settings.py
├── change_settings_test.py
├── check_frame_status.py
├── live_mode.py
├── meta_data.py
├── multi_camera.py
├── multi_rois.py
├── newest_frame.py
├── seq_mode.py
├── single_image_polling.py
├── single_image_polling_show.py
├── stream_to_disk.py
├── sw_trigger.py
└── test_camera.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | .pybuilder/
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | # For a library or package, you might want to ignore these files since the code is
87 | # intended to run in multiple environments; otherwise, check them in:
88 | # .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # poetry
98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99 | # This is especially recommended for binary packages to ensure reproducibility, and is more
100 | # commonly ignored for libraries.
101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102 | #poetry.lock
103 |
104 | # pdm
105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106 | #pdm.lock
107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108 | # in version control.
109 | # https://pdm.fming.dev/#use-with-ide
110 | .pdm.toml
111 |
112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113 | __pypackages__/
114 |
115 | # Celery stuff
116 | celerybeat-schedule
117 | celerybeat.pid
118 |
119 | # SageMath parsed files
120 | *.sage.py
121 |
122 | # Environments
123 | .env
124 | .venv
125 | env/
126 | venv/
127 | ENV/
128 | env.bak/
129 | venv.bak/
130 |
131 | # Spyder project settings
132 | .spyderproject
133 | .spyproject
134 |
135 | # Rope project settings
136 | .ropeproject
137 |
138 | # mkdocs documentation
139 | /site
140 |
141 | # mypy
142 | .mypy_cache/
143 | .dmypy.json
144 | dmypy.json
145 |
146 | # Pyre type checker
147 | .pyre/
148 |
149 | # pytype static type analyzer
150 | .pytype/
151 |
152 | # Cython debug symbols
153 | cython_debug/
154 |
155 | # PyCharm
156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158 | # and can be added to the global gitignore or merged into this file. For a more nuclear
159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160 | .idea/
161 |
162 | # Act secrets (https://github.com/nektos/act)
163 | .secrets
164 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 HerrZiffer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PyVCAM Wrapper
2 |
3 | PyVCAM Wrapper is a Python3.X wrapper for the PVCAM SDK.
4 |
5 | ## Getting Started
6 | Follow the instructions below to get PyVCAM up and running on your machine for development and testing.
7 |
8 |
9 | ### Prerequisites
10 | * An understanding of PVCAM is very helpful for understanding PyVCAM.
11 | * A C/C++ compiler is needed to build native source code. For Windows, MSVC 1928 was used for testing.
12 | * The newest version of Python 3 which can be downloaded [here](https://www.python.org/downloads/).
13 | * The latest PVCAM and PVCAM SDK which can be downloaded [here](https://www.photometrics.com/support/software/#software).
14 | * PyVCAM was developed and tested using Microsoft Windows 10/64-bit. The build package also supports Linux, but testing has been minimal.
15 |
16 |
17 | ### Installing
18 | When you are ready to install the wrapper use your command prompt to navigate into the directory that contains
19 | setup.py and run ```pip install .```
20 |
21 |
22 | ### How to use the wrapper
23 | #### Create Camera Example
24 | This will create a camera object using the first camera that is found that can then be used to interact with the camera.
25 | ```
26 | from pyvcam import pvc
27 | from pyvcam.camera import Camera
28 |
29 | pvc.init_pvcam() # Initialize PVCAM
30 | cam = next(Camera.detect_camera()) # Use generator to find first camera.
31 | cam.open() # Open the camera.
32 | ```
33 |
34 | #### Single Image Example
35 | This captures a single image with a 20 ms exposure time and prints the values of the first 5 pixels.
36 | ```
37 | # A camera object named cam has already been created
38 | frame = cam.get_frame(exp_time=20)
39 | print("First five pixels of frame: {}, {}, {}, {}, {}".format(*frame[:5]))
40 | ```
41 |
42 | #### Changing Settings Example
43 | This is an example of how to change some of the settings on the cameras.
44 | ```
45 | # A camera object named cam has already been created
46 | cam.clear_mode = "Never"
47 | cam.exp_mode = "Ext Trig Trig First"
48 | cam.readout_port = 0
49 | cam.speed_table_index = 0
50 | cam.gain = 1
51 | ```
52 |
53 | More information on how to use this wrapper and how it works can be found [here](https://github.com/Photometrics/PyVCAM/blob/master/docs/PyVCAM%20Wrapper.md).
54 |
--------------------------------------------------------------------------------
/docs/CheetSheet.txt:
--------------------------------------------------------------------------------
1 | [[Installation]]
2 | pip install .
3 |
4 | [[Camera Class]]
5 |
6 | [Camera Selection]
7 | get_available_camera_names
8 | detect_camera
9 | select_camera
10 | open
11 | close
12 |
13 | [Basic Frame Acquisition]
14 | get_frame
15 | get_sequence
16 | get_vtm_sequence
17 |
18 | [Advanced Frame Acquisition]
19 | start_live
20 | start_seq
21 | check_frame_status
22 | poll_frame
23 | abort
24 | finish
25 |
26 | [Configure Acquisition]
27 | update_mode
28 | reset_pp
29 | reset_rois
30 | set_roi
31 |
32 | [Parameters]
33 | get_param
34 | set_param
35 | check_param
36 | get_post_processing_param
37 | set_post_processing_param
38 | read_enum
39 |
40 | [Camera Object Data]
41 | centroids_modes
42 | clear_modes
43 | exp_modes
44 | exp_out_modes
45 | exp_resolutions
46 | handle
47 | is_open
48 | name
49 | port_speed_gain_table
50 | post_processing_table
51 | prog_scan_modes
52 | prog_scan_dirs
53 | rois
54 | shape
55 |
56 | [Camera Properties]
57 | adc_offset get
58 | bin_x get|set
59 | bin_y get|set
60 | binning get|set
61 | bit_depth get
62 | cam_fw get
63 | centroids_mode get|set
64 | chip_name get
65 | clear_mode get|set
66 | clear_time get
67 | driver_version get
68 | exp_mode get|set
69 | exp_out_mode get|set
70 | exp_res get|set
71 | exp_res_index get
72 | exp_time get|set
73 | gain get|set
74 | last_exp_time get
75 | meta_data_enabled get|set
76 | pix_time get
77 | post_trigger_delay get
78 | pre_trigger_delay get
79 | prog_scan_mode get|set
80 | prog_scan_dir get|set
81 | prog_scan_dir_reset get|set
82 | prog_scan_line_delay get|set
83 | prog_scan_width get|set
84 | readout_port get|set
85 | readout_time get
86 | scan_line_time get
87 | sensor_size get
88 | serial_no get
89 | speed_table_index get|set
90 | temp get
91 | temp_setpoint get|set
92 | trigger_table get
93 | vtm_exp_time get|set
94 |
--------------------------------------------------------------------------------
/docs/PyVCAM Wrapper.md:
--------------------------------------------------------------------------------
1 | # PyVCAM Wrapper
2 |
3 | - [PyVCAM Wrapper](#pyvcam-wrapper)
4 | * [src](#src)
5 | * [pyvcam](#pyvcam)
6 | + [camera.py](#camerapy)
7 | - [Create Camera Example](#create-camera-example)
8 | - [Attributes of Camera:](#attributes-of-camera-)
9 | - [Methods of Camera:](#methods-of-camera-)
10 | * [Camera Selection](#camera-selection)
11 | * [Basic Frame Acquisition](#basic-frame-acquisition)
12 | * [Advanced Frame Acquisition](#advanced-frame-acquisition)
13 | * [Acquisition Trigger](#acquisition-trigger)
14 | * [Parameters](#parameters)
15 | * [Internal methods](#internal-methods)
16 | - [Getters/Setters of Camera:](#getters-setters-of-camera-)
17 | * [Using Getters/Setters](#using-getters-setters)
18 | * [List of Getters/Setters](#list-of-getters-setters)
19 | + [constants.py](#constantspy)
20 | + [pvcmodule.cpp](#pvcmodulecpp)
21 | - [General Structure of a pvcmodule Function](#general-structure-of-a-pvcmodule-function)
22 | - [Retrieving Data](#retrieving-data)
23 | - [Arguments of PyArg_ParseTuple](#arguments-of-pyarg-parsetuple)
24 | - [PyArg_ParseTuple Example](#pyarg-parsetuple-example)
25 | - [Processing Acquired Data](#processing-acquired-data)
26 | - [Return Data to a Python Script](#return-data-to-a-python-script)
27 | - [Cast to Python Type](#cast-to-python-type)
28 | - [Functions of pvcmodule.cpp](#functions-of-pvcmodulecpp)
29 | - [The Method Table](#the-method-table)
30 | - [The Module Definition](#the-module-definition)
31 | - [Module Creation](#module-creation)
32 | - [Creating Extension Module](#creating-extension-module)
33 | + [constants_generator.py](#constants-generatorpy)
34 | - [Requirements](#requirements)
35 | - [Running the Script](#running-the-script)
36 | * [tests](#tests)
37 | + [change_settings_test.py (needs camera_settings.py)](#change-settings-testpy--needs-camera-settingspy-)
38 | + [check_frame_status.py](#check-frame-statuspy)
39 | + [live_mode.py](#live-modepy)
40 | + [meta_data.py](#meta-datapy)
41 | + [multi_camera.py](#multi-camerapy)
42 | + [seq_mode.py](#seq-modepy)
43 | + [single_image_polling.py](#single-image-pollingpy)
44 | + [single_image_polling_show.py](#single-image-polling-showpy)
45 | + [sw_trigger.py](#sw-triggerpy)
46 | + [test_camera.py](#test-camerapy)
47 | * [setup.py](#setuppy)
48 | + [Variables](#variables)
49 | + [Installing the Package](#installing-the-package)
50 | - [setup.py Install Command](#setuppy-install-command)
51 | + [Creating a PyVCAM Wheel Package](#creating-a-pyvcam-wheel-package)
52 | - [setup.py Create Wheels Package Command](#setuppy-create-wheels-package-command)
53 |
54 | ## src
55 | 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.
56 |
57 | ## pyvcam
58 | The directory that contains the source code to the pyvcam module. These are the files installed when users install the module.
59 |
60 | ### camera.py
61 | 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.
62 |
63 | #### Create Camera Example
64 | This will create a camera object using the first camera that is found that can then be used to interact with the camera
65 |
66 | ```
67 | from pyvcam import pvc
68 | from pyvcam.camera import Camera
69 |
70 | pvc.init_pvcam() # Initialize PVCAM
71 | cam = next(Camera.detect_camera()) # Use generator to find first camera.
72 | cam.open() # Open the camera.
73 | ```
74 |
75 | #### Attributes of Camera:
76 | | Attribute | Description |
77 | | ------------- | ------------- |
78 | | __name | A private instance variable that contains the camera's name. Note that this should be a read only attribute, meaning it should never be changed or set. PVCAM will handle the generation of camera names and will be set to the appropriate name when a Camera is constructed. |
79 | | __handle | A private instance variable that contains the camera's handle. Note that this is a read only variable, meaning it should never be changed or set. PVCAM will handle the updating of this attribute when the camera is opened or closed. |
80 | | __is_open | A private instance variable that is set to True if the camera is currently opened and False otherwise. Note that this is a read only variable, meaning it should never changed or set. PVCAM will handle the updating of this attribute whenever a camera is opened or close. |
81 | | __acquisition_mode | A private instance variable that is to be used internally for determining camera status. The variable is set to Live upon calling start_live, Sequence upon calling start_seq or None upon calling finish.
82 | | __exposure_bytes| A private instance variable that is to be used internally for setting up and capturing a live image with a continuous circular buffer. Note that this is a read only variable, meaning that it should never be changed or set manually. This should only be modified/ read by the start_live and get_live_frame functions. |
83 | | __mode| A private instance variable that is to be used internally for setting the correct exposure mode and expose out mode for the camera acquisition setups. Note that his is a read only variable, meaning that it should never be changed or set manually. This should only be modified by the magic __init__ function and _update_mode function. If you want to change the mode, change the corresponding exposure modes with setters bellow. |
84 | | __exp_time | A private instance variable that is to be used internally as the default exposure time to be used for all exposures. Although this variable is read only, you can access it and change it with setters and getters below. The basic idea behind this abstraction is to use this variable all the time for all exposures, but if you need a single, quick capture at a specific exposure time, you can pass it in the get_frame, get_sequence, and get_live_frame functions as the optional parameter. |
85 | | __rois | A private instance variable that stores a list of regions of interest (rois) objects for acquisitions. A region of interest defines both a box of pixels and binning factors. It's used for setting up acquisitions with the specified rois and resizing the returned pixel data to 2D numpy array. Although this variable is read only, you can access/ modify it below with the set_roi and reset_rois functions. |
86 | | __dtype | A private instance variable that stores the numpy data type for the currently selected port and speed following a call to start_live or start_seq. |
87 | | __port_speed_gain_table | A private instance variable containing definitions of port, speeds and gains available on the camera. Definitions for each speed include pixel_time. Definitions for each gain include bit_depth. |
88 | | __post_processing_table | A private instance variable containing definitions of post-processing parameters available on the camera. The definitions include a valid range for each parameter. |
89 | | __centroids_modes | A private instance variable containing centroid modes supported by the camera. |
90 | | __clear_modes | A private instance variable containing clear modes supported by the camera. |
91 | | __exp_modes | A private instance variable containing exposure modes supported by the camera. |
92 | | __exp_out_modes | A private instance variable containing exposure out modes supported by the camera. |
93 | | __exp_resolutions | A private instance variable containing exposure resolutions supported by the camera. |
94 | | __prog_scan_modes | A private instance variable containing programmable scan modes supported by the camera. |
95 | | __prog_scan_dirs | A private instance variable containing programmable scan directions supported by the camera. |
96 |
97 | #### Methods of Camera:
98 | ##### Camera Selection
99 | | Method | Description |
100 | | ------------- | ------------- |
101 | | \_\_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 current system. |
102 | | \_\_repr__ | (Magic Method) Returns the name of the Camera.|
103 | | get_available_camera_names | Return a list of cameras connected to the system. Use this method in conjunction with select_camera. Refer to multi_camera.py for a usage example. |
104 | | 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 sample for creating a camera. |
105 | | 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. |
106 | | open | Opens a 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. |
107 | | close | Closes a 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. |
108 |
109 | ##### Basic Frame Acquisition
110 | | Method | Description |
111 | | ------------- | ------------- |
112 | | get_frame | Calls the pvcmodule's get_frame function with cameras current 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:**
- Optional: exp_time (int): The exposure time to use.
- Optional: timeout_ms (int): Duration to wait for new frames. Default is WAIT_FOREVER.
|
113 | | get_sequence | Calls the pvcmodule'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.
**Example:**
**Getting a sequence**
*# 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:**
- num_frames (int): The number of frames to be captured in the sequence.
- Optional: exp_time (int): The exposure time to use.
- Optional: timeout_ms (int): Duration to wait for new frames. Default is WAIT_FOREVER.
|
114 | | 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 its captured the number of frames given. Multiple ROIs are not supported.
**Parameters:**
- time_list (list of integers): The timings to be used by the camera in Variable Timed Mode
- exp_res (int): The exposure time resolution. Currently only has milliseconds (0) and microseconds (1). Refer to the PVCAM User Manual class 3 parameter EXP_RES and EXP_RES_INDEX
- num_frames (int): The number of frames to be captured in the sequence.
- Optional: timeout_ms (int): Duration to wait for new frames. Default is WAIT_FOREVER.
- Optional: interval (int): Time to between each sequence frame (in milliseconds).
|
115 |
116 | ##### Advanced Frame Acquisition
117 | | Method | Description |
118 | | ------------- | ------------- |
119 | | start_live | Calls pvc.start_live to setup a live mode acquisition. This must be called before poll_frame.
**Parameters:**
- Optional: exp_time (int): The exposure time for the acquisition. If not provided, the exp_time attribute is used.
- Optional: buffer_frame_count (int): The number of frames in the circular frame buffer. The default is 16 frames.
- Optional: stream_to_disk_path (str): The file path for data written directly to disk by PVCAM. The default is None which disables this feature.
|
120 | | start_seq | Calls pvc.start_seq to setup a seq mode acquisition. This must be called before poll_frame.
**Parameters:**
- Optional: exp_time (int): The exposure time for the acquisition. If not provided, the exp_time attribute is used.
|
121 | | 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:**
|
122 | | poll_frame | Returns a single frame as a dictionary with optional meta data if available. This method must be called after either stat_live or start_seq and before either abort or finish. Pixel data can be accessed via the pixel_data key. Available meta data 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. Meta data will also contain information for ech ROI.
Use set_param(constants.PARAM_METADATA_ENABLED, True) to enable meta data.
**Parameters:**
- Optional: timeout_ms (int): Duration to wait for new frames. Default is WAIT_FOREVER.
- Optional: oldestFrame (bool): If True, the returned frame will the oldest frame and will be popped off the queue. If False, the returned frame will be the newest frame and will not be removed from the queue. Default is True.
- Optional: copyData (bool): Returned numpy frames will contain a copy of image data. Without this copy, the numpy frame image data will point directly to the underlying frame buffer used by PVCAM. Disabling this copy will improve performance and decrease memory usage, but care must be taken. In live and sequence mode, frame memory is unallocated when calling abort or finish. In live mode, a circular frame buffer is used so frames are continuously overwritten. Default is True.
|
123 | | abort | Calls pvc.abort to return the camera to it's normal state prior to completing acquisition.
**Parameters:**
|
124 | | finish | Calls either pvc.stop_live or finish_seq to return the camera to it's normal state after acquiring live images.
**Parameters:**
|
125 |
126 | ##### Acquisition Configuration
127 | | Method | Description |
128 | | ------------- | ------------- |
129 | | reset_rois | Restores region of interest to default which is full frame with binning disabled
**Parameters:**
|
130 | | 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:**
- s1(int): Serial coordinate 1.
- p1(int): Parllel coordinate 1.
- width(int): Num pixels in serial direction.
- height(int): Num pixels in parallel direction.
|
131 | | shape | Returns the reshape factor to be used when acquiring a ROI. This is equivalent to an acquired images shape.
**Parameters:**
|
132 |
133 | ##### Acquisition Trigger
134 | | Method | Description |
135 | | ------------- | ------------- |
136 | | 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. |
137 |
138 | ##### Parameters
139 | | Method | Description |
140 | | ------------- | ------------- |
141 | | get_param | (Method) Gets the current value of a specified parameter. Usually not called directly since the getters/setters (see below) will handle most cases of getting camera attributes. However, not all cases may be covered by the getters/setters and a direct call may need to be made to PVCAM's get_param function. For more information about how to use get_param, refer to the Using get_param and set_param section of the README for the project.
**Parameters**:
- param_id (int): The PVCAM defined value that corresponds to a parameter. Refer to the PVCAM User Manual and constants.py section for list of available parameter ID values.
- param_attr (int): The PVCAM defined value that corresponds to an attribute of a parameter. Refer to the PVCAM User Manual and constants.py section for list of available attribute ID values.
|
142 | | set_param| (Method) Sets a specified camera parameter to a new value. Usually not called directly since the getters/setters (see below) will handle most cases of setting camera attributes. However, not all cases may be covered by the getters/setters and a direct call may need to be made to PVCAM's set_param function. For more information about how to use set_param, refer to the Using get_param and set_param section of the README for the project.
**Parameters:**
- param_id (int): The PVCAM defined value that corresponds to a parameter. Refer to the PVCAM User Manual and constants.py section for list of available parameter ID values.
- value (various): The value to set the camera setting to. Make sure that it's type closely matches the attribute's type so it can be properly set. Refer to the PVCAM User Manual for all possible attributes and their types.
|
143 | | check_param | (Method) 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:**
- param_id (int): The PVCAM defined value that corresponds to a parameter. Refer to the PVCAM User Manual and constants.py section for list of available parameter ID values.
|
144 | | get_post_processing_param | (Method) Gets the current value of a specified post-processing parameter.
**Parameters**:
- feature_name (str): A string name for the post-processing feature using this parameter. Feature names can be determined from the post_processing_table attribute.
- param_name (str): A string name for the post-processing parameter. Parameter names can be determined from the post_processing_table attribute.
|
145 | | set_post_processing_param | (Method) Sets the value of a specified post-processing parameter.
**Parameters**:
- feature_name (str): A string name for the post-processing feature using this parameter. Feature names can be determined from the post_processing_table attribute.
- param_name (str): A string name for the post-processing parameter. Parameter names can be determined from the post_processing_table attribute.
- value (int): The value to be assigned to the post-processing parameter. Value must fall within the range provided by the post_processing_table attribute.
|
146 | | 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:**
|
147 | | read_enum |(Method) Returns all settings names paired with their values of a specified setting.
**Parameters:**
- param_id (int): The parameter ID.
|
148 |
149 | ##### Internal methods
150 | | Method | Description |
151 | | ------------- | ------------- |
152 | | _set_dtype | This method sets the __dtype attribute for numpy pixel data based on current port and speed settings.
**Parameters:**
|
153 | | _update_mode | This method updates the mode of the camera, which is the bit-wise or between exposure mode and expose out mode. It also sets up a temporary sequence to the exposure mode and expose out mode getters will read as expected. This should really only be called internally (and automatically) when exposure mode or expose out mode is modified.
|
154 |
155 | #### Getters/Setters of Camera:
156 | All getters and setters can be accessed using the example below. There is one large implementation point to make note of:
157 | - All getters/setters are accessed by attribute. This means that it will appear that we are accessing instance variables from a camera, but in reality, these getters/setters are making specially formatted calls to the Camera method get_param/set_param. These getters/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 pvcmodule 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.
158 |
159 |
160 | ##### Using Getters/Setters
161 | ```
162 | # Assume cam is an already constructed camera.
163 | curr_gain = cam.gain # To call getter, simply access it by attribute from the camera.
164 | ```
165 | ##### List of Getters/Setters
166 | | Attribute | Getter/Setter Description |
167 | | ------------- | ------------- |
168 | | adc_offset | (Getter only) **Warning: Camera specific setting. Not all camera's support this attribute. If an unsupported camera attempts to access it's readout_port, an AttributeError will be raised.**
Returns the camera's current ADC offset value. Only CCD camera's have ADCs (analog-to-digital converters). |
169 | | bin_x | (Getter and Setter) Returns/ changes the current serial binning value. **Deprecated, use binning **|
170 | | bin_y | (Getter and Setter) Returns/ changes the current parallel binning value. **Deprecated, use binning **|
171 | | binning | (Getter and Setter) Returns/ 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 and get_live_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.|
172 | | bit_depth | (Getter only) Returns the bit depth of pixel data for images collected with this camera. Bit depth cannot be changed directly; instead, users must select a desired speed table index value 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 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 index is currently selected. |
173 | | cam_fw | (Getter only) Returns the cameras current firmware version as a string. |
174 | | centroids_mode | (Getter and Setter) **Warning: Camera specific setting. Not all camera's support this attribute. If an unsupported camera attempts to access it's readout_port, an AttributeError will be raised.**
Returns/changes the current centroids mode, Locate, Track or Blob. |
175 | | centroids_modes | (Getter only) Returns a dictionary containing centroid modes supported by the camera. |
176 | | chip_name | (Getter only) Returns the camera sensor's name as a string. |
177 | | clear_mode | (Getter and Setter): **Warning: Camera specific setting. Not all camera's support this attribute. If an unsupported camera attempts to access it's readout_port, 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. |
178 | | clear_modes | (Getter only) Returns a dictionary containing clear modes supported by the camera. |
179 | | clear_time | (Getter only): **Warning: Camera specific setting. Not all camera's support this attribute. If an unsupported camera attempts to access it's readout_port, an AttributeError will be raised.**
Returns the last acquisition's clearing time as reported by the camera in microseconds. |
180 | | driver_version | (Getter 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.|
181 | | exp_mode | (Getter and Setter): Returns/ 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.|
182 | | exp_modes | (Getter only) Returns a dictionary containing exposure modes supported by the camera. |
183 | | exp_out_mode | (Getter and Setter): **Warning: Camera specific setting. Not all camera's support this attribute. If an unsupported camera attempts to access it's readout_port, an AttributeError will be raised.**
Returns/ 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.|
184 | | exp_out_modes | (Getter only) Returns a dictionary containing exposure out modes supported by the camera. |
185 | | 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. |
186 | | exp_res_index | (Getter only): Returns the current exposure resolution index. |
187 | | exp_resolutions | (Getter only) Returns a dictionary containing exposure resolutions supported by the camera. |
188 | | exp_time | (Getter and Setter): Returns/ 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. |
189 | | gain | (Getter and Setter) Returns/changes the current gain index for a camera. A ValueError will be raised if an invalid gain index is supplied to the setter. |
190 | | handle | (Getter only) Returns the value currently stored inside the Camera's __handle instance variable. |
191 | | is_open | (Getter only) Returns the value currently stored inside the Camera's __is_open instance variable. |
192 | | last_exp_time | (Getter 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. |
193 | | name |(Getter only) Returns the value currently stored inside the Camera's __name instance variable.|
194 | | pix_time | (Getter 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 table index value 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 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 index is currently selected.|
195 | | port_speed_gain_table | (Getter 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 index and gain.|
196 | | post_processing_table | (Getter only) Returns a dictionary containing post-processing features and parameters as well as the minimum and maximum value for each parameter.|
197 | | post_trigger_delay | (Getter only): **Warning: Camera specific setting. Not all camera's support this attribute. If an unsupported camera attempts to access it's readout_port, an AttributeError will be raised.**
Returns the last acquisition's post-trigger delay as reported by the camera in microseconds. |
198 | | pre_trigger_delay | (Getter only): **Warning: Camera specific setting. Not all camera's support this attribute. If an unsupported camera attempts to access it's readout_port, an AttributeError will be raised.**
Returns the last acquisition's pre-trigger delay as reported by the camera in microseconds|
199 | | prog_scan_mode | (Getter and Setter) **Warning: Camera specific setting. Not all camera's support this attribute. If an unsupported camera attempts to access it's readout_port, an AttributeError will be raised.**
Returns/changes the current programmable scan mode, Auto, Line Delay or Scan Width. |
200 | | prog_scan_modes | (Getter only) Returns a dictionary containing programmable scan modes supported by the camera. |
201 | | prog_scan_dir | (Getter and Setter) **Warning: Camera specific setting. Not all camera's support this attribute. If an unsupported camera attempts to access it's readout_port, an AttributeError will be raised.**
Returns/changes the current programmable scan direction, Auto, Line Delay or Scan Width. |
202 | | prog_scan_dirs | (Getter only) Returns a dictionary containing programmable scan directions supported by the camera. |
203 | | prog_scan_dir_reset | (Getter and Setter) **Warning: Camera specific setting. Not all camera's support this attribute. If an unsupported camera attempts to access it's readout_port, 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. |
204 | | prog_scan_line_delay | (Getter and Setter) **Warning: Camera specific setting. Not all camera's support this attribute. If an unsupported camera attempts to access it's readout_port, an AttributeError will be raised.**
Returns/changes the scan line delay. The parameter access mode depends on the prog_scan_mode selection. |
205 | | prog_scan_width | (Getter and Setter) **Warning: Camera specific setting. Not all camera's support this attribute. If an unsupported camera attempts to access it's readout_port, an AttributeError will be raised.**
Returns/changes the scan width. The parameter access mode depends on the prog_scan_mode selection. |
206 | | readout_port |(Getter and Setter) **Warning: Camera specific setting. Not all camera's support this attribute. If an unsupported camera attempts to access it's readout_port, an AttributeError will be raised.**
Some camera's may have many readout ports, which are output nodes from which a pixel stream can be read from. For more information about readout ports, refer to the Port and Speed Choices section inside the PVCAM User Manual|
207 | | readout_time | (Getter only): Returns the last acquisition's readout time as reported by the camera in microseconds. |
208 | | scan_line_time | (Getter) **Warning: Camera specific setting. Not all camera's support this attribute. If an unsupported camera attempts to access it's readout_port, an AttributeError will be raised.**
Returns the scan line time of camera in nano seconds. |
209 | | sensor_size | (Getter only) Returns the sensor size of the current camera in a tuple in the form (serial sensor size, parallel sensor size)|
210 | | serial_no | (Getter only) Returns the camera's serial number as a string.|
211 | | speed_table_index| (Getter and Setter) Returns/changes the current numerical index of the speed table of a camera. See the Port and Speed Choices section inside the PVCAM User Manual for a detailed explanation about PVCAM speed tables.|
212 | | temp | (Getter only): **Warning: Camera specific setting. Not all camera's support this attribute. If an unsupported camera attempts to access it's readout_port, an AttributeError will be raised.**
Returns the current temperature of a camera in Celsius. |
213 | | temp_setpoint | (Getter and Setter): **Warning: Camera specific setting. Not all camera's support this attribute. If an unsupported camera attempts to access it's readout_port, 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 it's temperature (in Celsius) at.|
214 | | trigger_table | (Getter 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'. |
215 | | vtm_exp_time | (Getter and Setter): **Warning: Camera specific setting. Not all camera's support this attribute. If an unsupported camera attempts to access it's readout_port, an AttributeError will be raised.**
Returns/ changes the variable timed exposure time the camera uses for the "Variable Timed" exposure mode. |
216 |
217 | ### constants.py
218 | 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.
219 | 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 build/rebuild 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.
220 | There are four main sections to this file that are found following a formatted comment like this ### ###:
221 | 1. Defines
222 | 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 who's name is the macros name and the value is what the macro expands to.
223 | 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.
224 | 2. Enums
225 | 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.
226 | 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 who's values where the integer assigned to them in the pvcam.h file.
227 | 3. Structs
228 | 1. While not used (yet), structs are preserved as Python classes. No testing/implementation has been done with these, so results may be unexpected if implemented.
229 | 4. Added By Hand
230 | 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.
231 |
232 | ### pvcmodule.cpp
233 | 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.
234 | 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.
235 |
236 | #### General Structure of a pvcmodule Function
237 | The functions for a pvcmodule function usually follow a three step process:
238 | 1. Retrieve data/query from Python script
239 | 2. Process acquired data
240 | 3. Return data to Python scrip
241 |
242 | #### Retrieving Data
243 | Functions receive data dynamically through use of parameters, and the pvcmodule'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 of 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.
244 |
245 | | C Type | Character Representation |
246 | | -------------- | ------------------------ |
247 | | long | l |
248 | | int | i |
249 | | double | d |
250 | | float | f |
251 | | string (char*) | s |
252 | | PyObject | O |
253 |
254 | #### Arguments of PyArg_ParseTuple
255 | 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".
256 | 2. format (char *) A String containing the format units for all of 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".
257 |
258 | 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).
259 |
260 | #### PyArg_ParseTuple Example
261 | ```
262 | static PyObject *example(PyObject *self, PyObject *args) {
263 | int myNum;
264 | char *myString;
265 | PyArg_ParseTuple(args, "is", &myNum, &myString);
266 | printf("myNum: %d\n", myNum); // Prints "myNum: 1"
267 | printf("myString: %s\n", myString); // Prints "myString: test"
268 | Py_RETURN_NONE;
269 | }
270 | ```
271 |
272 | #### Processing Acquired Data
273 | 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.
274 |
275 | 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.
276 |
277 | 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.
278 |
279 | #### Return Data to a Python Script
280 | 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.
281 |
282 | 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.
283 |
284 | #### Cast to Python Type
285 | ```
286 | {
287 | char *myString = "ika";
288 | return PyUnicode_FromString(myString); // Returns a Python string back to the calling function.
289 | }
290 | ```
291 |
292 | 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!)
293 | If you wish to return None, simply use the Py_RETURN_NONE macro (see the PyArg_ParseTuple example for a visual representation).
294 |
295 | #### Functions of pvcmodule.cpp
296 | **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.
297 |
298 | | Function Name | Description |
299 | | ------------- | ----------- |
300 | | NewFrameHandler | Call-back function registered with PVCAM when a new frame is available. |
301 | | check_meta_data_enabled | Given a camera handle, checks if meta data is enabled.
**Parameters:**
- Python int (camera handle)
|
302 | | is_avail | Given a camera handle, checks if the parameter ID is available.
**Parameters:**
- Python int (camera handle).
- Python int (parameter ID).
|
303 | | pvc_abort | Given a camera handle, aborts any ongoing acquisition and de-registers the frame handler callback function.
**Parameters:**
- Python int (camera handle).
|
304 | | pvc_check_frame_status | Given a camera handle, returns the current frame status as a string. Possible return values:- READOUT_NOT_ACTIVE
- EXPOSURE_IN_PROGRESS
READOUT_IN_PROGRESS- READOUT_COMPLETE/FRAME_AVAILABLE
- READOUT_FAILED
**Parameters:**
- Python int (camera handle).
|
305 | | pvc_check_param | Given a camera handle and parameter ID, returns True if the parameter is available on the camera.
**Parameters:**
- Python int (camera handle).
- Python int (parameter ID).
|
306 | | pvc_close_camera | Given a Python string corresponding to a camera's name, close the camera. Returns True upon success. ValueError is raised if invalid parameter is supplied. RuntimeError raised otherwise.
**Parameters:**
- Python string (camera name).
|
307 | | pvc_finish_seq | Given a camera handle, finalizes sequence acquistion and cleans up resources. If a sequence is in progress, acquisition will be aborted.
**Parameters:**
- Python int (camera handle).
|
308 | | pvc_get_cam_fw_version | Given a camera handle, returns camera firmware version as a sring.
**Parameters:**
- Python int (camera handle).
|
309 | | pvc_get_cam_name | Given a Python integer corresponding to a camera handle, returns the name of the camera with the associate handle.
**Parameters:**
- Python integer (camera handle).
|
310 | | pvc_get_cam_total | Returns the total number of cameras currently attached to the system as a Python integer. |
311 | | pvc_get_frame | Given a camera and a region, return a Python numpy array of the pixel values of the data. Numpy array returned on success. ValueError raised if invalid parameters are supplied. MemoryError raised if unable to allocate memory for the camera frame. RuntimeError raised otherwise.
**Parameters:**
- Python int (camera handle).
- Python list (Region of Interest objects)
- Python int (Numpy data type enumeration value)
- Python int (Frame timeout in milliseconds. Negative values will wait forever)
- Python bool (Flag selecting oldest or newest frame)
|
312 | | pvc_get_param | Given a camera handle, a parameter ID, and the attribute of the parameter in question (AVAIL, CURRENT, etc.) return the value of the parameter at the current attribute. **Note: This setting will only return a Python int or a Python string. Currently no other type is supported, but it is possible to extend the function as needed.** ValueError is raised if invalid parameters are supplied. AttributeError is raised if camera does not support the specified parameter. RuntimeError is raised otherwise.
**Parameters:**
- Python int (camera handle).
- Python int (parameter ID).
- Python int (parameter attribute).
|
313 | | pvc_get_pvcam_version | Returns a Python Unicode String of the current PVCAM version.|
314 | | pvc_init_pvcam | Initializes the PVCAM library. Returns True upon success, RuntimeError is raised otherwise. |
315 | | pvc_open_camera |Given a Python string corresponding to a camera's name, open the camera. Returns True upon success. ValueError is raised if invalid parameter is supplied. RuntimeError raised otherwise.
**Parameters:**
- Python string (camera name).
|
316 | | pvc_read_enum | Function that when given a camera handle and a enumerated parameter will return a list mapping all valid setting names to their values for the camera. ValueError is raised if invalid parameters are supplied. AttributeError is raised if an invalid setting for the camera is supplied. RuntimeError is raised upon failure. A Python list of dictionaries is returned upon success.
**Parameters:**
- Python int (camera handle).
- Python int (parameter ID).
317 | | pvc_reset_pp | Given a camera handle, resets all camera post-processing parameters back to their default state.
**Parameters:**
- Python int (camera handle).
|
318 | | pvc_set_exp_modes | Given a camera, exposure mode, and an expose out mode, change the camera's exposure mode to be the bitwise-or of the exposure mode and expose out mode parameters. ValueError is raised if invalid parameters are supplied including invalid modes for either exposure mode or expose out mode. RuntimeError is raised upon failure.
**Parameters:**
- Python int (camera handle).
- Python int (exposure mode).
- Python int (expose out mode).
|
319 | | pvc_set_param | Given a camera handle, a parameter ID, and a new value for the parameter, set the camera's parameter to the new value. ValueError is raised if invalid parameters are supplied. AttributeError is raised when attempting to set a parameter not supported by a camera. RuntimeError is raised upon failure.
**Parameters:**
- Python int (camera handle).
- Python int (parameter ID).
- Generic Python value (any type) (new value for parameter).
|
320 | | pvc_start_live | Given a camera handle, region of interest, binning factors, exposure time and exposure mode, sets up a live mode acquisition.
**Parameters:**
- Python int (camera handle).
- Python list (Region of Interest objects)
- Python int (exposure time).
- Python int (Exposure mode).
- Python int (buffer_frame_count).
- Python str (stream_to_disk_path).
|
321 | | pvc_start_seq | Given a camera handle, region of interest, binning factors, exposure time and exposure mode, sets up a sequence mode acquisition.
**Parameters:**
- Python int (camera handle).
- Python list (Region of Interest objects)
- Python int (exposure time).
- Python int (Exposure mode).
- Python int (Total Frames).
|
322 | | pvc_stop_live | Given a camera handle, stops live acquistion and cleans up resources. If a sequence is in progress, acquisition will be aborted.
**Parameters:**
- Python int (camera handle).
|
323 | | pvc_sw_trigger | Given a camera handle, performs a software trigger. Prior to using this function, the camera must be set to use either the EXT_TRIG_SOFTWARE_FIRST or EXT_TRIG_SOFTWARE_EDGE exposure mode.
**Parameters:**
- Python int (camera handle).
|
324 | | pvc_uninit_pvcam | Uninitializes the PVCAM library. Returns True upon success, RuntimeError is raised otherwise. |
325 | | populateMetaDataFrameHeader | Helper function that populates frame header meta-data |
326 | | populateMetaDataRoiHeader | Helper function that populates ROI header meta-data |
327 | | populateRegions | Helper function that converts a Python RegionOfInterest object to it's C counterpart. |
328 | | set_g_msg | Helper function that when called, will set the global error message to whatever the error of the last PVCAM error message was. Used before raising a Python Error. |
329 | | valid_enum_param | Helper function that determines if a given value is a valid selection for an enumerated type. Should any PVCAM function calls in this function fail, a "falsy" value wil be returned. "Truthy" is returned if it is a valid enumerated value for a parameter. **Note: This function is not exposed to Python, so no Python parameters are required.**
**Parameters:**
- hcam (int16): The handle of the camera.
- param_id (uns32): The parameter ID.
- selected_val (int32): The value to check if it is a valid selection.
|
330 |
331 |
332 | #### The Method Table
333 | All functions that need to be exposed to Python programs need to be included in the method table. The method table is partially responsible for allowing Python programs to call functions from an extension module. It does this by creating a list of PyMethodDef structs with a sentinel struct at the end of the list. The list of method definitions are then passed to the PyModuleDef struct, which contains the necessary information to construct the module.
334 |
335 | The method table is a list of PyMethodDef structs, which have the following four fields:
336 |
337 | | Field Name | C Type | Description |
338 | | -----------| ----------- | ----------------------------------------------------------------------- |
339 | | ml_name | char * | Name of the method (when called from Python) |
340 | | ml_meth | PyCFunction | Pointer to the C implementation of the function in the module. |
341 | | ml_flags | int | Flag bits indicating how the call to the function should be constructed |
342 | | ml_doc | char * | Points to the contents of the docstring for the method. |
343 |
344 | All docstrings for the functions inside of pvcmodule.cpp are statically defined in the pvcmodule.h file.
345 |
346 | #### The Module Definition
347 | The PyModuleDef structure contains all of the information required to create the top-level module object.
348 |
349 | | Field Name | C Type | Description |
350 | | -----------| ---------------- | ------------------------------------------------------------------------ |
351 | | m_base | PyModuleDef_Base | Always initialize this member to PyModuleDef_HEAD_INIT |
352 | | m_name | char * |Name for the new module (must match the name in the Module Init function).|
353 | | m_doc | char * | Docstring for the module (statically defined in the header file) |
354 | | m_size | Py_ssize_t | Specifies the additional amount of memory a module requires for it's "state".
Only needed if running in sub-interpreters; otherwise set to -1, signifying that the module does not support subinterpreters because it has global state. |
355 | | m_methods | PyMethodDef* | pointer to the method table. Can be NULL if no functions are present. |
356 |
357 | After creating the module definition structure, it can then be passed into the module creation function.
358 | #### Module Creation
359 | The module initialization function will create and return the module object directly.
360 |
361 | To initialize a module, write the PyInit_{modulename} function, which calls and returns the value of PyModule_Create. See example below:
362 |
363 | #### Creating Extension Module
364 | ```
365 | PyMODINIT_FUNC
366 | PyInit_pvc(void)
367 | }
368 | return PyModule_Create(&pvcmodule);
369 | }
370 | ```
371 |
372 | ### constants_generator.py
373 | The purpose of the constants_generator.py file is to easily construct a new constants.py data file should the file become tainted or a new version of PVCAM is released.
374 |
375 | The script targets three main parts of the header file: the predefined macros, the enums, and the structs.
376 |
377 | #### Requirements
378 | The constants generator targets the install location of the PVCAM SDK on your machine, meaning that the script will fail to run if you do not have the SDK installed.
379 |
380 | #### Running the Script
381 | In order to run the script, ensure that you are running it from /PyVCAM/pyvcam/src/, or else it will fail to find the correct directory to write the generated constants.py file to.
382 |
383 | The script can be run using the following command when you are in the correct directory: python constants_generator.py
384 | ***
385 | ## tests
386 | The tests directory contains unit tests to ensure the quality of the code of the module and to also include some basic examples on how to perform basic operations on a camera.
387 |
388 | ### change_settings_test.py (needs camera_settings.py)
389 | change_settings_test.py is used to show one way of keeping camera settings in one file and importing them to update a camera's settings in another file.
390 |
391 | This allows the user to quickly change the settings they wish to test on a camera without having to dig through a large testing script and manually changing the settings within it.
392 |
393 | **Note:** camera_settings.py needs to be included in the same directory in order to run this test.
394 |
395 | ### check_frame_status.py
396 | check_frame_status.py is used to demonstrate how to querry frame status for both live and sequence acquisition modes.
397 |
398 | ### live_mode.py
399 | live_mode.py is used to demonstrate how to peroform live frame acquistion using the advanced frame acquistion features of PyVCAM.
400 |
401 | ### meta_data.py
402 | meta_data.py is used to demonstrate how to enable frame meta data. Meta data is only supported when using the advanced frame acquistion features of PyVCAM.
403 |
404 | ### multi_camera.py
405 | multi_camera.py is used to demonstrate how control acquire from multiple cameras simultaneously.
406 |
407 | ### multi_rois.py
408 | multi_camera.py is used to demonstrate how control acquire multiple regions of interest.
409 |
410 | ### newest_frame.py
411 | newest_frame.py is used to demonstrate how to acquire both the newest frame using the optional parameter to poll_frame.
412 |
413 | ### seq_mode.py
414 | seq_mode.py is used to demonstrate how to perform sequence frame acquistion using the advanced frame acquistion features of PyVCAM.
415 |
416 | ### single_image_polling.py
417 | single_image_polling.py is used to demonstrate how to collect single frames from a camera, starting from the detection and opening of an available camera to calling the get_frame function.
418 |
419 | Note that this test does not display the frame; only saves it locally to a variable and prints a few pixel points from it. If you want an example of how to quickly display a frame, see single_image_polling_show.py.
420 |
421 | ### single_image_polling_show.py
422 | single_image_polling_show.py is used to demonstrate how to collect a single frame from a camera and use matplotlib's pyplot subpackage in order to display the captured frame.
423 |
424 | **Note:** The test reverses the camera's sensor size when reshaping the array. This is because the camera sensor size tuple is row x column, and the shape of a numpy array is specified by column x row.
425 |
426 | ### stream_to_disk.py
427 | stream_to_disk.py is used to demonstrate how to stream frames directly to disk from a PVCAM C++ call-back.
428 |
429 | ### sw_trigger.py
430 | sw_trigger.py is used to demonstrate how to perform a software trigger using two Python threads, one to configure acquisition and one to perform the trigger.
431 |
432 | ### test_camera.py
433 | test_camera.py contains the unit tests for this module. It tests the getting, setting, and edge cases of all available settings.
434 |
435 | All unit tests can be run from the command line using the command python -m unittest discover
436 | ***
437 | ## setup.py
438 | setup.py is the installer script for this module. Once the package has been downloaded, navigate the the src directory to run the setup script.
439 |
440 | ### Variables
441 | |Variable | Description |
442 | | ------- | ------------ |
443 | | packages | List that contains the names of all of the package(s) that will be built with the setup script. In our case, there is only one package. |
444 | | package_dir | Dictionary that maps the name of the package from the packages list to the directory that contains their source code. Again, this will only have a single member since we only have one package. |
445 | | pvc_modules | List containing the names of the modules contained within the package. In our case, we only have two Python modules: constants.py and camera.py |
446 | | include_dirs | List of directories that contain necessary files to include for the python extension module. For example; the pvcam.h file needs to be included, and so does the numpy.h file. |
447 | | lib_dirs | List of directories that contain necessary libraries needed to compile the python extension module. |
448 | | libs | List of libraries that can be found from the lib_dirs list that are needed for the compilation of the python extension module. |
449 | | ext_module | List of Extension objects that model a Python extension module that include: - The name of the module (pyvcam.pvc)
- Where the source code is located
- All necessary include directories
- All necessary library directories
- All necessary libraries
|
450 |
451 | ### Installing the Package
452 | When you are ready to install the package, navigate to the directory that contains setup.py and run:
453 | #### setup.py Install Command
454 | python setup.py install
455 |
456 | ### Creating a PyVCAM Wheel Package
457 | To create a PyVCAM Wheel package, navigate to the directory that contains setup.py and run:
458 | #### setup.py Create Wheels Package Command
459 | python setup.py dist bdist_wheel
460 |
461 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import os, platform
2 | from setuptools import setup
3 | from setuptools.extension import Extension
4 | from setuptools.command.build_ext import build_ext as _build_ext
5 |
6 | is_windows = 'win' in platform.system().lower()
7 | is_linux = 'lin' in platform.system().lower()
8 |
9 | #print('Operating system: ' + platform.system())
10 | #print('Machine architecture: ' + platform.machine())
11 |
12 | if not is_linux and not is_windows:
13 | print('Operating systems other than Windows or Linux are not supported.')
14 | quit()
15 |
16 | elif is_linux:
17 | is_arch_aarch64 = 'aarch64' in platform.machine().lower()
18 | is_arch_x86_64 = 'x86_64' in platform.machine().lower()
19 | is_arch_i686 = 'i686' in platform.machine().lower()
20 |
21 | if not is_arch_aarch64 and not is_arch_x86_64 and not is_arch_i686:
22 | print('Machine architecture is not supported, it must be aarch64, x86_64 or i686.')
23 | quit()
24 |
25 | elif is_windows:
26 | is_arch_x86_64 = 'amd64' in platform.machine().lower()
27 | is_32bit = 'x86' in platform.machine().lower()
28 |
29 | if not is_arch_x86_64 and not is_32bit:
30 | print('Machine architecture is not supported, it must be amd64 or x86 32bit.')
31 | quit()
32 |
33 | pvcam_sdk_path = os.environ['PVCAM_SDK_PATH']
34 | include_dirs = []
35 | lib_dirs = []
36 | libs = []
37 | extra_compile_args = []
38 | sources = []
39 |
40 | class build_ext(_build_ext):
41 | def finalize_options(self) -> None:
42 | # Prevent numpy from thinking it is still in its setup process
43 | # https://github.com/singingwolfboy/nose-progressive/commit/d8dce093910724beff86081327565b11ea28dfbc#diff-fa26a70e79ad69caf5b4f938fcd73179afaf4c05229b19acb326ab89e4759fbfR25
44 | def _set_builtin(name, value):
45 | if isinstance(__builtins__, dict):
46 | __builtins__[name] = value
47 | else:
48 | setattr(__builtins__, name, value)
49 |
50 | _build_ext.finalize_options(self)
51 | _set_builtin('__NUMPY_SETUP__', False)
52 | import numpy
53 | include_dirs.append(numpy.get_include())
54 |
55 | if is_linux:
56 | extra_compile_args.append('-std=c++11')
57 | include_dirs.append('{}/include'.format(pvcam_sdk_path))
58 | if is_arch_aarch64:
59 | lib_dirs.append('{}/library/aarch64'.format(pvcam_sdk_path))
60 | elif is_arch_x86_64:
61 | lib_dirs.append('{}/library/x86_64'.format(pvcam_sdk_path))
62 | elif is_arch_i686:
63 | lib_dirs.append('{}/library/i686'.format(pvcam_sdk_path))
64 | libs.append('pvcam')
65 |
66 | elif is_windows:
67 | include_dirs.append('{}/inc'.format(pvcam_sdk_path))
68 | if is_arch_x86_64:
69 | lib_dirs.append('{}/Lib/amd64'.format(pvcam_sdk_path))
70 | libs.append('pvcam64')
71 | elif is_32bit:
72 | lib_dirs.append('{}/Lib/i386'.format(pvcam_sdk_path))
73 | libs.append('pvcam32')
74 |
75 | include_dirs.append('src/pyvcam')
76 | sources.append('src/pyvcam/pvcmodule.cpp')
77 |
78 | ext_modules = [Extension('pyvcam.pvc',
79 | sources=sources,
80 | extra_compile_args=extra_compile_args,
81 | include_dirs=include_dirs,
82 | library_dirs=lib_dirs,
83 | libraries=libs)]
84 |
85 | setup(name='pyvcam',
86 | version='2.1.9',
87 | author='Teledyne Photometrics',
88 | author_email='Steve.Bellinger@Teledyne.com',
89 | url='https://github.com/Photometrics/PyVCAM',
90 | description='Python wrapper for PVCAM functionality.',
91 | packages=['pyvcam'],
92 | package_dir={'pyvcam': 'src/pyvcam'},
93 | py_modules=['pyvcam.constants'],
94 | cmdclass={'build_ext': build_ext},
95 | setup_requires=['numpy'],
96 | install_requires=['numpy'],
97 | ext_modules=ext_modules)
98 |
--------------------------------------------------------------------------------
/src/constants_generator.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # File: constants_generator.py
3 | # Author: Cameron Smith
4 | # Date of Last Edit: June 2, 2017
5 | #
6 | # Purpose: Generate the necessary constants used in pvcam.h into a reusable
7 | # python module.
8 | #
9 | # Notes: No formal testing exists for this script. Please report all bugs to
10 | # to whoever is in charge of this project currently.
11 | #
12 | # Bugs: Enums are not created as Enum objects. Any struct that assigns to an
13 | # enum will result in a NameError as the Enum does not exist; only its
14 | # entities. Update occurrences manually to be a ctypes C type until fix.
15 | # (Known occurrence(s): md_ext_item_info [struct]. Currently patched by
16 | # assigning values to be of type `void`.
17 | ###############################################################################
18 | import os
19 | import re
20 | from datetime import date
21 |
22 | pvcam_sdk_path = os.environ["PVCAM_SDK_PATH"]
23 | pvcam = r'{}inc/pvcam.h'.format(pvcam_sdk_path)
24 | constants = r'./pyvcam/constants.py'
25 |
26 |
27 | def define_writer(match):
28 | """Constructs a string representing defines in header files.
29 |
30 | Parameters:
31 | match: The matched pattern from the regular expression containing the
32 | named groups of the variable name and its associated value.
33 | """
34 |
35 | return match.group('var'), match.group('val')
36 |
37 |
38 | def enum_writer(match, infile):
39 | """Constructs a tuple containing the data to represent enums in python.
40 |
41 | The tuple will have the form (, ), where the string will
42 | contain the comment block denoting which enum the following list is from,
43 | and the list contains tuples pairing variables to values.
44 |
45 | Parameters:
46 | match: The matched pattern from the regular expression containing the
47 | name of the current enum.
48 | infile: The file being read from.
49 | """
50 |
51 | enums = []
52 | position = 0 # The current position inside of `enums`
53 | enum_group = match.group('name')
54 |
55 | # Skip two lines ahead to ignore the '{' line
56 | next(infile)
57 | line = next(infile)
58 | line = remove_comment(line, infile)
59 |
60 | # Set up the regular expressions to find name and values of enums
61 | enum_name = '\s+(?P\w+)'
62 | enum_val = enum_name + '\s*=(?P[^,]*)'
63 |
64 | # Loop through the file until the end of the current enum is reached.
65 | # If the current line matches the pattern = , then append
66 | # a tuple onto `enums` that pairs with . If the current
67 | # line does not match that pattern, check if it matches the pattern
68 | # . If it does, append a tuple to `enums` pairing with
69 | # the previous position's + 1. If position = 0, then it is the
70 | # first element in the enum, so pair with 0 instead. If the
71 | # line does not match either of these patters, move to the next line.
72 | while line != '}\n':
73 | val = re.search(enum_val, line)
74 | if val:
75 | enums.append((val.group('name'), val.group('val')))
76 | else:
77 | name = re.search(enum_name, line)
78 | if name and position == 0:
79 | enums.append((name.group('name'), '0'))
80 | elif name:
81 | enums.append((name.group('name'),
82 | enums[position - 1][0] + ' + 1')) # Prev. var. + 1
83 | # Offset the increment to position if nothing is being added
84 | else: position -= 1
85 | line = next(infile)
86 | line = remove_comment(line, infile)
87 | position += 1
88 | return (enum_group, enums)
89 |
90 |
91 | def struct_writer(infile):
92 | """Builds a tuple containing the data to represent structs in python.
93 |
94 | Parameters:
95 | match: The matched pattern from the regular expression containing the
96 | name of the current enum.
97 | infile: The file being read from.
98 |
99 | Returns:
100 | A tuple in the form (, [list of fields])
101 | """
102 |
103 | # Maps the data types used in the header file to the ctypes equivalent.
104 | types = {'uns32': 'ctypes.c_uint32', 'uns16': 'ctypes.c_uint16',
105 | 'uns64': 'ctypes.c_uint64', 'uns8': 'ctypes.c_uint8',
106 | 'int32': 'ctypes.c_int32', 'int16': 'ctypes.c_int16',
107 | 'int64': 'ctypes.c_int64', 'int8': 'ctypes.c_int8',
108 | 'long64': 'ctypes.c_long', 'char*': 'ctypes.c_char_p',
109 | 'void*': 'ctypes.c_void_p', 'flt64': 'ctypes.c_float',
110 | 'rs_bool': 'ctypes.c_short', 'char': 'ctypes.c_char',
111 | 'void': 'ctypes.c_void_p'}
112 |
113 | fields = []
114 | # Skip two lines ahead to ignore the '{' line
115 | next(infile)
116 | line = next(infile)
117 | line = remove_comment(line, infile)
118 |
119 | struct_pattern = '^\s+(const )?(?P\w+\*?)\s+(?P\w+)\[?(?P\d+)?\]?;$'
120 |
121 | while '}' not in line:
122 | match = re.search(struct_pattern, line)
123 | if match:
124 | # EAFP
125 | try:
126 | var_type = types[match.group('type')]
127 | except KeyError:
128 | if match.group('type')[-1] == '*' and match.group('type')[:-1] in types:
129 | var_type = types[match.group('type')[:-1]]
130 | else:
131 | var_type = types['void'] # Handle bug for md_ext_item_info
132 | variable = match.group('var')
133 | if match.group('num'):
134 | var_type += ' * {}'.format(match.group('num'))
135 | if var_type[-1] == '*':
136 | var_type = 'ctypes.POINTER({})'.format(var_type[:-1])
137 | fields.append((variable, var_type))
138 | line = next(infile)
139 | line = remove_comment(line, infile)
140 |
141 | line = next(infile)
142 | line = remove_comment(line, infile)
143 | struct_name = '^(?P\w+);$'
144 | name = re.search(struct_name, line)
145 | return name.group('name'), fields
146 |
147 |
148 | def remove_comment(to_remove, infile):
149 | """Removes trailing block comments from the end of a string.
150 |
151 | Parameters:
152 | to_remove: The string to remove the comment from.
153 | infile: The file being read from.
154 |
155 | Returns:
156 | The parameter string with the block comment removed (if comment was
157 | present in string).
158 | """
159 | start_comment = re.search('\s*(/\*|//)', to_remove)
160 |
161 | # Remove comments if they are in the matched group.
162 | if start_comment:
163 | end_comment = re.search('.*\*/', to_remove)
164 | if end_comment or ('//' in to_remove and not '/*' in to_remove) :
165 | removed = to_remove[:start_comment.start(0)] + '\n'
166 | return removed
167 | while not end_comment:
168 | to_remove = next(infile)
169 | end_comment = end_comment = re.search('.*\*\/', to_remove)
170 | return ''
171 | else:
172 | removed = to_remove
173 | return removed
174 |
175 |
176 | def parse_line(line, infile, collection):
177 | """Determines if a line is a define or a start of an enum or struct.
178 |
179 | After determining the structure of a current line, parse_line will then
180 | call the appropriate function in order to deal with the current line.
181 |
182 | Parameters:
183 | line: The current line being read in from the input file.
184 | infile: The opened input file.
185 | collection: The dictionary containing all the lists of data.
186 | """
187 | # Define, enum, and struct check pattern.
188 | define = '^#define (?P\w+)\s+(?P.+)$'
189 | enum = '^typedef enum (?P\w+)$'
190 | struct = '^typedef struct .+$'
191 |
192 | expressions = {'defines': define, 'enums': enum, 'structs': struct}
193 |
194 | for key in expressions:
195 | match = re.search(expressions[key], line)
196 | if match:
197 | if key == 'defines':
198 | collection[key].append(define_writer(match))
199 | if key == 'enums':
200 | collection[key].append(enum_writer(match, infile))
201 | if key == 'structs':
202 | collection[key].append(struct_writer(infile))
203 |
204 |
205 |
206 |
207 | if __name__ == '__main__':
208 | header_comment = \
209 | """###############################################################################
210 | # File: constants.py
211 | # Author: Cameron Smith
212 | # Date of Last Edit: {}
213 | #
214 | # Purpose: To maintain the naming conventions used with pvcam.h for Python
215 | # scripts.
216 | #
217 | # Notes: This file is generated by constants_generator.py. See that script for
218 | # details about implementation. Please do not alter this file. Instead,
219 | # make any additional changes to the constants_generator.py if
220 | # additional data is needed.
221 | #
222 | # Bugs: [See constants_generator.py]
223 | ###############################################################################\n"""\
224 | .format(date.today())
225 |
226 | defines = []
227 | macros = []
228 | enums = []
229 | structs = []
230 | collection = {'defines': defines, 'macros': macros, 'enums': enums,
231 | 'structs': structs}
232 | # Open file and read parse each line before writing
233 | try:
234 | with open(pvcam, 'r') as infile:
235 | for line in infile:
236 | line = remove_comment(line, infile)
237 | parse_line(line, infile, collection)
238 |
239 | except FileNotFoundError:
240 | print(pvcam + " not found. Make sure that the PVCAM SDK is installed "
241 | "on this machine.")
242 | exit(-1)
243 |
244 | with open(constants, 'w') as outfile:
245 | outfile.write(header_comment)
246 | outfile.write('import ctypes\n')
247 | outfile.write('\n### DEFINES ###\n\n')
248 | for tup in collection['defines']:
249 | outfile.write('{} = {}\n'.format(tup[0], tup[1]))
250 | outfile.write('\n### ENUMS ###\n')
251 | for tup in collection['enums']:
252 | outfile.write('\n# {}\n'.format(tup[0]))
253 | for item in tup[1]:
254 | outfile.write('{} = {}\n'.format(item[0], item[1]))
255 | outfile.write('\n### STRUCTS ###\n')
256 | for tup in collection['structs']:
257 | outfile.write('\nclass {}(ctypes.Structure):\n'.format(tup[0]))
258 | outfile.write(' _fields_ = [\n')
259 | for item in tup[1]:
260 | outfile.write(' (\'{}\', {}),\n'.format(item[0],
261 | item[1]))
262 | outfile.write(' ]\n')
263 |
264 | # Diagnostic
265 | if False:
266 | print('Defines: {}'.format(len(collection['defines'])))
267 | print('Enums: {}'.format(len(collection['enums'])))
268 | for tup in collection['enums']:
269 | print('---{}: {}'.format(tup[0].ljust(20), len(tup[1])))
270 | print('Structs: {}'.format(len(collection['structs'])))
271 | for item in collection['structs']:
272 | print('---{}: {}'.format(item[0], item[1]))
273 |
--------------------------------------------------------------------------------
/src/pyvcam/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Photometrics/PyVCAM/a7485f04e533f0fff1c49847d05d1dde3c798a89/src/pyvcam/__init__.py
--------------------------------------------------------------------------------
/src/pyvcam/camera.py:
--------------------------------------------------------------------------------
1 | from pyvcam import pvc
2 | from pyvcam import constants as const
3 |
4 | from copy import deepcopy
5 | import os
6 | import time
7 | import numpy as np
8 |
9 |
10 | class Camera:
11 | """Models a class currently connected to the system.
12 |
13 | Attributes:
14 | __name(str): String containing the name of the camera.
15 | __handle(int): The current camera's handle.
16 | __is_open(bool): True if camera is opened, False otherwise.
17 |
18 | __exposure_bytes(int): How large the buffer for live imaging needs to be.
19 |
20 | __mode(int): The bit-wise OR between exposure mode and expose out mode.
21 | __exp_time(int): Integer representing the exposure time to be used for captures.
22 | """
23 |
24 | class ReversibleEnumDict(dict):
25 | # Helper class to populate enumerated parameter dictionaries and ease conversion of keys and values.
26 | # The param_id must support enum attribute
27 | # This dictionary will accept both keys and values to the __getitem__ operator, rather than just keys like
28 | # regular dictionaries. If a value is provided, the matching key is returned.
29 | def __init__(self, name, camera_instance, param_id):
30 | try:
31 | enum_dict = camera_instance.read_enum(param_id)
32 | except AttributeError:
33 | enum_dict = {}
34 |
35 | super(Camera.ReversibleEnumDict, self).__init__(enum_dict)
36 | self.name = name
37 |
38 | def __getitem__(self, key_or_value):
39 | try:
40 | if isinstance(key_or_value, str):
41 | return super(Camera.ReversibleEnumDict, self).__getitem__(key_or_value)
42 | else:
43 | return [key for key, item_value in self.items() if key_or_value == item_value][0]
44 | except KeyError:
45 | raise ValueError('Invalid key: {0} for {1} - Available keys are: {2}'.format(key_or_value, self.name, list(self.keys())))
46 | except IndexError:
47 | raise ValueError('Invalid value: {0} for {1} - Available values are: {2}'.format(key_or_value, self.name, list(self.values())))
48 |
49 | class RegionOfInterest:
50 |
51 | def __init__(self, s1, s2, sbin, p1, p2, pbin):
52 | # TODO: s1 & p1 could be also aligned to binning factors
53 | self.s1 = s1
54 | self.s2 = s2 - ((s2 - s1 + 1) % sbin) # Clip partially binned pixels
55 | self.sbin = sbin
56 | self.p1 = p1
57 | self.p2 = p2 - ((p2 - p1 + 1) % pbin) # Clip partially binned pixels
58 | self.pbin = pbin
59 |
60 | def __eq__(self, other):
61 | if not isinstance(other, Camera.RegionOfInterest):
62 | raise NotImplementedError
63 |
64 | # TODO: Add also binning factors to the comparison?
65 | return self.s1 == other.s1 and self.s2 == other.s2 and self.p1 == other.p1 and self.p2 == other.p2
66 |
67 | def checkOverlap(self, other):
68 | return (self.s1 <= other.s1 <= self.s1 and self.p1 <= other.p1 <= self.p1) or \
69 | (self.s2 <= other.s2 <= self.s2 and self.p2 <= other.p2 <= self.p2)
70 |
71 | @property
72 | def shape(self):
73 | return int((self.s2 - self.s1 + 1) / self.sbin), int((self.p2 - self.p1 + 1) / self.pbin)
74 |
75 | WAIT_FOREVER = -1
76 |
77 | def __init__(self, name):
78 | """NOTE: CALL Camera.detect_camera() to get a camera object."""
79 |
80 | self.__name = name
81 | self.__handle = -1
82 | self.__is_open = False
83 | self.__acquisition_mode = None
84 |
85 | # Memory for live circular buffer
86 | self.__exposure_bytes = None
87 |
88 | # Exposure Settings
89 | self.__exp_mode = const.TIMED_MODE
90 | self.__exp_out_mode = const.EXPOSE_OUT_FIRST_ROW
91 | self.__mode = None
92 | self.__exp_time = 0
93 |
94 | # Regions of interest
95 | self.__defaultRoi = None # The default RegionOfInterest object
96 | self.__rois = [] # A list of RegionOfInterest objects
97 |
98 | # Enumeration parameters
99 | self.__centroids_modes = {}
100 | self.__clear_modes = {}
101 | self.__exp_modes = {}
102 | self.__exp_out_modes = {}
103 | self.__exp_resolutions = {}
104 | self.__prog_scan_modes = {}
105 | self.__prog_scan_dirs = {}
106 |
107 | self.__port_speed_gain_table = {}
108 | self.__post_processing_table = {}
109 |
110 | self.__dtype = np.dtype('u2') # uns16 by default
111 |
112 | def __repr__(self):
113 | return self.__name
114 |
115 | @staticmethod
116 | def get_available_camera_names():
117 | """Gets the name for each available camera.
118 |
119 | Returns:
120 | List of camera names, sorted by index.
121 | """
122 |
123 | ret = []
124 |
125 | total = pvc.get_cam_total()
126 | for index in range(total):
127 | ret.append(pvc.get_cam_name(index))
128 |
129 | return ret
130 |
131 | @classmethod
132 | def detect_camera(cls):
133 | """Detects and creates a new Camera object.
134 |
135 | Returns:
136 | A Camera object generator.
137 | """
138 |
139 | cam_count = 0
140 | total = pvc.get_cam_total()
141 | while cam_count < total:
142 | try:
143 | yield Camera(pvc.get_cam_name(cam_count))
144 | cam_count += 1
145 | except RuntimeError:
146 | raise RuntimeError('Failed to create a detected camera.')
147 |
148 | @classmethod
149 | def select_camera(cls, name):
150 | """Select camera by name and creates a new Camera object.
151 |
152 | Returns:
153 | A Camera object.
154 | """
155 |
156 | total = pvc.get_cam_total()
157 | for index in range(total):
158 | try:
159 | if name == pvc.get_cam_name(index):
160 | return Camera(name)
161 | except RuntimeError:
162 | raise RuntimeError('Failed to create a detected camera.')
163 |
164 | raise RuntimeError('Failed to create a detected camera. Invalid name')
165 |
166 | def open(self):
167 | """Opens the camera.
168 |
169 | Side Effect(s):
170 | - changes self.__handle upon successful call to pvc module.
171 | - changes self.__is_open to True
172 | - changes self.__roi to sensor's full frame
173 |
174 | Returns:
175 | None
176 | """
177 |
178 | try:
179 | self.__handle = pvc.open_camera(self.__name)
180 | self.__is_open = True
181 | except:
182 | raise RuntimeError('Failed to open camera.')
183 |
184 | # If the camera is frame transfer capable, then set its p-mode to
185 | # frame transfer, otherwise set it to normal mode.
186 | try:
187 | self.get_param(const.PARAM_FRAME_CAPABLE, const.ATTR_CURRENT)
188 | self.set_param(const.PARAM_PMODE, const.PMODE_FT)
189 | except AttributeError:
190 | self.set_param(const.PARAM_PMODE, const.PMODE_NORMAL)
191 |
192 | # Set ROI to full frame with no binning
193 | self.__defaultRoi = Camera.RegionOfInterest(0, self.sensor_size[0] - 1, 1, 0, self.sensor_size[1] - 1, 1)
194 | self.reset_rois()
195 |
196 | # Setup correct mode
197 | self.__exp_mode = self.get_param(const.PARAM_EXPOSURE_MODE)
198 | if self.check_param(const.PARAM_EXPOSE_OUT_MODE):
199 | self.__exp_out_mode = self.get_param(const.PARAM_EXPOSE_OUT_MODE)
200 | else:
201 | self.__exp_out_mode = 0
202 |
203 | self.__mode = self.__exp_mode | self.__exp_out_mode
204 |
205 | # Populate enumerated values
206 | self.__centroids_modes = Camera.ReversibleEnumDict('centroids_modes', self, const.PARAM_CENTROIDS_MODE)
207 | self.__clear_modes = Camera.ReversibleEnumDict('clear_modes', self, const.PARAM_CLEAR_MODE)
208 | self.__exp_modes = Camera.ReversibleEnumDict('exp_modes', self, const.PARAM_EXPOSURE_MODE)
209 | self.__exp_out_modes = Camera.ReversibleEnumDict('exp_out_modes', self, const.PARAM_EXPOSE_OUT_MODE)
210 | self.__exp_resolutions = Camera.ReversibleEnumDict('exp_resolutions', self, const.PARAM_EXP_RES)
211 | self.__prog_scan_modes = Camera.ReversibleEnumDict('prog_scan_modes', self, const.PARAM_SCAN_MODE)
212 | self.__prog_scan_dirs = Camera.ReversibleEnumDict('prog_scan_dirs', self, const.PARAM_SCAN_DIRECTION)
213 |
214 | # Learn ports, speeds and gains
215 | self.__port_speed_gain_table = {}
216 | supports_speed_name = self.check_param(const.PARAM_SPDTAB_NAME)
217 | supports_gain_name = self.check_param(const.PARAM_GAIN_NAME)
218 | for port_name, port_value in self.read_enum(const.PARAM_READOUT_PORT).items():
219 | self.readout_port = port_value
220 |
221 | self.__port_speed_gain_table[port_name] = {
222 | 'port_value': port_value,
223 | }
224 |
225 | num_speeds = self.get_param(const.PARAM_SPDTAB_INDEX, const.ATTR_COUNT)
226 | for speed_index in range(num_speeds):
227 | self.speed_table_index = speed_index
228 |
229 | speed_name = 'Speed_' + str(speed_index)
230 | if supports_speed_name:
231 | try:
232 | speed_name = self.get_param(const.PARAM_SPDTAB_NAME, const.ATTR_CURRENT)
233 | except AttributeError:
234 | pass
235 |
236 | gain_min = self.get_param(const.PARAM_GAIN_INDEX, const.ATTR_MIN)
237 | gain_max = self.get_param(const.PARAM_GAIN_INDEX, const.ATTR_MAX)
238 | gain_increment = self.get_param(const.PARAM_GAIN_INDEX, const.ATTR_INCREMENT)
239 |
240 | num_gains = int((gain_max - gain_min) / gain_increment + 1)
241 | gains = [(gain_min + i * gain_increment) for i in range(num_gains)]
242 |
243 | self.__port_speed_gain_table[port_name][speed_name] = {
244 | 'speed_index': speed_index,
245 | 'pixel_time': self.pix_time,
246 | 'bit_depth': self.bit_depth,
247 | 'gain_range': gains,
248 | }
249 |
250 | for gain_index in gains:
251 | self.gain = gain_index
252 |
253 | gain_name = 'Gain_' + str(gain_index)
254 | if supports_gain_name:
255 | try:
256 | gain_name = self.get_param(const.PARAM_GAIN_NAME, const.ATTR_CURRENT)
257 | except AttributeError:
258 | pass
259 |
260 | self.__port_speed_gain_table[port_name][speed_name][gain_name] = {
261 | 'gain_index': gain_index,
262 | }
263 |
264 | # Reset speed table back to default
265 | self.readout_port = 0
266 | self.speed_table_index = 0
267 |
268 | self._set_dtype()
269 |
270 | # Learn post-processing features
271 | self.__post_processing_table = {}
272 | try:
273 | feature_count = self.get_param(const.PARAM_PP_INDEX, const.ATTR_COUNT)
274 | for feature_index in range(feature_count):
275 | self.set_param(const.PARAM_PP_INDEX, feature_index)
276 |
277 | feature_id = self.get_param(const.PARAM_PP_FEAT_ID)
278 | feature_name = self.get_param(const.PARAM_PP_FEAT_NAME)
279 | self.__post_processing_table[feature_name] = {}
280 |
281 | param_count = self.get_param(const.PARAM_PP_PARAM_INDEX, const.ATTR_COUNT)
282 | for param_index in range(param_count):
283 | self.set_param(const.PARAM_PP_PARAM_INDEX, param_index)
284 |
285 | param_id = self.get_param(const.PARAM_PP_PARAM_ID)
286 | param_name = self.get_param(const.PARAM_PP_PARAM_NAME)
287 | param_min = self.get_param(const.PARAM_PP_PARAM, const.ATTR_MIN)
288 | param_max = self.get_param(const.PARAM_PP_PARAM, const.ATTR_MAX)
289 | self.__post_processing_table[feature_name][param_name] = {
290 | 'feature_index': feature_index,
291 | 'feature_id': feature_id,
292 | 'param_index': param_index,
293 | 'param_id': param_id,
294 | 'param_min': param_min,
295 | 'param_max': param_max,
296 | }
297 |
298 | except AttributeError:
299 | pass
300 |
301 | def close(self):
302 | """Closes the camera.
303 |
304 | Side Effect(s):
305 | - changes self.__handle upon successful call to pvc module.
306 | - changes self.__is_open to False
307 |
308 | Returns:
309 | None
310 | """
311 |
312 | try:
313 | pvc.close_camera(self.__handle)
314 | self.__handle = -1
315 | self.__is_open = False
316 | except:
317 | raise RuntimeError('Failed to close camera.')
318 |
319 | def check_frame_status(self):
320 | """Gets the frame transfer status. Will raise an exception if called prior to initiating acquisition
321 |
322 | Parameter(s):
323 | None
324 |
325 | Returns:
326 | String representation of PL_IMAGE_STATUSES enum from pvcam.h
327 | 'READOUT_NOT_ACTIVE' - The system is @b idle, no data is expected. If any arrives, it will be discarded.
328 | 'EXPOSURE_IN_PROGRESS' - The data collection routines are @b active. They are waiting for data to arrive, but none has arrived yet.
329 | 'READOUT_IN_PROGRESS' - The data collection routines are @b active. The data has started to arrive.
330 | 'READOUT_COMPLETE' - All frames available in sequence mode.
331 | 'FRAME_AVAILABLE' - At least one frame is available in live mode
332 | 'READOUT_FAILED' - Something went wrong.
333 | """
334 |
335 | status = pvc.check_frame_status(self.__handle)
336 |
337 | # TODO: pvcam currently returns FRAME_AVAILABLE/READOUT_COMPLETE after a sequence is finished.
338 | # Until this behavior is resolved, force status to READOUT_NOT_ACTIVE while not acquiring
339 | status = 'READOUT_NOT_ACTIVE' if (self.__acquisition_mode is None) else status
340 | return status
341 |
342 | def get_param(self, param_id, param_attr=const.ATTR_CURRENT):
343 | """Gets the current value of a specified parameter.
344 |
345 | Parameter(s):
346 | param_id (int): The parameter to get. Refer to constants.py for
347 | defined constants for each parameter.
348 | param_attr (int): The desired attribute of the parameter to
349 | identify. Refer to constants.py for defined
350 | constants for each attribute.
351 |
352 | Returns:
353 | Value of specified parameter.
354 | """
355 |
356 | return pvc.get_param(self.__handle, param_id, param_attr)
357 |
358 | def set_param(self, param_id, value):
359 | """Sets a specified setting of a camera to a specified value.
360 |
361 | Note that pvc will raise a RuntimeError if the camera setting can not be
362 | applied. Pvc will also raise a ValueError if the supplied arguments are
363 | invalid for the specified parameter.
364 |
365 | Side Effect(s):
366 | - changes camera's internal setting.
367 |
368 | Parameters:
369 | param_id (int): An int that corresponds to a camera setting. Refer to
370 | constants.py for valid parameter values.
371 | value (Varies): The value to set the camera setting to.
372 | """
373 |
374 | pvc.set_param(self.__handle, param_id, value)
375 |
376 | def check_param(self, param_id):
377 | """Checks if a specified setting of a camera is available to read/ modify.
378 |
379 | Side Effect(s):
380 | - None
381 |
382 | Parameters:
383 | param_id (int): An int that corresponds to a camera setting. Refer to
384 | constants.py for valid parameter values.
385 |
386 | Returns:
387 | Boolean true if available, false if not
388 | """
389 |
390 | return pvc.check_param(self.__handle, param_id)
391 |
392 | def read_enum(self, param_id):
393 | """ Returns all settings names paired with their values of a parameter.
394 |
395 | Parameter:
396 | param_id (int): The parameter ID.
397 |
398 | Returns:
399 | A dictionary containing strings mapped to values.
400 | """
401 |
402 | return pvc.read_enum(self.__handle, param_id)
403 |
404 | def reset_pp(self):
405 | """Resets the post-processing settings to default.
406 |
407 | Returns:
408 | None
409 | """
410 |
411 | try:
412 | pvc.reset_pp(self.__handle)
413 | except:
414 | raise RuntimeError('Failed to reset post-processing settings.')
415 |
416 | def _update_mode(self):
417 | """Updates the mode of the camera, which is the bit-wise OR between
418 | exposure mode and expose out mode. It then sets up a small sequence
419 | so the exposure mode and expose out mode getters will read properly.
420 | This function should only be called internally whenever either exposure
421 | setting is changed.
422 |
423 | Side Effect(s):
424 | - Changes self.__mode
425 | - Sets up a small sequence so the camera will read out the exposure
426 | modes correctly with get_param.
427 |
428 | Returns:
429 | None
430 | """
431 |
432 | self.__mode = self.__exp_mode | self.__exp_out_mode
433 | pvc.set_exp_modes(self.__handle, self.__mode)
434 |
435 | def reset_rois(self):
436 | """Resets the ROI list to default, which is full frame.
437 |
438 | Returns:
439 | None
440 | """
441 |
442 | self.__rois = [self.__defaultRoi]
443 |
444 | def set_roi(self, s1, p1, w, h):
445 | """Configures a ROI for the camera. The default ROI is the full frame. If the default is
446 | set or only a single ROI is supported, this function will over-write that ROI. Otherwise,
447 | this function will attempt to append this ROI to the list.
448 |
449 | Returns:
450 | None
451 | """
452 |
453 | if w < 1 or h < 1:
454 | raise ValueError('Width and height must be >= 1')
455 |
456 | s2 = s1 + w - 1
457 | p2 = p1 + h - 1
458 |
459 | # Check if new ROI is in bounds
460 | in_bounds = 0 <= s1 <= self.sensor_size[0] \
461 | and 0 <= s2 <= self.sensor_size[0] \
462 | and 0 <= p1 <= self.sensor_size[1] \
463 | and 0 <= p2 <= self.sensor_size[1]
464 |
465 | if not in_bounds:
466 | raise ValueError('Could not add ROI. ROI extends beyond limits of frame')
467 |
468 | # Get the total supported ROIs
469 | roi_count = self.get_param(const.PARAM_ROI_COUNT, const.ATTR_MAX)
470 |
471 | # Check if current ROI list only contains the default
472 | using_default_roi = False
473 | if len(self.__rois) == 1:
474 | using_default_roi = self.__rois[0] == self.__defaultRoi
475 |
476 | new_roi = Camera.RegionOfInterest(s1, s2, self.bin_x, p1, p2, self.bin_y)
477 | if roi_count == 1 or using_default_roi:
478 | self.__rois = [new_roi]
479 |
480 | elif len(self.__rois) < roi_count:
481 |
482 | # Check that new ROI doesn't overlap with existing ROIs
483 | for roi in self.__rois:
484 | if roi.checkOverlap(new_roi):
485 | raise ValueError('Could not add ROI. New ROI overlaps existing ROI')
486 |
487 | self.__rois.append(new_roi)
488 | else:
489 | raise ValueError('Could not add ROI. Camera only supports {} rois'.format(roi_count))
490 |
491 | def poll_frame(self, timeout_ms=WAIT_FOREVER, oldestFrame=True, copyData=True):
492 | """Calls the pvc.get_frame function with the current camera settings.
493 |
494 | Parameter:
495 | oldestFrame (bool): Selects whether to return the oldest or newest frame. Only the oldest frame will be popped off the underlying queue of frames. (optional).
496 | copyData (bool): Selects whether to return a copy of the numpy frame which points to a new buffer, or the original numpy frame which points to the
497 | buffer used directly by PVCAM. Disabling this copy is not recommended for most situations. Refer to PyVCAM Wrapper.md for more details. (optional).
498 |
499 | None
500 | Returns:
501 | A dictionary with the frame containing available metadata and 2D np.array pixel data, frames per second and frame count.
502 | """
503 |
504 | frame, fps, frame_count = pvc.get_frame(self.__handle, self.__rois, self.__dtype.num, timeout_ms, oldestFrame)
505 |
506 | num_rois = len(frame['pixel_data'])
507 | if copyData:
508 | frame_tmp = {'pixel_data': [None] * num_rois}
509 | for roi_index in range(num_rois):
510 | frame_tmp['pixel_data'][roi_index] = np.copy(frame['pixel_data'][roi_index])
511 |
512 | if 'meta_data' in frame.keys():
513 | frame_tmp['meta_data'] = deepcopy(frame['meta_data'])
514 |
515 | frame = frame_tmp
516 |
517 | # If using a single ROI, remove list container
518 | if num_rois == 1:
519 | frame['pixel_data'] = frame['pixel_data'][0]
520 |
521 | return frame, fps, frame_count
522 |
523 | def get_frame(self, exp_time=None, timeout_ms=WAIT_FOREVER):
524 | """Calls the pvc.get_frame function with the current camera settings.
525 |
526 | Parameter:
527 | exp_time (int): The exposure time (optional).
528 | Returns:
529 | A 2D np.array containing the pixel data from the captured frame.
530 | """
531 |
532 | self.start_seq(exp_time=exp_time, num_frames=1)
533 | frame, fps, frame_count = self.poll_frame(timeout_ms=timeout_ms)
534 | self.finish()
535 |
536 | return frame['pixel_data']
537 |
538 | def get_sequence(self, num_frames, exp_time=None, timeout_ms=WAIT_FOREVER, interval=None):
539 | """Calls the pvc.get_frame function with the current camera settings in
540 | rapid-succession for the specified number of frames
541 |
542 | Parameter:
543 | num_frames (int): Number of frames to capture in the sequence
544 | exp_time (int): The exposure time (optional)
545 | interval (int): The time in milliseconds to wait between captures
546 | Returns:
547 | A 3D np.array containing the pixel data from the captured frames.
548 | """
549 |
550 | if len(self.__rois) > 1:
551 | raise ValueError('get_sequence does not support multi-roi captures')
552 |
553 | shape = self.__rois[0].shape
554 | stack = np.empty((num_frames, shape[1], shape[0]), dtype=self.__dtype)
555 |
556 | for i in range(num_frames):
557 | stack[i] = self.get_frame(exp_time=exp_time, timeout_ms=timeout_ms)
558 |
559 | if isinstance(interval, int):
560 | time.sleep(interval / 1000)
561 |
562 | return stack
563 |
564 | def get_vtm_sequence(self, time_list, exp_res, num_frames, timeout_ms=WAIT_FOREVER, interval=None):
565 | """Calls the pvc.get_frame function within a loop, setting vtm expTime
566 | between each capture.
567 |
568 | Parameter:
569 | time_list (list of ints): List of vtm timings
570 | exp_res (int): vtm exposure time resolution (0:milli, 1:micro, 2:seconds, use constants.EXP_RES_ONE_*SEC)
571 | num_frames (int): Number of frames to capture in the sequence
572 | interval (int): The time in milliseconds to wait between captures
573 | Returns:
574 | A 3D np.array containing the pixel data from the captured sequence.
575 | """
576 |
577 | old_res = self.exp_res
578 | self.exp_res = exp_res
579 |
580 | if len(self.__rois) > 1:
581 | raise ValueError('get_vtm_sequence does not support multi-roi captures')
582 |
583 | shape = self.__rois[0].shape
584 | stack = np.empty((num_frames, shape[1], shape[0]), dtype=self.__dtype)
585 |
586 | for i in range(num_frames):
587 | exp_time = time_list[i]
588 | try:
589 | self.vtm_exp_time = exp_time
590 | stack[i] = self.get_frame(exp_time=self.vtm_exp_time, timeout_ms=timeout_ms)
591 | except Exception:
592 | raise ValueError('Could not collect vtm frame')
593 |
594 | if isinstance(interval, int):
595 | time.sleep(interval / 1000)
596 |
597 | self.exp_res = old_res
598 | return stack
599 |
600 | def start_live(self, exp_time=None, buffer_frame_count=16, stream_to_disk_path=None):
601 | """Calls the pvc.start_live function to set up a circular buffer acquisition.
602 |
603 | Parameter:
604 | exp_time (int): The exposure time (optional).
605 | Returns:
606 | None
607 | """
608 |
609 | if not isinstance(exp_time, int):
610 | exp_time = self.exp_time
611 |
612 | if isinstance(stream_to_disk_path, str):
613 | stream_to_disk_path_abs = os.path.abspath(stream_to_disk_path)
614 | directory, filename = os.path.split(stream_to_disk_path_abs)
615 | if os.path.exists(directory):
616 | try:
617 | os.remove(filename)
618 | except OSError:
619 | pass
620 | else:
621 | raise ValueError('Invalid directory for stream to disk: ' + directory)
622 |
623 | self.__acquisition_mode = 'Live'
624 | self.__exposure_bytes = pvc.start_live(self.__handle, self.__rois, exp_time, self.__mode, buffer_frame_count, stream_to_disk_path)
625 |
626 | def start_seq(self, exp_time=None, num_frames=1):
627 | """Calls the pvc.start_seq function to set up a non-circular buffer acquisition.
628 |
629 | Parameter:
630 | exp_time (int): The exposure time (optional).
631 | Returns:
632 | None
633 | """
634 |
635 | if not isinstance(exp_time, int):
636 | exp_time = self.exp_time
637 |
638 | self.__acquisition_mode = 'Sequence'
639 | self.__exposure_bytes = pvc.start_seq(self.__handle, self.__rois, exp_time, self.__mode, num_frames)
640 |
641 | def finish(self):
642 | """Ends a previously started live or sequence acquisition.
643 |
644 | Parameter:
645 | None
646 | Returns:
647 | None
648 | """
649 |
650 | if self.__acquisition_mode == 'Live':
651 | pvc.stop_live(self.__handle)
652 | elif self.__acquisition_mode == 'Sequence':
653 | pvc.finish_seq(self.__handle)
654 |
655 | self.__acquisition_mode = None
656 | return
657 |
658 | def abort(self):
659 | """Calls the pvc.abort function that aborts acquisition.
660 |
661 | Parameter:
662 | None
663 | Returns:
664 | None
665 | """
666 |
667 | return pvc.abort(self.__handle)
668 |
669 | def sw_trigger(self):
670 | """Performs an SW trigger. This trigger behaves analogously to a HW external trigger.
671 | Will throw an exception if trigger fails.
672 |
673 | Parameter:
674 | None
675 | Returns:
676 | None
677 | """
678 |
679 | pvc.sw_trigger(self.__handle)
680 |
681 | def set_post_processing_param(self, feature_name, param_name, value):
682 | """Sets the value of a post-processing parameter.
683 |
684 | Parameter:
685 | Feature name and parameter name as specified in post_processing_table
686 | Returns:
687 | None
688 | """
689 |
690 | if feature_name in self.__post_processing_table.keys():
691 | if param_name in self.__post_processing_table[feature_name].keys():
692 | pp_param = self.__post_processing_table[feature_name][param_name]
693 |
694 | if pp_param['param_min'] <= value <= pp_param['param_max']:
695 | self.set_param(const.PARAM_PP_INDEX, pp_param['feature_index'])
696 | self.set_param(const.PARAM_PP_PARAM_INDEX, pp_param['param_index'])
697 | self.set_param(const.PARAM_PP_PARAM, value)
698 |
699 | self._set_dtype()
700 | else:
701 | raise AttributeError('Could not set post processing param. Value ' + str(value) + ' out of range ('
702 | + str(pp_param['param_min']) + ', ' + str(pp_param['param_max']) + ')')
703 | else:
704 | raise AttributeError('Could not set post processing param. param_name not found')
705 | else:
706 | raise AttributeError('Could not set post processing param. feature_name not found')
707 |
708 | def get_post_processing_param(self, feature_name, param_name):
709 | """Gets the current value of a post-processing parameter.
710 |
711 | Parameter:
712 | Feature name and parameter name as specified in post_processing_table
713 | Returns:
714 | Value of specified post-processing parameter
715 | """
716 |
717 | if feature_name in self.__post_processing_table.keys():
718 | if param_name in self.__post_processing_table[feature_name].keys():
719 | pp_param = self.__post_processing_table[feature_name][param_name]
720 | self.set_param(const.PARAM_PP_INDEX, pp_param['feature_index'])
721 | self.set_param(const.PARAM_PP_PARAM_INDEX, pp_param['param_index'])
722 | return self.get_param(const.PARAM_PP_PARAM)
723 | else:
724 | raise AttributeError('Could not set post processing param. param_name not found')
725 | else:
726 | raise AttributeError('Could not set post processing param. feature_name not found')
727 |
728 | def _set_dtype(self):
729 | if self.check_param(const.PARAM_BIT_DEPTH_HOST):
730 | bit_depth = self.get_param(const.PARAM_BIT_DEPTH_HOST)
731 | else:
732 | bit_depth = self.get_param(const.PARAM_BIT_DEPTH)
733 | bytes_per_pixel = int(np.ceil(bit_depth / 8))
734 | dtype_kind = 'u{}'.format(bytes_per_pixel)
735 | self.__dtype = np.dtype(dtype_kind)
736 |
737 | ### Getters/Setters below ###
738 |
739 | @property
740 | def handle(self):
741 | return self.__handle
742 |
743 | @property
744 | def is_open(self):
745 | return self.__is_open
746 |
747 | @property
748 | def name(self):
749 | return self.__name
750 |
751 | @property
752 | def post_processing_table(self):
753 | return self.__post_processing_table
754 |
755 | @property
756 | def port_speed_gain_table(self):
757 | return self.__port_speed_gain_table
758 |
759 | @property
760 | def centroids_modes(self):
761 | return self.__centroids_modes
762 |
763 | @property
764 | def clear_modes(self):
765 | return self.__clear_modes
766 |
767 | @property
768 | def exp_modes(self):
769 | return self.__exp_modes
770 |
771 | @property
772 | def exp_out_modes(self):
773 | return self.__exp_out_modes
774 |
775 | @property
776 | def exp_resolutions(self):
777 | return self.__exp_resolutions
778 |
779 | @property
780 | def prog_scan_modes(self):
781 | return self.__prog_scan_modes
782 |
783 | @property
784 | def prog_scan_dirs(self):
785 | return self.__prog_scan_dirs
786 |
787 | @property
788 | def driver_version(self):
789 | dd_ver = self.get_param(const.PARAM_DD_VERSION)
790 | # The device driver version is returned as a highly formatted 16-bit
791 | # integer where the first 8 bits are the major version, bits 9-12 are
792 | # the minor version, and bits 13-16 are the build number. Uses of masks
793 | # and bit shifts are required to extract the full version number.
794 | return '{}.{}.{}'.format(dd_ver & 0xff00 >> 8,
795 | dd_ver & 0x00f0 >> 4,
796 | dd_ver & 0x000f)
797 |
798 | @property
799 | def cam_fw(self):
800 | return pvc.get_cam_fw_version(self.__handle)
801 |
802 | @property
803 | def chip_name(self):
804 | return self.get_param(const.PARAM_CHIP_NAME)
805 |
806 | @property
807 | def sensor_size(self):
808 | return (self.get_param(const.PARAM_SER_SIZE),
809 | self.get_param(const.PARAM_PAR_SIZE))
810 |
811 | @property
812 | def serial_no(self):
813 | # HACK: cytocam fix for messed up serial numbers
814 | try:
815 | serial_no = self.get_param(const.PARAM_HEAD_SER_NUM_ALPHA)
816 | return serial_no
817 | except AttributeError:
818 | return 'N/A'
819 |
820 | @property
821 | def bit_depth(self):
822 | return self.get_param(const.PARAM_BIT_DEPTH)
823 |
824 | @property
825 | def pix_time(self):
826 | return self.get_param(const.PARAM_PIX_TIME)
827 |
828 | @property
829 | def readout_port(self):
830 | # Camera specific setting: will raise AttributeError if called with a
831 | # camera that does not support this setting.
832 | return self.get_param(const.PARAM_READOUT_PORT)
833 |
834 | @readout_port.setter
835 | def readout_port(self, value):
836 | # Camera specific setting: will raise AttributeError if called with a
837 | # camera that does not support this setting.
838 | num_ports = self.get_param(const.PARAM_READOUT_PORT, const.ATTR_COUNT)
839 |
840 | if value >= num_ports:
841 | raise ValueError('{} only supports '
842 | '{} readout ports.'.format(self, num_ports))
843 | self.set_param(const.PARAM_READOUT_PORT, value)
844 |
845 | self._set_dtype()
846 |
847 | @property
848 | def speed_table_index(self):
849 | return self.get_param(const.PARAM_SPDTAB_INDEX)
850 |
851 | @speed_table_index.setter
852 | def speed_table_index(self, value):
853 | num_entries = self.get_param(const.PARAM_SPDTAB_INDEX,
854 | const.ATTR_COUNT)
855 | if value >= num_entries:
856 | raise ValueError('{} only supports '
857 | '{} speed entries'.format(self, num_entries))
858 | self.set_param(const.PARAM_SPDTAB_INDEX, value)
859 |
860 | self._set_dtype()
861 |
862 | @property
863 | def trigger_table(self):
864 | # Returns a dictionary containing information about the last capture.
865 | # Note some features are camera specific.
866 |
867 | # Default resolution is const.EXP_RES_ONE_MILLISEC
868 | # even if the parameter isn't available
869 | exp_unit = ' ms'
870 | try:
871 | exp_res = self.exp_res
872 | if exp_res == const.EXP_RES_ONE_SEC:
873 | exp_unit = ' s'
874 | elif exp_res == const.EXP_RES_ONE_MICROSEC:
875 | exp_unit = ' μs'
876 | except AttributeError:
877 | pass
878 | try:
879 | exp = str(self.last_exp_time) + exp_unit
880 | except AttributeError:
881 | exp = 'N/A'
882 |
883 | try:
884 | read = str(self.readout_time) + ' μs'
885 | except AttributeError:
886 | read = 'N/A'
887 |
888 | # If the camera has clear time, then it has pre- and post-trigger delays
889 | try:
890 | clear = str(self.clear_time) + ' ns'
891 | pre = str(self.pre_trigger_delay) + ' ns'
892 | post = str(self.post_trigger_delay) + ' ns'
893 | except AttributeError:
894 | clear = 'N/A'
895 | pre = 'N/A'
896 | post = 'N/A'
897 |
898 | return {'Exposure Time': exp,
899 | 'Readout Time': read,
900 | 'Clear Time': clear,
901 | 'Pre-trigger Delay': pre,
902 | 'Post-trigger Delay': post}
903 |
904 | @property
905 | def adc_offset(self):
906 | # Camera specific setting: will raise AttributeError if called with a
907 | # camera that does not support this setting.
908 | return self.get_param(const.PARAM_ADC_OFFSET)
909 |
910 | @property
911 | def gain(self):
912 | return self.get_param(const.PARAM_GAIN_INDEX)
913 |
914 | @gain.setter
915 | def gain(self, value):
916 | min_gain = self.get_param(const.PARAM_GAIN_INDEX, const.ATTR_MIN)
917 | max_gain = self.get_param(const.PARAM_GAIN_INDEX, const.ATTR_MAX)
918 | if not (min_gain <= value <= max_gain):
919 | raise ValueError("Invalid value: {} - {} only supports gain "
920 | "indices from {} - {}.".format(value, self, min_gain, max_gain))
921 | self.set_param(const.PARAM_GAIN_INDEX, value)
922 |
923 | self._set_dtype()
924 |
925 | @property
926 | def binning(self):
927 | return self.bin_x, self.bin_y
928 |
929 | @binning.setter
930 | def binning(self, value):
931 | if isinstance(value, tuple):
932 | self.bin_x = value[0]
933 | self.bin_y = value[1]
934 | else:
935 | self.binning = (value, value)
936 |
937 | @property
938 | def bin_x(self):
939 | return self.__rois[0].sbin
940 |
941 | @bin_x.setter
942 | def bin_x(self, value):
943 | # Will raise ValueError if incompatible binning is set
944 | if (
945 | not self.check_param(const.PARAM_BINNING_SER) or
946 | value in self.read_enum(const.PARAM_BINNING_SER).values()
947 | ):
948 | for roi in self.__rois:
949 | roi.sbin = value
950 | return
951 |
952 | raise ValueError('{} only supports {} binnings'.format(self,
953 | self.read_enum(const.PARAM_BINNING_SER).items()))
954 |
955 | @property
956 | def bin_y(self):
957 | return self.__rois[0].pbin
958 |
959 | @bin_y.setter
960 | def bin_y(self, value):
961 | # Will raise ValueError if incompatible binning is set
962 | if (
963 | not self.check_param(const.PARAM_BINNING_PAR) or
964 | value in self.read_enum(const.PARAM_BINNING_PAR).values()
965 | ):
966 | for roi in self.__rois:
967 | roi.pbin = value
968 | return
969 |
970 | raise ValueError('{} only supports {} binnings'.format(self,
971 | self.read_enum(const.PARAM_BINNING_PAR).items()))
972 |
973 | def shape(self, roi_index=0):
974 | return self.__rois[roi_index].shape
975 |
976 | @property
977 | def last_exp_time(self):
978 | return self.get_param(const.PARAM_EXPOSURE_TIME)
979 |
980 | @property
981 | def exp_res(self):
982 | return self.get_param(const.PARAM_EXP_RES)
983 |
984 | @exp_res.setter
985 | def exp_res(self, key_or_value):
986 | # Will raise ValueError if provided with an unrecognized key.
987 | value = self.__exp_resolutions[key_or_value] if isinstance(key_or_value, str) else key_or_value
988 |
989 | # Verify value is in range by attempting to look up the key
990 | # noinspection PyUnusedLocal
991 | key = self.__exp_resolutions[value]
992 |
993 | self.set_param(const.PARAM_EXP_RES, value)
994 |
995 | @property
996 | def exp_res_index(self):
997 | return self.get_param(const.PARAM_EXP_RES_INDEX)
998 |
999 | @property
1000 | def exp_time(self):
1001 | # TODO: Testing
1002 | return self.__exp_time
1003 |
1004 | @exp_time.setter
1005 | def exp_time(self, value):
1006 | min_exp_time = self.get_param(const.PARAM_EXPOSURE_TIME, const.ATTR_MIN)
1007 | max_exp_time = self.get_param(const.PARAM_EXPOSURE_TIME, const.ATTR_MAX)
1008 |
1009 | if not value in range(min_exp_time, max_exp_time + 1):
1010 | raise ValueError("Invalid value: {} - {} only supports exposure "
1011 | "times between {} and {}".format(value, self,
1012 | min_exp_time,
1013 | max_exp_time))
1014 | self.__exp_time = value
1015 |
1016 | @property
1017 | def exp_mode(self):
1018 | return self.get_param(const.PARAM_EXPOSURE_MODE)
1019 |
1020 | @exp_mode.setter
1021 | def exp_mode(self, key_or_value):
1022 | # Will raise ValueError if provided with an unrecognized key.
1023 | self.__exp_mode = self.__exp_modes[key_or_value] if isinstance(key_or_value, str) else key_or_value
1024 |
1025 | # Verify value is in range by attempting to look up the key
1026 | # noinspection PyUnusedLocal
1027 | key = self.__exp_modes[self.__exp_mode]
1028 |
1029 | self._update_mode()
1030 |
1031 | @property
1032 | def exp_out_mode(self):
1033 | return self.get_param(const.PARAM_EXPOSE_OUT_MODE)
1034 |
1035 | @exp_out_mode.setter
1036 | def exp_out_mode(self, key_or_value):
1037 | # Will raise ValueError if provided with an unrecognized key.
1038 | self.__exp_out_mode = self.__exp_out_modes[key_or_value] if isinstance(key_or_value, str) else key_or_value
1039 |
1040 | # Verify value is in range by attempting to look up the key
1041 | # noinspection PyUnusedLocal
1042 | key = self.__exp_out_modes[self.__exp_out_mode]
1043 |
1044 | self._update_mode()
1045 |
1046 | @property
1047 | def vtm_exp_time(self):
1048 | return self.get_param(const.PARAM_EXP_TIME)
1049 |
1050 | @vtm_exp_time.setter
1051 | def vtm_exp_time(self, value):
1052 | min_exp_time = self.get_param(const.PARAM_EXPOSURE_TIME, const.ATTR_MIN)
1053 | max_exp_time = self.get_param(const.PARAM_EXPOSURE_TIME, const.ATTR_MAX)
1054 |
1055 | if not value in range(min_exp_time, max_exp_time + 1):
1056 | raise ValueError("Invalid value: {} - {} only supports exposure "
1057 | "times between {} and {}".format(value, self,
1058 | min_exp_time,
1059 | max_exp_time))
1060 | self.set_param(const.PARAM_EXP_TIME, value)
1061 |
1062 | @property
1063 | def clear_mode(self):
1064 | # Camera specific setting: will raise AttributeError if called with a
1065 | # camera that does not support this setting.
1066 | return self.get_param(const.PARAM_CLEAR_MODE)
1067 |
1068 | @clear_mode.setter
1069 | def clear_mode(self, key_or_value):
1070 | # Camera specific setting: will raise AttributeError if called with a
1071 | # camera that does not support this setting. Will raise ValueError if provided with an unrecognized key.
1072 | value = self.__clear_modes[key_or_value] if isinstance(key_or_value, str) else key_or_value
1073 |
1074 | # Verify value is in range by attempting to look up the key
1075 | # noinspection PyUnusedLocal
1076 | key = self.__clear_modes[value]
1077 |
1078 | self.set_param(const.PARAM_CLEAR_MODE, value)
1079 |
1080 | @property
1081 | def temp(self):
1082 | # Camera specific setting: will raise AttributeError if called with a
1083 | # camera that does not support this setting.
1084 | return self.get_param(const.PARAM_TEMP) / 100.0
1085 |
1086 | @property
1087 | def temp_setpoint(self):
1088 | # Camera specific setting: will raise AttributeError if called with a
1089 | # camera that does not support this setting.
1090 | return self.get_param(const.PARAM_TEMP_SETPOINT) / 100.0
1091 |
1092 | @temp_setpoint.setter
1093 | def temp_setpoint(self, value):
1094 | # Camera specific setting: will raise AttributeError if called with a
1095 | # camera that does not support this setting.
1096 | try:
1097 | self.set_param(const.PARAM_TEMP_SETPOINT, value * 100)
1098 | except RuntimeError:
1099 | min_temp = self.get_param(const.PARAM_TEMP_SETPOINT, const.ATTR_MIN)
1100 | max_temp = self.get_param(const.PARAM_TEMP_SETPOINT, const.ATTR_MAX)
1101 | raise ValueError("Invalid temp {} : Valid temps are in the range {} "
1102 | "- {}.".format(value, min_temp / 100, max_temp / 100))
1103 |
1104 | @property
1105 | def readout_time(self):
1106 | # Camera specific setting: will raise AttributeError if called with a
1107 | # camera that does not support this setting.
1108 | return self.get_param(const.PARAM_READOUT_TIME)
1109 |
1110 | @property
1111 | def clear_time(self):
1112 | # Camera specific setting: will raise AttributeError if called with a
1113 | # camera that does not support this setting.
1114 | return self.get_param(const.PARAM_CLEARING_TIME)
1115 |
1116 | @property
1117 | def pre_trigger_delay(self):
1118 | # Camera specific setting: will raise AttributeError if called with a
1119 | # camera that does not support this setting.
1120 | return self.get_param(const.PARAM_PRE_TRIGGER_DELAY)
1121 |
1122 | @property
1123 | def post_trigger_delay(self):
1124 | # Camera specific setting: will raise AttributeError if called with a
1125 | # camera that does not support this setting.
1126 | return self.get_param(const.PARAM_POST_TRIGGER_DELAY)
1127 |
1128 | @property
1129 | def centroids_mode(self):
1130 | # Camera specific setting: will raise AttributeError if called with a
1131 | # camera that does not support this setting.
1132 | return self.get_param(const.PARAM_CENTROIDS_MODE)
1133 |
1134 | @centroids_mode.setter
1135 | def centroids_mode(self, key_or_value):
1136 | # Camera specific setting: will raise AttributeError if called with a
1137 | # camera that does not support this setting. Will raise ValueError if
1138 | # provided with an unrecognized key
1139 | value = self.__centroids_modes[key_or_value] if isinstance(key_or_value, str) else key_or_value
1140 |
1141 | # Verify value is in range by attempting to look up the key
1142 | # noinspection PyUnusedLocal
1143 | key = self.__centroids_modes[value]
1144 |
1145 | self.set_param(const.PARAM_CENTROIDS_MODE, value)
1146 |
1147 | @property
1148 | def scan_line_time(self):
1149 | return self.get_param(const.PARAM_SCAN_LINE_TIME)
1150 |
1151 | @property
1152 | def prog_scan_mode(self):
1153 | # Camera specific setting: Will raise AttributeError if called with a
1154 | # camera that does not support this setting.
1155 | return self.get_param(const.PARAM_SCAN_MODE)
1156 |
1157 | @prog_scan_mode.setter
1158 | def prog_scan_mode(self, key_or_value):
1159 | # Camera specific setting: will raise AttributeError if called with a
1160 | # camera that does not support this setting. Will raise ValueError if
1161 | # provided with an unrecognized key
1162 | value = self.__prog_scan_modes[key_or_value] if isinstance(key_or_value, str) else key_or_value
1163 |
1164 | # Verify value is in range by attempting to look up the key
1165 | # noinspection PyUnusedLocal
1166 | key = self.__prog_scan_modes[value]
1167 |
1168 | self.set_param(const.PARAM_SCAN_MODE, value)
1169 |
1170 | @property
1171 | def prog_scan_dir(self):
1172 | # Camera specific setting: Will raise AttributeError if called with a
1173 | # camera that does not support this setting.
1174 | return self.get_param(const.PARAM_SCAN_DIRECTION)
1175 |
1176 | @prog_scan_dir.setter
1177 | def prog_scan_dir(self, key_or_value):
1178 | # Camera specific setting. Will raise AttributeError if called with a
1179 | # camera that does not support this setting. Will raise ValueError if
1180 | # provided with an unrecognized key or value
1181 | value = self.__prog_scan_dirs[key_or_value] if isinstance(key_or_value, str) else key_or_value
1182 |
1183 | # Verify value is in range by attempting to look up the key
1184 | # noinspection PyUnusedLocal
1185 | key = self.__prog_scan_dirs[value]
1186 |
1187 | self.set_param(const.PARAM_SCAN_DIRECTION, value)
1188 |
1189 | @property
1190 | def prog_scan_dir_reset(self):
1191 | return self.get_param(const.PARAM_SCAN_DIRECTION_RESET)
1192 |
1193 | @prog_scan_dir_reset.setter
1194 | def prog_scan_dir_reset(self, value):
1195 | self.set_param(const.PARAM_SCAN_DIRECTION_RESET, value)
1196 |
1197 | @property
1198 | def prog_scan_line_delay(self):
1199 | return self.get_param(const.PARAM_SCAN_LINE_DELAY)
1200 |
1201 | @prog_scan_line_delay.setter
1202 | def prog_scan_line_delay(self, value):
1203 | self.set_param(const.PARAM_SCAN_LINE_DELAY, value)
1204 |
1205 | @property
1206 | def prog_scan_width(self):
1207 | return self.get_param(const.PARAM_SCAN_WIDTH)
1208 |
1209 | @prog_scan_width.setter
1210 | def prog_scan_width(self, value):
1211 | self.set_param(const.PARAM_SCAN_WIDTH, value)
1212 |
1213 | @property
1214 | def meta_data_enabled(self):
1215 | return self.get_param(const.PARAM_METADATA_ENABLED)
1216 |
1217 | @meta_data_enabled.setter
1218 | def meta_data_enabled(self, value):
1219 | self.set_param(const.PARAM_METADATA_ENABLED, value)
1220 |
--------------------------------------------------------------------------------
/src/pyvcam/constants.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # File: constants.py
3 | # Author: Cameron Smith
4 | # Date of Last Edit: 2025-06-03
5 | #
6 | # Purpose: To maintain the naming conventions used with PVCAM.h for Python
7 | # scripts.
8 | #
9 | # Notes: This file is generated by constants_generator.py. See that script for
10 | # details about implementation. Please do not alter this file. Instead,
11 | # make any additional changes to the constants_generator.py if
12 | # additional data is needed.
13 | #
14 | # Bugs: [See constants_generator.py]
15 | ###############################################################################
16 | import ctypes
17 |
18 | ### DEFINES ###
19 |
20 | MAX_CAM = 16
21 | CAM_NAME_LEN = 32
22 | PARAM_NAME_LEN = 32
23 | ERROR_MSG_LEN = 255
24 | CCD_NAME_LEN = 17
25 | MAX_ALPHA_SER_NUM_LEN = 32
26 | MAX_PP_NAME_LEN = 32
27 | MAX_SYSTEM_NAME_LEN = 32
28 | MAX_VENDOR_NAME_LEN = 32
29 | MAX_PRODUCT_NAME_LEN = 32
30 | MAX_CAM_PART_NUM_LEN = 32
31 | MAX_GAIN_NAME_LEN = 32
32 | MAX_SPDTAB_NAME_LEN = 32
33 | MAX_CAM_SYSTEMS_INFO_LEN = 1024
34 | PP_MAX_PARAMETERS_PER_FEATURE = 10
35 | PL_MD_FRAME_SIGNATURE = 5328208
36 | PL_MD_EXT_TAGS_MAX_SUPPORTED = 255
37 | TYPE_INT16 = 1
38 | TYPE_INT32 = 2
39 | TYPE_FLT64 = 4
40 | TYPE_UNS8 = 5
41 | TYPE_UNS16 = 6
42 | TYPE_UNS32 = 7
43 | TYPE_UNS64 = 8
44 | TYPE_ENUM = 9
45 | TYPE_BOOLEAN = 11
46 | TYPE_INT8 = 12
47 | TYPE_CHAR_PTR = 13
48 | TYPE_VOID_PTR = 14
49 | TYPE_VOID_PTR_PTR = 15
50 | TYPE_INT64 = 16
51 | TYPE_SMART_STREAM_TYPE = 17
52 | TYPE_SMART_STREAM_TYPE_PTR = 18
53 | TYPE_FLT32 = 19
54 | TYPE_RGN_TYPE = 20
55 | TYPE_RGN_TYPE_PTR = 21
56 | TYPE_RGN_LIST_TYPE = 22
57 | TYPE_RGN_LIST_TYPE_PTR = 23
58 | CLASS0 = 0
59 | CLASS2 = 2
60 | CLASS3 = 3
61 | PARAM_DD_INFO_LENGTH = ((CLASS0<<16) + (TYPE_INT16<<24) + 1)
62 | PARAM_DD_VERSION = ((CLASS0<<16) + (TYPE_UNS16<<24) + 2)
63 | PARAM_DD_RETRIES = ((CLASS0<<16) + (TYPE_UNS16<<24) + 3)
64 | PARAM_DD_TIMEOUT = ((CLASS0<<16) + (TYPE_UNS16<<24) + 4)
65 | PARAM_DD_INFO = ((CLASS0<<16) + (TYPE_CHAR_PTR<<24) + 5)
66 | PARAM_CAM_INTERFACE_TYPE = ((CLASS0<<16) + (TYPE_ENUM<<24) + 10)
67 | PARAM_CAM_INTERFACE_MODE = ((CLASS0<<16) + (TYPE_ENUM<<24) + 11)
68 | PARAM_ADC_OFFSET = ((CLASS2<<16) + (TYPE_INT16<<24) + 195)
69 | PARAM_CHIP_NAME = ((CLASS2<<16) + (TYPE_CHAR_PTR<<24) + 129)
70 | PARAM_SYSTEM_NAME = ((CLASS2<<16) + (TYPE_CHAR_PTR<<24) + 130)
71 | PARAM_VENDOR_NAME = ((CLASS2<<16) + (TYPE_CHAR_PTR<<24) + 131)
72 | PARAM_PRODUCT_NAME = ((CLASS2<<16) + (TYPE_CHAR_PTR<<24) + 132)
73 | PARAM_CAMERA_PART_NUMBER = ((CLASS2<<16) + (TYPE_CHAR_PTR<<24) + 133)
74 | PARAM_COOLING_MODE = ((CLASS2<<16) + (TYPE_ENUM<<24) + 214)
75 | PARAM_PREAMP_DELAY = ((CLASS2<<16) + (TYPE_UNS16<<24) + 502)
76 | PARAM_COLOR_MODE = ((CLASS2<<16) + (TYPE_ENUM<<24) + 504)
77 | PARAM_MPP_CAPABLE = ((CLASS2<<16) + (TYPE_ENUM<<24) + 224)
78 | PARAM_PREAMP_OFF_CONTROL = ((CLASS2<<16) + (TYPE_UNS32<<24) + 507)
79 | PARAM_PREMASK = ((CLASS2<<16) + (TYPE_UNS16<<24) + 53)
80 | PARAM_PRESCAN = ((CLASS2<<16) + (TYPE_UNS16<<24) + 55)
81 | PARAM_POSTMASK = ((CLASS2<<16) + (TYPE_UNS16<<24) + 54)
82 | PARAM_POSTSCAN = ((CLASS2<<16) + (TYPE_UNS16<<24) + 56)
83 | PARAM_PIX_PAR_DIST = ((CLASS2<<16) + (TYPE_UNS16<<24) + 500)
84 | PARAM_PIX_PAR_SIZE = ((CLASS2<<16) + (TYPE_UNS16<<24) + 63)
85 | PARAM_PIX_SER_DIST = ((CLASS2<<16) + (TYPE_UNS16<<24) + 501)
86 | PARAM_PIX_SER_SIZE = ((CLASS2<<16) + (TYPE_UNS16<<24) + 62)
87 | PARAM_SUMMING_WELL = ((CLASS2<<16) + (TYPE_BOOLEAN<<24) + 505)
88 | PARAM_FWELL_CAPACITY = ((CLASS2<<16) + (TYPE_UNS32<<24) + 506)
89 | PARAM_PAR_SIZE = ((CLASS2<<16) + (TYPE_UNS16<<24) + 57)
90 | PARAM_SER_SIZE = ((CLASS2<<16) + (TYPE_UNS16<<24) + 58)
91 | PARAM_ACCUM_CAPABLE = ((CLASS2<<16) + (TYPE_BOOLEAN<<24) + 538)
92 | PARAM_FLASH_DWNLD_CAPABLE = ((CLASS2<<16) + (TYPE_BOOLEAN<<24) + 539)
93 | PARAM_READOUT_TIME = ((CLASS2<<16) + (TYPE_FLT64<<24) + 179)
94 | PARAM_CLEARING_TIME = ((CLASS2<<16) + (TYPE_INT64<<24) + 180)
95 | PARAM_POST_TRIGGER_DELAY = ((CLASS2<<16) + (TYPE_INT64<<24) + 181)
96 | PARAM_PRE_TRIGGER_DELAY = ((CLASS2<<16) + (TYPE_INT64<<24) + 182)
97 | PARAM_CLEAR_CYCLES = ((CLASS2<<16) + (TYPE_UNS16<<24) + 97)
98 | PARAM_CLEAR_MODE = ((CLASS2<<16) + (TYPE_ENUM<<24) + 523)
99 | PARAM_FRAME_CAPABLE = ((CLASS2<<16) + (TYPE_BOOLEAN<<24) + 509)
100 | PARAM_PMODE = ((CLASS2<<16) + (TYPE_ENUM <<24) + 524)
101 | PARAM_TEMP = ((CLASS2<<16) + (TYPE_INT16<<24) + 525)
102 | PARAM_TEMP_SETPOINT = ((CLASS2<<16) + (TYPE_INT16<<24) + 526)
103 | PARAM_CAM_FW_VERSION = ((CLASS2<<16) + (TYPE_UNS16<<24) + 532)
104 | PARAM_HEAD_SER_NUM_ALPHA = ((CLASS2<<16) + (TYPE_CHAR_PTR<<24) + 533)
105 | PARAM_PCI_FW_VERSION = ((CLASS2<<16) + (TYPE_UNS16<<24) + 534)
106 | PARAM_FAN_SPEED_SETPOINT = ((CLASS2<<16) + (TYPE_ENUM<<24) + 710)
107 | PARAM_CAM_SYSTEMS_INFO = ((CLASS2<<16) + (TYPE_CHAR_PTR<<24) + 536)
108 | PARAM_EXPOSURE_MODE = ((CLASS2<<16) + (TYPE_ENUM<<24) + 535)
109 | PARAM_EXPOSE_OUT_MODE = ((CLASS2<<16) + (TYPE_ENUM<<24) + 560)
110 | PARAM_BIT_DEPTH = ((CLASS2<<16) + (TYPE_INT16<<24) + 511)
111 | PARAM_BIT_DEPTH_HOST = ((CLASS2<<16) + (TYPE_INT16<<24) + 551)
112 | PARAM_IMAGE_FORMAT = ((CLASS2<<16) + (TYPE_ENUM<<24) + 248)
113 | PARAM_IMAGE_FORMAT_HOST = ((CLASS2<<16) + (TYPE_ENUM<<24) + 552)
114 | PARAM_IMAGE_COMPRESSION = ((CLASS2<<16) + (TYPE_ENUM<<24) + 249)
115 | PARAM_IMAGE_COMPRESSION_HOST = ((CLASS2<<16) + (TYPE_ENUM<<24) + 253)
116 | PARAM_SCAN_MODE = ((CLASS3<<16) + (TYPE_ENUM<<24) + 250)
117 | PARAM_SCAN_DIRECTION = ((CLASS3<<16) + (TYPE_ENUM<<24) + 251)
118 | PARAM_SCAN_DIRECTION_RESET = ((CLASS3<<16) + (TYPE_BOOLEAN<<24) + 252)
119 | PARAM_SCAN_LINE_DELAY = ((CLASS3<<16) + (TYPE_UNS16<<24) + 253)
120 | PARAM_SCAN_LINE_TIME = ((CLASS3<<16) + (TYPE_INT64<<24) + 254)
121 | PARAM_SCAN_WIDTH = ((CLASS3<<16) + (TYPE_UNS16<<24) + 255)
122 | PARAM_HOST_FRAME_ROTATE = ((CLASS2<<16) + (TYPE_ENUM<<24) + 256)
123 | PARAM_FRAME_ROTATE = (PARAM_HOST_FRAME_ROTATE)
124 | PARAM_HOST_FRAME_FLIP = ((CLASS2<<16) + (TYPE_ENUM<<24) + 257)
125 | PARAM_FRAME_FLIP = (PARAM_HOST_FRAME_FLIP)
126 | PARAM_HOST_FRAME_SUMMING_ENABLED = ((CLASS2<<16) + (TYPE_BOOLEAN<<24) + 258)
127 | PARAM_HOST_FRAME_SUMMING_COUNT = ((CLASS2<<16) + (TYPE_UNS32<<24) + 259)
128 | PARAM_HOST_FRAME_SUMMING_FORMAT = ((CLASS2<<16) + (TYPE_ENUM<<24) + 260)
129 | PARAM_HOST_FRAME_DECOMPRESSION_ENABLED = ((CLASS2<<16) + (TYPE_BOOLEAN<<24) + 261)
130 | PARAM_GAIN_INDEX = ((CLASS2<<16) + (TYPE_INT16<<24) + 512)
131 | PARAM_SPDTAB_INDEX = ((CLASS2<<16) + (TYPE_INT16<<24) + 513)
132 | PARAM_GAIN_NAME = ((CLASS2<<16) + (TYPE_CHAR_PTR<<24) + 514)
133 | PARAM_SPDTAB_NAME = ((CLASS2<<16) + (TYPE_CHAR_PTR<<24) + 515)
134 | PARAM_READOUT_PORT = ((CLASS2<<16) + (TYPE_ENUM<<24) + 247)
135 | PARAM_PIX_TIME = ((CLASS2<<16) + (TYPE_UNS16<<24) + 516)
136 | PARAM_SHTR_CLOSE_DELAY = ((CLASS2<<16) + (TYPE_UNS16<<24) + 519)
137 | PARAM_SHTR_OPEN_DELAY = ((CLASS2<<16) + (TYPE_UNS16<<24) + 520)
138 | PARAM_SHTR_OPEN_MODE = ((CLASS2<<16) + (TYPE_ENUM <<24) + 521)
139 | PARAM_SHTR_STATUS = ((CLASS2<<16) + (TYPE_ENUM <<24) + 522)
140 | PARAM_IO_ADDR = ((CLASS2<<16) + (TYPE_UNS16<<24) + 527)
141 | PARAM_IO_TYPE = ((CLASS2<<16) + (TYPE_ENUM<<24) + 528)
142 | PARAM_IO_DIRECTION = ((CLASS2<<16) + (TYPE_ENUM<<24) + 529)
143 | PARAM_IO_STATE = ((CLASS2<<16) + (TYPE_FLT64<<24) + 530)
144 | PARAM_IO_BITDEPTH = ((CLASS2<<16) + (TYPE_UNS16<<24) + 531)
145 | PARAM_GAIN_MULT_FACTOR = ((CLASS2<<16) + (TYPE_UNS16<<24) + 537)
146 | PARAM_GAIN_MULT_ENABLE = ((CLASS2<<16) + (TYPE_BOOLEAN<<24) + 541)
147 | PARAM_PP_FEAT_NAME = ((CLASS2<<16) + (TYPE_CHAR_PTR<<24) + 542)
148 | PARAM_PP_INDEX = ((CLASS2<<16) + (TYPE_INT16<<24) + 543)
149 | PARAM_ACTUAL_GAIN = ((CLASS2<<16) + (TYPE_UNS16<<24) + 544)
150 | PARAM_PP_PARAM_INDEX = ((CLASS2<<16) + (TYPE_INT16<<24) + 545)
151 | PARAM_PP_PARAM_NAME = ((CLASS2<<16) + (TYPE_CHAR_PTR<<24) + 546)
152 | PARAM_PP_PARAM = ((CLASS2<<16) + (TYPE_UNS32<<24) + 547)
153 | PARAM_READ_NOISE = ((CLASS2<<16) + (TYPE_UNS16<<24) + 548)
154 | PARAM_PP_FEAT_ID = ((CLASS2<<16) + (TYPE_UNS16<<24) + 549)
155 | PARAM_PP_PARAM_ID = ((CLASS2<<16) + (TYPE_UNS16<<24) + 550)
156 | PARAM_SMART_STREAM_MODE_ENABLED = ((CLASS2<<16) + (TYPE_BOOLEAN<<24) + 700)
157 | PARAM_SMART_STREAM_MODE = ((CLASS2<<16) + (TYPE_UNS16<<24) + 701)
158 | PARAM_SMART_STREAM_EXP_PARAMS = ((CLASS2<<16) + (TYPE_VOID_PTR<<24) + 702)
159 | PARAM_SMART_STREAM_DLY_PARAMS = ((CLASS2<<16) + (TYPE_VOID_PTR<<24) + 703)
160 | PARAM_EXP_TIME = ((CLASS3<<16) + (TYPE_UNS16<<24) + 1)
161 | PARAM_EXP_RES = ((CLASS3<<16) + (TYPE_ENUM<<24) + 2)
162 | PARAM_EXP_RES_INDEX = ((CLASS3<<16) + (TYPE_UNS16<<24) + 4)
163 | PARAM_EXPOSURE_TIME = ((CLASS3<<16) + (TYPE_UNS64<<24) + 8)
164 | PARAM_BOF_EOF_ENABLE = ((CLASS3<<16) + (TYPE_ENUM<<24) + 5)
165 | PARAM_BOF_EOF_COUNT = ((CLASS3<<16) + (TYPE_UNS32<<24) + 6)
166 | PARAM_BOF_EOF_CLR = ((CLASS3<<16) + (TYPE_BOOLEAN<<24) + 7)
167 | PARAM_CIRC_BUFFER = ((CLASS3<<16) + (TYPE_BOOLEAN<<24) + 299)
168 | PARAM_FRAME_BUFFER_SIZE = ((CLASS3<<16) + (TYPE_UNS64<<24) + 300)
169 | PARAM_BINNING_SER = ((CLASS3<<16) + (TYPE_ENUM<<24) + 165)
170 | PARAM_BINNING_PAR = ((CLASS3<<16) + (TYPE_ENUM<<24) + 166)
171 | PARAM_METADATA_ENABLED = ((CLASS3<<16) + (TYPE_BOOLEAN<<24) + 168)
172 | PARAM_METADATA_RESET_TIMESTAMP = ((CLASS3<<16) + (TYPE_BOOLEAN<<24) + 176)
173 | PARAM_ROI_COUNT = ((CLASS3<<16) + (TYPE_UNS16 <<24) + 169)
174 | PARAM_ROI = ((CLASS3<<16) + (TYPE_RGN_TYPE<<24) + 1)
175 | PARAM_CENTROIDS_ENABLED = ((CLASS3<<16) + (TYPE_BOOLEAN<<24) + 170)
176 | PARAM_CENTROIDS_RADIUS = ((CLASS3<<16) + (TYPE_UNS16 <<24) + 171)
177 | PARAM_CENTROIDS_COUNT = ((CLASS3<<16) + (TYPE_UNS16 <<24) + 172)
178 | PARAM_CENTROIDS_MODE = ((CLASS3<<16) + (TYPE_ENUM <<24) + 173)
179 | PARAM_CENTROIDS_BG_COUNT = ((CLASS3<<16) + (TYPE_ENUM <<24) + 174)
180 | PARAM_CENTROIDS_THRESHOLD = ((CLASS3<<16) + (TYPE_UNS32 <<24) + 175)
181 | PARAM_TRIGTAB_SIGNAL = ((CLASS3<<16) + (TYPE_ENUM<<24) + 180)
182 | PARAM_LAST_MUXED_SIGNAL = ((CLASS3<<16) + (TYPE_UNS8<<24) + 181)
183 | PARAM_FRAME_DELIVERY_MODE = ((CLASS3<<16) + (TYPE_ENUM <<24) + 400)
184 |
185 | ### ENUMS ###
186 |
187 | # PL_OPEN_MODES
188 | OPEN_EXCLUSIVE = 0
189 |
190 | # PL_COOL_MODES
191 | NORMAL_COOL = 0
192 | CRYO_COOL = NORMAL_COOL + 1
193 | NO_COOL = CRYO_COOL + 1
194 |
195 | # PL_MPP_MODES
196 | MPP_UNKNOWN = 0
197 | MPP_ALWAYS_OFF = MPP_UNKNOWN + 1
198 | MPP_ALWAYS_ON = MPP_ALWAYS_OFF + 1
199 | MPP_SELECTABLE = MPP_ALWAYS_ON + 1
200 |
201 | # PL_SHTR_MODES
202 | SHTR_FAULT = 0
203 | SHTR_OPENING = SHTR_FAULT + 1
204 | SHTR_OPEN = SHTR_OPENING + 1
205 | SHTR_CLOSING = SHTR_OPEN + 1
206 | SHTR_CLOSED = SHTR_CLOSING + 1
207 | SHTR_UNKNOWN = SHTR_CLOSED + 1
208 |
209 | # PL_PMODES
210 | PMODE_NORMAL = 0
211 | PMODE_FT = PMODE_NORMAL + 1
212 | PMODE_MPP = PMODE_FT + 1
213 | PMODE_FT_MPP = PMODE_MPP + 1
214 | PMODE_ALT_NORMAL = PMODE_FT_MPP + 1
215 | PMODE_ALT_FT = PMODE_ALT_NORMAL + 1
216 | PMODE_ALT_MPP = PMODE_ALT_FT + 1
217 | PMODE_ALT_FT_MPP = PMODE_ALT_MPP + 1
218 |
219 | # PL_COLOR_MODES
220 | COLOR_NONE = 0
221 | COLOR_RESERVED = 1
222 | COLOR_RGGB = 2
223 | COLOR_GRBG = COLOR_RGGB + 1
224 | COLOR_GBRG = COLOR_GRBG + 1
225 | COLOR_BGGR = COLOR_GBRG + 1
226 |
227 | # PL_IMAGE_FORMATS
228 | PL_IMAGE_FORMAT_MONO16 = 0
229 | PL_IMAGE_FORMAT_BAYER16 = PL_IMAGE_FORMAT_MONO16 + 1
230 | PL_IMAGE_FORMAT_MONO8 = PL_IMAGE_FORMAT_BAYER16 + 1
231 | PL_IMAGE_FORMAT_BAYER8 = PL_IMAGE_FORMAT_MONO8 + 1
232 | PL_IMAGE_FORMAT_MONO24 = PL_IMAGE_FORMAT_BAYER8 + 1
233 | PL_IMAGE_FORMAT_BAYER24 = PL_IMAGE_FORMAT_MONO24 + 1
234 | PL_IMAGE_FORMAT_RGB24 = PL_IMAGE_FORMAT_BAYER24 + 1
235 | PL_IMAGE_FORMAT_RGB48 = PL_IMAGE_FORMAT_RGB24 + 1
236 | PL_IMAGE_FORMAT_RGB72 = PL_IMAGE_FORMAT_RGB48 + 1
237 | PL_IMAGE_FORMAT_MONO32 = PL_IMAGE_FORMAT_RGB72 + 1
238 | PL_IMAGE_FORMAT_BAYER32 = PL_IMAGE_FORMAT_MONO32 + 1
239 | PL_IMAGE_FORMAT_RGB96 = PL_IMAGE_FORMAT_BAYER32 + 1
240 |
241 | # PL_IMAGE_COMPRESSIONS
242 | PL_IMAGE_COMPRESSION_NONE = 0
243 | PL_IMAGE_COMPRESSION_RESERVED8 = 8
244 | PL_IMAGE_COMPRESSION_BITPACK9 = PL_IMAGE_COMPRESSION_RESERVED8 + 1
245 | PL_IMAGE_COMPRESSION_BITPACK10 = PL_IMAGE_COMPRESSION_BITPACK9 + 1
246 | PL_IMAGE_COMPRESSION_BITPACK11 = PL_IMAGE_COMPRESSION_BITPACK10 + 1
247 | PL_IMAGE_COMPRESSION_BITPACK12 = PL_IMAGE_COMPRESSION_BITPACK11 + 1
248 | PL_IMAGE_COMPRESSION_BITPACK13 = PL_IMAGE_COMPRESSION_BITPACK12 + 1
249 | PL_IMAGE_COMPRESSION_BITPACK14 = PL_IMAGE_COMPRESSION_BITPACK13 + 1
250 | PL_IMAGE_COMPRESSION_BITPACK15 = PL_IMAGE_COMPRESSION_BITPACK14 + 1
251 | PL_IMAGE_COMPRESSION_RESERVED16 = 16
252 | PL_IMAGE_COMPRESSION_BITPACK17 = PL_IMAGE_COMPRESSION_RESERVED16 + 1
253 | PL_IMAGE_COMPRESSION_BITPACK18 = PL_IMAGE_COMPRESSION_BITPACK17 + 1
254 | PL_IMAGE_COMPRESSION_RESERVED24 = 24
255 | PL_IMAGE_COMPRESSION_RESERVED32 = 32
256 |
257 | # PL_FRAME_ROTATE_MODES
258 | PL_FRAME_ROTATE_MODE_NONE = 0
259 | PL_FRAME_ROTATE_MODE_90CW = PL_FRAME_ROTATE_MODE_NONE + 1
260 | PL_FRAME_ROTATE_MODE_180CW = PL_FRAME_ROTATE_MODE_90CW + 1
261 | PL_FRAME_ROTATE_MODE_270CW = PL_FRAME_ROTATE_MODE_180CW + 1
262 |
263 | # PL_FRAME_FLIP_MODES
264 | PL_FRAME_FLIP_MODE_NONE = 0
265 | PL_FRAME_FLIP_MODE_X = PL_FRAME_FLIP_MODE_NONE + 1
266 | PL_FRAME_FLIP_MODE_Y = PL_FRAME_FLIP_MODE_X + 1
267 | PL_FRAME_FLIP_MODE_XY = PL_FRAME_FLIP_MODE_Y + 1
268 |
269 | # PL_FRAME_SUMMING_FORMATS
270 | PL_FRAME_SUMMING_FORMAT_16_BIT = 0
271 | PL_FRAME_SUMMING_FORMAT_24_BIT = PL_FRAME_SUMMING_FORMAT_16_BIT + 1
272 | PL_FRAME_SUMMING_FORMAT_32_BIT = PL_FRAME_SUMMING_FORMAT_24_BIT + 1
273 |
274 | # PL_PARAM_ATTRIBUTES
275 | ATTR_CURRENT = 0
276 | ATTR_COUNT = ATTR_CURRENT + 1
277 | ATTR_TYPE = ATTR_COUNT + 1
278 | ATTR_MIN = ATTR_TYPE + 1
279 | ATTR_MAX = ATTR_MIN + 1
280 | ATTR_DEFAULT = ATTR_MAX + 1
281 | ATTR_INCREMENT = ATTR_DEFAULT + 1
282 | ATTR_ACCESS = ATTR_INCREMENT + 1
283 | ATTR_AVAIL = ATTR_ACCESS + 1
284 | ATTR_LIVE = ATTR_AVAIL + 1
285 |
286 | # PL_PARAM_ACCESS
287 | ACC_READ_ONLY = 1
288 | ACC_READ_WRITE = ACC_READ_ONLY + 1
289 | ACC_EXIST_CHECK_ONLY = ACC_READ_WRITE + 1
290 | ACC_WRITE_ONLY = ACC_EXIST_CHECK_ONLY + 1
291 |
292 | # PL_IO_TYPE
293 | IO_TYPE_TTL = 0
294 | IO_TYPE_DAC = IO_TYPE_TTL + 1
295 |
296 | # PL_IO_DIRECTION
297 | IO_DIR_INPUT = 0
298 | IO_DIR_OUTPUT = IO_DIR_INPUT + 1
299 | IO_DIR_INPUT_OUTPUT = IO_DIR_OUTPUT + 1
300 |
301 | # PL_READOUT_PORTS
302 | READOUT_PORT_0 = 0
303 | READOUT_PORT_1 = READOUT_PORT_0 + 1
304 | READOUT_PORT_2 = READOUT_PORT_1 + 1
305 | READOUT_PORT_3 = READOUT_PORT_2 + 1
306 |
307 | # PL_CLEAR_MODES
308 | CLEAR_NEVER = 0
309 | CLEAR_AUTO = CLEAR_NEVER
310 | CLEAR_PRE_EXPOSURE = CLEAR_AUTO + 1
311 | CLEAR_PRE_SEQUENCE = CLEAR_PRE_EXPOSURE + 1
312 | CLEAR_POST_SEQUENCE = CLEAR_PRE_SEQUENCE + 1
313 | CLEAR_PRE_POST_SEQUENCE = CLEAR_POST_SEQUENCE + 1
314 | CLEAR_PRE_EXPOSURE_POST_SEQ = CLEAR_PRE_POST_SEQUENCE + 1
315 | MAX_CLEAR_MODE = CLEAR_PRE_EXPOSURE_POST_SEQ + 1
316 |
317 | # PL_SHTR_OPEN_MODES
318 | OPEN_NEVER = 0
319 | OPEN_PRE_EXPOSURE = OPEN_NEVER + 1
320 | OPEN_PRE_SEQUENCE = OPEN_PRE_EXPOSURE + 1
321 | OPEN_PRE_TRIGGER = OPEN_PRE_SEQUENCE + 1
322 | OPEN_NO_CHANGE = OPEN_PRE_TRIGGER + 1
323 |
324 | # PL_EXPOSURE_MODES
325 | TIMED_MODE = 0
326 | STROBED_MODE = TIMED_MODE + 1
327 | BULB_MODE = STROBED_MODE + 1
328 | TRIGGER_FIRST_MODE = BULB_MODE + 1
329 | FLASH_MODE = TRIGGER_FIRST_MODE + 1
330 | VARIABLE_TIMED_MODE = FLASH_MODE + 1
331 | INT_STROBE_MODE = VARIABLE_TIMED_MODE + 1
332 | MAX_EXPOSE_MODE = 7
333 | EXT_TRIG_INTERNAL = (7 + 0) << 8
334 | EXT_TRIG_TRIG_FIRST = (7 + 1) << 8
335 | EXT_TRIG_EDGE_RISING = (7 + 2) << 8
336 | EXT_TRIG_LEVEL = (7 + 3) << 8
337 | EXT_TRIG_SOFTWARE_FIRST = (7 + 4) << 8
338 | EXT_TRIG_SOFTWARE_EDGE = (7 + 5) << 8
339 | EXT_TRIG_LEVEL_OVERLAP = (7 + 6) << 8
340 | EXT_TRIG_LEVEL_PULSED = (7 + 7) << 8
341 |
342 |
343 | # PL_SW_TRIG_STATUSES
344 | PL_SW_TRIG_STATUS_TRIGGERED = 0
345 | PL_SW_TRIG_STATUS_IGNORED = PL_SW_TRIG_STATUS_TRIGGERED + 1
346 |
347 | # PL_EXPOSE_OUT_MODES
348 | EXPOSE_OUT_FIRST_ROW = 0
349 | EXPOSE_OUT_ALL_ROWS = EXPOSE_OUT_FIRST_ROW + 1
350 | EXPOSE_OUT_ANY_ROW = EXPOSE_OUT_ALL_ROWS + 1
351 | EXPOSE_OUT_ROLLING_SHUTTER = EXPOSE_OUT_ANY_ROW + 1
352 | EXPOSE_OUT_LINE_TRIGGER = EXPOSE_OUT_ROLLING_SHUTTER + 1
353 | EXPOSE_OUT_GLOBAL_SHUTTER = EXPOSE_OUT_LINE_TRIGGER + 1
354 | MAX_EXPOSE_OUT_MODE = EXPOSE_OUT_GLOBAL_SHUTTER + 1
355 |
356 | # PL_FAN_SPEEDS
357 | FAN_SPEED_HIGH = 0
358 | FAN_SPEED_MEDIUM = FAN_SPEED_HIGH + 1
359 | FAN_SPEED_LOW = FAN_SPEED_MEDIUM + 1
360 | FAN_SPEED_OFF = FAN_SPEED_LOW + 1
361 |
362 | # PL_TRIGTAB_SIGNALS
363 | PL_TRIGTAB_SIGNAL_EXPOSE_OUT = 0
364 |
365 | # PL_FRAME_DELIVERY_MODES
366 | PL_FRAME_DELIVERY_MODE_MAX_FPS = 0
367 | PL_FRAME_DELIVERY_MODE_CONSTANT_INTERVALS = PL_FRAME_DELIVERY_MODE_MAX_FPS + 1
368 |
369 | # PL_CAM_INTERFACE_TYPES
370 | PL_CAM_IFC_TYPE_UNKNOWN = 0
371 | PL_CAM_IFC_TYPE_1394 = 0x100
372 | PL_CAM_IFC_TYPE_1394_A = PL_CAM_IFC_TYPE_1394 + 1
373 | PL_CAM_IFC_TYPE_1394_B = PL_CAM_IFC_TYPE_1394_A + 1
374 | PL_CAM_IFC_TYPE_USB = 0x200
375 | PL_CAM_IFC_TYPE_USB_1_1 = PL_CAM_IFC_TYPE_USB + 1
376 | PL_CAM_IFC_TYPE_USB_2_0 = PL_CAM_IFC_TYPE_USB_1_1 + 1
377 | PL_CAM_IFC_TYPE_USB_3_0 = PL_CAM_IFC_TYPE_USB_2_0 + 1
378 | PL_CAM_IFC_TYPE_USB_3_1 = PL_CAM_IFC_TYPE_USB_3_0 + 1
379 | PL_CAM_IFC_TYPE_PCI = 0x400
380 | PL_CAM_IFC_TYPE_PCI_LVDS = PL_CAM_IFC_TYPE_PCI + 1
381 | PL_CAM_IFC_TYPE_PCIE = 0x800
382 | PL_CAM_IFC_TYPE_PCIE_LVDS = PL_CAM_IFC_TYPE_PCIE + 1
383 | PL_CAM_IFC_TYPE_PCIE_X1 = PL_CAM_IFC_TYPE_PCIE_LVDS + 1
384 | PL_CAM_IFC_TYPE_PCIE_X4 = PL_CAM_IFC_TYPE_PCIE_X1 + 1
385 | PL_CAM_IFC_TYPE_PCIE_X8 = PL_CAM_IFC_TYPE_PCIE_X4 + 1
386 | PL_CAM_IFC_TYPE_VIRTUAL = 0x1000
387 | PL_CAM_IFC_TYPE_ETHERNET = 0x2000
388 |
389 |
390 | # PL_CAM_INTERFACE_MODES
391 | PL_CAM_IFC_MODE_UNSUPPORTED = 0
392 | PL_CAM_IFC_MODE_CONTROL_ONLY = PL_CAM_IFC_MODE_UNSUPPORTED + 1
393 | PL_CAM_IFC_MODE_IMAGING = PL_CAM_IFC_MODE_CONTROL_ONLY + 1
394 |
395 | # PL_CENTROIDS_MODES
396 | PL_CENTROIDS_MODE_LOCATE = 0
397 | PL_CENTROIDS_MODE_TRACK = PL_CENTROIDS_MODE_LOCATE + 1
398 | PL_CENTROIDS_MODE_BLOB = PL_CENTROIDS_MODE_TRACK + 1
399 |
400 | # PL_SCAN_MODES
401 | PL_SCAN_MODE_AUTO = 0
402 | PL_SCAN_MODE_PROGRAMMABLE_LINE_DELAY = PL_SCAN_MODE_AUTO + 1
403 | PL_SCAN_MODE_PROGRAMMABLE_SCAN_WIDTH = PL_SCAN_MODE_PROGRAMMABLE_LINE_DELAY + 1
404 |
405 | # PL_SCAN_DIRECTIONS
406 | PL_SCAN_DIRECTION_DOWN = 0
407 | PL_SCAN_DIRECTION_UP = PL_SCAN_DIRECTION_DOWN + 1
408 | PL_SCAN_DIRECTION_DOWN_UP = PL_SCAN_DIRECTION_UP + 1
409 |
410 | # PP_FEATURE_IDS
411 | PP_FEATURE_RING_FUNCTION = 0
412 | PP_FEATURE_BIAS = PP_FEATURE_RING_FUNCTION + 1
413 | PP_FEATURE_BERT = PP_FEATURE_BIAS + 1
414 | PP_FEATURE_QUANT_VIEW = PP_FEATURE_BERT + 1
415 | PP_FEATURE_BLACK_LOCK = PP_FEATURE_QUANT_VIEW + 1
416 | PP_FEATURE_TOP_LOCK = PP_FEATURE_BLACK_LOCK + 1
417 | PP_FEATURE_VARI_BIT = PP_FEATURE_TOP_LOCK + 1
418 | PP_FEATURE_RESERVED = PP_FEATURE_VARI_BIT + 1
419 | PP_FEATURE_DESPECKLE_BRIGHT_HIGH = PP_FEATURE_RESERVED + 1
420 | PP_FEATURE_DESPECKLE_DARK_LOW = PP_FEATURE_DESPECKLE_BRIGHT_HIGH + 1
421 | PP_FEATURE_DEFECTIVE_PIXEL_CORRECTION = PP_FEATURE_DESPECKLE_DARK_LOW + 1
422 | PP_FEATURE_DYNAMIC_DARK_FRAME_CORRECTION = PP_FEATURE_DEFECTIVE_PIXEL_CORRECTION + 1
423 | PP_FEATURE_HIGH_DYNAMIC_RANGE = PP_FEATURE_DYNAMIC_DARK_FRAME_CORRECTION + 1
424 | PP_FEATURE_DESPECKLE_BRIGHT_LOW = PP_FEATURE_HIGH_DYNAMIC_RANGE + 1
425 | PP_FEATURE_DENOISING = PP_FEATURE_DESPECKLE_BRIGHT_LOW + 1
426 | PP_FEATURE_DESPECKLE_DARK_HIGH = PP_FEATURE_DENOISING + 1
427 | PP_FEATURE_ENHANCED_DYNAMIC_RANGE = PP_FEATURE_DESPECKLE_DARK_HIGH + 1
428 | PP_FEATURE_FRAME_SUMMING = PP_FEATURE_ENHANCED_DYNAMIC_RANGE + 1
429 | PP_FEATURE_LARGE_CLUSTER_CORRECTION = PP_FEATURE_FRAME_SUMMING + 1
430 | PP_FEATURE_FRAME_AVERAGING = PP_FEATURE_LARGE_CLUSTER_CORRECTION + 1
431 | PP_FEATURE_MAX = PP_FEATURE_FRAME_AVERAGING + 1
432 |
433 | # PP_PARAMETER_IDS
434 | PP_PARAMETER_RF_FUNCTION = (PP_FEATURE_RING_FUNCTION * PP_MAX_PARAMETERS_PER_FEATURE)
435 | PP_FEATURE_BIAS_ENABLED = (PP_FEATURE_BIAS * PP_MAX_PARAMETERS_PER_FEATURE)
436 | PP_FEATURE_BIAS_LEVEL = PP_FEATURE_BIAS_ENABLED + 1
437 | PP_FEATURE_BERT_ENABLED = (PP_FEATURE_BERT * PP_MAX_PARAMETERS_PER_FEATURE)
438 | PP_FEATURE_BERT_THRESHOLD = PP_FEATURE_BERT_ENABLED + 1
439 | PP_FEATURE_QUANT_VIEW_ENABLED = (PP_FEATURE_QUANT_VIEW * PP_MAX_PARAMETERS_PER_FEATURE)
440 | PP_FEATURE_QUANT_VIEW_E = PP_FEATURE_QUANT_VIEW_ENABLED + 1
441 | PP_FEATURE_BLACK_LOCK_ENABLED = (PP_FEATURE_BLACK_LOCK * PP_MAX_PARAMETERS_PER_FEATURE)
442 | PP_FEATURE_BLACK_LOCK_BLACK_CLIP = PP_FEATURE_BLACK_LOCK_ENABLED + 1
443 | PP_FEATURE_TOP_LOCK_ENABLED = (PP_FEATURE_TOP_LOCK * PP_MAX_PARAMETERS_PER_FEATURE)
444 | PP_FEATURE_TOP_LOCK_WHITE_CLIP = PP_FEATURE_TOP_LOCK_ENABLED + 1
445 | PP_FEATURE_VARI_BIT_ENABLED = (PP_FEATURE_VARI_BIT * PP_MAX_PARAMETERS_PER_FEATURE)
446 | PP_FEATURE_VARI_BIT_BIT_DEPTH = PP_FEATURE_VARI_BIT_ENABLED + 1
447 | PP_FEATURE_DESPECKLE_BRIGHT_HIGH_ENABLED = (PP_FEATURE_DESPECKLE_BRIGHT_HIGH * PP_MAX_PARAMETERS_PER_FEATURE)
448 | PP_FEATURE_DESPECKLE_BRIGHT_HIGH_THRESHOLD = PP_FEATURE_DESPECKLE_BRIGHT_HIGH_ENABLED + 1
449 | PP_FEATURE_DESPECKLE_BRIGHT_HIGH_MIN_ADU_AFFECTED = PP_FEATURE_DESPECKLE_BRIGHT_HIGH_THRESHOLD + 1
450 | PP_FEATURE_DESPECKLE_DARK_LOW_ENABLED = (PP_FEATURE_DESPECKLE_DARK_LOW * PP_MAX_PARAMETERS_PER_FEATURE)
451 | PP_FEATURE_DESPECKLE_DARK_LOW_THRESHOLD = PP_FEATURE_DESPECKLE_DARK_LOW_ENABLED + 1
452 | PP_FEATURE_DESPECKLE_DARK_LOW_MAX_ADU_AFFECTED = PP_FEATURE_DESPECKLE_DARK_LOW_THRESHOLD + 1
453 | PP_FEATURE_DEFECTIVE_PIXEL_CORRECTION_ENABLED = (PP_FEATURE_DEFECTIVE_PIXEL_CORRECTION * PP_MAX_PARAMETERS_PER_FEATURE)
454 | PP_FEATURE_DYNAMIC_DARK_FRAME_CORRECTION_ENABLED = (PP_FEATURE_DYNAMIC_DARK_FRAME_CORRECTION * PP_MAX_PARAMETERS_PER_FEATURE)
455 | PP_FEATURE_HIGH_DYNAMIC_RANGE_ENABLED = (PP_FEATURE_HIGH_DYNAMIC_RANGE * PP_MAX_PARAMETERS_PER_FEATURE)
456 | PP_FEATURE_DESPECKLE_BRIGHT_LOW_ENABLED = (PP_FEATURE_DESPECKLE_BRIGHT_LOW * PP_MAX_PARAMETERS_PER_FEATURE)
457 | PP_FEATURE_DESPECKLE_BRIGHT_LOW_THRESHOLD = PP_FEATURE_DESPECKLE_BRIGHT_LOW_ENABLED + 1
458 | PP_FEATURE_DESPECKLE_BRIGHT_LOW_MAX_ADU_AFFECTED = PP_FEATURE_DESPECKLE_BRIGHT_LOW_THRESHOLD + 1
459 | PP_FEATURE_DENOISING_ENABLED = (PP_FEATURE_DENOISING * PP_MAX_PARAMETERS_PER_FEATURE)
460 | PP_FEATURE_DENOISING_NO_OF_ITERATIONS = PP_FEATURE_DENOISING_ENABLED + 1
461 | PP_FEATURE_DENOISING_GAIN = PP_FEATURE_DENOISING_NO_OF_ITERATIONS + 1
462 | PP_FEATURE_DENOISING_OFFSET = PP_FEATURE_DENOISING_GAIN + 1
463 | PP_FEATURE_DENOISING_LAMBDA = PP_FEATURE_DENOISING_OFFSET + 1
464 | PP_FEATURE_DESPECKLE_DARK_HIGH_ENABLED = (PP_FEATURE_DESPECKLE_DARK_HIGH * PP_MAX_PARAMETERS_PER_FEATURE)
465 | PP_FEATURE_DESPECKLE_DARK_HIGH_THRESHOLD = PP_FEATURE_DESPECKLE_DARK_HIGH_ENABLED + 1
466 | PP_FEATURE_DESPECKLE_DARK_HIGH_MIN_ADU_AFFECTED = PP_FEATURE_DESPECKLE_DARK_HIGH_THRESHOLD + 1
467 | PP_FEATURE_ENHANCED_DYNAMIC_RANGE_ENABLED = (PP_FEATURE_ENHANCED_DYNAMIC_RANGE * PP_MAX_PARAMETERS_PER_FEATURE)
468 | PP_FEATURE_FRAME_SUMMING_ENABLED = (PP_FEATURE_FRAME_SUMMING * PP_MAX_PARAMETERS_PER_FEATURE)
469 | PP_FEATURE_FRAME_SUMMING_COUNT = PP_FEATURE_FRAME_SUMMING_ENABLED + 1
470 | PP_FEATURE_FRAME_SUMMING_32_BIT_MODE = PP_FEATURE_FRAME_SUMMING_COUNT + 1
471 | PP_FEATURE_LARGE_CLUSTER_CORRECTION_ENABLED = (PP_FEATURE_LARGE_CLUSTER_CORRECTION * PP_MAX_PARAMETERS_PER_FEATURE)
472 | PP_FEATURE_FRAME_AVERAGING_ENABLED = (PP_FEATURE_FRAME_AVERAGING * PP_MAX_PARAMETERS_PER_FEATURE)
473 | PP_FEATURE_FRAME_AVERAGING_COUNT_FACTOR = PP_FEATURE_FRAME_AVERAGING_ENABLED + 1
474 | PP_PARAMETER_ID_MAX = PP_FEATURE_FRAME_AVERAGING_COUNT_FACTOR + 1
475 |
476 | # PL_SMT_MODES
477 | SMTMODE_ARBITRARY_ALL = 0
478 | SMTMODE_MAX = SMTMODE_ARBITRARY_ALL + 1
479 |
480 | # PL_IMAGE_STATUSES
481 | READOUT_NOT_ACTIVE = 0
482 | EXPOSURE_IN_PROGRESS = READOUT_NOT_ACTIVE + 1
483 | READOUT_IN_PROGRESS = EXPOSURE_IN_PROGRESS + 1
484 | READOUT_COMPLETE = READOUT_IN_PROGRESS + 1
485 | FRAME_AVAILABLE = READOUT_COMPLETE
486 | READOUT_FAILED = FRAME_AVAILABLE + 1
487 | ACQUISITION_IN_PROGRESS = READOUT_FAILED + 1
488 | MAX_CAMERA_STATUS = ACQUISITION_IN_PROGRESS + 1
489 |
490 | # PL_CCS_ABORT_MODES
491 | CCS_NO_CHANGE = 0
492 | CCS_HALT = CCS_NO_CHANGE + 1
493 | CCS_HALT_CLOSE_SHTR = CCS_HALT + 1
494 | CCS_CLEAR = CCS_HALT_CLOSE_SHTR + 1
495 | CCS_CLEAR_CLOSE_SHTR = CCS_CLEAR + 1
496 | CCS_OPEN_SHTR = CCS_CLEAR_CLOSE_SHTR + 1
497 | CCS_CLEAR_OPEN_SHTR = CCS_OPEN_SHTR + 1
498 |
499 | # PL_IRQ_MODES
500 | NO_FRAME_IRQS = 0
501 | BEGIN_FRAME_IRQS = NO_FRAME_IRQS + 1
502 | END_FRAME_IRQS = BEGIN_FRAME_IRQS + 1
503 | BEGIN_END_FRAME_IRQS = END_FRAME_IRQS + 1
504 |
505 | # PL_CIRC_MODES
506 | CIRC_NONE = 0
507 | CIRC_OVERWRITE = CIRC_NONE + 1
508 | CIRC_NO_OVERWRITE = CIRC_OVERWRITE + 1
509 |
510 | # PL_EXP_RES_MODES
511 | EXP_RES_ONE_MILLISEC = 0
512 | EXP_RES_ONE_MICROSEC = EXP_RES_ONE_MILLISEC + 1
513 | EXP_RES_ONE_SEC = EXP_RES_ONE_MICROSEC + 1
514 |
515 | # PL_SRC_MODES
516 | SCR_PRE_OPEN_SHTR = 0
517 | SCR_POST_OPEN_SHTR = SCR_PRE_OPEN_SHTR + 1
518 | SCR_PRE_FLASH = SCR_POST_OPEN_SHTR + 1
519 | SCR_POST_FLASH = SCR_PRE_FLASH + 1
520 | SCR_PRE_INTEGRATE = SCR_POST_FLASH + 1
521 | SCR_POST_INTEGRATE = SCR_PRE_INTEGRATE + 1
522 | SCR_PRE_READOUT = SCR_POST_INTEGRATE + 1
523 | SCR_POST_READOUT = SCR_PRE_READOUT + 1
524 | SCR_PRE_CLOSE_SHTR = SCR_POST_READOUT + 1
525 | SCR_POST_CLOSE_SHTR = SCR_PRE_CLOSE_SHTR + 1
526 |
527 | # PL_CALLBACK_EVENT
528 | PL_CALLBACK_BOF = 0
529 | PL_CALLBACK_EOF = PL_CALLBACK_BOF + 1
530 | PL_CALLBACK_CHECK_CAMS = PL_CALLBACK_EOF + 1
531 | PL_CALLBACK_CAM_REMOVED = PL_CALLBACK_CHECK_CAMS + 1
532 | PL_CALLBACK_CAM_RESUMED = PL_CALLBACK_CAM_REMOVED + 1
533 | PL_CALLBACK_MAX = PL_CALLBACK_CAM_RESUMED + 1
534 |
535 | # PL_MD_FRAME_FLAGS
536 | PL_MD_FRAME_FLAG_ROI_TS_SUPPORTED = 0x01
537 | PL_MD_FRAME_FLAG_UNUSED_2 = 0x02
538 | PL_MD_FRAME_FLAG_UNUSED_3 = 0x04
539 | PL_MD_FRAME_FLAG_UNUSED_4 = 0x10
540 | PL_MD_FRAME_FLAG_UNUSED_5 = 0x20
541 | PL_MD_FRAME_FLAG_UNUSED_6 = 0x40
542 | PL_MD_FRAME_FLAG_UNUSED_7 = 0x80
543 |
544 |
545 | # PL_MD_ROI_FLAGS
546 | PL_MD_ROI_FLAG_INVALID = 0x01
547 | PL_MD_ROI_FLAG_HEADER_ONLY = 0x02
548 | PL_MD_ROI_FLAG_UNUSED_3 = 0x04
549 | PL_MD_ROI_FLAG_UNUSED_4 = 0x10
550 | PL_MD_ROI_FLAG_UNUSED_5 = 0x20
551 | PL_MD_ROI_FLAG_UNUSED_6 = 0x40
552 | PL_MD_ROI_FLAG_UNUSED_7 = 0x80
553 |
554 |
555 | # PL_MD_EXT_TAGS
556 | PL_MD_EXT_TAG_PARTICLE_ID = 0
557 | PL_MD_EXT_TAG_PARTICLE_M0 = PL_MD_EXT_TAG_PARTICLE_ID + 1
558 | PL_MD_EXT_TAG_PARTICLE_M2 = PL_MD_EXT_TAG_PARTICLE_M0 + 1
559 | PL_MD_EXT_TAG_MAX = PL_MD_EXT_TAG_PARTICLE_M2 + 1
560 |
561 | ### STRUCTS ###
562 |
563 | class PVCAM_FRAME_INFO_GUID(ctypes.Structure):
564 | _fields_ = [
565 | ('f1', ctypes.c_uint32),
566 | ('f2', ctypes.c_uint16),
567 | ('f3', ctypes.c_uint16),
568 | ('f4', ctypes.c_uint8 * 8),
569 | ]
570 |
571 | class FRAME_INFO(ctypes.Structure):
572 | _fields_ = [
573 | ('FrameInfoGUID', ctypes.c_void_p),
574 | ('hCam', ctypes.c_int16),
575 | ('FrameNr', ctypes.c_int32),
576 | ('TimeStamp', ctypes.c_long),
577 | ('ReadoutTime', ctypes.c_int32),
578 | ('TimeStampBOF', ctypes.c_long),
579 | ]
580 |
581 | class smart_stream_type(ctypes.Structure):
582 | _fields_ = [
583 | ('entries', ctypes.c_uint16),
584 | ('params', ctypes.c_uint32),
585 | ]
586 |
587 | class rgn_type(ctypes.Structure):
588 | _fields_ = [
589 | ('s1', ctypes.c_uint16),
590 | ('s2', ctypes.c_uint16),
591 | ('sbin', ctypes.c_uint16),
592 | ('p1', ctypes.c_uint16),
593 | ('p2', ctypes.c_uint16),
594 | ('pbin', ctypes.c_uint16),
595 | ]
596 |
597 | class io_entry(ctypes.Structure):
598 | _fields_ = [
599 | ('io_port', ctypes.c_uint16),
600 | ('io_type', ctypes.c_uint32),
601 | ('state', ctypes.c_float),
602 | ]
603 |
604 | class io_list(ctypes.Structure):
605 | _fields_ = [
606 | ('pre_open', ctypes.c_void_p),
607 | ('post_open', ctypes.c_void_p),
608 | ('pre_flash', ctypes.c_void_p),
609 | ('post_flash', ctypes.c_void_p),
610 | ('pre_integrate', ctypes.c_void_p),
611 | ('post_integrate', ctypes.c_void_p),
612 | ('pre_readout', ctypes.c_void_p),
613 | ('post_readout', ctypes.c_void_p),
614 | ('pre_close', ctypes.c_void_p),
615 | ('post_close', ctypes.c_void_p),
616 | ]
617 |
618 | class active_camera_type(ctypes.Structure):
619 | _fields_ = [
620 | ('shutter_close_delay', ctypes.c_uint16),
621 | ('shutter_open_delay', ctypes.c_uint16),
622 | ('rows', ctypes.c_uint16),
623 | ('cols', ctypes.c_uint16),
624 | ('prescan', ctypes.c_uint16),
625 | ('postscan', ctypes.c_uint16),
626 | ('premask', ctypes.c_uint16),
627 | ('postmask', ctypes.c_uint16),
628 | ('preflash', ctypes.c_uint16),
629 | ('clear_count', ctypes.c_uint16),
630 | ('preamp_delay', ctypes.c_uint16),
631 | ('mpp_selectable', ctypes.c_short),
632 | ('frame_selectable', ctypes.c_short),
633 | ('do_clear', ctypes.c_int16),
634 | ('open_shutter', ctypes.c_int16),
635 | ('mpp_mode', ctypes.c_short),
636 | ('frame_transfer', ctypes.c_short),
637 | ('alt_mode', ctypes.c_short),
638 | ('exp_res', ctypes.c_uint32),
639 | ('io_hdr', ctypes.c_void_p),
640 | ]
641 |
642 | class md_frame_header(ctypes.Structure):
643 | _fields_ = [
644 | ('signature', ctypes.c_uint32),
645 | ('version', ctypes.c_uint8),
646 | ('frameNr', ctypes.c_uint32),
647 | ('roiCount', ctypes.c_uint16),
648 | ('timestampBOF', ctypes.c_uint32),
649 | ('timestampEOF', ctypes.c_uint32),
650 | ('timestampResNs', ctypes.c_uint32),
651 | ('exposureTime', ctypes.c_uint32),
652 | ('exposureTimeResNs', ctypes.c_uint32),
653 | ('roiTimestampResNs', ctypes.c_uint32),
654 | ('bitDepth', ctypes.c_uint8),
655 | ('colorMask', ctypes.c_uint8),
656 | ('flags', ctypes.c_uint8),
657 | ('extendedMdSize', ctypes.c_uint16),
658 | ('imageFormat', ctypes.c_uint8),
659 | ('imageCompression', ctypes.c_uint8),
660 | ('_reserved', ctypes.c_uint8 * 6),
661 | ]
662 |
663 | class md_frame_header_v3(ctypes.Structure):
664 | _fields_ = [
665 | ('signature', ctypes.c_uint32),
666 | ('version', ctypes.c_uint8),
667 | ('frameNr', ctypes.c_uint32),
668 | ('roiCount', ctypes.c_uint16),
669 | ('timestampBOF', ctypes.c_void_p),
670 | ('timestampEOF', ctypes.c_void_p),
671 | ('exposureTime', ctypes.c_void_p),
672 | ('bitDepth', ctypes.c_uint8),
673 | ('colorMask', ctypes.c_uint8),
674 | ('flags', ctypes.c_uint8),
675 | ('extendedMdSize', ctypes.c_uint16),
676 | ('imageFormat', ctypes.c_uint8),
677 | ('imageCompression', ctypes.c_uint8),
678 | ('_reserved', ctypes.c_uint8 * 6),
679 | ]
680 |
681 | class md_frame_roi_header(ctypes.Structure):
682 | _fields_ = [
683 | ('roiNr', ctypes.c_uint16),
684 | ('timestampBOR', ctypes.c_uint32),
685 | ('timestampEOR', ctypes.c_uint32),
686 | ('roi', ctypes.c_void_p),
687 | ('flags', ctypes.c_uint8),
688 | ('extendedMdSize', ctypes.c_uint16),
689 | ('roiDataSize', ctypes.c_uint32),
690 | ('_reserved', ctypes.c_uint8 * 3),
691 | ]
692 |
693 | class md_ext_item_info(ctypes.Structure):
694 | _fields_ = [
695 | ('tag', ctypes.c_void_p),
696 | ('type', ctypes.c_uint16),
697 | ('size', ctypes.c_uint16),
698 | ('name', ctypes.c_char_p),
699 | ]
700 |
701 | class md_ext_item(ctypes.Structure):
702 | _fields_ = [
703 | ('tagInfo', ctypes.c_void_p),
704 | ('value', ctypes.c_void_p),
705 | ]
706 |
707 | class md_ext_item_collection(ctypes.Structure):
708 | _fields_ = [
709 | ('count', ctypes.c_uint16),
710 | ]
711 |
712 | class md_frame_roi(ctypes.Structure):
713 | _fields_ = [
714 | ('header', ctypes.c_void_p),
715 | ('data', ctypes.c_void_p),
716 | ('dataSize', ctypes.c_uint32),
717 | ('extMdData', ctypes.c_void_p),
718 | ('extMdDataSize', ctypes.c_uint16),
719 | ]
720 |
721 | class md_frame(ctypes.Structure):
722 | _fields_ = [
723 | ('header', ctypes.c_void_p),
724 | ('extMdData', ctypes.c_void_p),
725 | ('extMdDataSize', ctypes.c_uint16),
726 | ('impliedRoi', ctypes.c_void_p),
727 | ('roiArray', ctypes.c_void_p),
728 | ('roiCapacity', ctypes.c_uint16),
729 | ('roiCount', ctypes.c_uint16),
730 | ]
731 |
--------------------------------------------------------------------------------
/src/pyvcam/pvcmodule.h:
--------------------------------------------------------------------------------
1 | #ifndef PVC_MODULE_H_
2 | #define PVC_MODULE_H_
3 |
4 | // System
5 | #include
6 | #include
7 | #include
8 |
9 | // PVCAM
10 | #include
11 | #include
12 |
13 | /*
14 | *Global Variables
15 | */
16 | char g_msg[ERROR_MSG_LEN]; // Global Error Message Variable.
17 | bool DEBUG = false;
18 |
19 | /*
20 | * Documentation Strings
21 | */
22 | static char module_docstring[] = "This module provides an interface for various PVCAM functions.";
23 | static char get_pvcam_version_docstring[] = "Returns the current version of PVCAM.";
24 | static char get_cam_fw_version_docstring[] = "Gets the cameras firmware version";
25 | static char get_cam_total_docstring[] = "Returns the number of available cameras currently connected to the system.";
26 | static char init_pvcam_docstring[] = "Initializes PVCAM library.";
27 | static char uninit_pvcam_docstring[] = "Uninitializes PVCAM library.";
28 | static char get_cam_name_docstring[] = "Return the name of a camera given its handle/camera number.";
29 | static char open_camera_docstring[] = "Opens a specified camera.";
30 | static char close_camera_docstring[] = "Closes a specified camera.";
31 | static char get_param_docstring[] = "Returns the value of a camera associated with the specified parameter.";
32 | static char set_param_docstring[] = "Sets a specified parameter to a specified value.";
33 | static char check_param_docstring[] = "Checks if a specified setting of a camera is available.";
34 | static char get_enum_param_docstring[] = "Returns the enumerated value of the specified parameter at `index`.";
35 | static char start_live_docstring[] = "Starts live mode acquisition";
36 | static char start_seq_docstring[] = "Starts sequence mode acquisition";
37 | static char check_frame_status_docstring[] = "Checks status of frame transfer";
38 | static char get_frame_docstring[] = "Gets latest frame";
39 | static char stop_live_docstring[] = "Stops live acquisition";
40 | static char finish_seq_docstring[] = "Finishes sequence mode acquisition. Must be called prior to re-calling start_seq.";
41 | static char abort_docstring[] = "Aborts acquisition";
42 | static char set_exp_modes_docstring[] = "Sets a camera's exposure mode or expose out mode.";
43 | static char read_enum_docstring[] = "Returns a list of all key-value pairs of a given enum type.";
44 | static char reset_pp_docstring[] = "Resets all post-processing modules to their default values.";
45 | static char my_set_callback_docstring[] = "Initializes a python callback";
46 | static char sw_trigger_docstring[] = "Triggers exposure using current camera settings";
47 |
48 | /*
49 | * Functions
50 | */
51 | static PyObject* pvc_init_pvcam(PyObject* self, PyObject* args);
52 | static PyObject* pvc_uninit_pvcam(PyObject* self, PyObject* args);
53 | static PyObject* pvc_get_pvcam_version(PyObject* self, PyObject* args);
54 | static PyObject* pvc_get_cam_fw_version(PyObject* self, PyObject* args);
55 |
56 | static PyObject* pvc_get_cam_total(PyObject* self, PyObject* args);
57 | static PyObject* pvc_get_cam_name(PyObject* self, PyObject* args);
58 | static PyObject* pvc_open_camera(PyObject* self, PyObject* args);
59 | static PyObject* pvc_close_camera(PyObject* self, PyObject* args);
60 |
61 | static PyObject* pvc_get_param(PyObject* self, PyObject* args);
62 | static PyObject* pvc_set_param(PyObject* self, PyObject* args);
63 | static PyObject* pvc_check_param(PyObject* self, PyObject* args);
64 |
65 | static PyObject* pvc_start_live(PyObject* self, PyObject* args);
66 | static PyObject* pvc_start_seq(PyObject* self, PyObject* args);
67 | static PyObject* pvc_check_frame_status(PyObject* self, PyObject* args);
68 | static PyObject* pvc_get_frame(PyObject* self, PyObject* args);
69 | static PyObject* pvc_stop_live(PyObject* self, PyObject* args);
70 | static PyObject* pvc_finish_seq(PyObject* self, PyObject* args);
71 | static PyObject* pvc_abort(PyObject* self, PyObject* args);
72 |
73 | static PyObject* pvc_set_exp_modes(PyObject* self, PyObject* args);
74 | static PyObject* pvc_reset_pp(PyObject* self, PyObject* args);
75 | static PyObject* pvc_sw_trigger(PyObject* self, PyObject* args);
76 |
77 | #endif // PVC_MODULE_H_
78 |
--------------------------------------------------------------------------------
/tests/camera_settings.py:
--------------------------------------------------------------------------------
1 | """camera_settings.py
2 |
3 | Note: Currently not used. Proof of concept to show how to change camera settings
4 | in one file and have them applied to a camera from a different script.
5 | """
6 |
7 | def apply_settings(camera):
8 | """Changes the settings of a camera."""
9 | camera.clear_mode = 0
10 | camera.exp_mode = "Internal Trigger"
11 | camera.readout_port = 0
12 | camera.speed_table_index = 0
13 | camera.gain = 1
14 |
--------------------------------------------------------------------------------
/tests/change_settings_test.py:
--------------------------------------------------------------------------------
1 | """change_settings_test.py
2 |
3 | Note: Written for Prime 2020; some settings may not be valid for other cameras!
4 | Change settings in the `camera_settings.py` file.
5 | """
6 | from pyvcam import pvc
7 | from pyvcam.camera import Camera
8 | from camera_settings import apply_settings
9 |
10 |
11 | def print_settings(camera):
12 | print("clear mode: {}".format(camera.clear_modes[camera.clear_mode]))
13 | print("exposure mode: {}".format(camera.exp_modes[camera.exp_mode]))
14 | print("readout port: {}".format(camera.readout_port))
15 | print("speed table index: {}".format(camera.speed_table_index))
16 | print("gain: {}".format(camera.gain))
17 |
18 | # Initialize PVCAM
19 | pvc.init_pvcam()
20 |
21 | # Find the first available camera.
22 | camera = next(Camera.detect_camera())
23 | camera.open()
24 |
25 | # Show camera name and speed table.
26 | print(camera)
27 |
28 | print("\nBefore changing settings:")
29 | print_settings(camera)
30 |
31 | # Change the camera settings from the separate file.
32 | apply_settings(camera)
33 |
34 | print("\nAfter changing settings:")
35 | print_settings(camera)
36 |
37 | # Cleanup before exit
38 | camera.close()
39 | pvc.uninit_pvcam()
40 |
--------------------------------------------------------------------------------
/tests/check_frame_status.py:
--------------------------------------------------------------------------------
1 | import time
2 | import numpy as np
3 |
4 | from pyvcam import pvc
5 | from pyvcam.camera import Camera
6 |
7 | def main():
8 | pvc.init_pvcam()
9 | cam = next(Camera.detect_camera())
10 | cam.open()
11 |
12 | cnt = 0
13 |
14 | # Check status from sequence collect
15 | cam.start_seq(exp_time=1000, num_frames=2)
16 |
17 | acquisitionInProgress = True
18 | while acquisitionInProgress:
19 |
20 | frameStatus = cam.check_frame_status()
21 | print('Seq frame status: ' + frameStatus)
22 |
23 | if frameStatus == 'READOUT_NOT_ACTIVE' or frameStatus == 'FRAME_AVAILABLE' or frameStatus == 'READOUT_COMPLETE' or frameStatus == 'READOUT_FAILED':
24 | acquisitionInProgress = False
25 |
26 | if acquisitionInProgress:
27 | time.sleep(0.05)
28 |
29 | if frameStatus != 'READOUT_FAILED':
30 | frame, fps, frame_count = cam.poll_frame()
31 |
32 | low = np.amin(frame['pixel_data'])
33 | high = np.amax(frame['pixel_data'])
34 | average = np.average(frame['pixel_data'])
35 |
36 | print('Min:{}\tMax:{}\tAverage:{:.0f}\tFrame Rate: {:.1f}\tFrame Count: {:.0f}'.format(low, high, average, fps, frame_count))
37 |
38 | cam.finish()
39 |
40 | frameStatus = cam.check_frame_status()
41 | print('Seq post-acquisition frame status: ' + frameStatus + '\n')
42 |
43 | # Check status from live collect. Status will only report FRAME_AVAILABLE between acquisitions, so an indeterminate number of frames are needed
44 | # before we catch the FRAME_AVAILABLE status
45 | cam.start_live(exp_time=10)
46 |
47 | acquisitionInProgress = True
48 | while acquisitionInProgress:
49 |
50 | frameStatus = cam.check_frame_status()
51 | print('Live frame status: ' + frameStatus)
52 |
53 | if frameStatus == 'READOUT_NOT_ACTIVE' or frameStatus == 'FRAME_AVAILABLE' or frameStatus == 'READOUT_FAILED':
54 | acquisitionInProgress = False
55 |
56 | if acquisitionInProgress:
57 | time.sleep(0.05)
58 |
59 | if frameStatus != 'READOUT_FAILED':
60 | frame, fps, frame_count = cam.poll_frame()
61 |
62 | low = np.amin(frame['pixel_data'])
63 | high = np.amax(frame['pixel_data'])
64 | average = np.average(frame['pixel_data'])
65 |
66 | print('Min:{}\tMax:{}\tAverage:{:.0f}\tFrame Rate: {:.1f}\tFrame Count: {:.0f}'.format(low, high, average, fps, frame_count))
67 |
68 | cam.finish()
69 |
70 | frameStatus = cam.check_frame_status()
71 | print('Live post-acquisition frame status: ' + frameStatus)
72 |
73 | cam.close()
74 |
75 | pvc.uninit_pvcam()
76 |
77 | # print('Total frames: {}\n'.format(cnt))
78 |
79 |
80 | if __name__ == "__main__":
81 | main()
82 |
--------------------------------------------------------------------------------
/tests/live_mode.py:
--------------------------------------------------------------------------------
1 | import time
2 | import cv2
3 | import numpy as np
4 |
5 | from pyvcam import pvc
6 | from pyvcam.camera import Camera
7 |
8 | def main():
9 | pvc.init_pvcam()
10 | cam = next(Camera.detect_camera())
11 | cam.open()
12 | cam.start_live(exp_time=20)
13 | cnt = 0
14 | tot = 0
15 | t1 = time.time()
16 | start = time.time()
17 | width = 800
18 | height = int(cam.sensor_size[1] * width / cam.sensor_size[0])
19 | dim = (width, height)
20 | fps = 0
21 |
22 | while True:
23 | frame, fps, frame_count = cam.poll_frame()
24 | frame['pixel_data'] = cv2.resize(frame['pixel_data'], dim, interpolation = cv2.INTER_AREA)
25 | cv2.imshow('Live Mode', frame['pixel_data'])
26 |
27 | low = np.amin(frame['pixel_data'])
28 | high = np.amax(frame['pixel_data'])
29 | average = np.average(frame['pixel_data'])
30 |
31 | if cnt == 10:
32 | t1 = time.time() - t1
33 | fps = 10/t1
34 | t1 = time.time()
35 | cnt = 0
36 | if cv2.waitKey(10) == 27:
37 | break
38 | print('Min:{}\tMax:{}\tAverage:{:.0f}\tFrame Rate: {:.1f}\n'.format(low, high, average, fps))
39 | cnt += 1
40 | tot += 1
41 |
42 | cam.finish()
43 | cam.close()
44 | pvc.uninit_pvcam()
45 |
46 | print('Total frames: {}\nAverage fps: {}\n'.format(tot, (tot/(time.time()-start))))
47 | if __name__ == "__main__":
48 | main()
49 |
--------------------------------------------------------------------------------
/tests/meta_data.py:
--------------------------------------------------------------------------------
1 | import time
2 | import numpy as np
3 |
4 | from pyvcam import pvc
5 | from pyvcam.camera import Camera
6 | from pyvcam import constants
7 |
8 | def main():
9 | pvc.init_pvcam()
10 | cam = next(Camera.detect_camera())
11 | cam.open()
12 | cam.meta_data_enabled = True
13 | cam.set_roi = (0, 0, 1000, 1000)
14 |
15 | num_frames = 1
16 | cnt = 0
17 |
18 | cam.start_seq(exp_time=20, num_frames=num_frames)
19 | while cnt < num_frames:
20 | frame, fps, frame_count = cam.poll_frame()
21 |
22 | low = np.amin(frame['pixel_data'])
23 | high = np.amax(frame['pixel_data'])
24 | average = np.average(frame['pixel_data'])
25 |
26 | print('Min:{}\tMax:{}\tAverage:{:.0f}\tFrame Rate: {:.1f}\tFrame Count: {:.0f}\n'.format(low, high, average, fps, frame_count))
27 | cnt += 1
28 |
29 | time.sleep(0.05)
30 |
31 | cam.finish()
32 | cam.close()
33 | pvc.uninit_pvcam()
34 |
35 | print('Total frames: {}\n'.format(cnt))
36 |
37 |
38 | if __name__ == "__main__":
39 | main()
40 |
--------------------------------------------------------------------------------
/tests/multi_camera.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | import threading
3 |
4 | from pyvcam import pvc
5 | from pyvcam.camera import Camera
6 |
7 | NUM_FRAMES = 100
8 |
9 | class TriggerThreadRun (threading.Thread):
10 | def __init__(self, cam):
11 | threading.Thread.__init__(self)
12 | self.cam = cam
13 | self.output = ''
14 |
15 | def run(self):
16 | cnt = 0
17 |
18 | self.cam.open()
19 | self.appendOutput('Camera Opened. Name: ' + self.cam.name + ' Sensor Size: ' + str(self.cam.sensor_size))
20 |
21 | width = 400
22 | height = int(self.cam.sensor_size[1] * width / self.cam.sensor_size[0])
23 | dim = (width, height)
24 |
25 | self.cam.start_live(exp_time=20)
26 |
27 | while cnt < NUM_FRAMES:
28 |
29 | frame, fps, frame_count = self.cam.poll_frame()
30 |
31 | pixelData = cv2.resize(frame['pixel_data'], dim, interpolation=cv2.INTER_AREA)
32 | cv2.imshow(self.cam.name, pixelData)
33 | cv2.waitKey(10)
34 |
35 | if self.output != '':
36 | self.output += '\n'
37 |
38 | self.appendOutput('CamName:{}\tFrame Rate: {:.1f}\tFrame Count: {:.0f}\tReturned Count: {:.0f}'.format(self.cam.name, fps, frame_count, cnt))
39 | cnt += 1
40 |
41 | self.cam.finish()
42 | self.cam.close()
43 |
44 | self.appendOutput('Camera Closed. Name: ' + self.cam.name)
45 |
46 | def appendOutput(self, outputLine):
47 | if self.output != '':
48 | self.output += '\n'
49 |
50 | self.output += outputLine
51 |
52 | def getOutput(self):
53 | ret = self.output
54 | self.output = ''
55 | return ret
56 |
57 | def main():
58 | pvc.init_pvcam()
59 |
60 | camera_names = Camera.get_available_camera_names()
61 | print('Available cameras: ' + str(camera_names))
62 |
63 | threadList = []
64 | for cameraName in camera_names:
65 | cam = Camera.select_camera(cameraName)
66 | thread = TriggerThreadRun(cam)
67 | thread.start()
68 | threadList.append(thread)
69 |
70 | checkComplete = False
71 | while not checkComplete:
72 | checkComplete = True
73 | for thread in threadList:
74 | checkComplete &= not thread.is_alive()
75 |
76 | threadOutput = thread.getOutput()
77 | if threadOutput != '':
78 | print(threadOutput)
79 |
80 | print('All cameras complete')
81 | pvc.uninit_pvcam()
82 |
83 | if __name__ == "__main__":
84 | main()
85 |
--------------------------------------------------------------------------------
/tests/multi_rois.py:
--------------------------------------------------------------------------------
1 | import time
2 | import cv2
3 | import matplotlib.pyplot as plt
4 | import numpy as np
5 |
6 | from pyvcam import pvc
7 | from pyvcam.camera import Camera
8 | from pyvcam.constants import rgn_type
9 |
10 | def main():
11 | pvc.init_pvcam()
12 | cam = next(Camera.detect_camera())
13 | cam.open()
14 |
15 | cam.meta_data_enabled = True
16 |
17 | cam.set_roi(0, 0, 250, 250)
18 | cam.set_roi(300, 100, 400, 900)
19 | cam.set_roi(750, 300, 200, 200)
20 |
21 | NUM_FRAMES = 1
22 |
23 | cnt = 0
24 |
25 | cam.start_seq(exp_time=20, num_frames=NUM_FRAMES)
26 | while cnt < NUM_FRAMES:
27 | frame, fps, frame_count = cam.poll_frame()
28 |
29 | num_rois = len(frame['pixel_data'])
30 | for roi_index in range(num_rois):
31 | low = np.amin(frame['pixel_data'][roi_index])
32 | high = np.amax(frame['pixel_data'][roi_index])
33 | average = np.average(frame['pixel_data'][roi_index])
34 |
35 | s1 = frame['meta_data']['roi_headers'][roi_index]['roi']['s1']
36 | s2 = frame['meta_data']['roi_headers'][roi_index]['roi']['s2']
37 | p1 = frame['meta_data']['roi_headers'][roi_index]['roi']['p1']
38 | p2 = frame['meta_data']['roi_headers'][roi_index]['roi']['p2']
39 | print('ROI:{:2} ({:4}, {:4}) ({:4}, {:4})\tMin:{}\tMax:{}\tAverage:{:.0f}\tFrame Rate: {:.1f}\tFrame Count: {:.0f}'.format(roi_index, s1, p1, s2, p2, low, high, average, fps, frame_count))
40 |
41 | alpha = 2.0 # Contrast control (1.0-3.0)
42 | beta = 10 # Brightness control (0-100)
43 | img = cv2.convertScaleAbs(frame['pixel_data'][roi_index], alpha=alpha, beta=beta)
44 |
45 | window_name = 'Region: {}'.format(roi_index)
46 | cv2.imshow(window_name, img)
47 | cv2.moveWindow(window_name, s1, p1)
48 |
49 | cnt += 1
50 |
51 | cv2.waitKey(0)
52 |
53 | cam.finish()
54 | cam.close()
55 | pvc.uninit_pvcam()
56 |
57 | print('Total frames: {}\n'.format(cnt))
58 |
59 |
60 | if __name__ == "__main__":
61 | main()
62 |
--------------------------------------------------------------------------------
/tests/newest_frame.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import threading
3 | import time
4 |
5 | from pyvcam import pvc
6 | from pyvcam.camera import Camera
7 |
8 | NUM_FRAMES = 5
9 | EXPOSE_TIME_ms = 200
10 |
11 | def printFrameDetails(frame, fps, frame_count, desc):
12 | low = np.amin(frame['pixel_data'])
13 | high = np.amax(frame['pixel_data'])
14 | average = np.average(frame['pixel_data'])
15 | print(desc + ': Min:{}\tMax:{}\tAverage:{:.0f}\tFrame Count: {:.0f}'.format(low, high, average, frame_count))
16 |
17 | def main():
18 | # Initialize PVCAM and find the first available camera.
19 | pvc.init_pvcam()
20 |
21 | cam = [cam for cam in Camera.detect_camera()][0]
22 | cam.open()
23 |
24 | cam.start_seq(exp_time=EXPOSE_TIME_ms, num_frames=NUM_FRAMES)
25 |
26 | # Wait for 3 frames to be captured. Frames take a slightly longer than expose time, so wait a little extra time
27 | time.sleep(3 * EXPOSE_TIME_ms * 1e-3 + 0.1)
28 |
29 | cnt = 0
30 | while cnt < NUM_FRAMES:
31 | frame, fps, frame_count = cam.poll_frame(oldestFrame=False)
32 | printFrameDetails(frame, fps, frame_count, 'newest')
33 |
34 | frame, fps, frame_count = cam.poll_frame()
35 | printFrameDetails(frame, fps, frame_count, 'oldest')
36 |
37 | cnt += 1
38 |
39 | cam.finish()
40 | cam.close()
41 | pvc.uninit_pvcam()
42 |
43 | print('\nTotal frames: {}'.format(cnt))
44 |
45 | if __name__ == "__main__":
46 | main()
47 |
--------------------------------------------------------------------------------
/tests/seq_mode.py:
--------------------------------------------------------------------------------
1 | import time
2 | import numpy as np
3 |
4 | from pyvcam import pvc
5 | from pyvcam.camera import Camera
6 | from pyvcam import constants
7 |
8 | def main():
9 | pvc.init_pvcam()
10 | cam = next(Camera.detect_camera())
11 | cam.open()
12 | print('Camera: {}'.format(cam.name))
13 |
14 | NUM_FRAMES = 10
15 | cnt = 0
16 |
17 | print('\nUsing start_seq+poll_frame:')
18 | cam.start_seq(exp_time=20, num_frames=NUM_FRAMES)
19 | while cnt < NUM_FRAMES:
20 | frame, fps, frame_count = cam.poll_frame()
21 | cnt += 1
22 | low = np.amin(frame['pixel_data'])
23 | high = np.amax(frame['pixel_data'])
24 | average = np.average(frame['pixel_data'])
25 | print('Min: {}\tMax: {}\tAverage: {:.0f}\tFrames: {:.0f}\tFrame Rate: {:.1f}'
26 | .format(low, high, average, frame_count, fps))
27 | time.sleep(0.05)
28 | cam.finish()
29 |
30 | print('\nUsing get_sequence:')
31 | frames = cam.get_sequence(NUM_FRAMES)
32 | for frame in frames:
33 | cnt += 1
34 | low = np.amin(frame)
35 | high = np.amax(frame)
36 | average = np.average(frame)
37 | print('Min: {}\tMax: {}\tAverage: {:.0f}\tFrames: {:.0f}'
38 | .format(low, high, average, cnt))
39 |
40 | print('\nUsing get_vtm_sequence:')
41 | time_list = [i*10 for i in range(1, NUM_FRAMES+1)]
42 | frames = cam.get_vtm_sequence(time_list, constants.EXP_RES_ONE_MILLISEC, NUM_FRAMES)
43 | i = 0
44 | for frame in frames:
45 | cnt += 1
46 | low = np.amin(frame)
47 | high = np.amax(frame)
48 | average = np.average(frame)
49 | print('Min: {}\tMax: {}\tAverage: {:.0f}\tFrames: {:.0f}\tExp. time: {}'
50 | .format(low, high, average, cnt, time_list[i]))
51 | i += 1
52 |
53 | cam.close()
54 | pvc.uninit_pvcam()
55 |
56 | print('\nTotal frames: {}'.format(cnt))
57 |
58 |
59 | if __name__ == "__main__":
60 | main()
61 |
--------------------------------------------------------------------------------
/tests/single_image_polling.py:
--------------------------------------------------------------------------------
1 | from pyvcam import pvc
2 | from pyvcam.camera import Camera
3 |
4 |
5 | def main():
6 | # Initialize PVCAM and find the first available camera.
7 | pvc.init_pvcam()
8 | cam = [cam for cam in Camera.detect_camera()][0]
9 | cam.open()
10 | cam.speed_table_index = 0
11 | for i in range(5):
12 | frame = cam.get_frame(exp_time=20)
13 | print("First five pixels of frame: {}, {}, {}, {}, {}".format(*frame[:5]))
14 | cam.close()
15 | pvc.uninit_pvcam()
16 |
17 | if __name__ == "__main__":
18 | main()
19 |
--------------------------------------------------------------------------------
/tests/single_image_polling_show.py:
--------------------------------------------------------------------------------
1 | from pyvcam import pvc
2 | from pyvcam.camera import Camera
3 | from matplotlib import pyplot as plt
4 |
5 | def main():
6 | pvc.init_pvcam()
7 | cam = next(Camera.detect_camera())
8 | cam.open()
9 |
10 | frame = cam.get_frame(exp_time=20).reshape(cam.sensor_size[::-1])
11 | plt.imshow(frame, cmap="gray")
12 | plt.show()
13 | cam.close()
14 | pvc.uninit_pvcam()
15 |
16 | if __name__=="__main__":
17 | main()
18 |
--------------------------------------------------------------------------------
/tests/stream_to_disk.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import time
3 |
4 | from pyvcam import pvc
5 | from pyvcam import constants as const
6 | from pyvcam.camera import Camera
7 |
8 | NUM_FRAMES = 200
9 | FRAME_DATA_PATH = r'data.bin'
10 | BUFFER_FRAME_COUNT = 16
11 | WIDTH = 1000
12 | HEIGHT = 1000
13 |
14 | # Warning. This test can only succeed if the camera supports meta_data since that is needed to confirm frames were not dropped
15 |
16 | def main():
17 | pvc.init_pvcam()
18 | cam = next(Camera.detect_camera())
19 | cam.open()
20 | cam.meta_data_enabled = True
21 | cam.set_roi(0, 0, WIDTH, HEIGHT)
22 | cam.start_live(exp_time=100, buffer_frame_count=BUFFER_FRAME_COUNT, stream_to_disk_path=FRAME_DATA_PATH)
23 |
24 | # Data is streamed to disk in a C++ call-back function invoked directly by PVCAM. To not overburden the system,
25 | # only poll for frames in python at a slow rate, then exit when the frame count indicates all frames have been
26 | # written to disk
27 | while True:
28 | frame, fps, frame_count = cam.poll_frame()
29 |
30 | if frame_count >= NUM_FRAMES:
31 | low = np.amin(frame['pixel_data'])
32 | high = np.amax(frame['pixel_data'])
33 | average = np.average(frame['pixel_data'])
34 | print('Min:{}\tMax:{}\tAverage:{:.0f}\tFrame Count:{:.0f} Frame Rate: {:.1f}'.format(low, high, average, frame_count, fps))
35 | break
36 |
37 | time.sleep(1)
38 |
39 | cam.finish()
40 |
41 | imageFormat = cam.get_param(const.PARAM_IMAGE_FORMAT)
42 | if imageFormat == const.PL_IMAGE_FORMAT_MONO8:
43 | BYTES_PER_PIXEL = 1
44 | else:
45 | BYTES_PER_PIXEL = 2
46 |
47 | cam.close()
48 | pvc.uninit_pvcam()
49 |
50 | # Read out meta data from stored frames
51 | FRAME_ALIGNMENT = 0 # Typically 0. 32 for Kinetix PCIe
52 | FRAME_BUFFER_ALIGNMENT = 4096
53 | META_DATA_FRAME_HEADER_SIZE = 48
54 | META_DATA_ROI_SIZE = 32
55 | META_DATA_SIZE = META_DATA_FRAME_HEADER_SIZE + META_DATA_ROI_SIZE
56 | pixelsPerFrame = WIDTH * HEIGHT
57 | bytesPerFrameUnpadded = pixelsPerFrame * BYTES_PER_PIXEL + META_DATA_SIZE
58 | framePad = 0 if FRAME_ALIGNMENT == 0 else FRAME_ALIGNMENT - (bytesPerFrameUnpadded % FRAME_ALIGNMENT)
59 | bytesPerFrame = bytesPerFrameUnpadded + framePad
60 | frameBufferAlignmentPad = FRAME_BUFFER_ALIGNMENT - ((BUFFER_FRAME_COUNT * bytesPerFrame) % FRAME_BUFFER_ALIGNMENT)
61 |
62 | with open(FRAME_DATA_PATH, "rb") as f:
63 |
64 | badMetaDataCount = 0
65 | for frame_index in range(NUM_FRAMES):
66 | frame_number = frame_index + 1
67 |
68 | # Read frame number from meta data header
69 | # Every time the circular buffer wraps around, bytes are padded into the file. This is a result of needing to
70 | # write data in chunks that are multiples of the alignment boundary.
71 | frameBufferPad = int(frame_index / BUFFER_FRAME_COUNT) * frameBufferAlignmentPad
72 |
73 | FRAME_NUMBER_OFFSET = 5
74 | filePos = frame_index * bytesPerFrame + FRAME_NUMBER_OFFSET + frameBufferPad
75 | f.seek(filePos, 0)
76 | frameNumberBytes = f.read(4)
77 |
78 | frame_number_meta_data = int.from_bytes(frameNumberBytes, 'little')
79 |
80 | if frame_number != frame_number_meta_data:
81 | badMetaDataCount += 1
82 | print('Expected: ' + str(frame_number) + ' Observed: ' + str(frame_number_meta_data))
83 |
84 | print('Verified ' + str(NUM_FRAMES) + ' frames:')
85 | print(' Meta data errors: ' + str(badMetaDataCount))
86 |
87 | if badMetaDataCount > 0:
88 | print('\nMeta data error troubleshooting:')
89 | print(' 1. Verify FRAME_ALIGNMENT for camera and interface')
90 | print(' 2. Increase exposure time')
91 |
92 | if __name__ == "__main__":
93 | main()
94 |
--------------------------------------------------------------------------------
/tests/sw_trigger.py:
--------------------------------------------------------------------------------
1 | import threading
2 | import time
3 |
4 | from pyvcam import pvc
5 | from pyvcam.camera import Camera
6 |
7 | NUM_FRAMES = 20
8 |
9 | class TriggerThreadRun (threading.Thread):
10 | def __init__(self, cam):
11 | threading.Thread.__init__(self)
12 | self.cam = cam
13 | self.end = False
14 |
15 | def run(self):
16 | while not self.end:
17 | try:
18 | self.cam.sw_trigger()
19 | print('SW Trigger success')
20 | time.sleep(0.05)
21 | except Exception as e:
22 | pass
23 |
24 | def stop(self):
25 | self.end = True
26 |
27 | def collectFrames(cam):
28 | framesReceived = 0
29 |
30 | while framesReceived < NUM_FRAMES:
31 | time.sleep(0.005)
32 |
33 | try:
34 | frame, fps, frame_count = cam.poll_frame()
35 | print('Count: {} FPS: {} First five pixels of frame: {}'.format(frame_count, round(fps, 2), frame['pixel_data'][0, 0:5]))
36 | framesReceived += 1
37 | except Exception as e:
38 | print(str(e))
39 |
40 | return framesReceived
41 |
42 | def main():
43 | # Initialize PVCAM and find the first available camera.
44 | pvc.init_pvcam()
45 |
46 | cam = [cam for cam in Camera.detect_camera()][0]
47 | cam.open()
48 | cam.speed_table_index = 0
49 | cam.exp_mode = 'Software Trigger Edge'
50 |
51 | # Start a thread for executing the trigger
52 | t1 = TriggerThreadRun(cam)
53 | t1.start()
54 |
55 | # Collect frames in live mode
56 | cam.start_live()
57 | framesReceived = collectFrames(cam)
58 | cam.finish()
59 | print('Received live frames: ' + str(framesReceived) + '\n')
60 |
61 | # Collect frames in sequence mode
62 | cam.start_seq(num_frames=NUM_FRAMES)
63 | framesReceived = collectFrames(cam)
64 | cam.finish()
65 | print('Received seq frames: ' + str(framesReceived) + '\n')
66 |
67 | t1.stop()
68 | cam.close()
69 | pvc.uninit_pvcam()
70 |
71 | print('Done')
72 |
73 | if __name__ == "__main__":
74 | main()
75 |
--------------------------------------------------------------------------------
/tests/test_camera.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from pyvcam import pvc
3 | from pyvcam import camera
4 | from pyvcam import constants as const
5 |
6 |
7 | class CameraConstructionTests(unittest.TestCase):
8 |
9 | def setUp(self):
10 | pvc.init_pvcam()
11 | try:
12 | self.test_cam = next(camera.Camera.detect_camera())
13 | except:
14 | raise unittest.SkipTest('No available camera found')
15 |
16 | def tearDown(self):
17 | # if self.test_cam.is_open():
18 | # self.test_cam.close()
19 | pvc.uninit_pvcam()
20 |
21 | def test_init(self):
22 | test_cam_name = 'test'
23 | test_cam = camera.Camera(test_cam_name)
24 | self.assertEqual(test_cam.name, test_cam_name)
25 | self.assertEqual(test_cam.handle, -1)
26 | self.assertEqual(test_cam._Camera__is_open, False)
27 |
28 | def test_get_dd_version(self):
29 | self.test_cam.open()
30 | dd_ver = pvc.get_param(self.test_cam.handle,
31 | const.PARAM_DD_VERSION,
32 | const.ATTR_CURRENT)
33 | dd_ver = '{}.{}.{}'.format(dd_ver & 0xff00 >> 8,
34 | dd_ver & 0x00f0 >> 4,
35 | dd_ver & 0x000f)
36 | self.assertEqual(dd_ver, self.test_cam.driver_version)
37 |
38 | def test_get_dd_version_fail(self):
39 | with self.assertRaises(RuntimeError):
40 | self.test_cam.driver_version
41 |
42 | def test_get_bin_x(self):
43 | self.test_cam.open()
44 | try:
45 | self.assertEqual(self.test_cam.bin_x, 1) # Defaults to 1
46 | except AttributeError:
47 | self.skipTest("test_get_bin_x: This camera does not "
48 | "support binning.")
49 |
50 | def test_get_bin_y(self):
51 | self.test_cam.open()
52 | try:
53 | self.assertEqual(self.test_cam.bin_y, 1) # Defaults to 1
54 | except AttributeError:
55 | self.skipTest("test_get_bin_y: This camera does not "
56 | "support binning.")
57 |
58 | def test_get_chip_name(self):
59 | self.test_cam.open()
60 | chip_name = pvc.get_param(self.test_cam.handle,
61 | const.PARAM_CHIP_NAME,
62 | const.ATTR_CURRENT)
63 | self.assertEqual(chip_name, self.test_cam.chip_name)
64 |
65 | def test_get_chip_name_fail(self):
66 | with self.assertRaises(RuntimeError):
67 | self.test_cam.chip_name
68 |
69 | def test_get_serial_no(self):
70 | self.test_cam.open()
71 | ser_no = pvc.get_param(self.test_cam.handle,
72 | const.PARAM_HEAD_SER_NUM_ALPHA,
73 | const.ATTR_CURRENT)
74 | self.assertEqual(ser_no, self.test_cam.serial_no)
75 |
76 | def test_get_speed_table_index(self):
77 | self.test_cam.open()
78 | spdtab_index = pvc.get_param(self.test_cam.handle,
79 | const.PARAM_SPDTAB_INDEX,
80 | const.ATTR_CURRENT)
81 | self.assertEqual(spdtab_index, self.test_cam.speed_table_index)
82 |
83 | def test_get_speed_table_index_fail(self):
84 | with self.assertRaises(RuntimeError):
85 | self.test_cam.speed_table_index
86 |
87 | def test_set_speed_table_index(self):
88 | self.test_cam.open()
89 | num_entries = self.test_cam.get_param(const.PARAM_SPDTAB_INDEX,
90 | const.ATTR_COUNT)
91 | for i in range(num_entries):
92 | self.test_cam.speed_table_index = i
93 | self.assertEqual(i, self.test_cam.speed_table_index)
94 |
95 | def test_set_speed_table_index_out_of_bounds(self):
96 | self.test_cam.open()
97 | num_entries = self.test_cam.get_param(const.PARAM_SPDTAB_INDEX,
98 | const.ATTR_COUNT)
99 | with self.assertRaises(ValueError):
100 | self.test_cam.speed_table_index = num_entries
101 |
102 | def test_set_speed_table_index_no_open_fail(self):
103 | with self.assertRaises(RuntimeError):
104 | self.test_cam.speed_table_index = 0
105 |
106 | def test_get_readout_port(self):
107 | self.test_cam.open()
108 | readout_port = pvc.get_param(self.test_cam.handle, const.PARAM_READOUT_PORT,
109 | const.ATTR_CURRENT)
110 | self.assertEqual(readout_port, self.test_cam.readout_port)
111 |
112 | def test_get_readout_port_fail(self):
113 | with self.assertRaises(RuntimeError):
114 | self.test_cam.readout_port
115 |
116 | def test_set_readout_port_index(self):
117 | self.test_cam.open()
118 | num_entries = self.test_cam.get_param(const.PARAM_READOUT_PORT,
119 | const.ATTR_COUNT)
120 | for i in range(num_entries):
121 | self.test_cam.readout_port = i
122 | self.assertEqual(i, self.test_cam.readout_port)
123 |
124 | def test_set_readout_port_out_of_bounds(self):
125 | self.test_cam.open()
126 | num_entries = self.test_cam.get_param(const.PARAM_READOUT_PORT,
127 | const.ATTR_COUNT)
128 | with self.assertRaises(ValueError):
129 | self.test_cam.readout_port = num_entries
130 |
131 | def test_set_readout_port_no_open_fail(self):
132 | with self.assertRaises(RuntimeError):
133 | self.test_cam.readout_port = 0
134 |
135 | def test_get_bit_depth(self):
136 | self.test_cam.open()
137 | curr_bit_depth = pvc.get_param(self.test_cam.handle,
138 | const.PARAM_BIT_DEPTH,
139 | const.ATTR_CURRENT)
140 | self.assertEqual(curr_bit_depth, self.test_cam.bit_depth)
141 |
142 | def test_get_bit_depth_fail(self):
143 | with self.assertRaises(RuntimeError):
144 | self.test_cam.bit_depth
145 |
146 | def test_get_pix_time(self):
147 | self.test_cam.open()
148 | curr_pix_time = pvc.get_param(self.test_cam.handle,
149 | const.PARAM_PIX_TIME,
150 | const.ATTR_CURRENT)
151 | self.assertEqual(curr_pix_time, self.test_cam.pix_time)
152 |
153 | def test_get_pix_time_fail(self):
154 | with self.assertRaises(RuntimeError):
155 | self.test_cam.pix_time
156 |
157 | def test_get_gain(self):
158 | self.test_cam.open()
159 | curr_gain_index = pvc.get_param(self.test_cam.handle,
160 | const.PARAM_GAIN_INDEX,
161 | const.ATTR_CURRENT)
162 | self.assertEqual(curr_gain_index, self.test_cam.gain)
163 |
164 | def test_get_gain_fail(self):
165 | with self.assertRaises(RuntimeError):
166 | self.test_cam.gain
167 |
168 | def test_set_gain(self):
169 | self.test_cam.open()
170 | max_gain = pvc.get_param(self.test_cam.handle,
171 | const.PARAM_GAIN_INDEX,
172 | const.ATTR_MAX)
173 | for i in range(1, max_gain + 1):
174 | self.test_cam.gain = i
175 | curr_gain_index = pvc.get_param(self.test_cam.handle,
176 | const.PARAM_GAIN_INDEX,
177 | const.ATTR_CURRENT)
178 | self.assertEqual(curr_gain_index, self.test_cam.gain)
179 |
180 | def test_set_gain_less_than_zero(self):
181 | self.test_cam.open()
182 | with self.assertRaises(ValueError):
183 | self.test_cam.gain = -1
184 |
185 | def test_set_gain_more_than_max(self):
186 | self.test_cam.open()
187 | max_gain = pvc.get_param(self.test_cam.handle,
188 | const.PARAM_GAIN_INDEX,
189 | const.ATTR_MAX)
190 | with self.assertRaises(ValueError):
191 | self.test_cam.gain = max_gain + 1
192 |
193 | def test_set_gain_no_open(self):
194 | with self.assertRaises(RuntimeError):
195 | self.test_cam.gain = 1
196 |
197 | def test_adc_offset(self):
198 | self.test_cam.open()
199 | curr_adc_offset = pvc.get_param(self.test_cam.handle,
200 | const.PARAM_ADC_OFFSET,
201 | const.ATTR_CURRENT)
202 | self.assertEqual(curr_adc_offset, self.test_cam.adc_offset)
203 |
204 | def test_adc_offset_no_open(self):
205 | # self.assertRaises(RuntimeError, getattr, self.test_cam, "adc_offset")
206 | with self.assertRaises(RuntimeError):
207 | self.test_cam.adc_offset
208 |
209 | def test_get_clear_mode(self):
210 | self.test_cam.open()
211 | curr_clear_mode = pvc.get_param(self.test_cam.handle,
212 | const.PARAM_CLEAR_MODE,
213 | const.ATTR_CURRENT)
214 | self.assertEqual(curr_clear_mode, self.test_cam.clear_mode)
215 | # reversed_dict = {val: key for key, val in const.clear_modes.items()}
216 | # self.assertEqual(reversed_dict[curr_clear_mode],
217 | # self.test_cam.clear_mode)
218 |
219 | def test_get_clear_mode_no_open(self):
220 | with self.assertRaises(RuntimeError):
221 | self.test_cam.clear_mode
222 |
223 | def test_set_clear_mode_by_name(self):
224 | self.test_cam.open()
225 | for mode in self.test_cam.clear_modes.values():
226 | self.test_cam.clear_mode = mode
227 | curr_clear_mode = pvc.get_param(self.test_cam.handle,
228 | const.PARAM_CLEAR_MODE,
229 | const.ATTR_CURRENT)
230 | self.assertEqual(curr_clear_mode, self.test_cam.clear_mode)
231 |
232 | def test_set_clear_mode_by_value(self):
233 | self.test_cam.open()
234 | for mode in self.test_cam.clear_modes.values():
235 | self.test_cam.clear_mode = mode
236 | curr_clear_mode = pvc.get_param(self.test_cam.handle,
237 | const.PARAM_CLEAR_MODE,
238 | const.ATTR_CURRENT)
239 | self.assertEqual(curr_clear_mode, self.test_cam.clear_mode)
240 |
241 | def test_set_clear_mode_bad_name_fail(self):
242 | self.test_cam.open()
243 | self.assertRaises(ValueError, setattr, self.test_cam,
244 | "clear_mode", "")
245 |
246 | def test_set_clear_mode_bad_value_fail(self):
247 | self.test_cam.open()
248 | self.assertRaises(ValueError, setattr, self.test_cam,
249 | "clear_mode", -1)
250 |
251 | def test_set_clear_mode_no_open_fail(self):
252 | self.assertRaises(AttributeError, setattr, self.test_cam,
253 | "clear_mode", 0)
254 |
255 | # TODO: All setters should raise Runtime if not open
256 | def test_set_clear_mode_no_open_bad_value_fail(self):
257 | self.assertRaises(AttributeError, setattr, self.test_cam,
258 | "clear_mode", -1)
259 |
260 | def test_get_temp(self):
261 | self.test_cam.open()
262 | curr_temp = pvc.get_param(self.test_cam.handle, const.PARAM_TEMP,
263 | const.ATTR_CURRENT)
264 | new_temp = self.test_cam.temp
265 | # Less than +/-20% variation of temperature between calls.
266 | self.assertLessEqual((abs(curr_temp/100 -new_temp) / new_temp), 0.98)
267 |
268 | def test_get_temp_no_open_fail(self):
269 | self.assertRaises(RuntimeError, getattr, self.test_cam, "temp")
270 |
271 | def test_get_temp_setpoint(self):
272 | self.test_cam.open()
273 | curr_temp_setpoint = pvc.get_param(self.test_cam.handle,
274 | const.PARAM_TEMP_SETPOINT,
275 | const.ATTR_CURRENT)
276 | self.assertEqual(curr_temp_setpoint/100, self.test_cam.temp_setpoint)
277 |
278 | def test_get_temp_setpoint_no_open_fail(self):
279 | self.assertRaises(RuntimeError, getattr, self.test_cam, "temp_setpoint")
280 |
281 | def test_set_temp_setpoint(self):
282 | self.test_cam.open()
283 | min_temp = self.test_cam.get_param(const.PARAM_TEMP_SETPOINT,
284 | const.ATTR_MIN)
285 | max_temp = self.test_cam.get_param(const.PARAM_TEMP_SETPOINT,
286 | const.ATTR_MAX)
287 | min_temp_divid100 = (int)(min_temp/100)
288 | max_temp_divid100 = (int)(max_temp/100)
289 | for i in range(max_temp_divid100, min_temp_divid100-1, -1):
290 | self.test_cam.temp_setpoint = i
291 | self.assertEqual(i, self.test_cam.temp_setpoint)
292 |
293 | def test_set_temp_to_high_fail(self):
294 | self.test_cam.open()
295 | one_over_max_temp = self.test_cam.get_param(const.PARAM_TEMP_SETPOINT,
296 | const.ATTR_MAX) + 1
297 | self.assertRaises(ValueError, setattr, self.test_cam,
298 | "temp_setpoint", one_over_max_temp)
299 |
300 | def test_set_temp_to_low_fail(self):
301 | self.test_cam.open()
302 | one_below_max_temp = self.test_cam.get_param(const.PARAM_TEMP_SETPOINT,
303 | const.ATTR_MIN) - 1
304 | self.assertRaises(ValueError, setattr, self.test_cam,
305 | "temp_setpoint", one_below_max_temp)
306 |
307 | def test_set_temp_no_open_fail(self):
308 | self.assertRaises(RuntimeError, setattr, self.test_cam,
309 | "temp_setpoint", 0)
310 |
311 | def test_get_exp_res(self):
312 | self.test_cam.open()
313 | curr_exp_res_index = pvc.get_param(self.test_cam.handle,
314 | const.PARAM_EXP_RES_INDEX,
315 | const.ATTR_CURRENT)
316 | self.assertEqual(curr_exp_res_index, self.test_cam.exp_res_index)
317 |
318 | def test_set_exp_res_by_name(self):
319 | self.test_cam.open()
320 | for i in range(self.test_cam.exp_res_index):
321 | self.test_cam.exp_res = self.test_cam.exp_resolutions[i]
322 | curr_exp_res = pvc.get_param(self.test_cam.handle,
323 | const.PARAM_EXP_RES,
324 | const.ATTR_CURRENT)
325 | self.assertEqual(curr_exp_res, self.test_cam.exp_res)
326 |
327 | def test_set_exp_res_bad_name_fail(self):
328 | self.test_cam.open()
329 | self.assertRaises(ValueError, setattr, self.test_cam,
330 | "exp_res", "")
331 |
332 | def test_set_exp_res_bad_value_fail(self):
333 | self.test_cam.open()
334 | self.assertRaises(ValueError, setattr, self.test_cam,
335 | "exp_res", -1)
336 |
337 | def test_set_exp_res_no_open_fail(self):
338 | self.assertRaises(AttributeError, setattr, self.test_cam,
339 | "exp_res", 0)
340 |
341 | def test_get_sensor_size(self):
342 | self.test_cam.open()
343 | rows = pvc.get_param(self.test_cam.handle, const.PARAM_SER_SIZE,
344 | const.ATTR_CURRENT)
345 | cols = pvc.get_param(self.test_cam.handle, const.PARAM_PAR_SIZE,
346 | const.ATTR_CURRENT)
347 | self.assertEqual((rows, cols), self.test_cam.sensor_size)
348 |
349 | def test_get_sensor_no_open_fail(self):
350 | self.assertRaises(RuntimeError, getattr, self.test_cam, "sensor_size")
351 |
352 | def test_get_exp_mode(self):
353 | self.test_cam.open()
354 | curr_exp_mode = pvc.get_param(self.test_cam.handle,
355 | const.PARAM_EXPOSURE_MODE,
356 | const.ATTR_CURRENT)
357 | self.assertEqual(curr_exp_mode, self.test_cam.exp_mode)
358 |
359 | def test_get_exp_mode_no_open(self):
360 | self.assertRaises(RuntimeError, getattr, self.test_cam, "exp_mode")
361 |
362 | def main():
363 | unittest.main()
364 |
365 | if __name__ == '__main__':
366 | main()
367 |
--------------------------------------------------------------------------------