class TestHarvesterCoreBase(unittest.TestCase): _cti_file_path = get_cti_file_path() sys.path.append(_cti_file_path) def __init__(self, *args, **kwargs): # super().__init__(*args, **kwargs) # self._harvester = None self._ia = None self._thread = None self._logger = get_logger(name='harvesters', level=DEBUG) self._buffers = [] def setUp(self): # super().setUp() # self._harvester = Harvester(logger=self._logger) self._harvester.add_file(self._cti_file_path) self._harvester.update() def tearDown(self): # if self.ia: self.ia.destroy() # self._harvester.reset() # self._ia = None # super().tearDown() @property def harvester(self): return self._harvester @property def ia(self): return self._ia @ia.setter def ia(self, value): self._ia = value @property def general_purpose_thread(self): return self._thread @general_purpose_thread.setter def general_purpose_thread(self, value): self._thread = value def is_running_with_default_target(self): return True if 'TLSimu.cti' in self._cti_file_path else False
def open(self) -> None: """TBW.""" if self._simulate: return h = Harvester() locs = [ r"~/tools/mvImpact/lib/x86_64/mvGenTLProducer.cti", r"C:\Program Files\MATRIX VISION\mvIMPACT Acquire\bin\x64\mvGenTLProducer.cti" ] cti = "" for loc in locs: if Path(loc).expanduser().exists(): cti = loc if not cti: raise FileNotFoundError( "Could not locate cti file: mvGenTLProducer.cti") cti = str(Path(cti).expanduser()) h.add_file(cti) h.update() len(h.device_info_list) h.device_info_list[0] print("creating ia....") ia = h.create_image_acquirer(0) print("ia created") # ia = h.create_image_acquirer(serial_number='050200047485') # ia.remote_device.node_map.Width.value = 1024 # max: 1312 # ia.remote_device.node_map.Height.value = 1024 # max: 1082 # ia.remote_device.node_map.PixelFormat.value = 'Mono12' from harvesters.core import ImageAcquirer print(ImageAcquirer.Events.__members__) from harvesters.core import Callback class CallbackOnNewBuffer(Callback): def __init__(self, ia: ImageAcquirer): # super().__init__() # self._ia = ia def emit(self, context): # # You would implement this method by yourself. # with _ia.fetch_buffer() as buffer: # # Work with the fetched buffer. # print(buffer) print(datetime.utcnow(), "New image") on_new_buffer = CallbackOnNewBuffer(self) ia.add_callback(ia.Events.NEW_BUFFER_AVAILABLE, on_new_buffer) ia.start_acquisition(run_in_background=True) self._dev = h self._ia = ia self.on_new_buffer = on_new_buffer
class HarvestersSource(Node, Observable): def __init__(self): Node.__init__(self, "Harvesters") Observable.__init__(self, self._on_subscribe) self.cfg = self.config[SECTION] self.harvester = Harvester() if self.cfg["disableInternalLogger"]: self.harvester._logger.setLevel(100) self.cti_file = self.cfg["ctiFile"] self.harvester.add_file(self.cti_file) self.logger.info(f"Loaded harvester cti file {self.cti_file}") self.harvester.update() self.logger.info( f"Found {len(self.harvester.device_info_list)} devices.") def _on_subscribe(self, observer: Observer, scheduler=None): self.acquirer = self.harvester.create_image_acquirer(list_index=0) self.reload_camera_driver() self.acquirer.add_callback( self.acquirer.Events.NEW_BUFFER_AVAILABLE, CallbackOnNewBuffer(self.acquirer, observer)) self.acquirer.start_image_acquisition(run_in_background=True) self.logger.info("Started acquisition in background") def dispose(): def _async_dispose(*args): # prevent join in the same thread. self.logger.info("Stopping image acquisition") self.acquirer.stop_image_acquisition() self.acquirer.destroy() self.logger.info("Stopped image acquisition") (scheduler or NewThreadScheduler()).schedule(_async_dispose) return Disposable(dispose) def reload_camera_driver(self): node = self.acquirer.device.node_map node.LineSelector.value = self.cfg["lineSelector"] node.LineMode.value = self.cfg["lineMode"] node.LineInverter.value = self.cfg["lineInverter"] node.LineSource.value = "ExposureActive" node.ExposureTime.value = self.cfg["exposureTime"] if node.has_node("acquisitionFrameRateMode"): # Allied Vision Camera node.AcquisitionFrameRateMode.value = "Basic" node.AcquisitionFrameRate.value = self.cfg["acquisitionFrameRate"] if self.cfg["extraNodes"]: extra_nodes: dict = self.cfg["extraNodes"] for k, v in extra_nodes.items(): node.get_node(k).value = v
class TestHarvesterNoCleanUp(TestHarvesterBase): def __init__(self, *args, **kwargs): # super().__init__(*args, **kwargs) def setUp(self): # super().setUp() # self._harvester = Harvester(logger=self._logger, do_clean_up=False) self._harvester.add_file(self._cti_file_path) self._harvester.update()
def run_camera(): # Create a Harvester object: h = Harvester() # Load a GenTL Producer; you can load many more if you want to: ##find producer h.add_file( 'C:\Program Files\Allied Vision\Vimba_4.2\VimbaGigETL\Bin\Win64\VimbaGigETL.cti' ) # Enumerate the available devices that GenTL Producers can handle: h.update() print(h.device_info_list) # Select a target device and create an ImageAcquire object that # controls the device: ia = h.create_image_acquirer(0) # Configure the target device; it looks very small but this is just # for demonstration: # ia.remote_device.node_map.Width.value = 1936 # ia.remote_device.node_map.Height.value = 1216 # ia.remote_device.node_map.PixelFormat.value = 'RGB8Packed' # Allow the ImageAcquire object to start image acquisition: ia.start_acquisition() # We are going to fetch a buffer filled up with an image: # Note that you'll have to queue the buffer back to the # ImageAcquire object once you consumed the buffer; the # with statement takes care of it on behalf of you: with ia.fetch_buffer() as buffer: # Let's create an alias of the 2D image component; it can be # lengthy which is not good for typing. In addition, note that # a 3D camera can give you 2 or more components: component = buffer.payload.components[0] # Reshape the NumPy array into a 2D array: image_rgb = component.data.reshape(component.height, component.width, 3) image_bgr = cv2.cvtColor(image_rgb, cv2.COLOR_RGB2BGR) # img_inference runs both models processing_time, boxes, scores, labels = img_inference(image_bgr) # Stop the ImageAcquier object acquiring images: ia.stop_acquisition() # We're going to leave here shortly: ia.destroy() h.reset()
def run(self): h = Harvester() h.add_file(self._cti_file_path) h.update() try: ia = h.create_image_acquirer(0) except: # Transfer the exception anyway: self._message_queue.put(sys.exc_info()) else: ia.start_acquisition() ia.stop_acquisition() ia.destroy() h.reset()
class Camera: def __init__(self): self.connection_exceptions = () self.__harv = Harvester() self.__img_cap = None pass def connect(self): try: self.__harv.add_file('/usr/lib/ids/cti/ids_gevgentl.cti') self.__harv.update() except Exception as e: print(e) pass def disconnect(self): self.__img_cap.destroy() self.__harv.reset() pass def initialize(self): try: self.__img_cap = self.__harv.create_image_acquirer(list_index=0) self.__img_cap.remote_device.node_map.PixelFormat.value = 'Mono8' except Exception as e: print(e) pass def capture(self) -> str: try: # Start acquisition self.__img_cap.start_acquisition() with self.__img_cap.fetch_buffer() as buffer: component = buffer.payload.components[0] # reshape to a 2D array _2d = component.data.reshape(component.height, component.width) # Stop acquisition self.__img_cap.stop_acquisition() # SERIALIZE _2d _2d_bytes = pickle.dumps(_2d) encoded = base64.b64encode(_2d_bytes) print(_2d) print(_2d_bytes) print(encoded) # MUST RETURN A JSON STRING AS: image = { "filename": "my_image{}".format(randrange(2000)), "image": encoded.decode('ascii') } # To consume #decoded = base64.b64decode(image['image']) #_2d_loaded = pickle.loads(decoded) #print(decoded) #print(_2d_loaded) return json.dumps(image) except Exception as e: print(e) return None def configure(self, parameters): pass
class TestTutorials2(unittest.TestCase): def setUp(self) -> None: # The following block is just for administrative purpose; # you should not include it in your code: self.cti_file_path = get_cti_file_path() if 'TLSimu.cti' not in self.cti_file_path: self.skipTest('The target is not TLSimu.') # Create a Harvester object: self.harvester = Harvester() def tearDown(self) -> None: # self.harvester.reset() def test_traversable_tutorial(self): # Add a CTI file path: self.harvester.add_file(self.cti_file_path) self.harvester.update() # Connect to the first camera in the list: ia = self.harvester.create_image_acquirer(0) # num_images_to_acquire = 0 # Then start image acquisition: ia.start_acquisition() while num_images_to_acquire < 100: # with ia.fetch_buffer() as buffer: # self.do_something(buffer) pass num_images_to_acquire += 1 # We don't need the ImageAcquirer object. Destroy it: ia.destroy() def test_ticket_127(self): # self.harvester.add_cti_file(self.cti_file_path) self.harvester.remove_cti_file(self.cti_file_path) # self.harvester.add_cti_file(self.cti_file_path) self.harvester.remove_cti_files() # self.harvester.add_cti_file(self.cti_file_path) self.assertIsNotNone(self.harvester.cti_files) # self.harvester.update_device_info_list() # Connect to the first camera in the list: ia = self.harvester.create_image_acquirer(0) # ia.start_image_acquisition() self.assertTrue(ia.is_acquiring_images()) ia.stop_image_acquisition() self.assertFalse(ia.is_acquiring_images())
# Issues: camera startup is troublesome, GenTL module presents errors after fetch_buffer call import time import numpy as np #from genicam.genapi import NodeMap from harvesters.core import Harvester from harvesters.util.pfnc import mono_location_formats import png h = Harvester() # h.add_file('/opt/cvb-13.02.002/drivers/genicam/libGevTL.cti') # h.add_file('/opt/baumer-gapi-sdk/lib/libbgapi2_gige.cti') h.add_file('/opt/mvIMPACT_Acquire/lib/x86_64/mvGenTLProducer.cti') h.update() print(h.device_info_list) #activates cam, light green ia = h.create_image_acquirer(0) #print(dir(ia.remote_device.node_map)) # ia.remote_device.node_map.load_xml_from_string('/home/gus/GigE-V/xml/Teledyne DALSA/TeledyneDALSA_Nano-IMX267-304_Mono_9M-12M_40769e92_ECA18.0014.xml') ia.remote_device.node_map.acquisitionFrameRateControlMode = 'Programmable' ia.remote_device.node_map.AcquisitionMode.value = 'Continuous' ia.remote_device.node_map.ExposureMode.value = 'Timed' ia.remote_device.node_map.ExposureTime.value = 200024
def test_issue_181_with_invalid_file(self): h = Harvester() with self.assertRaises(OSError): h.add_file(__file__, check_validity=True)
def test_issue_181_with_nonexistent_file(self): h = Harvester() with self.assertRaises(FileNotFoundError): h.add_file('just a string', check_existence=True)
class ImageAcquirer: # Storage for camera modules GigE = [] # Maximum fetch tries until reconnect and abort fetchTimeout = None fetchSoftReboot = None fetchAbort = None cv2 = None np = None # Function runs when initializing class def __init__(self, SetCameraLighting, Config_module=None, Warnings_module=None): # Misc from time import sleep import sys self.sleep = sleep self.warnings = Warnings_module self.sys = sys # Store custom module self.FileConfig = Config_module # Lights function self.SetCameraLighting = SetCameraLighting # GenICam helper from harvesters.core import Harvester, TimeoutException # Init harvester self.harvester = Harvester() self.TimeoutException = TimeoutException # Temperature self.criticalTemp = self.FileConfig.Get( "Cameras")["Generic"]["Temperature"]["Critical"] self.warningTemp = self.FileConfig.Get( "Cameras")["Generic"]["Temperature"]["Warning"] # Storage for camera modules self.n_camera = self.FileConfig.Get("QuickSettings")["ActiveCameras"] self.ImportCTI() # import cti file self.Scan() # check if producer is available self.Create() # define image Acquirer objects from discovered devices self.Config() # configure image acquirer objects self.ImportOpenCV() # Create opencv module # Import cti file from GenTL producer def ImportCTI(self): # path to GenTL producer CTIPath = self.FileConfig.Get("Cameras")["Generic"]["CTIPath"] from os import path if path.isfile(CTIPath): self.harvester.add_file(CTIPath) else: print( "\nCould not find the GenTL producer for GigE\nCheck the file path given in VQuIT_config.json>Cameras>Generic>CTIPath" ) self.sys.exit(1) def ImportOpenCV(self): if self.cv2 is None: print("Importing OpenCV") import cv2 self.cv2 = cv2 return self.cv2 def ImportNumpy(self): if self.np is None: print("Importing Numpy") import numpy as np self.np = np return self.np # Scan for available producers def Scan(self): tries = 100 for i in range(0, tries): self.harvester.update() foundDevices = len(self.harvester.device_info_list) print('Scanning for available cameras... ' + str(foundDevices) + " of " + str(self.n_camera) + " (Attempt " + str(i + 1) + " of " + str(tries) + ")", end='\r') if foundDevices >= self.n_camera: break self.sleep(1) if len(self.harvester.device_info_list) < self.n_camera: print("Error: Found ", len(self.harvester.device_info_list), " of ", self.n_camera, "requested producers in network") self.sys.exit(1) # print(self.harvester.device_info_list) # Show details of connected devices # Create image acquirer objects def Create(self): cameraInfo = self.FileConfig.Get("Cameras")["Advanced"] for i in range(0, self.n_camera): try: # Create camera instances in order written in VQuIT_Config.json>Cameras>Advanced newIA = self.harvester.create_image_acquirer( id_=cameraInfo[i]["ID"]) self.GigE.append(newIA) except: print( "Error: ID '" + str(cameraInfo[i]["ID"]) + "' not found\nMake sure no other instances are connected to the cameras" ) exit() # Configure image acquirer objects def Config(self): # Load configuration file (Use ["Description"] instead of ["Value"] to get a description of said parameter) qs = self.FileConfig.Get("QuickSettings") c = self.FileConfig.Get("Cameras") cameraInfo = c["Advanced"] imgFormat = c["Generic"]["ImageFormatControl"] acquisition = c["Generic"]["AcquisitionControl"] transport = c["Generic"]["TransportLayerControl"] trigger = c["Generic"]["TimedTriggered_Parameters"] fetchError = c["Generic"]["FetchError"] # Maximum fetch tries per camera self.fetchTimeout = fetchError["Timeout"] self.fetchSoftReboot = fetchError["SoftReboot"] self.fetchAbort = fetchError["Abort"] # Jumbo packets jumboPackets = qs["JumboPackets"] if jumboPackets: print("Jumbo packets Active\n") packetSize = transport["GevSCPSPacketSize"]["Value"][0] else: print("\r") self.warnings.warn( "Running script without jumbo packets can cause quality and reliability issues" ) self.sleep(0.2) packetSize = transport["GevSCPSPacketSize"]["Value"][1] # Binning binning = qs["Binning"] if binning: print("Binning Active") imgWidth = int(imgFormat["Resolution"]["Width"] / 4) imgHeight = int(imgFormat["Resolution"]["Height"] / 4) binningType = imgFormat["BinningType"]["Value"][1] else: imgWidth = imgFormat["Resolution"]["Width"] imgHeight = imgFormat["Resolution"]["Height"] binningType = imgFormat["BinningType"]["Value"][0] # Set standard camera parameters for cameraID in range(0, len(self.GigE)): print("Setting up camera " + cameraInfo[cameraID]["Camera"] + "...", end="\r") # ImageFormatControl self.GigE[ cameraID].remote_device.node_map.PixelFormat.value = imgFormat[ "PixelFormat"]["Value"][0] self.GigE[ cameraID].remote_device.node_map.Binning.value = binningType self.GigE[cameraID].remote_device.node_map.ReverseX.value = False self.GigE[cameraID].remote_device.node_map.ReverseY.value = False # AcquisitionControl self.GigE[ cameraID].remote_device.node_map.ExposureMode.value = acquisition[ "ExposureMode"]["Value"][0] # TransportLayerControl self.GigE[ cameraID].remote_device.node_map.GevSCPSPacketSize.value = packetSize # Stock: 1060 | recommended 8228 # TimedTriggered parameters self.GigE[ cameraID].remote_device.node_map.FrameAverage.value = trigger[ "FrameAverage"]["Value"] self.GigE[ cameraID].remote_device.node_map.MultiExposureNumber.value = trigger[ "MultiExposureNumber"]["Value"] self.GigE[cameraID].remote_device.node_map.MultiExposureInactiveRaw.value = \ trigger["MultiExposureInactive"][ "Value"] # Not in use # AcquisitionPeriod (Integration time - irrelevant when using TimedTriggered) # value: microseconds (min: 102775 µs @4096 x 3008 - BayerRG8 - Binning Disabled (Max frame rate 9.73 Hz) , max: 60s) # Set resolution self.SetROI(imgHeight, imgWidth) # Start image acquisition def Start(self): print("\nStart image acquisition\n") for i in range(0, len(self.GigE)): self.GigE[i].start_acquisition() # Set Region Of Interest resolution and center resulting image (very experimental) def SetROI(self, height, width, disableAcquisition=None): # Check if height and width are valid heightMax = 3000 # Absolute max 3008 widthMax = 3072 # Absolute max 4096 if height <= heightMax and width <= widthMax: # Increment height and width until a valid combination (for which the pixel area is dividable by 4096) is found # (not fool proof but work with most decent aspect ratios that increment with 100) self.warnings.warn( "Dynamic ROI is still an experimental feature and can cause errors" ) rightValue = False heightMaxReached = False while not rightValue: # Try height and width if ((height * width) % 4096) == 0: rightValue = True else: # Try height + 1 and width height += 1 if height > heightMax: heightMaxReached = True height = heightMax if ((height * width) % 4096) == 0: rightValue = True else: # Try height and width + 1 if heightMaxReached is False: height -= 1 width += 1 if width > widthMax: width = widthMax if ((height * width) % 4096) == 0: rightValue = True else: # Set height + 1 and width + 1 and run loop again height += 1 if height > heightMax: heightMaxReached = True height = heightMax print("Dynamic ROI calculator result: " + str(width) + "x" + str(height)) # Change settings for all available cameras for cameraID in range(0, len(self.GigE)): # Check if requested resolution does not exceed the max for each camera widthMax = self.GigE[ cameraID].remote_device.node_map.WidthMax.value heightMax = self.GigE[ cameraID].remote_device.node_map.HeightMax.value widthMin = 512 heightMin = 512 # Check boundaries if width in range(widthMin, (widthMax + 1)) and height in range( heightMin, (heightMax + 1)): # Image acquisition cannot be on when changing this setting if disableAcquisition is True: self.GigE[cameraID].stop_acquisition() # Set width and height self.GigE[cameraID].remote_device.node_map.Width.value = width self.GigE[ cameraID].remote_device.node_map.Height.value = height # Set offsets offsetX = round((widthMax - width) / 2) offsetY = round((widthMax - width) / 2) self.GigE[ cameraID].remote_device.node_map.OffsetX.value = offsetX self.GigE[ cameraID].remote_device.node_map.OffsetY.value = offsetY # Turn image acquisition back on if disableAcquisition is True: self.GigE[cameraID].start_acquisition() else: raise ValueError("Requested ROI (" + str(width) + "x" + str(height) + ") must lie between " + str(widthMin) + "x" + str(heightMin) + " and " + str(widthMax) + "x" + str(heightMax) + " for camera " + str(cameraID)) def SetCameraConfig(self, productInfo): # Stop image acquisition to make changes self.Stop() # Set configuration for all cameras based on acode of product for cameraID in range(0, len(self.GigE)): if (cameraID % 2) == 0: # Camera number is even -> bottom camera cameraPosition = "BottomCameras" else: # Top camera cameraPosition = "TopCameras" cameraConfig = productInfo["Configuration"][cameraPosition] self.camConfig(cameraID, exposure=cameraConfig["ExposureTime"], gain=cameraConfig["Gain"], blackLevel=cameraConfig["BlackLevel"]) # Set ROI self.SetROI(2560, 2560) # Restart image acquisition after changes are made self.Start() # self.data_Top_Lighting = [] # for lights in cameraConfig["Lighting"]["U"]: # self.data_Top_Lighting.append(lights) # for lights in cameraConfig["Lighting"]["D"]: # self.data_Top_Lighting.append(lights) # Tweak camera settings on the go def camConfig(self, camNr, exposure=None, gain=None, blackLevel=None): if exposure: self.GigE[ camNr].remote_device.node_map.ExposureTimeRaw.value = exposure if gain: self.GigE[camNr].remote_device.node_map.GainRaw.value = gain if blackLevel: self.GigE[ camNr].remote_device.node_map.BlackLevelRaw.value = blackLevel # Retrieve camera data def RequestFrame(self, camNr): cv2 = self.ImportOpenCV() # Loop process until successful loop = 0 fetchImage = True while fetchImage: loop += 1 try: if loop > 1 and (loop % 2) is not 0: # Wait before sending new trigger every odd try that is not the first self.sleep(0.5) # Turn on lights self.SetCameraLighting(camNr, 1) # Trigger camera self.GigE[ camNr].remote_device.node_map.TriggerSoftware.execute() # Wait for buffer until timeout print("Camera " + str(camNr) + ": Fetch buffer (try " + str(loop) + ")...", end='\r') with self.GigE[camNr].fetch_buffer( timeout=self.fetchTimeout) as buffer: print("Camera " + str(camNr) + ": Fetched (try " + str(loop) + ")", end='\r') # access the image payload component = buffer.payload.components[0] if component is not None: image = component.data.reshape(component.height, component.width) # Turn off lights self.SetCameraLighting(camNr, 0) # BayerRG -> RGB (Does not work proper when image is already scaled down) image = cv2.cvtColor(image, cv2.COLOR_BayerRG2RGB) # Transpose + flip to rotate fetched images by +-90 deg image = cv2.transpose(image) if camNr % 2 == 0: # Flip x to rotate bottom cameras -90 deg flipCode = 0 else: # Flip y to rotate top cameras +90 deg flipCode = 1 image = cv2.flip(image, flipCode=flipCode) return image except self.TimeoutException: print("Camera " + str(camNr) + ": Fetch timeout (try " + str(loop) + ")") except KeyboardInterrupt: print("Camera " + str(camNr) + ": Fetch interrupted by user (try " + str(loop) + ")") # except: # print("Camera " + str(camNr) + ": Unexpected error (try " + str(loop) + ")") if loop >= self.fetchSoftReboot: print("Camera" + str(camNr) + ": Failed...trying soft reboot (try " + str(loop) + ")") self.SoftReboot() if loop >= self.fetchAbort: print("Check camera" + str(camNr) + ": Too manny tries (try " + str(loop) + " of " + str(self.fetchAbort) + ")") fetchImage = False # Something went wrong return False # Get camera temperature def getTemperature(self, camNr): return float( self.GigE[camNr].remote_device.node_map.DeviceTemperatureRaw.value / 100) # Return thermal performance of the camera def thermalCondition(self): for i in range(0, self.n_camera): temp = self.getTemperature(i) if temp > self.criticalTemp: self.warnings.warn("Camera temperature critical") return "Critical" elif temp > self.warningTemp: self.warnings.warn("Camera temperature above " + str(self.warningTemp)) return "Warning" return "Normal" # Get camera features def getCameraAttributes(self): return dir(self.GigE[0].remote_device.node_map) # Stop image acquisition def Stop(self): print("Stop image acquisition") for i in range(0, len(self.GigE)): self.GigE[i].stop_acquisition() # Stop image acquisition def Destroy(self): print("Destroy image acquire objects") for i in range(0, len(self.GigE)): self.GigE[i].destroy() # Reset harvester def Reset(self): self.harvester.reset() # Soft reboot def SoftReboot(self): self.Stop() self.Start()
""" ##pip install git+https://github.com/genicam/harvesters.git && cd harvesters && python setup.py install #pip install arena_api-2.1.4-py3-none-any.whl from harvesters.core import Harvester #Then instantiate a Harvester object; we're going to use h that stands for Harvester as its identifier. h = Harvester() ##And load a CTI file; loading a CTI file, you can communicate with the GenTL Producer: # ATTENTION! Please use the CTI file in the original location! # Why? Visit https://github.com/genicam/harvesters/wiki/FAQ and # read "I pointed out a CTI file but Harvester says the image doesn't # exist (Part 2)." h.add_file('C:/Users/jaf54iq/Anaconda3/Lib/site-packages/genicam') #Note that you can add one or more CTI files on a single Harvester Core object. To add another CTI file, just repeat calling add_file method passing another target CTI file: #h.add_file('path/to/another_gentl_producer.cti') #And the following code will let you know the CTI files that have been loaded on the Harvester object: h.files #In a contrary sense, you can remove a specific CTI file that you have added with the following code: #h.remove_file('path/to/gentl_producer.cti') #And now you have to update the list of remote devices; it fills up your device information list and you'll select a remote device to control from the list: h.update() #The following code will let you know the remote devices that you can control:
class Camera_harvester(Camera_template): def __init__(self, producer_paths=None): super(Camera_harvester, self).__init__() self.name = "Harvester" ##Harvester object used to communicate with Harvester module self.h = Harvester() ##paths to GenTL producers self.paths = [] if (producer_paths): for path in producer_paths: self.add_gentl_producer(path) ##Image acquifier object used by Harvester self.ia = None def get_camera_list(self, ): """!@brief Connected camera discovery @details Uses Harvester object to discover all connected cameras @return List of Dictionaries cantaining informations about cameras """ self.h.update() self.devices_info = [] for device in self.h.device_info_list: d = { 'id_': device.id_, 'model': device.model, 'vendor': device.vendor } self.devices_info.append(d) return self.devices_info def select_camera(self, selected_device): """!@brief choose camera to connect to @details Select camera you will be using and set Camera object accordingly @param[in] selected_device ID of a camera you want to connect to """ #translate selected device to index in harvester's device info list for index, camera in enumerate(self.devices_info): if camera['id_'] == selected_device: harvester_index = index break self.active_camera = harvester_index self.ia = self.h.create_image_acquirer(harvester_index) try: self.ia.remote_device.node_map.GevSCPSPacketSize.value = 1500 except: pass def get_parameters(self, feature_queue, flag, visibility=Config_level.Unknown): """!@brief Read parameters from camera @details Loads all available camera parameters @param[in] feature_queue each parameter's dictionary is put into this queue @param[in] flag used to signal that the method finished (threading object) @param[in] visibility Defines level of parameters that should be put in the queue @return True if success else False """ features = dir(self.ia.remote_device.node_map) for feature in features: if (feature.startswith('_')): continue try: feature_obj = getattr(self.ia.remote_device.node_map, feature).node feature = getattr(self.ia.remote_device.node_map, feature) #Some information is accessible through harvester feature, #for some information we need to go deeper into Genapi itself feat_acc = feature_obj.get_access_mode() except: continue #according to genicam standard #0 - not implemented #1 - not availablle #3 - write only #4 - read only #5 - read and write if (feat_acc == 0 or feat_acc == 1): continue feat_vis = feature_obj.visibility if (feat_vis < visibility): features_out = {} features_out['name'] = feature_obj.name #disp_name = feature.get_display_name() features_out['attr_name'] = feature_obj.display_name #Set feature's write mode try: if (feat_acc == 5 or feat_acc == 3): attr = False else: attr = True except: attr = None features_out['attr_enabled'] = attr #Get feature's type if it exists #intfIValue = 0 #: IValue interface #intfIBase = 1 #: IBase interface #intfIInteger = 2 #: IInteger interface #intfIBoolean = 3 #: IBoolean interface #intfICommand = 4 #: ICommand interface #intfIFloat = 5 #: IFloat interface #intfIString = 6 #: IString interface #intfIRegister = 7 #: IRegister interface #intfICategory = 8 #: ICategory interface #intfIEnumeration = 9 #: IEnumeration interface #ntfIEnumEntry = 10 #: IEnumEntry interface #intfIPort = 11 #: IPort interface try: attr = feature_obj.principal_interface_type if (attr == 2): attr = "IntFeature" elif (attr == 3): attr = "BoolFeature" elif (attr == 4): attr = "CommandFeature" elif (attr == 5): attr = "FloatFeature" elif (attr == 6): attr = "StringFeature" elif (attr == 9): attr = "EnumFeature" else: attr = None except: attr = None features_out['attr_type'] = attr features_out['attr_enums'] = None features_out['attr_cat'] = None #Get feature's value if it exists try: attr = feature.value except: attr = None features_out['attr_value'] = attr #Get feature's range if it exists try: attr = [feature.min, feature.max] except: attr = None features_out['attr_range'] = attr #Get feature's increment if it exists try: attr = feature.inc except: attr = None features_out['attr_increment'] = attr #Get feature's max length if it exists try: attr = feature.max_length except: attr = None features_out['attr_max_length'] = attr try: attr = feature_obj.tooltip except: attr = None features_out['attr_tooltip'] = attr feature_queue.put(features_out) flag.set() return def read_param_value(self, param_name): """!@brief Used to get value of one parameter based on its name @param[in] param_name Name of the parametr whose value we want to read @return A value of the selected parameter """ try: val = getattr(self.ia.remote_device.node_map, param_name).value return val except: return None def set_parameter(self, parameter_name, new_value): """!@brief Method for setting camera's parameters @details Sets parameter to value defined by new_value @param[in] parameter_name A name of the parameter to be changed @param[in] new_value Variable compatible with value key in parameter @return True if success else returns False """ try: getattr(self.ia.remote_device.node_map, parameter_name).value = new_value except: return False def execute_command(self, command_feature): """@brief Execute command feature type @param[in] command_feature Name of the selected command feature """ try: getattr(self.ia.remote_device.node_map, command_feature).execute() except: pass def get_single_frame(self, ): """!@brief Grab single frame from camera @return Unmodified frame from camera """ self.ia.start_acquisition() with self.ia.fetch_buffer() as buffer: frame = buffer.payload.components[0] pixel_format = self.ia.remote_device.node_map.PixelFormat.value return [frame.data, pixel_format] self.ia.stop_acquisition() def load_config(self, path): """!@brief Load existing camera configuration @param[in] path Defines a path and a name of the file containing the configuration of the camera @return True if success else False """ param = {} val = None attr_type = None with open(path, 'r') as config: config_dense = (line for line in config if line) for line in config_dense: line = line.rstrip('\n') if line.startswith('attr_value') and param: val = line.split('=') if (attr_type[1] == 'IntFeature'): self.set_parameter(param, int(val[1])) elif (attr_type[1] == 'FloatFeature'): self.set_parameter(param, float(val[1])) elif (attr_type[1] == 'EnumFeature'): self.set_parameter(param, val[1]) elif (attr_type[1] == 'BoolFeature'): if (val[1] == 'True'): self.set_parameter(param, True) else: self.set_parameter(param, False) elif (attr_type[1] == 'StringFeature'): self.set_parameter(param, val[1]) val = None param.clear() attr_type = None elif line.startswith('attr_type') and param: attr_type = line.split('=') elif line.startswith('name'): param['name'] = line.split('=')[1] val = None return True def save_config(self, path): """!@brief Saves configuration of a camera to .xml file @param[in] path A path where the file will be saved """ #At the time of writing this code not Harvester nor genapi for Python #supported saving .xml config, so the format of saved data created #here is nonstandard and simplified for now. #More here https://github.com/genicam/harvesters/issues/152 parameters = queue.Queue() tmp_flag = threading.Event() self.get_parameters(parameters, tmp_flag, Config_level.Invisible) with open(path, 'w') as config: while not parameters.empty(): param = parameters.get_nowait() for key, val in param.items(): config.write(key + "=" + str(val) + '\n') config.write('\n') def _frame_producer(self): """!@brief Gets frames from camera while continuous acquisition is active @details Loads frames from camera as they come and stores them in a frame queue for consumer thread to process. The thread runs until stream_stop_switch is set """ self.ia.start_acquisition() while (not self._stream_stop_switch.is_set()): with self.ia.fetch_buffer() as buffer: frame = buffer.payload.components[0] pixel_format = self.ia.remote_device.node_map.PixelFormat.value global_queue.frame_queue[self.cam_id].put_nowait( [frame.data.copy(), pixel_format]) #data should contain numpy array which should be compatible #with opencv image ig not do some conversion here self.ia.stop_acquisition() def disconnect_camera(self): """!@brief Disconnect camera and restores the object to its initial state""" self.stop_recording() self.disconnect_harvester() global_queue.remove_frame_queue(self.cam_id) self.__init__() #______________Unique methods___________________ def disconnect_harvester(self, ): """!@brief Destroys harvester object so other APIs can access cameras """ self.h.reset() def add_gentl_producer(self, producer_path): """!@brief Add a new frame producer to the harvester object @details Adds .cti file specified by producer_path to the harvester object @param[in] producer_path Path to a .cti file @return list of all active producers """ if (not producer_path in self.paths): if (producer_path.endswith(".cti")): self.h.add_file(producer_path) self.paths.append(producer_path) return self.paths def remove_gentl_producer(self, producer_path): """!@brief Remove existing frame producer from the harvester object @details Removes .cti file specified by producer_path from the harvester object @param[in] producer_path Path to a .cti file @return tuple of a list of remaining gentl producers and boolean value signaling whether the removal was succesful """ if (producer_path in self.paths): self.paths.remove(producer_path) self.h.remove_file(producer_path) return (self.paths, True) else: return (None, False) def get_gentl_producers(self): """!@brief Used to get a list of all path used by Harvesters in a present moment @return List of defined cti file paths """ return self.paths