Source code for pyDCAM.dcamapi

import ctypes
import numpy as np
from .dcamapi_enum import *
from .dcamapi_struct import *
from .dcamprop import *
from typing import Tuple
import os

if "BUILDING_DOCS" not in os.environ:
    dcamapi = ctypes.windll.dcamapi


DCAM_DEFAULT_ARG = 0

_pixel_type_to_ctypes = {
    DCAM_PIXELTYPE.DCAM_PIXELTYPE_MONO16: ctypes.c_uint16,
    DCAM_PIXELTYPE.DCAM_PIXELTYPE_MONO8: ctypes.c_uint8
}

_pixel_type_to_numpy = {
    DCAM_PIXELTYPE.DCAM_PIXELTYPE_MONO16: np.uint16,
    DCAM_PIXELTYPE.DCAM_PIXELTYPE_MONO8: np.uint8
}


[docs] def failed(dcamerr): return True if dcamerr < 0 else False
[docs] class DCAMError(Exception): def __init__(self, error_code): self.error_code = error_code def __str__(self): return "{0}".format( DCAMERR(self.error_code).name )
[docs] def check_status(dcamerr): if not dcamerr == DCAMERR.DCAMERR_SUCCESS: raise DCAMError(dcamerr)
[docs] def dcamapi_init(): """Initialize the DCAM-API. It should be called before using any other DCAM-API functions.""" # TODO Add init options # option = (ctypes.c_int32 * 2)() # option[0] = DCAMAPI_INITOPTION.DCAMAPI_INITOPTION_APIVER__LATEST # option[1] = DCAMAPI_INITOPTION.DCAMAPI_INITOPTION_ENDMARK param = DCAMAPI_INIT() param.size = ctypes.sizeof(param) # param.initoption = ctypes.cast(ctypes.pointer(option), ctypes.c_char_p) # param.initoptionbytes = ctypes.sizeof(option) param.initoption = None param.guid = None check_status(dcamapi.dcamapi_init(ctypes.byref(param))) return param.iDeviceCount
[docs] def dcamapi_uninit(): """Uninitialize the DCAM-API.""" check_status(dcamapi.dcamapi_uninit())
class _USE_DCAMAPI(object): def __enter__(self): return dcamapi_init() def __exit__(self, exc_type, exc_val, exc_tb): dcamapi_uninit() use_dcamapi = _USE_DCAMAPI()
[docs] class HDCAM(object): """Camera handle. It's recommended to use with statement to ensure the camera is closed properly. Parameters ---------- index : int, optional The index of the camera to open. Defaults to 0. Examples -------- >>> with HDCAM() as hdcam: >>> print(hdcam.model) >>> print(hdcam.camera_id) """ def __init__(self, index=0): param = DCAMDEV_OPEN() param.index = index param.size = ctypes.sizeof(param) param.size = ctypes.sizeof(param) check_status(dcamapi.dcamdev_open(ctypes.byref(param))) self.hdcam = param.hdcam def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.dcamdev_close()
[docs] def dcamdev_close(self): """Close the camera handle.""" check_status(dcamapi.dcamdev_close(self.hdcam))
[docs] def dcamdev_getstring(self, iString: DCAM_IDSTR) -> str: """Get the string of the camera. Parameters ---------- iString : DCAM_IDSTR The string to get. """ param = DCAMDEV_STRING() param.size = ctypes.sizeof(param) param.iString = iString TEXT_BYTES = 100 text = ctypes.create_string_buffer(TEXT_BYTES) param.text = ctypes.cast(text, ctypes.c_char_p) param.textbytes = TEXT_BYTES check_status( dcamapi.dcamdev_getstring(self.hdcam, ctypes.byref(param)) ) return text.value.decode("ascii")
[docs] def dcamprop_getattr(self, iProp: DCAMPROPMODEVALUE): param = DCAMPROP_ATTR() param.cbSize = ctypes.sizeof(param) param.iProp = iProp # param.option = 0 # reserved # param.iReserved1 = 0 # reserved # param.iGroup = 0 # reserved # param.iReserved3 = 0 # reserved # TODO the return value is weird... err = dcamapi.dcamprop_getattr(self.hdcam, ctypes.byref(param)) if failed(err): raise Exception # TODO custom exception return dict( attribute=param.attribute, # TODO use enum iUnit=DCAMPROPUNIT(param.iUnit), attribute2=param.attribute2, # TODO use enum valuemin=param.valuemin, valuemax=param.valuemax, valuestep=param.valuestep, valuedefault=param.valuedefault, nMaxChannel=param.nMaxChannel, nMaxView=param.nMaxView, # TODO add iProp array )
[docs] def dcamprop_getvalue(self, iProp: DCAMIDPROP) -> float: """Get the value of the property. Parameters ---------- iProp : DCAMIDPROP The property ID. Returns ------- float The value of the property. """ fValue = ctypes.c_double() check_status( dcamapi.dcamprop_getvalue(self.hdcam, iProp, ctypes.byref(fValue)) ) return fValue.value
[docs] def dcamprop_setvalue(self, iProp: DCAMIDPROP, fValue): """Set the value of the property. Parameters ---------- iProp : DCAMIDPROP The property ID. Raises ------ DCAMError If the device does not support a property. An error with code DCAMERR_NOTSUPPORT is raised. If a property does not have auto-rounding and the fValue is not a valid value, an error with code DCAMERR_INVALIDPARAM is raised. """ fValue = ctypes.c_double(float(fValue)) check_status( dcamapi.dcamprop_setvalue(self.hdcam, iProp, fValue) )
[docs] def dcamprop_setgetvalue(self, iProp: DCAMIDPROP, fValue: float) -> float: """Set the value of the property and get the accurate value if successful. Parameters ---------- iProp : DCAMIDPROP The property ID. Returns ------- float The value of the property. Raises ------ DCAMError If the device does not support a property. An error with code DCAMERR_NOTSUPPORT is raised. If a property does not have auto-rounding and the fValue is not a valid value, an error with code DCAMERR_INVALIDPARAM is raised. """ fValue = ctypes.c_double(fValue) check_status( dcamapi.dcamprop_setgetvalue(self.hdcam, iProp, ctypes.byref(fValue), 0) ) return fValue.value
[docs] def dcamprop_ids(self, option: DCAMPROPOPTION = DCAMPROPOPTION.DCAMPROP_OPTION_SUPPORT) -> DCAMIDPROP: """Generator for enumerating the property IDs. Parameters ---------- option: DCAMPROPOPTION, optional Defaults to the properties supported by the device. Yields ------ iProp: DCAMIDPROP The property ID. Examples -------- >>> with HDCAM() as hdcam: >>> for iProp in hdcam.dcamprop_ids(): >>> print(hdcam.dcamprop_getname(iProp)) """ iProp = ctypes.c_int32(0) while True: err = dcamapi.dcamprop_getnextid(self.hdcam, ctypes.byref(iProp), option) if err == DCAMERR.DCAMERR_NOPROPERTY: break else: yield iProp.value
[docs] def dcamprop_getname(self, iProp: DCAMIDPROP) -> str: """Get the name of the property. Parameters ---------- iProp : DCAMIDPROP The property ID. Returns ------- str The name of the property. """ textbytes = 64 text = ctypes.create_string_buffer(textbytes) check_status( dcamapi.dcamprop_getname(self.hdcam, iProp, ctypes.byref(text), textbytes) ) return text.value.decode("ascii")
[docs] def dcambuf_alloc(self, framecount=64): """Allocates internal image buffers for image acquisition. This function does not start the capture. To start capture, dcamcap_start() should be called. When the internal buffers are no longer necessary, call dcambuf_release() to release them. Parameters ---------- framecount : int, optional The number of frames to allocate. Defaults to 64. """ check_status( dcamapi.dcambuf_alloc(self.hdcam, framecount) )
[docs] def dcambuf_release(self, iKind = 0): """Release the internal image buffers allocated by dcambuf_alloc().""" check_status( dcamapi.dcambuf_release(self.hdcam, iKind) )
[docs] def dcambuf_lockframe(self, iFrame=-1) -> np.ndarray: """ Returns a NumPy array pointing to the captured image buffer. If the host software dcambuf_copyframe needs to copy the image data into its own memory, use dcambuf_copyframe() instead of this function. Parameters ---------- iFrame : int, optional The frame index. Defaults to -1, which retrieves the latest captured image. Returns ------- img: np.ndarray The captured image buffer. See Also -------- HDCAM.dcambuf_copyframe : The function that copied the captured image buffer instead of pointing to it. """ frame = DCAMBUF_FRAME() frame.size = ctypes.sizeof(frame) frame.iFrame = iFrame # This can be set to -1 to retrieve the latest captured image. check_status( dcamapi.dcambuf_lockframe(self.hdcam, ctypes.byref(frame)) ) img = np.ctypeslib.as_array(ctypes.cast(frame.buf, ctypes.POINTER(_pixel_type_to_ctypes[frame.type])), shape=(frame.height, frame.width)) return img
[docs] def dcambuf_copyframe(self, iFrame=-1) -> np.ndarray: """Returns a NumPy array containing the captured image copied from the buffer. Parameters ---------- iFrame : int, optional The frame index. Defaults to -1, which retrieves the latest captured image. Returns ------- img: np.ndarray The captured image buffer. """ frame = DCAMBUF_FRAME() frame.size = ctypes.sizeof(frame) frame.iFrame = iFrame # This can be set to -1 to retrieve the latest captured image. # TODO Support offset # TODO Not sure if this should be image rowbytes of buffer rowbytes frame.rowbytes = int(self.dcamprop_getvalue(DCAMIDPROP.DCAM_IDPROP_IMAGE_ROWBYTES)) frame.width = int(self.dcamprop_getvalue(DCAMIDPROP.DCAM_IDPROP_IMAGE_WIDTH)) frame.height = int(self.dcamprop_getvalue(DCAMIDPROP.DCAM_IDPROP_IMAGE_HEIGHT)) frame.left = 0 frame.top = 0 pixel_type = DCAM_PIXELTYPE(self.dcamprop_getvalue(DCAMIDPROP.DCAM_IDPROP_IMAGE_PIXELTYPE)) img = np.empty(shape=(frame.height, frame.width), dtype=_pixel_type_to_numpy[pixel_type]) frame.buf = img.ctypes.data check_status( dcamapi.dcambuf_copyframe(self.hdcam, ctypes.byref(frame)) ) return img
[docs] def dcamcap_start(self, mode=DCAMCAP_START.DCAMCAP_START_SEQUENCE): """start capturing images. Before calling this function, a capturing buffer should be prepared. Parameters ---------- mode : DCAMCAP_START, optional The capture mode. Defaults to DCAMCAP_START_SEQUENCE. With the DCAMCAP_START_SEQUENCE mode, capturing will be continuing until the dcamcap_stop() function is called. With the DCAMCAP_START_SNAP mode, capturing is terminated when the capturing buffer is filled or until the dcamcap_stop() function is called. See Also -------- HDCAM.dcamcap_stop : Stop capturing images. HDCAM.dcambuf_alloc : Allocate internal image buffers for image acquisition. """ check_status( dcamapi.dcamcap_start(self.hdcam, mode) )
[docs] def dcamcap_stop(self): """Stop capturing images. See Also -------- HDCAM.dcamcap_start : Start capturing images. """ check_status( dcamapi.dcamcap_stop(self.hdcam) )
[docs] def dcamcap_status(self): iStatus = ctypes.c_int32(0) check_status( dcamapi.dcamcap_status(self.hdcam, ctypes.byref(iStatus)) ) return DCAMCAP_STATUS(iStatus)
[docs] def dcamcap_transferinfo(self) -> Tuple[int, int]: """Get the transfer information. Returns ------- nNewestFrameIndex: int The index of the newest frame of the transferred image. nFrameCount: int The number of iamges has been transferred. """ param = DCAMCAP_TRANSFERINFO() param.size = ctypes.sizeof(param) check_status( dcamapi.dcamcap_transferinfo(self.hdcam, ctypes.byref(param)) ) return param.nNewestFrameIndex, param.nFrameCount
[docs] def dcamcap_firetrigger(self): """Fire a software trigger. This is only effective if the software trigger mode. """ check_status( dcamapi.dcamcap_firetrigger(self.hdcam, ctypes.c_int32(0)) )
[docs] def dcamwait_open(self) -> "HDCAMWAIT": """Open a wait handle for the camera. See Also -------- HDCAMWAIT.dcamwait_close : Close the wait handle. """ param = DCAMWAIT_OPEN() param.size = ctypes.sizeof(param) param.hdcam = self.hdcam check_status( dcamapi.dcamwait_open(ctypes.byref(param)) ) return HDCAMWAIT(param.hwait, param.supportevent)
# ===== quick API ===== @property def readout_speed(self) -> DCAMPROPMODEVALUE: return DCAMPROPMODEVALUE(self.dcamprop_getvalue(DCAMIDPROP.DCAM_IDPROP_READOUTSPEED)) @readout_speed.setter def readout_speed(self, value: DCAMPROPMODEVALUE): self.dcamprop_setvalue(DCAMIDPROP.DCAM_IDPROP_READOUTSPEED, float(value)) @property def camera_id(self) -> str: """The camera ID.""" return self.dcamdev_getstring(DCAM_IDSTR.DCAM_IDSTR_CAMERAID) @property def model(self) -> str: """The camera model.""" return self.dcamdev_getstring(DCAM_IDSTR.DCAM_IDSTR_MODEL) @property def bus(self) -> str: return self.dcamdev_getstring(DCAM_IDSTR.DCAM_IDSTR_BUS) @property def exposure_time(self) -> float: return self.dcamprop_getvalue(DCAMIDPROP.DCAM_IDPROP_EXPOSURETIME) @exposure_time.setter def exposure_time(self, value: float): self.dcamprop_setvalue(DCAMIDPROP.DCAM_IDPROP_EXPOSURETIME, value) @property def subarray_pos(self) -> Tuple[int, int]: """The subarray position in (x, y)""" return (int(self.dcamprop_getvalue(DCAMIDPROP.DCAM_IDPROP_SUBARRAYHPOS)), int(self.dcamprop_getvalue(DCAMIDPROP.DCAM_IDPROP_SUBARRAYVPOS))) @subarray_pos.setter def subarray_pos(self, value: Tuple[int, int]): self.dcamprop_setvalue(DCAMIDPROP.DCAM_IDPROP_SUBARRAYHPOS, value[0]) self.dcamprop_setvalue(DCAMIDPROP.DCAM_IDPROP_SUBARRAYVPOS, value[1]) @property def subarray_mode(self) -> bool: """The subarray mode. True for on, False for off.""" mode = self.dcamprop_getvalue(DCAMIDPROP.DCAM_IDPROP_SUBARRAYMODE) if mode == DCAMPROPMODEVALUE.DCAMPROP_MODE__ON: return True elif mode == DCAMPROPMODEVALUE.DCAMPROP_MODE__ON: return False else: raise AssertionError @subarray_mode.setter def subarray_mode(self, value: bool): mode = DCAMPROPMODEVALUE.DCAMPROP_MODE__ON if value else DCAMPROPMODEVALUE.DCAMPROP_MODE__OFF self.dcamprop_setvalue(DCAMIDPROP.DCAM_IDPROP_SUBARRAYMODE, mode) @property def subarray_size(self) -> Tuple[int, int]: """The subarray size in (width, height)""" return (int(self.dcamprop_getvalue(DCAMIDPROP.DCAM_IDPROP_SUBARRAYHSIZE)), int(self.dcamprop_getvalue(DCAMIDPROP.DCAM_IDPROP_SUBARRAYVSIZE))) @subarray_size.setter def subarray_size(self, value: Tuple[int, int]): self.dcamprop_setvalue(DCAMIDPROP.DCAM_IDPROP_SUBARRAYHSIZE, value[0]) self.dcamprop_setvalue(DCAMIDPROP.DCAM_IDPROP_SUBARRAYVSIZE, value[1])
[docs] class HDCAMWAIT(object): """Wait handle for the camera. It is used to block the program and wait for a specific event. Instead of instantiating this class directly, use HDCAM.dcamwait_open() to open a wait handle. See Also -------- HDCAM.dcamwait_open : Open a wait handle for the camera. """ def __init__(self, hwait, supportevent): self.h = hwait self.supportevent = supportevent
[docs] def dcamwait_close(self): check_status( dcamapi.dcamwait_close(self.h) )
[docs] def dcamwait_start(self, eventmask=DCAMWAIT_EVENT.DCAMWAIT_CAPEVENT_FRAMEREADY, timeout=DCAMWAIT_TIMEOUT.DCAMWAIT_TIMEOUT_INFINITE): """Start waiting for a specific event. Parameters ---------- eventmask : DCAMWAIT_EVENT, optional The event to wait for. Defaults to waiting for the next frame ready event. timeout : int, optional The timeout in milliseconds. Defaults to no timeout. """ param = DCAMWAIT_START() param.size = ctypes.sizeof(param) param.eventmask = eventmask param.timeout = timeout check_status( dcamapi.dcamwait_start(self.h, ctypes.byref(param)) ) return param.eventhappened
[docs] def dcamwait_abort(self): """Abort waiting for the event.""" check_status( dcamapi.dcamwait_abort(self.h) )