portugueslab / stytra

A modular package to control stimulation and track behaviour
http://www.portugueslab.com/stytra/
GNU General Public License v3.0
42 stars 27 forks source link

Made changes to spinnaker.py open_camera() /read() methods. Added main() #7

Closed EricThomson closed 5 years ago

EricThomson commented 5 years ago
EricThomson commented 5 years ago

I think this is a mistake:

return "Invalid parameters" + ex

Where ex is a SpinnakerException. It will yield an error because the exception isn't a string. Maybe either str(ex) or just cut the +ex part, like in the original.

vilim commented 5 years ago

Thanks Eric, we will test it and merge it as soon as we verify it works.

EricThomson commented 5 years ago

I'm currently making a bunch of changes: adding ROI functionality and fixing a problem with updating the frame rate that I found for a camera (I have now fixed this). Will update this branch in next day or so.

vigji commented 5 years ago

Ok! Testing it with our camera I saw that I need to remove the return False if the enable_rate_control is not writable (frame rate is adjustable anyway). Just for you to consider!

enable_rate_control = PySpin.CBooleanPtr(nodemap.GetNode("AcquisitionFrameRateEnabled"))
if not PySpin.IsAvailable(enable_rate_control
        ) or not PySpin.IsWritable(enable_rate_control):
            print("enable_rate_control not writable.")
        else:
            enable_rate_control.SetValue(True)
        if self.cam.AcquisitionFrameRate.GetAccessMode() != PySpin.RW:  
            print("Frame rate mode not set to read/write.")

self.cam.BeginAcquisition()
return "Spinnaker API camera successfully opened"
EricThomson commented 5 years ago

Thanks -- good catch I will absorb your comment into next iteration I hadn't changed that.

EricThomson commented 5 years ago

So this most recent commit has roi, gain, exposure, and frame rate.

In terms of the previous question about checking PySpin.IsWritable(enable_rate_control): the reason we would want to include that is (my understanding anyway) is that this will tell us when we cannot toggle the mode. This might be if the camera is already streaming, for instance, in which case that node could be locked so we would not be able to adjust it.

That said, if this updated version still leads to an error for you, and removing that one check gets rid of the error, that argues in favor of removing that check. If you still are getting that error, I will try to reproduce it and fix it.

One thing I would say is be sure you don't already have the camera streaming in spinview: I did and there are a few things that cannot be set when there is already a stream open, as some nodes are locked in this use case. I did add one warning for that where it happened (in setting the gain). But maybe that happens in this case too? Just guessing though.

Also, it may well be a bad design to just return False when these tests fail. If you would rather replace that pattern with a more forgiving one, that seems like a good idea. For instance, I could change it so if we have 'false' in those nodes the camera will just initialize with default values rather than throw an exception. In some cases you will still end up with a failure (e.g., if the camera can't go above some minimal FPS requirement), or an exception will be thrown anyway when someone tries to write to a node that isn't writeable, but it would be better probably.

vigji commented 5 years ago

Hey Eric, I tested your code and yes, the return False design is quite troublesome with our camera. Several attributes would fail to be set in that way with the camera we are using. I had to remove all the ones concerning framerate, enable_rate_mode, node_frame_rate_auto_off and acquisition_rate_node to have it working.

I believe you are a more experienced user of the Pyspin interface then us (default lab cameras are ximea and avt), so I would leave the choice of how to replace it to you - whether with my suggestions or with something else!

PS: we should add this to the developer docs somewhere, but note that if you want to return an error or warning message, as in line 236 of spinnaker.py, the err string should start with an "E:" for errors and "W:" for warnings.

vigji commented 5 years ago

In order to support our cameras and yours, we will make some changes to your suggestions. Also, to comply with the standards of the rest of the camera interfaces in the package, we will make some adjustments in how errors are handled and unify the code formatting. Please let us know when you are done with your changes so we can avoid merge issues! Thanks

EricThomson commented 5 years ago

@vigji . Shoot too bad it didn't work. I will make some changes that seem to be least annoying to the workflow, and then let you guys decide. In middle of experiments, should get to it over the next day or so. Do you mind letting me know which model of Point Grey camera you are using? If someone here has the same/similar model I will try to reproduce the errors.

vigji commented 5 years ago

Great, thank you! I am testing on a BFS-U3-13Y3M-C USB3 Blackfly camera.

EricThomson commented 5 years ago

When you get the chance, could you please run the attached and let me know the log file/printout produced, and whether the gain/frame rate/roi/exposure sets properly on your camera? Feel free to send it to my email account instead of here if you want to continue discussion offline.

Unfortunately I couldn't get my hands on a blackfly camera, so I made this debugger class to test the spinnaker api. You can either print to stdout or file, depending on which line at the top you comment out.....

import numpy as np  
import logging
#To log to file:
#logging.basicConfig(level = logging.DEBUG, filename = 'spincam.log', filemode = 'w') #set to 'a' to append

#To log to stdout:
logging.basicConfig(level = logging.DEBUG)

try:
    import PySpin
except ImportError:
    logging.critical("Pyspin not found. There is no sense going on.")
    raise ImportError
else:
    logging.debug("PySpin imported successfully")

class SpinnakerCamera:
    """Class for simple control of a Point Grey camera.

    Uses Spinnaker API. Module documentation `here
    <https://www.flir.com/products/spinnaker-sdk/>`_.
    """
    def __init__(self, roi, frame_rate, gain, exposure):
        self.system = PySpin.System.GetInstance()
        self.cam = self.system.GetCameras()[0]
        self.roi = roi
        self.frame_rate = frame_rate
        self.exposure = exposure
        self.gain = gain
        assert isinstance(self.cam, PySpin.CameraPtr)
        logging.debug("Working with a {0} camera".format(self.cam_info()))

    def cam_info(self):
        nodemap_tldevice = self.cam.GetTLDeviceNodeMap()
        camera_name = PySpin.CStringPtr(nodemap_tldevice.GetNode('DeviceDisplayName'))
        self.camera_name = camera_name.ToString()
        return self.camera_name  

    def open_camera(self):
        self.cam.Init()
        nodemap = self.cam.GetNodeMap()

        ####################################
        # SET TO CONTINUOUS ACQUISITION MODE
        logging.info("\nACQUISITION MODE ")
        acquisition_mode_node = PySpin.CEnumerationPtr(nodemap.GetNode("AcquisitionMode"))
        if PySpin.IsAvailable(acquisition_mode_node):
            logging.debug("    acquisition_mode is available.")
        else:
            logging.debug("    acquisition_mode NOT available.")

        if PySpin.IsWritable(acquisition_mode_node):
            logging.debug("    acquisition_mode is writeable")
        else:
            logging.debug("    acquisition_mode is NOT writeable.")

        # Retrieve entry node from enumeration node
        acquisition_mode_continuous_node = acquisition_mode_node.GetEntryByName("Continuous")
        if PySpin.IsAvailable(acquisition_mode_continuous_node):
           logging.debug("    continuos_mode available")
        else: 
            logging.debug("    continuous_mode NOT available")

        if PySpin.IsReadable(acquisition_mode_continuous_node):
            logging.debug("    continuous_mode readable (so you can read and set it).")
        else:
            logging.debug("    continuous_mode NOT readable")

        #Regardless of above results, try to set
        try:
            acquisition_mode_continuous = acquisition_mode_continuous_node.GetValue()
            acquisition_mode_node.SetIntValue(acquisition_mode_continuous)
            logging.info("    Acquisition Mode successfully set to Continuous...")
        except Exception as ex:
            logging.error("    acquisition mode not set: {0}.".format(ex))

        ###########
        # ROI
        #Set this first as frame rate limits depend on this
        #Note need to check increment because some cameras restrict increments of width/offset
        if self.roi[0] > 0:
            try:
                logging.info("\nROI")
                #Width 
                #Note set width/height before x/y offset becuase upon initialization max offset is 0 b/c 
                # it is assuming full-frame
                width_node = PySpin.CIntegerPtr(nodemap.GetNode('Width'))
                if PySpin.IsAvailable(width_node):
                    logging.debug("    ROI width_node available.")
                else:
                    logging.debug("    ROI width_node NOT available.")

                if PySpin.IsWritable(width_node):
                    logging.debug("    ROI width_node is writeable")
                else:
                    logging.debug("    ROI width_node NOT writeable.")

                #Regardless, try to set
                #width_to_set = width_node.GetMax()    #default
                width_inc = width_node.GetInc()
                width_to_set = self.roi[2]
                if np.mod(width_to_set, width_inc) != 0:
                    width_to_set = (width_to_set//width_inc)*width_inc
                    logging.warning("    Need to set width in increments of {0}, resetting to {1}.".format(width_inc, width_to_set))
                try:
                    width_node.SetValue(width_to_set)
                    logging.info('    Width set to {0}.'.format(width_node.GetValue()))           
                except Exception as ex:
                    logging.error(    "ROI width not set: {0}.".format(ex))

                # Height
                height_node = PySpin.CIntegerPtr(nodemap.GetNode('Height'))
                if PySpin.IsAvailable(height_node):
                    logging.debug("    ROI height node available.")
                else:
                    logging.debug("    ROI height node NOT available.")

                if PySpin.IsWritable(height_node):
                    logging.debug("    ROI height node is writeable.")
                else:
                    logging.debug("    ROI height node is NOT writeable")

                #Regardless, try to set height
                #height_to_set = height_node.GetMax()  #default
                height_inc = height_node.GetInc()
                height_to_set = self.roi[3]
                if np.mod(height_to_set, height_inc) != 0:
                    height_to_set = (height_to_set//height_inc)*height_inc
                    logging.warning("    Need to set height in increments of {0}, resetting to {1}.".format(height_inc, height_to_set))
                try:
                    height_node.SetValue(height_to_set)
                    logging.info('    Height set to {0}'.format(height_node.GetValue()))
                except Exception as ex:
                    logging.error(    "ROI height not set: {0}.".format(ex))

                # x-offset
                offset_x_node = PySpin.CIntegerPtr(nodemap.GetNode('OffsetX'))
                if PySpin.IsAvailable(offset_x_node):
                    logging.debug("    ROI x available")
                else:
                    logging.debug("    ROI x NOT available")
                if  PySpin.IsWritable(offset_x_node):
                    logging.debug("    ROI x writeable")
                else:
                    logging.debug("    ROI x NOT writeable")
                #Try to set
                #x_to_set = offset_x_node.GetMin()  #default (usually 0)
                x_to_set = self.roi[0]
                try:
                    offset_x_node.SetValue(x_to_set)
                    logging.info('    x offset set to {0}'.format(offset_x_node.GetValue()))
                except Exception as ex:
                    logging.error("    x offset not set: {0}.".format(ex))

                # y-offset
                offset_y_node = PySpin.CIntegerPtr(nodemap.GetNode('OffsetY'))
                if PySpin.IsAvailable(offset_y_node) and PySpin.IsWritable(offset_y_node):
                    #y_to_set = offset_y_node.GetMin()  #default (usually 0)
                    #print("    Min, max y: ", offset_y_node.GetMin(), offset_y_node.GetMax())
                    y_to_set = self.roi[1]
                    offset_y_node.SetValue(y_to_set)
                    logging.info('    y offset set to {0}'.format(offset_y_node.GetValue()))
                else:
                    logging.warning('    ROI: Offset Y not available...')
                    return False
                logging.info('    ROI successfully enabled')
            except Exception as ex:
                logging.error('    Could not set ROI. Exeption: {0}.'.format(ex))

        #############################
        # FRAME RATE 
        # Disable auto frame rate
        #Set this second: exposure time limits depend on this
        logging.info("\nFRAME RATE")
        frame_rate_auto_node = PySpin.CEnumerationPtr(nodemap.GetNode("AcquisitionFrameRateAuto"))
        if PySpin.IsAvailable(frame_rate_auto_node):
            logging.debug("    frame_rate_auto available")
        else: 
            logging.debug("    frame_rate_auto NOT available")

        if PySpin.IsWritable(frame_rate_auto_node):
            logging.debug("    frame_rate_auto writeable")
        else:
            logging.debug("    frame_rate_auto NOT writeable")
        node_frame_rate_auto_off = frame_rate_auto_node.GetEntryByName("Off")
        if PySpin.IsAvailable(node_frame_rate_auto_off):
            logging.debug("    frame_auto_off available")
        else: 
            logging.debug("    frame_auto_off NOT available")
        if PySpin.IsReadable(node_frame_rate_auto_off):
            logging.debug("    frame_rate_auto readable")
        else:
            logging.debug("    frame_rate_auto NOT readable") 
        try:
            frame_rate_auto_off = node_frame_rate_auto_off.GetValue()
            frame_rate_auto_node.SetIntValue(frame_rate_auto_off)     
            logging.info( "    Frame Rate Auto set to Off...")
        except Exception as ex:
            logging.error("    Frame_rate_aut could not be turned off: {0}.".format(ex))

        # Enable frame rate control
        enable_rate_mode = PySpin.CBooleanPtr(nodemap.GetNode("AcquisitionFrameRateEnabled"))
        if PySpin.IsAvailable(enable_rate_mode):
            logging.debug("    rate_mode_enable available")
        else:
            logging.debug("    rate_mode_enable NOT available")

        if PySpin.IsWritable(enable_rate_mode):
            logging.debug("    rate_mode_enable writeable")
        else:
            logging.debug("    rate_mode_enable NOT writeable")
        try:
            enable_rate_mode.SetValue(True)
            logging.info("    enable_rate_mode set to True")
        except Exception as ex:
            logging.error("    enable_rate_mode could not be set: {0}.".format(ex))
        # Check to make sure you successfully made frame rate writeable
        acquisition_rate_node = self.cam.AcquisitionFrameRate
        rate_max = acquisition_rate_node.GetMax()
        rate_min = acquisition_rate_node.GetMin()
        logging.info("    Frame rate min|max: {0} | {1}.".format(rate_min, rate_max))
        if acquisition_rate_node.GetAccessMode() == PySpin.RW:  
            logging.debug("    Frame rate mode is read/write.")
        else:
            logging.debug("    frame rate node NOT read/write mode.")

        #Try setting frame rate
        if self.frame_rate > rate_max:
            logging.warning("Attempt to set fps greater than max, setting to {0}".format(rate_max))
            self.frame_rate = rate_max
        elif frame_rate < rate_min:
            logging.warning("Attempt to set fps less than min, setting to {0}".format(rate_min))
            self.frame_rate = rate_min
        try:   
            acquisition_rate_node.SetValue(self.frame_rate)  
            logging.debug("    Frame rate successfully set to {0} Hz".format(frame_rate))
        except Exception as ex:
            logging.error("    Frame rate not set: {0}.".format(ex))

        ##############
        # EXPOSURE 
        logging.info("\nEXPOSURE")
        #Turn off auto exposure
        exposure_auto_node = self.cam.ExposureAuto
        if exposure_auto_node.GetAccessMode() == PySpin.RW:
            logging.debug("    exposure_auto_node is in read/write mode")
        else:
            logging.debug("    exposure_auto_mode is NOT in read/write mode")
        try:
            exposure_auto_node.SetValue(PySpin.ExposureAuto_Off)
            logging.debug('    Automatic exposure disabled...')
        except Exception as ex:
            logging.error("    Autoexposure Not turned off: {0}".format(ex))

        # Check for availability/writeability of exposure time
        exposure_time_node = self.cam.ExposureTime
        if exposure_time_node.GetAccessMode() == PySpin.RW:
            logging.debug('    exposure time is r/w')
        else:
            logging.debug('    exposure time is NOT r/w')

        # Set exposure time (and ensure doesn't exceed max)
        exposure_max = exposure_time_node.GetMax()
        exposure_min = exposure_time_node.GetMin()
        logging.info("    min/max exposure times (ms): {0} | {1}".format(exposure_min/1000, exposure_max/1000))

        # camera wants exposure in us:
        exposure_time_to_set = self.exposure*1000  #convert to microseconds
        if exposure_time_to_set > exposure_max:
            logging.warning("    exposure time greater than max: setting it to max of {0}".format(exposure_max/1000))
            exposure_time_to_set = exposure_max
        elif exposure_time_to_set < exposure_min:
            logging.warning("    exposure time less than min: setting it to min of {0}".format(exposure_min/1000))
            exposure_time_to_set = exposure_min
        try:
            exposure_time_node.SetValue(exposure_time_to_set)
            logging.debug("    Exposure sucessfully set to {0} ms".format(exposure_time_to_set/1000))
        except Exception as ex:
            logging.error("    Exposure NOT set: {0}".format(ex))

        #####################################
        # GAIN 
        logging.info("\nGAIN")
        #Turn off auto-gain
        gain_auto_node = self.cam.GainAuto
        if gain_auto_node.GetAccessMode() == PySpin.RW:
            logging.debug("    autogain in rw mode")
        else:
            logging.debug("    autogain is NOT in rw mode")
        try:
            gain_auto_node.SetValue(PySpin.GainAuto_Off)
            logging.debug("    Automatic gain disabled...")
        except Exception as ex:
            logging.error("   Automatic gain NOT disabled: {0}".format(ex))
        # Set new gain
        gain_node = self.cam.Gain
        gain_min = gain_node.GetMin()
        gain_max = gain_node.GetMax()
        logging.info("    Gain: current, min|max: {0}, {1} | {2} ".format(gain_node.GetValue(), gain_min, gain_max))
        gain_to_set = self.gain
        if gain_to_set >gain_max:
            logging.warning("    gain greater than max - setting it to max of {0}".format(gain_max))
            gain_to_set = gain_max
        elif gain_to_set < gain_min:
            logging.warning("    gain less than min - setting it to min of {0}".format(gain_min))
            gain_to_set = gain_min
        if gain_node.GetAccessMode() == PySpin.RW:
            logging.debug("    Gain is in RW mode")
        else:
            logging.debug("    Gain is NOT in rw mode")
        try:
            gain_node.SetValue(gain_to_set)
            logging.debug("    Gain successfully set to {0}.".format(gain_to_set))
        except Exception as ex:
            logging.error("    Gain not set: {0}".format(ex))

        #  START ACQUISITION
        self.cam.BeginAcquisition()
        return "SpinnakerCamera instance successfully opened"

    def read(self):
        #get next image: if it is good, return it
        try:
            image_result = self.cam.GetNextImage()
            if image_result.IsIncomplete():
                return
            else:
                image_converted = np.array(image_result.GetData(), dtype="uint8").reshape((image_result.GetHeight(), 
                                                                                           image_result.GetWidth()) );
                image_result.Release()
                return image_converted

        except PySpin.SpinnakerException as ex:
            logging.error("read error: {0}".format(ex))
            return None

    def release(self):
        self.cam.EndAcquisition()
        self.cam.DeInit()
        del self.cam
        self.system.ReleaseInstance()

if __name__ == "__main__":
    """ 
    Test PySpin api/SpinnakerCamera() using opencv.
    """
    roi = [500, 200, 640, 320]  #x, y, width, height
    frame_rate = 20.0
    gain = 0.6
    exposure_ms = 20.
    logging.info("\n**Testing SpinnakerCamera()**".format(frame_rate))
    import cv2
    cv2.namedWindow("SpinCam", cv2.WINDOW_NORMAL)
    spinCam = SpinnakerCamera(roi, frame_rate, gain, exposure_ms)
    spinCam.open_camera()
    while True:
        image = spinCam.read()
        cv2.imshow("SpinCam", image)
        key = cv2.waitKey(1)  
        if key == 27: #escape key
            logging.info("Streaming stopped")
            cv2.destroyAllWindows()
            spinCam.release()
            break
vigji commented 5 years ago

I've run it, and this is the log and the error that I get:

DEBUG:root:PySpin imported successfully
INFO:root:
**Testing SpinnakerCamera()**
DEBUG:root:Working with a Point Grey Research Blackfly S BFS-U3-13Y3M camera
INFO:root:
ACQUISITION MODE 
DEBUG:root:    acquisition_mode is available.
DEBUG:root:    acquisition_mode is writeable
DEBUG:root:    continuos_mode available
DEBUG:root:    continuous_mode readable (so you can read and set it).
INFO:root:    Acquisition Mode successfully set to Continuous...
INFO:root:
ROI
DEBUG:root:    ROI width_node available.
DEBUG:root:    ROI width_node is writeable
INFO:root:    Width set to 640.
DEBUG:root:    ROI height node available.
DEBUG:root:    ROI height node is writeable.
INFO:root:    Height set to 320
DEBUG:root:    ROI x available
DEBUG:root:    ROI x writeable
INFO:root:    x offset set to 500
INFO:root:    y offset set to 200
INFO:root:    ROI successfully enabled
INFO:root:
FRAME RATE
DEBUG:root:    frame_rate_auto NOT available
DEBUG:root:    frame_rate_auto NOT writeable
Traceback (most recent call last):
  File "C:/Users/lpetrucco/.PyCharm2018.3/config/scratches/scratch_4.py", line 399, in <module>
    spinCam.open_camera()
  File "C:/Users/lpetrucco/.PyCharm2018.3/config/scratches/scratch_4.py", line 204, in open_camera
    node_frame_rate_auto_off = frame_rate_auto_node.GetEntryByName("Off")
  File "C:\Users\lpetrucco\AppData\Local\Continuum\Anaconda3\lib\site-packages\PySpin\PySpin.py", line 15184, in GetEntryByName
    return _PySpin.CEnumerationPtr_GetEntryByName(self, Symbolic)
_PySpin.SpinnakerException: Spinnaker: LogicalErrorException NULL pointer dereferenced [-2005]

Process finished with exit code -1073740791 (0xC0000409)
EricThomson commented 5 years ago

Thanks a lot this is really helpful: I will work on it and see what I can do :) Of course that's one of the few getters/setters I didn't wrap in a try/except loop, as I was thinking it would be overkill. Ha!

Edit added: Sorry to do this but could you please try the one below: it shouldn't give that same bug (though I do expect it to fail because it looks like frame_rate_auto is not available in your camera for some reason -- I just want to see how far it goes before it fails catastrophically versus gracefully). Thanks a lot for working with me on this...

Another thing it would be helpful if this does fail: can you change the FPS using the spinview GUI that comes with the SDK? This would let me know it is not an issue with your camera or the api itself, but the python code I'm trying to use.


import numpy as np  
import logging
#To log to file:
#logging.basicConfig(level = logging.DEBUG, filename = 'spincam.log', filemode = 'w') #set to 'a' to append

#To log to stdout:
logging.basicConfig(level = logging.DEBUG)

try:
    import PySpin
except ImportError:
    logging.critical("Pyspin not found. There is no sense going on.")
    raise ImportError
else:
    logging.debug("PySpin imported successfully")

class SpinnakerCamera:
    """Class for simple control of a Point Grey camera.

    Uses Spinnaker API. Module documentation `here
    <https://www.flir.com/products/spinnaker-sdk/>`_.
    """
    def __init__(self, roi, frame_rate, gain, exposure):
        self.system = PySpin.System.GetInstance()
        self.cam = self.system.GetCameras()[0]
        self.roi = roi
        self.frame_rate = frame_rate
        self.exposure = exposure
        self.gain = gain
        assert isinstance(self.cam, PySpin.CameraPtr)
        logging.debug("Working with a {0} camera".format(self.cam_info()))

    def cam_info(self):
        nodemap_tldevice = self.cam.GetTLDeviceNodeMap()
        camera_name = PySpin.CStringPtr(nodemap_tldevice.GetNode('DeviceDisplayName'))
        self.camera_name = camera_name.ToString()
        return self.camera_name  

    def open_camera(self):
        self.cam.Init()
        nodemap = self.cam.GetNodeMap()

        ####################################
        # SET TO CONTINUOUS ACQUISITION MODE
        logging.info("\nACQUISITION MODE ")
        acquisition_mode_node = PySpin.CEnumerationPtr(nodemap.GetNode("AcquisitionMode"))
        if PySpin.IsAvailable(acquisition_mode_node):
            logging.debug("    acquisition_mode is available.")
        else:
            logging.debug("    acquisition_mode NOT available.")

        if PySpin.IsWritable(acquisition_mode_node):
            logging.debug("    acquisition_mode is writeable")
        else:
            logging.debug("    acquisition_mode is NOT writeable.")

        # Retrieve entry node from enumeration node
        acquisition_mode_continuous_node = acquisition_mode_node.GetEntryByName("Continuous")
        if PySpin.IsAvailable(acquisition_mode_continuous_node):
           logging.debug("    continuos_mode available")
        else: 
            logging.debug("    continuous_mode NOT available")

        if PySpin.IsReadable(acquisition_mode_continuous_node):
            logging.debug("    continuous_mode readable (so you can read and set it).")
        else:
            logging.debug("    continuous_mode NOT readable")

        #Regardless of above results, try to set
        try:
            acquisition_mode_continuous = acquisition_mode_continuous_node.GetValue()
            acquisition_mode_node.SetIntValue(acquisition_mode_continuous)
            logging.info("    Acquisition Mode successfully set to Continuous...")
        except Exception as ex:
            logging.error("    acquisition mode not set: {0}.".format(ex))

        ###########
        # ROI
        #Set this first as frame rate limits depend on this
        #Note need to check increment because some cameras restrict increments of width/offset
        if self.roi[0] > 0:
            try:
                logging.info("\nROI")
                #Width 
                #Note set width/height before x/y offset becuase upon initialization max offset is 0 b/c 
                # it is assuming full-frame
                width_node = PySpin.CIntegerPtr(nodemap.GetNode('Width'))
                if PySpin.IsAvailable(width_node):
                    logging.debug("    ROI width_node available.")
                else:
                    logging.debug("    ROI width_node NOT available.")

                if PySpin.IsWritable(width_node):
                    logging.debug("    ROI width_node is writeable")
                else:
                    logging.debug("    ROI width_node NOT writeable.")

                #Regardless, try to set
                #width_to_set = width_node.GetMax()    #default
                width_inc = width_node.GetInc()
                width_to_set = self.roi[2]
                if np.mod(width_to_set, width_inc) != 0:
                    width_to_set = (width_to_set//width_inc)*width_inc
                    logging.warning("    Need to set width in increments of {0}, resetting to {1}.".format(width_inc, width_to_set))
                try:
                    width_node.SetValue(width_to_set)
                    logging.info('    Width set to {0}.'.format(width_node.GetValue()))           
                except Exception as ex:
                    logging.error(    "ROI width not set: {0}.".format(ex))

                # Height
                height_node = PySpin.CIntegerPtr(nodemap.GetNode('Height'))
                if PySpin.IsAvailable(height_node):
                    logging.debug("    ROI height node available.")
                else:
                    logging.debug("    ROI height node NOT available.")

                if PySpin.IsWritable(height_node):
                    logging.debug("    ROI height node is writeable.")
                else:
                    logging.debug("    ROI height node is NOT writeable")

                #Regardless, try to set height
                #height_to_set = height_node.GetMax()  #default
                height_inc = height_node.GetInc()
                height_to_set = self.roi[3]
                if np.mod(height_to_set, height_inc) != 0:
                    height_to_set = (height_to_set//height_inc)*height_inc
                    logging.warning("    Need to set height in increments of {0}, resetting to {1}.".format(height_inc, height_to_set))
                try:
                    height_node.SetValue(height_to_set)
                    logging.info('    Height set to {0}'.format(height_node.GetValue()))
                except Exception as ex:
                    logging.error(    "ROI height not set: {0}.".format(ex))

                # x-offset
                offset_x_node = PySpin.CIntegerPtr(nodemap.GetNode('OffsetX'))
                if PySpin.IsAvailable(offset_x_node):
                    logging.debug("    ROI x available")
                else:
                    logging.debug("    ROI x NOT available")
                if  PySpin.IsWritable(offset_x_node):
                    logging.debug("    ROI x writeable")
                else:
                    logging.debug("    ROI x NOT writeable")
                #Try to set
                #x_to_set = offset_x_node.GetMin()  #default (usually 0)
                x_to_set = self.roi[0]
                try:
                    offset_x_node.SetValue(x_to_set)
                    logging.info('    x offset set to {0}'.format(offset_x_node.GetValue()))
                except Exception as ex:
                    logging.error("    x offset not set: {0}.".format(ex))

                # y-offset
                offset_y_node = PySpin.CIntegerPtr(nodemap.GetNode('OffsetY'))
                if PySpin.IsAvailable(offset_y_node) and PySpin.IsWritable(offset_y_node):
                    #y_to_set = offset_y_node.GetMin()  #default (usually 0)
                    #print("    Min, max y: ", offset_y_node.GetMin(), offset_y_node.GetMax())
                    y_to_set = self.roi[1]
                    offset_y_node.SetValue(y_to_set)
                    logging.info('    y offset set to {0}'.format(offset_y_node.GetValue()))
                else:
                    logging.warning('    ROI: Offset Y not available...')
                    return False
                logging.info('    ROI successfully enabled')
            except Exception as ex:
                logging.error('    Could not set ROI. Exeption: {0}.'.format(ex))

        #############################
        # FRAME RATE 
        # Disable auto frame rate
        #Set this second: exposure time limits depend on this
        logging.info("\nFRAME RATE")
        frame_rate_auto_node = PySpin.CEnumerationPtr(nodemap.GetNode("AcquisitionFrameRateAuto"))
        if PySpin.IsAvailable(frame_rate_auto_node):
            logging.debug("    frame_rate_auto available")
        else: 
            logging.debug("    frame_rate_auto NOT available")

        if PySpin.IsWritable(frame_rate_auto_node):
            logging.debug("    frame_rate_auto writeable")
        else:
            logging.debug("    frame_rate_auto NOT writeable")
        try:
            node_frame_rate_auto_off = frame_rate_auto_node.GetEntryByName("Off")
            logging.debug("    got frame_auto_node off")
        except Exception as ex:
            logging.error("   could not get frame_auto_node off: {0}".format(ex))

        if PySpin.IsAvailable(node_frame_rate_auto_off):
            logging.debug("    frame_auto_off available")
        else: 
            logging.debug("    frame_auto_off NOT available")
        if PySpin.IsReadable(node_frame_rate_auto_off):
            logging.debug("    frame_rate_auto readable")
        else:
            logging.debug("    frame_rate_auto NOT readable") 
        try:
            frame_rate_auto_off = node_frame_rate_auto_off.GetValue()
            frame_rate_auto_node.SetIntValue(frame_rate_auto_off)     
            logging.info( "    Frame Rate Auto set to Off...")
        except Exception as ex:
            logging.error("    Frame_rate_aut could not be turned off: {0}.".format(ex))

        # Enable frame rate control
        try:
            enable_rate_mode = PySpin.CBooleanPtr(nodemap.GetNode("AcquisitionFrameRateEnabled"))
            logging.debug("    frame rate mode enabled acquired")
        except Exception as ex:
            logging.error("    could not acquire frame rate enable mode: {0}".format(ex))

        if PySpin.IsAvailable(enable_rate_mode):
            logging.debug("    rate_mode_enable available")
        else:
            logging.debug("    rate_mode_enable NOT available")

        if PySpin.IsWritable(enable_rate_mode):
            logging.debug("    rate_mode_enable writeable")
        else:
            logging.debug("    rate_mode_enable NOT writeable")
        try:
            enable_rate_mode.SetValue(True)
            logging.info("    enable_rate_mode set to True")
        except Exception as ex:
            logging.error("    enable_rate_mode could not be set: {0}.".format(ex))
        # Check to make sure you successfully made frame rate writeable
        acquisition_rate_node = self.cam.AcquisitionFrameRate
        rate_max = acquisition_rate_node.GetMax()
        rate_min = acquisition_rate_node.GetMin()
        logging.info("    Frame rate min|max: {0} | {1}.".format(rate_min, rate_max))
        if acquisition_rate_node.GetAccessMode() == PySpin.RW:  
            logging.debug("    Frame rate mode is read/write.")
        else:
            logging.debug("    frame rate node NOT read/write mode.")

        #Try setting frame rate
        if self.frame_rate > rate_max:
            logging.warning("Attempt to set fps greater than max, setting to {0}".format(rate_max))
            self.frame_rate = rate_max
        elif frame_rate < rate_min:
            logging.warning("Attempt to set fps less than min, setting to {0}".format(rate_min))
            self.frame_rate = rate_min
        try:   
            acquisition_rate_node.SetValue(self.frame_rate)  
            logging.debug("    Frame rate successfully set to {0} Hz".format(frame_rate))
        except Exception as ex:
            logging.error("    Frame rate not set: {0}.".format(ex))

        ##############
        # EXPOSURE 
        logging.info("\nEXPOSURE")
        #Turn off auto exposure
        exposure_auto_node = self.cam.ExposureAuto
        if exposure_auto_node.GetAccessMode() == PySpin.RW:
            logging.debug("    exposure_auto_node is in read/write mode")
        else:
            logging.debug("    exposure_auto_mode is NOT in read/write mode")
        try:
            exposure_auto_node.SetValue(PySpin.ExposureAuto_Off)
            logging.debug('    Automatic exposure disabled...')
        except Exception as ex:
            logging.error("    Autoexposure Not turned off: {0}".format(ex))

        # Check for availability/writeability of exposure time
        exposure_time_node = self.cam.ExposureTime
        if exposure_time_node.GetAccessMode() == PySpin.RW:
            logging.debug('    exposure time is r/w')
        else:
            logging.debug('    exposure time is NOT r/w')

        # Set exposure time (and ensure doesn't exceed max)
        exposure_max = exposure_time_node.GetMax()
        exposure_min = exposure_time_node.GetMin()
        logging.info("    min/max exposure times (ms): {0} | {1}".format(exposure_min/1000, exposure_max/1000))

        # camera wants exposure in us:
        exposure_time_to_set = self.exposure*1000  #convert to microseconds
        if exposure_time_to_set > exposure_max:
            logging.warning("    exposure time greater than max: setting it to max of {0}".format(exposure_max/1000))
            exposure_time_to_set = exposure_max
        elif exposure_time_to_set < exposure_min:
            logging.warning("    exposure time less than min: setting it to min of {0}".format(exposure_min/1000))
            exposure_time_to_set = exposure_min
        try:
            exposure_time_node.SetValue(exposure_time_to_set)
            logging.debug("    Exposure sucessfully set to {0} ms".format(exposure_time_to_set/1000))
        except Exception as ex:
            logging.error("    Exposure NOT set: {0}".format(ex))

        #####################################
        # GAIN 
        logging.info("\nGAIN")
        #Turn off auto-gain
        gain_auto_node = self.cam.GainAuto
        if gain_auto_node.GetAccessMode() == PySpin.RW:
            logging.debug("    autogain in rw mode")
        else:
            logging.debug("    autogain is NOT in rw mode")
        try:
            gain_auto_node.SetValue(PySpin.GainAuto_Off)
            logging.debug("    Automatic gain disabled...")
        except Exception as ex:
            logging.error("   Automatic gain NOT disabled: {0}".format(ex))
        # Set new gain
        gain_node = self.cam.Gain
        gain_min = gain_node.GetMin()
        gain_max = gain_node.GetMax()
        logging.info("    Gain: current, min|max: {0}, {1} | {2} ".format(gain_node.GetValue(), gain_min, gain_max))
        gain_to_set = self.gain
        if gain_to_set >gain_max:
            logging.warning("    gain greater than max - setting it to max of {0}".format(gain_max))
            gain_to_set = gain_max
        elif gain_to_set < gain_min:
            logging.warning("    gain less than min - setting it to min of {0}".format(gain_min))
            gain_to_set = gain_min
        if gain_node.GetAccessMode() == PySpin.RW:
            logging.debug("    Gain is in RW mode")
        else:
            logging.debug("    Gain is NOT in rw mode")
        try:
            gain_node.SetValue(gain_to_set)
            logging.debug("    Gain successfully set to {0}.".format(gain_to_set))
        except Exception as ex:
            logging.error("    Gain not set: {0}".format(ex))

        #  START ACQUISITION
        self.cam.BeginAcquisition()
        return "SpinnakerCamera instance successfully opened"

    def read(self):
        #get next image: if it is good, return it
        try:
            image_result = self.cam.GetNextImage()
            if image_result.IsIncomplete():
                return
            else:
                image_converted = np.array(image_result.GetData(), dtype="uint8").reshape((image_result.GetHeight(), 
                                                                                           image_result.GetWidth()) );
                image_result.Release()
                return image_converted

        except PySpin.SpinnakerException as ex:
            logging.error("read error: {0}".format(ex))
            return None

    def release(self):
        self.cam.EndAcquisition()
        self.cam.DeInit()
        del self.cam
        self.system.ReleaseInstance()

if __name__ == "__main__":
    """ 
    Test PySpin api/SpinnakerCamera() using opencv.
    """
    roi = [500, 200, 640, 320]  #x, y, width, height
    frame_rate = 20.0
    gain = 0.6
    exposure_ms = 20.
    logging.info("\n**Testing SpinnakerCamera()**".format(frame_rate))
    import cv2
    cv2.namedWindow("SpinCam", cv2.WINDOW_NORMAL)
    spinCam = SpinnakerCamera(roi, frame_rate, gain, exposure_ms)
    spinCam.open_camera()
    while True:
        image = spinCam.read()
        cv2.imshow("SpinCam", image)
        key = cv2.waitKey(1)  
        if key == 27: #escape key
            logging.info("Streaming stopped")
            cv2.destroyAllWindows()
            spinCam.release()
            break
vigji commented 5 years ago

Closer! I can control the frame rate from the SpinView interface, and I could also controlling correctly frame rate from our rudimentary API version.

Thank you for working on this!

DEBUG:root:PySpin imported successfully
INFO:root:
**Testing SpinnakerCamera()**
DEBUG:root:Working with a Point Grey Research Blackfly S BFS-U3-13Y3M camera
INFO:root:
ACQUISITION MODE 
DEBUG:root:    acquisition_mode is available.
DEBUG:root:    acquisition_mode is writeable
DEBUG:root:    continuos_mode available
DEBUG:root:    continuous_mode readable (so you can read and set it).
INFO:root:    Acquisition Mode successfully set to Continuous...
INFO:root:
ROI
DEBUG:root:    ROI width_node available.
DEBUG:root:    ROI width_node is writeable
INFO:root:    Width set to 640.
DEBUG:root:    ROI height node available.
DEBUG:root:    ROI height node is writeable.
INFO:root:    Height set to 320
DEBUG:root:    ROI x available
DEBUG:root:    ROI x writeable
INFO:root:    x offset set to 500
INFO:root:    y offset set to 200
INFO:root:    ROI successfully enabled
INFO:root:
FRAME RATE
DEBUG:root:    frame_rate_auto NOT available
DEBUG:root:    frame_rate_auto NOT writeable
ERROR:root:   could not get frame_auto_node off: Spinnaker: LogicalErrorException NULL pointer dereferenced [-2005]
Traceback (most recent call last):
  File "C:/Users/lpetrucco/.PyCharm2018.3/config/scratches/scratch_4.py", line 412, in <module>
    spinCam.open_camera()
  File "C:/Users/lpetrucco/.PyCharm2018.3/config/scratches/scratch_4.py", line 212, in open_camera
    if PySpin.IsAvailable(node_frame_rate_auto_off):
UnboundLocalError: local variable 'node_frame_rate_auto_off' referenced before assignment
EricThomson commented 5 years ago

Ah ok this is very helpful: I think this may be a firmware issue I will look into and and see if there is a solution so all cameras will work. My very first try was to just use the simpler code:

self.cam.AcquisitionFrameRateEnable.SetValue(True)
self.cam.AcquisitionFrameRate.SetValue(400)

And this created an error, and led me to the much more complicated code that works on my two new cameras (FYI I have chameleon3 and grasshopper3). But even the node name has changed, I think (I have to use "AcquisitionFrameRateEnabled" for my nodes: note the d).

Let me look more into this, I think it is important we figure it out because the simpler code is also floating around online so people clearly have cameras that it works with.

We probably just need a simple conditional: I'm hoping it won't be a list of cameras

EricThomson commented 5 years ago

Ok let's see I basically made a conditional where the older node is tried, and if that is not available I try the newer node. Hopefully this works. We may have to do a similar thing for the gain/exposure but let's try it.

import numpy as np  
import logging
#To log to file:
#logging.basicConfig(level = logging.DEBUG, filename = 'spincam.log', filemode = 'w') #set to 'a' to append

#To log to stdout:
logging.basicConfig(level = logging.DEBUG)

try:
    import PySpin
except ImportError:
    logging.critical("Pyspin not found. There is no sense going on.")
    raise ImportError
else:
    logging.debug("PySpin imported successfully")

class SpinnakerCamera:
    """Class for simple control of a Point Grey camera.

    Uses Spinnaker API. Module documentation `here
    <https://www.flir.com/products/spinnaker-sdk/>`_.
    """
    def __init__(self, roi, frame_rate, gain, exposure):
        self.system = PySpin.System.GetInstance()
        self.cam = self.system.GetCameras()[0]
        self.roi = roi
        self.frame_rate = frame_rate
        self.exposure = exposure
        self.gain = gain
        assert isinstance(self.cam, PySpin.CameraPtr)
        logging.debug("Working with a {0} camera".format(self.cam_info()))

    def cam_info(self):
        nodemap_tldevice = self.cam.GetTLDeviceNodeMap()
        camera_name = PySpin.CStringPtr(nodemap_tldevice.GetNode('DeviceDisplayName'))
        self.camera_name = camera_name.ToString()
        return self.camera_name  

    def open_camera(self):
        self.cam.Init()
        nodemap = self.cam.GetNodeMap()

        ####################################
        # SET TO CONTINUOUS ACQUISITION MODE
        logging.info("\nACQUISITION MODE ")
        acquisition_mode_node = PySpin.CEnumerationPtr(nodemap.GetNode("AcquisitionMode"))
        if PySpin.IsAvailable(acquisition_mode_node):
            logging.debug("    acquisition_mode is available.")
        else:
            logging.debug("    acquisition_mode NOT available.")

        if PySpin.IsWritable(acquisition_mode_node):
            logging.debug("    acquisition_mode is writeable")
        else:
            logging.debug("    acquisition_mode is NOT writeable.")

        # Retrieve entry node from enumeration node
        acquisition_mode_continuous_node = acquisition_mode_node.GetEntryByName("Continuous")
        if PySpin.IsAvailable(acquisition_mode_continuous_node):
           logging.debug("    continuos_mode available")
        else: 
            logging.debug("    continuous_mode NOT available")

        if PySpin.IsReadable(acquisition_mode_continuous_node):
            logging.debug("    continuous_mode readable (so you can read and set it).")
        else:
            logging.debug("    continuous_mode NOT readable")

        #Regardless of above results, try to set
        try:
            acquisition_mode_continuous = acquisition_mode_continuous_node.GetValue()
            acquisition_mode_node.SetIntValue(acquisition_mode_continuous)
            logging.info("    Acquisition Mode successfully set to Continuous...")
        except Exception as ex:
            logging.error("    acquisition mode not set: {0}.".format(ex))

        ###########
        # ROI
        #Set this first as frame rate limits depend on this
        #Note need to check increment because some cameras restrict increments of width/offset
        if self.roi[0] > 0:
            try:
                logging.info("\nROI")
                #Width 
                #Note set width/height before x/y offset becuase upon initialization max offset is 0 b/c 
                # it is assuming full-frame
                width_node = PySpin.CIntegerPtr(nodemap.GetNode('Width'))
                if PySpin.IsAvailable(width_node):
                    logging.debug("    ROI width_node available.")
                else:
                    logging.debug("    ROI width_node NOT available.")

                if PySpin.IsWritable(width_node):
                    logging.debug("    ROI width_node is writeable")
                else:
                    logging.debug("    ROI width_node NOT writeable.")

                #Regardless, try to set
                #width_to_set = width_node.GetMax()    #default
                width_inc = width_node.GetInc()
                width_to_set = self.roi[2]
                if np.mod(width_to_set, width_inc) != 0:
                    width_to_set = (width_to_set//width_inc)*width_inc
                    logging.warning("    Need to set width in increments of {0}, resetting to {1}.".format(width_inc, width_to_set))
                try:
                    width_node.SetValue(width_to_set)
                    logging.info('    Width set to {0}.'.format(width_node.GetValue()))           
                except Exception as ex:
                    logging.error(    "ROI width not set: {0}.".format(ex))

                # Height
                height_node = PySpin.CIntegerPtr(nodemap.GetNode('Height'))
                if PySpin.IsAvailable(height_node):
                    logging.debug("    ROI height node available.")
                else:
                    logging.debug("    ROI height node NOT available.")

                if PySpin.IsWritable(height_node):
                    logging.debug("    ROI height node is writeable.")
                else:
                    logging.debug("    ROI height node is NOT writeable")

                #Regardless, try to set height
                #height_to_set = height_node.GetMax()  #default
                height_inc = height_node.GetInc()
                height_to_set = self.roi[3]
                if np.mod(height_to_set, height_inc) != 0:
                    height_to_set = (height_to_set//height_inc)*height_inc
                    logging.warning("    Need to set height in increments of {0}, resetting to {1}.".format(height_inc, height_to_set))
                try:
                    height_node.SetValue(height_to_set)
                    logging.info('    Height set to {0}'.format(height_node.GetValue()))
                except Exception as ex:
                    logging.error(    "ROI height not set: {0}.".format(ex))

                # x-offset
                offset_x_node = PySpin.CIntegerPtr(nodemap.GetNode('OffsetX'))
                if PySpin.IsAvailable(offset_x_node):
                    logging.debug("    ROI x available")
                else:
                    logging.debug("    ROI x NOT available")
                if  PySpin.IsWritable(offset_x_node):
                    logging.debug("    ROI x writeable")
                else:
                    logging.debug("    ROI x NOT writeable")
                #Try to set
                #x_to_set = offset_x_node.GetMin()  #default (usually 0)
                x_to_set = self.roi[0]
                try:
                    offset_x_node.SetValue(x_to_set)
                    logging.info('    x offset set to {0}'.format(offset_x_node.GetValue()))
                except Exception as ex:
                    logging.error("    x offset not set: {0}.".format(ex))

                # y-offset
                offset_y_node = PySpin.CIntegerPtr(nodemap.GetNode('OffsetY'))
                if PySpin.IsAvailable(offset_y_node) and PySpin.IsWritable(offset_y_node):
                    #y_to_set = offset_y_node.GetMin()  #default (usually 0)
                    #print("    Min, max y: ", offset_y_node.GetMin(), offset_y_node.GetMax())
                    y_to_set = self.roi[1]
                    offset_y_node.SetValue(y_to_set)
                    logging.info('    y offset set to {0}'.format(offset_y_node.GetValue()))
                else:
                    logging.warning('    ROI: Offset Y not available...')
                    return False
                logging.info('    ROI successfully enabled')
            except Exception as ex:
                logging.error('    Could not set ROI. Exeption: {0}.'.format(ex))

        #############################
        # FRAME RATE 
        # Disable auto frame rate
        #Set this second: exposure time limits depend on this       
        logging.info("\nFRAME RATE")
        acquisition_rate_node = self.cam.AcquisitionFrameRate
        rate_max = acquisition_rate_node.GetMax()
        rate_min = acquisition_rate_node.GetMin()
        logging.info("    Frame rate min|max: {0} | {1}.".format(rate_min, rate_max))
        #Set frame rate within min/max limits
        if self.frame_rate > rate_max:
            logging.warning("    Attempt to set fps greater than max, setting to {0}".format(rate_max))
            self.frame_rate = rate_max
        elif frame_rate < rate_min:
            logging.warning("    Attempt to set fps less than min, setting to {0}".format(rate_min))
            self.frame_rate = rate_min
        else: 
            logging.info("    Your frame rate of {0} should be fine".format(self.frame_rate))

        #check for older node or newer node
        node_acquisition_frame_rate_control_enable = PySpin.CBooleanPtr(nodemap.GetNode("AcquisitionFrameRateEnable"))
        if PySpin.IsAvailable(node_acquisition_frame_rate_control_enable):
            self.cam.AcquisitionFrameRateEnable.SetValue(True)
            self.cam.AcquisitionFrameRate.SetValue(self.frame_rate)
            logging.debug("    successfully set frame rate using legacy node")
        else:
            frame_rate_auto_node = PySpin.CEnumerationPtr(nodemap.GetNode("AcquisitionFrameRateAuto"))
            if PySpin.IsAvailable(frame_rate_auto_node):
                logging.debug("    frame_rate_auto available")
            else: 
                logging.debug("    frame_rate_auto NOT available")

            if PySpin.IsWritable(frame_rate_auto_node):
                logging.debug("    frame_rate_auto writeable")
            else:
                logging.debug("    frame_rate_auto NOT writeable")
            try:
                node_frame_rate_auto_off = frame_rate_auto_node.GetEntryByName("Off")
                logging.debug("    got frame_auto_node off")
            except Exception as ex:
                logging.error("   could not get frame_auto_node off: {0}".format(ex))

            if PySpin.IsAvailable(node_frame_rate_auto_off):
                logging.debug("    frame_auto_off available")
            else: 
                logging.debug("    frame_auto_off NOT available")
            if PySpin.IsReadable(node_frame_rate_auto_off):
                logging.debug("    frame_rate_auto readable")
            else:
                logging.debug("    frame_rate_auto NOT readable") 
            try:
                frame_rate_auto_off = node_frame_rate_auto_off.GetValue()
                frame_rate_auto_node.SetIntValue(frame_rate_auto_off)     
                logging.info( "    Frame Rate Auto set to Off...")
            except Exception as ex:
                logging.error("    Frame_rate_aut could not be turned off: {0}.".format(ex))

            # Enable frame rate control
            try:
                enable_rate_mode = PySpin.CBooleanPtr(nodemap.GetNode("AcquisitionFrameRateEnabled"))
                logging.debug("    frame rate mode enabled acquired")
            except Exception as ex:
                logging.error("    could not acquire frame rate enable mode: {0}".format(ex))

            if PySpin.IsAvailable(enable_rate_mode):
                logging.debug("    rate_mode_enable available")
            else:
                logging.debug("    rate_mode_enable NOT available")

            if PySpin.IsWritable(enable_rate_mode):
                logging.debug("    rate_mode_enable writeable")
            else:
                logging.debug("    rate_mode_enable NOT writeable")
            try:
                enable_rate_mode.SetValue(True)
                logging.info("    enable_rate_mode set to True")
            except Exception as ex:
                logging.error("    enable_rate_mode could not be set: {0}.".format(ex))
            # Check to make sure you successfully made frame rate writeable

            if acquisition_rate_node.GetAccessMode() == PySpin.RW:  
                logging.debug("    Frame rate mode is read/write.")
            else:
                logging.debug("    frame rate node NOT read/write mode.")
            try:   
                acquisition_rate_node.SetValue(self.frame_rate)  
                logging.debug("    Frame rate successfully set to {0} Hz".format(frame_rate))
            except Exception as ex:
                logging.error("    Frame rate not set: {0}.".format(ex))

        ##############
        # EXPOSURE 
        logging.info("\nEXPOSURE")
        #Turn off auto exposure
        exposure_auto_node = self.cam.ExposureAuto
        if exposure_auto_node.GetAccessMode() == PySpin.RW:
            logging.debug("    exposure_auto_node is in read/write mode")
        else:
            logging.debug("    exposure_auto_mode is NOT in read/write mode")
        try:
            exposure_auto_node.SetValue(PySpin.ExposureAuto_Off)
            logging.debug('    Automatic exposure disabled...')
        except Exception as ex:
            logging.error("    Autoexposure Not turned off: {0}".format(ex))

        # Check for availability/writeability of exposure time
        exposure_time_node = self.cam.ExposureTime
        if exposure_time_node.GetAccessMode() == PySpin.RW:
            logging.debug('    exposure time is r/w')
        else:
            logging.debug('    exposure time is NOT r/w')

        # Set exposure time (and ensure doesn't exceed max)
        exposure_max = exposure_time_node.GetMax()
        exposure_min = exposure_time_node.GetMin()
        logging.info("    min/max exposure times (ms): {0} | {1}".format(exposure_min/1000, exposure_max/1000))

        # camera wants exposure in us:
        exposure_time_to_set = self.exposure*1000  #convert to microseconds
        if exposure_time_to_set > exposure_max:
            logging.warning("    exposure time greater than max: setting it to max of {0}".format(exposure_max/1000))
            exposure_time_to_set = exposure_max
        elif exposure_time_to_set < exposure_min:
            logging.warning("    exposure time less than min: setting it to min of {0}".format(exposure_min/1000))
            exposure_time_to_set = exposure_min
        try:
            exposure_time_node.SetValue(exposure_time_to_set)
            logging.debug("    Exposure sucessfully set to {0} ms".format(exposure_time_to_set/1000))
        except Exception as ex:
            logging.error("    Exposure NOT set: {0}".format(ex))

        #####################################
        # GAIN 
        logging.info("\nGAIN")
        #Turn off auto-gain
        gain_auto_node = self.cam.GainAuto
        if gain_auto_node.GetAccessMode() == PySpin.RW:
            logging.debug("    autogain in rw mode")
        else:
            logging.debug("    autogain is NOT in rw mode")
        try:
            gain_auto_node.SetValue(PySpin.GainAuto_Off)
            logging.debug("    Automatic gain disabled...")
        except Exception as ex:
            logging.error("   Automatic gain NOT disabled: {0}".format(ex))
        # Set new gain
        gain_node = self.cam.Gain
        gain_min = gain_node.GetMin()
        gain_max = gain_node.GetMax()
        logging.info("    Gain: current, min|max: {0}, {1} | {2} ".format(gain_node.GetValue(), gain_min, gain_max))
        gain_to_set = self.gain
        if gain_to_set >gain_max:
            logging.warning("    gain greater than max - setting it to max of {0}".format(gain_max))
            gain_to_set = gain_max
        elif gain_to_set < gain_min:
            logging.warning("    gain less than min - setting it to min of {0}".format(gain_min))
            gain_to_set = gain_min
        if gain_node.GetAccessMode() == PySpin.RW:
            logging.debug("    Gain is in RW mode")
        else:
            logging.debug("    Gain is NOT in rw mode")
        try:
            gain_node.SetValue(gain_to_set)
            logging.debug("    Gain successfully set to {0}.".format(gain_to_set))
        except Exception as ex:
            logging.error("    Gain not set: {0}".format(ex))

        #  START ACQUISITION
        self.cam.BeginAcquisition()
        return "SpinnakerCamera instance successfully opened"

    def read(self):
        #get next image: if it is good, return it
        try:
            image_result = self.cam.GetNextImage()
            if image_result.IsIncomplete():
                return
            else:
                image_converted = np.array(image_result.GetData(), dtype="uint8").reshape((image_result.GetHeight(), 
                                                                                           image_result.GetWidth()) );
                image_result.Release()
                return image_converted

        except PySpin.SpinnakerException as ex:
            logging.error("read error: {0}".format(ex))
            return None

    def release(self):
        self.cam.EndAcquisition()
        self.cam.DeInit()
        del self.cam
        self.system.ReleaseInstance()

if __name__ == "__main__":
    """ 
    Test PySpin api/SpinnakerCamera() using opencv.
    """
    roi = [500, 200, 640, 320]  #x, y, width, height
    frame_rate = 20.0
    gain = 0.6
    exposure_ms = 20.
    logging.info("\n**Testing SpinnakerCamera()**".format(frame_rate))
    import cv2
    cv2.namedWindow("SpinCam", cv2.WINDOW_NORMAL)
    spinCam = SpinnakerCamera(roi, frame_rate, gain, exposure_ms)
    spinCam.open_camera()
    while True:
        image = spinCam.read()
        cv2.imshow("SpinCam", image)
        key = cv2.waitKey(1)  
        if key == 27: #escape key
            logging.info("Streaming stopped")
            cv2.destroyAllWindows()
            spinCam.release()
            break
vigji commented 5 years ago

It seems to be working now! :) If it is fine for you, I will slightly polish the class to better fit within the stytra flow and pull a new version, and if that still works for you we can merge it!

EricThomson commented 5 years ago

vigji: that is great! I am not sure why they changed the api for frame rate it made it way too complicated, but I'm glad this works! I prefer the old one frankly. :)

note that class is not really meant for general use, but is an ugly debugger class just meant to troubleshoot. If that worked, I was planning to work it up into something that better conforms to the form of the other camera classes in Stytra, and post a final PR for you guys to work with, as you see fit. Does that sound good? I can do that sometime today.

vigji commented 5 years ago

Great! I have seen your new pull, if you are happy with it I will test and merge it tomorrow :)

vigji commented 5 years ago

Test this in a full stytra script with you camera if you can!

EricThomson commented 5 years ago

Great thanks will check this out today!