Skip to content

Snap Images

Snapping an image is the most fundamental camera operation available through the Inscoper API. It triggers the camera to expose, digitize, and return a single frame, without initiating a full acquisition sequence.

The Inscoper API provides three distinct approaches to snap images programmatically. Each approach reflects a different level of control and integration complexity, and is suited to different experimental or engineering contexts:

Approach Mechanism Recommended use
Method call Direct invocation of snapImage() on the camera object Simple, synchronous image capture with immediate data access
Image listener Event-driven callback via a subclass of ImageListener Collecting images asynchronously into a buffer, displaying them on an interface, or triggering downstream processing as each frame arrives
Status sequence Declarative sequence definition via Status objects and loadSequence() Structured, repeatable acquisition workflows with recipe-based execution

All three approaches share the same initialization pattern: instantiate a Bridge, load a hardware configuration file, and initialize the devices. Once the devices are ready, the camera object is retrieved from the bridge by name and used to trigger image capture.

Note

The examples on this page use a configuration named singleCamera and a camera named MyCamera. Replace these values with the names defined in your own hardware configuration file.

Snap through method call

Snap a single image by calling snapImage() directly on the camera object. The method returns an image object from which pixel data, dimensions, and bit depth can be retrieved immediately and passed to any downstream processing or display logic.

import os
import numpy as np
import matplotlib.pyplot as plt
import inscoper_api


# Macro to all Inscoper configurations
CONFIG_PATH = os.environ['InscoperConfigsPath']

my_bridge = inscoper_api.Bridge()

# Load a specific configuration file
my_bridge.loadConfigFile(os.sep.join([CONFIG_PATH, "singleCamera"]))

# Initialize all the devices that were loaded from the configuration file
my_bridge.initDevices()

# Retrieve a list of the names of all successfully loaded and initialized devices
devices = my_bridge.getLoadedDevices()
print("Loaded devices: ", my_bridge.getLoadedDevices())

# Retrieve the specific camera object from the bridge using its configured name ("Camera")
my_camera = my_bridge.getCamera("MyCamera")

# Command the camera to take a single picture (snap an image)
# The argument '0' is the camera channel index (0 for mono)
image = my_camera.snapImage(0)

# Get the raw image data. This is typically returned as a flat 1D array of pixel values
image_data = image.getImageData()

image_width = image.getWidth()
image_height = image.getHeight()

# Get the total size of a single pixel in bytes
pixel_size_bytes = image.getPixelSize()

# Get the bit depth of the image (e.g., 8-bit, 12-bit, or 16-bit)
bit_depth = image.getBitDepth()

# Calculate the number of bytes used to represent a single intensity value
# For example, an 8-bit image uses 1 byte per value (8 // 8), a 16-bit image uses 2 bytes (16 // 8)
bytes_per_value = bit_depth // 8

# Calculate the number of values per pixel
# For a grayscale image, this is usually 1
values_per_pixel = pixel_size_bytes // bytes_per_value

print("Image width: ", image_width)
print("Image height: ", image_height)
print("Number of values per pixel: ", values_per_pixel)

# Reshape the flat 1D raw image data array into a numpy array
img = np.reshape(image_data, (image_height, image_width, values_per_pixel))

plt.imshow(img[:,:,0], cmap="gray")
plt.show()

# Safely close the connection to the hardware, release resources, and disconnect devices
my_bridge.close()

Snap and collect using a listener

Register a custom subclass of ImageListener with the bridge before snapping. Each time a new image is acquired, the bridge automatically invokes the handleNewImage() callback on the registered listener. This approach decouples image capture from image handling, allowing the application logic to remain separate from the acquisition loop.

import os
import inscoper_api


# Macro to all Inscoper configurations
CONFIG_PATH = os.environ['InscoperConfigsPath']

# Define a custom image listener class
# Listeners are used to automatically handle events, such as when a new image is ready
class CustomImageListener(inscoper_api.ImageListener):
    def __init__(self):
        super().__init__()
        self._received_images = []
        self._image_counter = 0

    # This method is automatically called whenever a new image is acquired
    def handleNewImage(self, image):
        print(f"Image {self._image_counter} received!")
        self._image_counter += 1
        self._received_images.append(image)

    def getReceivedImages(self):
        return self._received_images

# Initialize the bridge
my_bridge = inscoper_api.Bridge()
my_bridge.loadConfigFile(os.sep.join([CONFIG_PATH, "singleCamera"]))
my_bridge.initDevices()

# Instantiate and register our custom image listener
my_listener = CustomImageListener()
my_bridge.addImageListener(my_listener)

my_camera = my_bridge.getCamera("MyCamera")

# Snap 5 images
for i in range(0,5):
    my_camera.snapImage(0)

# Retrieve all received images
received_images = my_listener.getReceivedImages()
print(f"Received {len(received_images)} images")  # should be 5

my_bridge.close()

Snap using Status

Define a sequence of acquisition states using Status objects and load them into the bridge with loadSequence(). Each Status object represents one step in the sequence and carries the tags required by the target recipe — in this case, the camera name for the Snap recipe. The sequence is then executed synchronously with runSequence(). Images are delivered to a registered listener as each state is processed.

import os
import inscoper_api


# Macro to all Inscoper configurations
CONFIG_PATH = os.environ['InscoperConfigsPath']

class CustomImageListener(inscoper_api.ImageListener):
    def __init__(self):
        super().__init__()
        self._received_images = []
        self._image_counter = 0

    def handleNewImage(self, image):
        print(f"Image {self._image_counter} received!")
        self._image_counter += 1
        self._received_images.append(image)

    def getReceivedImages(self):
        return self._received_images

# Initialize the bridge
my_bridge = inscoper_api.Bridge()
my_bridge.loadConfigFile(os.sep.join([CONFIG_PATH, "singleCamera"]))
my_bridge.initDevices()

# Instantiate and register our custom image listener
my_listener = CustomImageListener()
my_bridge.addImageListener(my_listener)

my_camera = my_bridge.getCamera("MyCamera")

# Create a sequence of actions using Status objects
# Instead of manually triggering the camera, we build a "Sequence"
# which can be executed sequentially
status_list = []
for i in range(0,5):
    # A Status represents the state of the system in an acquisition sequence
    status = inscoper_api.Status()

    # Calling the Snap recipe requires the name of the camera to be passed as a tag
    status.addTag("MyCamera")

    # Add this state to the status list.
    status_list.append(status)

# Create and load a sequence in the bridge
# - '0' is the sequence ID we are assigning it
# - '"Snap"' is the name of the recipe to be executed
# - 'status_list' contains the 5 individual states
my_bridge.loadSequence(0, "Snap", status_list)

# Run the sequence synchronously (the script will wait for the sequence to finish before continuing)
my_bridge.runSequence(0)

# Retrieve all received images from the listener after the sequence has finished running
received_images = my_listener.getReceivedImages()
print(f"Received {len(received_images)} images") # should be 5

my_bridge.close()