diff --git a/README.md b/README.md index e1ad55d..efac29b 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ or easy_install ALP4lib ``` -### Installation from source (Github) +### Installation from the source (Github) To install the latest version from Github, clone the repository, and install the package with the the following command. ```shell script python setup.py install diff --git a/src/ALP4.py b/src/ALP4.py index 5a20ed5..f972b25 100644 --- a/src/ALP4.py +++ b/src/ALP4.py @@ -366,7 +366,9 @@ class ALP4(object): This class controls a Vialux DMD board based on the Vialux ALP 4.X API. """ - def __init__(self, version='4.3', libDir=None): + def __init__(self, version='4.3', libDir=None, invert=False, verbose=False): + self.verbose = verbose + self.invert = invert os_type = platform.system() @@ -374,32 +376,35 @@ def __init__(self, version='4.3', libDir=None): try: reg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) key = _winreg.OpenKey(reg, r"SOFTWARE\ViALUX\ALP-" + version) - libDir = (_winreg.QueryValueEx(key, "Path"))[0] + "/ALP-{0} API/".format(version) + libPath = (_winreg.QueryValueEx(key, "Path"))[0] + "/ALP-{0} API/".format(version) except EnvironmentError: raise ValueError("Cannot auto detect libDir! Please specify it manually.") - - if libDir.endswith('/'): + elif libDir.endswith('/'): libPath = libDir else: libPath = libDir + '/' - ## Load the ALP dll - if (os_type == 'Windows'): - if (ct.sizeof(ct.c_voidp) == 8): ## 64bit + + if os_type == 'Windows': + if (ct.sizeof(ct.c_void_p) == 8): ## 64bit libPath += 'x64/' - elif not (ct.sizeof(ct.c_voidp) == 4): ## 32bit + elif not (ct.sizeof(ct.c_void_p) == 4): ## 32bit raise OSError('System not supported.') else: raise OSError('System not supported.') - if (version == '4.1'): + if version == '4.1': libPath += 'alpD41.dll' - elif (version == '4.2'): + elif version == '4.2': libPath += 'alpD41.dll' - elif (version == '4.3'): + elif version == '4.3': libPath += 'alp4395.dll' + else: + raise ValueError("Unavailable ALPlib version. Currently supported version: 4.1, 4.2, 4.3") - print('Loading library: ' + libPath) + if self.verbose: + print('Loading library: ' + libPath) + ## Load the ALP dll self._ALPLib = ct.CDLL(libPath) ## Class parameters @@ -449,7 +454,11 @@ def Initialize(self, DeviceNum=None): 'Inquery ALP_DEV_DISPLAY_HEIGHT fails.') self.nSizeY = nSizeY.value - print('DMD found, resolution = ' + str(self.nSizeX) + ' x ' + str(self.nSizeY) + '.') + if self.verbose: + print('DMD found, resolution = ' + str(self.nSizeX) + ' x ' + str(self.nSizeY) + '.') + + if self.invert: + self._checkError(self._ALPLib.AlpProjControl(self.ALP_ID, ALP_PROJ_INVERSION, ALP_DEFAULT+1), "Cannot set ALP_PROJ_INVERSION.") def SeqAlloc(self, nbImg=1, bitDepth=1): """ @@ -494,7 +503,32 @@ def SeqAlloc(self, nbImg=1, bitDepth=1): self._lastDDRseq = SequenceId return SequenceId - def SeqPutEx(self, imgData, LineOffset, LineLoad, SequenceId=None, PicOffset=0, PicLoad=0, dataFormat='Python'): + def _cast_imgData(self, imgData, SequenceId): + if not isinstance(imgData, np.ndarray): + raise ValueError("imgData must be an numpy ndarray.") + + bitplanes = self.SeqInquire(ALP_BITPLANES, SequenceId) + data_format = self.SeqInquire(ALP_DATA_FORMAT, SequenceId) + + if data_format != ALP_DEFAULT and data_format != ALP_DATA_LSB_ALIGN: + raise NotImplementedError("Unsupported ALP_DATA_FORMAT.") + + if 1 <= bitplanes <= 8: + imgData = imgData.astype(np.uint8) + if bitplanes < 8 and data_format == ALP_DATA_MSB_ALIGN: + imgData = np.left_shift(imgData, 8 - bitplanes) + elif 9 <= bitplanes <= 16: + imgData = imgData.astype(np.uint16) + if bitplanes < 16 and data_format == ALP_DATA_MSB_ALIGN: + imgData = np.left_shift(imgData, 16 - bitplanes) + else: + raise AssertionError + + + return imgData + + + def SeqPutEx(self, imgData, LineOffset, LineLoad, SequenceId=None, PicOffset=0, PicLoad=0): """ Image data transfer using AlpSeqPut is based on whole DMD frames. Applications that only update small regions inside a frame suffer from overhead of this default behavior. An extended @@ -516,7 +550,7 @@ def SeqPutEx(self, imgData, LineOffset, LineLoad, SequenceId=None, PicOffset=0, PARAMETERS ---------- - imgData : list, 1D array or 1D ndarray + imgData : numpy.ndarray Data stream corresponding to a sequence of nSizeX by nSizeX images. Values has to be between 0 and 255. LineOffset : int @@ -540,11 +574,6 @@ def SeqPutEx(self, imgData, LineOffset, LineLoad, SequenceId=None, PicOffset=0, Depends on ALP_DATA_FORMAT. PicLoad = 0 correspond to a complete sequence. By default, PicLoad = 0. - dataFormat : string, optional - Specify the type of data sent as image. - Should be ' Python' or 'C'. - If the data is of Python format, it is converted into a C array before sending to the DMD via the dll. - By default dataFormat = 'Python' """ if not SequenceId: @@ -556,13 +585,8 @@ def SeqPutEx(self, imgData, LineOffset, LineLoad, SequenceId=None, PicOffset=0, ct.c_long(LineOffset), ct.c_long(LineLoad)) - if dataFormat not in ['Python', 'C']: - raise ValueError('dataFormat must be one of "Python" or "C"') - - if dataFormat == 'Python': - pImageData = imgData.astype(np.uint8).ctypes.data_as(ct.c_void_p) - elif dataFormat == 'C': - pImageData = ct.cast(imgData, ct.c_void_p) + imgData = self._cast_imgData(imgData, SequenceId) + pImageData = imgData.ctypes.data_as(ct.c_void_p) self._checkError(self._ALPLib.AlpSeqPutEx(self.ALP_ID, SequenceId, LinePutParam, pImageData), 'Cannot send image sequence to device.') @@ -587,7 +611,7 @@ def SeqPut(self, imgData, SequenceId=None, PicOffset=0, PicLoad=0, dataFormat='P PARAMETERS ---------- - imgData : list, 1D array or 1D ndarray + imgData : numpy.ndarray Data stream corresponding to a sequence of nSizeX by nSizeX images. Values has to be between 0 and 255. SequenceId : ctypes c_long @@ -601,11 +625,6 @@ def SeqPut(self, imgData, SequenceId=None, PicOffset=0, PicLoad=0, dataFormat='P Depends on ALP_DATA_FORMAT. PicLoad = 0 correspond to a complete sequence. By default, PicLoad = 0. - dataFormat : string, optional - Specify the type of data sent as image. - Should be ' Python' or 'C'. - If the data is of Python format, it is converted into a C array before sending to the DMD via the dll. - By default dataFormat = 'Python' SEE ALSO -------- @@ -616,12 +635,9 @@ def SeqPut(self, imgData, SequenceId=None, PicOffset=0, PicLoad=0, dataFormat='P if not SequenceId: SequenceId = self._lastDDRseq - if dataFormat == 'Python': - pImageData = imgData.astype(np.uint8).ctypes.data_as(ct.c_void_p) - elif dataFormat == 'C': - pImageData = ct.cast(imgData, ct.c_void_p) - else: - raise ValueError('dataFormat must be one of "Python" or "C"') + imgData = self._cast_imgData(imgData, SequenceId) + + pImageData = imgData.ctypes.data_as(ct.c_void_p) self._checkError( self._ALPLib.AlpSeqPut(self.ALP_ID, SequenceId, ct.c_long(PicOffset), ct.c_long(PicLoad), pImageData), @@ -661,8 +677,8 @@ def ImgToBitPlane(self, imgArray, bitShift=0): return img_to_bitplane(imgArray, bitShift) - def SetTiming(self, SequenceId=None, illuminationTime=None, pictureTime=None, synchDelay=None, - synchPulseWidth=None, triggerInDelay=None): + def SetTiming(self, SequenceId=None, illuminationTime=ALP_DEFAULT, pictureTime=ALP_DEFAULT, synchDelay=ALP_DEFAULT, + synchPulseWidth=ALP_DEFAULT, triggerInDelay=ALP_DEFAULT): """ Set the timing properties of the sequence to display. @@ -701,17 +717,6 @@ def SetTiming(self, SequenceId=None, illuminationTime=None, pictureTime=None, sy if (SequenceId is None): raise ValueError('No sequence to display.') - if (synchDelay is None): - synchDelay = ALP_DEFAULT - if (synchPulseWidth is None): - synchPulseWidth = ALP_DEFAULT - if (triggerInDelay is None): - triggerInDelay = ALP_DEFAULT - if (illuminationTime is None): - illuminationTime = ALP_DEFAULT - if (pictureTime is None): - pictureTime = ALP_DEFAULT - self._checkError( self._ALPLib.AlpSeqTiming(self.ALP_ID, SequenceId, ct.c_long(illuminationTime), ct.c_long(pictureTime), ct.c_long(synchDelay), ct.c_long(synchPulseWidth), ct.c_long(triggerInDelay)), @@ -1023,7 +1028,7 @@ def Run(self, SequenceId=None, loop=True): if (SequenceId is None) and (self._lastDDRseq): SequenceId = self._lastDDRseq if (SequenceId is None): - self._raiseError('No sequence to display.') + raise ValueError('No sequence to display.') if loop: self._checkError(self._ALPLib.AlpProjStartCont(self.ALP_ID, SequenceId), 'Cannot launch sequence.')