Skip to content

LookupTable


# coding=utf-8
# =============================================================================
# Copyright (c) 2026 FLIR Integrated Imaging Solutions, Inc. All Rights Reserved.
#
# This software is the confidential and proprietary information of FLIR
# Integrated Imaging Solutions, Inc. ("Confidential Information"). You
# shall not disclose such Confidential Information and shall use it only in
# accordance with the terms of the license agreement you entered into
# with FLIR Integrated Imaging Solutions, Inc. (FLIR).
#
# FLIR MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
# SOFTWARE, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
# PURPOSE, OR NON-INFRINGEMENT. FLIR SHALL NOT BE LIABLE FOR ANY DAMAGES
# SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
# THIS SOFTWARE OR ITS DERIVATIVES.
# =============================================================================

# LookupTable.py
#
# LookupTable.py shows how to configure lookup tables on the camera.
# It relies on information provided in the Enumeration, Acquisition, and
# NodeMapInfo examples.
#
# It can also be helpful to familiarize yourself with the ImageFormatControl
# and Exposure examples. As they are somewhat shorter and simpler, either
# provides a strong introduction to camera customization.
#
# Lookup tables allow for the customization and control of individual pixels.
# This can be a very powerful and deeply useful tool; however, because use
# cases are context dependent, this example only explores lookup table
# configuration.
#
# Please leave us feedback at: https://www.surveymonkey.com/r/TDYMVAPI
# More source code examples at: https://github.com/Teledyne-MV/Spinnaker-Examples
# Need help? Check out our forum at: https://teledynevisionsolutions.zendesk.com/hc/en-us/community/topics

import os
import PySpin
import sys

NUM_IMAGES = 10  # number of images to grab


def print_retrieve_node_failure(node, name):
    """"
    This function handles the error prints when a node or entry is unavailable or
    not readable on the connected camera.

    :param node: Node type. "Node" or "Entry"
    :param name: Node name.
    :type node: String
    :type name: String
    :rtype: None
    """
    print("Unable to get {} ({} {} retrieval failed.)".format(node, name, node))
    print("The {} may not be available on all camera models...".format(node))
    print("Please try a Blackfly S camera.")


def configure_lookup_tables(nodemap):
    """
    This function configures lookup tables linearly. This involves selecting the
    type of lookup table, finding the appropriate increment calculated from the
    maximum value, and enabling lookup tables on the camera.

    :param nodemap: Device nodemap
    :type nodemap: INodeMap
    :return: returns True if successful, False otherwise
    :rtype: bool
    """
    result = True
    print("***CONFIGURING LOOKUP TABLES***\n")

    # Select lookup table type
    #
    # ***NOTES ***
    # Setting the lookup table selector. It is important to note that this
    # does not enable lookup tables.

    try:
        lut_selector = PySpin.CEnumerationPtr(nodemap.GetNode("LUTSelector"))
        if not PySpin.IsReadable(lut_selector) or not PySpin.IsWritable(lut_selector):
            print_retrieve_node_failure("node", "LUTSelector")
            return False

        lut_selector_lut1 = lut_selector.GetEntryByName("LUT1")
        if not PySpin.IsReadable(lut_selector_lut1):
            print_retrieve_node_failure("entry", "LUTSelector LUT1")
            return False

        lut_selector.SetIntValue(lut_selector_lut1.GetValue())
        print("Lookup table selector set to LUT 1...\n")

        # Determine pixel increment and set indexes and values as desired
        #
        # *** NOTES ***
        # To get the pixel increment, the maximum range of the value node must
        # first be retrieved. The value node represents an index, so its value
        # should be one less than a power of 2 (e.g. 511, 1023, etc.). Add 1 to
        # this index to get the maximum range. Divide the maximum range by 512
        # to calculate the pixel increment.
        #
        # Finally, all values (in the value node) and their corresponding
        # indexes (in the index node) need to be set. The goal of this example
        # is to set the lookup table linearly. As such, the slope of the values
        # should be set according to the increment, but the slope of the
        # indexes is inconsequential.

        # Retrieve value node
        lut_value = PySpin.CIntegerPtr(nodemap.GetNode("LUTValue"))
        if not PySpin.IsReadable(lut_value) or not PySpin.IsWritable(lut_value):
            print_retrieve_node_failure("node", "LUTValue")
            return False

        # Retrieve maximum range
        max_range = lut_value.GetMax() + 1
        print("\tMaximum Range: {}".format(max_range))

        # Calculate increment
        increment = max_range / 512
        print("\tIncrement: {}".format(increment))

        # Retrieve index node
        lut_index = PySpin.CIntegerPtr(nodemap.GetNode("LUTIndex"))
        if not PySpin.IsWritable(lut_index):
            print_retrieve_node_failure("node", "LUTIndex")
            return False

        # Set values and indexes
        i = 0
        while i < max_range:
            lut_index.SetValue(int(i))
            lut_value.SetValue(int(i))
            i += increment

        print("All lookup table values set...\n")

        # Enable lookup tables
        #
        # *** NOTES ***
        # Once lookup tables have been configured, don"t forget to enable them
        # with the appropriate node.
        #
        # *** LATER ***
        # Once the images with lookup tables have been collected, turn the
        # feature off with the same node.

        lut_enable = PySpin.CBooleanPtr(nodemap.GetNode("LUTEnable"))
        if not PySpin.IsWritable(lut_enable):
            print_retrieve_node_failure("node", "LUTEnable")
            return False

        lut_enable.SetValue(True)
        print("Lookup tables enabled...\n")

    except PySpin.SpinnakerException as ex:
        print("Error: {}".format(ex))
        result = False

    return result


def reset_lookup_tables(nodemap):
    """
    This function resets the camera by disabling lookup tables.

    :param nodemap: Device nodemap.
    :type nodemap: INodeMap
    :return: returns True if successful, False otherwise
    :rtype: bool
    """
    result = True

    # Disable lookup tables
    #
    # *** NOTES ***
    # Turn lookup tables off when they are not needed to reduce overhead

    try:
        lut_enable = PySpin.CBooleanPtr(nodemap.GetNode("LUTEnable"))
        if not PySpin.IsWritable(lut_enable):
            print("Unable to disable lookup tables. Non-fatal error...\n")
            return False

        lut_enable.SetValue(False)
        print("Lookup tables disabled...\n")

    except PySpin.SpinnakerException as ex:
        print("Error: {}".format(ex))
        result = False

    return result


def print_device_info(nodemap):
    """
    # This function prints the device information of the camera from the transport
    # layer; please see NodeMapInfo example for more in-depth comments on printing
    # device information from the nodemap.

    :param nodemap: Device nodemap.
    :type nodemap: INodeMap
    :return: returns True if successful, False otherwise
    :rtype: bool
    """
    result = True
    print("*** DEVICE INFORMATION ***\n")

    try:
        node_device_information = PySpin.CCategoryPtr(nodemap.GetNode("DeviceInformation"))

        if PySpin.IsReadable(node_device_information):
            features = node_device_information.GetFeatures()
            for feature in features:
                node_feature = PySpin.CValuePtr(feature)
                if PySpin.IsReadable(node_feature):
                    feature_string = node_feature.ToString()
                else:
                    feature_string = "Node not readable"

                print("{}: {}".format(node_feature.GetName(), feature_string))

        else:
            print("Device control information not readable.")

    except PySpin.SpinnakerException as ex:
        print("Error: {}".format(ex))
        result = False

    return result


def acquire_images(cam, nodemap, nodemap_tl_device):
    """
    This function acquires and saves 10 images from a device; please see
    Acquisition example for more in-depth comments on acquiring images.

    :param cam: Camera to acquire images from
    :param nodemap: Device nodemap
    :param nodemap_tl_device: Transport layer device nodemap.
    :type cam: CameraPtr
    :type nodemap: INodeMap
    :type nodemap_tl_device: INodeMap
    :return: returns True if successful, False otherwise
    :rtype: bool
    """
    result = True
    print("*** IMAGE ACQUISITION ***\n")

    # Set acquisition mode to continuous
    try:
        node_acquisition_mode = PySpin.CEnumerationPtr(nodemap.GetNode("AcquisitionMode"))
        if not PySpin.IsReadable(node_acquisition_mode) or not PySpin.IsWritable(node_acquisition_mode):
            print("Unable to set acquisition mode to continuous (node retrieval). Aborting...\n")
            return False

        node_acquisition_mode_continuous = node_acquisition_mode.GetEntryByName("Continuous")
        if not PySpin.IsReadable(node_acquisition_mode_continuous):
            print("Unable to set acquisition mode to continuous (entry 'continuous' retrieval). Aborting...\n")
            return False

        # Retrieve integer value from entry node
        acquisition_mode_continuous = node_acquisition_mode_continuous.GetValue()

        # Set integer value from entry node as new value of enumeration node
        node_acquisition_mode.SetIntValue(acquisition_mode_continuous)
        print("Acquisition mode set to continuous...\n")

        #  Begin acquiring images
        cam.BeginAcquisition()
        print("Acquiring images...\n")

        #  Retrieve device serial number for filename
        device_serial_number = ""
        node_device_serial_number = PySpin.CStringPtr(nodemap_tl_device.GetNode("DeviceSerialNumber"))
        if PySpin.IsReadable(node_device_serial_number):
            device_serial_number = node_device_serial_number.GetValue()
            print("Device serial number retrieved as {}...".format(device_serial_number))

        print("")

        # Retrieve, convert, and save images

        # Create ImageProcessor instance for post processing images
        processor = PySpin.ImageProcessor()

        # Set default image processor color processing method
        #
        # *** NOTES ***
        # By default, if no specific color processing algorithm is set, the image
        # processor will default to NEAREST_NEIGHBOR method.
        processor.SetColorProcessing(PySpin.SPINNAKER_COLOR_PROCESSING_ALGORITHM_HQ_LINEAR)

        for i in range(NUM_IMAGES):
            try:

                # Retrieve next received image and ensure image completion
                image_result = cam.GetNextImage(1000)

                if image_result.IsIncomplete():
                    print("Image incomplete with image status {}...".format(image_result.GetImageStatus()))

                else:
                    #  Print image information
                    width = image_result.GetWidth()
                    height = image_result.GetHeight()
                    print("Grabbed image {}, width = {}, height = {}".format(i, width, height))

                    #  Convert image to mono 8
                    image_converted = processor.Convert(image_result, PySpin.PixelFormat_Mono8)

                    # Create a unique filename
                    if device_serial_number:
                        filename = "LookupTable-{}-{}.jpg".format(device_serial_number, i)
                    else:  # if serial number is empty
                        filename = "LookupTable-{}.jpg".format(i)

                    #  Save image
                    image_converted.Save(filename)
                    print("Image saved at {}".format(filename))

                    #  Release image
                    image_result.Release()
                    print("")

            except PySpin.SpinnakerException as ex:
                print("Error: {}".format(ex))
                result = False

        #  End acquisition
        cam.EndAcquisition()

    except PySpin.SpinnakerException as ex:
        print("Error: {}".format(ex))
        return False

    return result


def run_single_camera(cam):
    """
    This function acts as the body of the example; please see NodeMapInfo example
    for more in-depth comments on setting up cameras.

    :param cam: Camera to run on.
    :type cam: CameraPtr
    :return: returns True if successful, False otherwise
    :rtype: bool
    """
    result = True

    try:
        # Retrieve TL device nodemap and print device information
        nodemap_tl_device = cam.GetTLDeviceNodeMap()

        result &= print_device_info(nodemap_tl_device)

        # Initialize camera
        cam.Init()

        # Retrieve GenICam nodemap
        nodemap = cam.GetNodeMap()

        # Configure lookup tables
        result &= configure_lookup_tables(nodemap)
        if not result:
            return result

        # Acquire images
        result &= acquire_images(cam, nodemap, nodemap_tl_device)

        # Reset lookup tables
        result &= reset_lookup_tables(nodemap)

        # Deinitialize camera
        cam.DeInit()
    except PySpin.SpinnakerException as ex:
        print("Error: {}".format(ex))
        result = False

    return result


def main():
    """
    Since this application saves images in the current folder
    we must ensure that we have permission to write to this folder.
    If we do not have permission, fail right away.

    :return: returns True if successful, False otherwise
    :rtype: bool
    """
    try:
        test_file = open("test.txt", "w+")
    except IOError:
        print("Unable to write to current directory. Please check permissions.\n")
        input("Press Enter to exit...")
        return False

    test_file.close()
    os.remove(test_file.name)

    result = True

    # Retrieve singleton reference to system object
    system = PySpin.System.GetInstance()

    # Get current library version
    version = system.GetLibraryVersion()
    print("Library version: {}.{}.{}.{}\n".format(version.major, version.minor, version.type, version.build))

    # Retrieve list of cameras from the system
    cam_list = system.GetCameras()

    num_cameras = cam_list.GetSize()

    print("Number of cameras detected: {}\n".format(num_cameras))

    # Finish if there are no cameras
    if num_cameras == 0:
        # Clear camera list before releasing system
        cam_list.Clear()
        # Release system instance
        system.ReleaseInstance()
        print("Not enough cameras!\n")
        input("Done! Press Enter to exit...")
        return False

    # Run example on each camera
    for i, cam in enumerate(cam_list):
        print("Running example for camera {}...\n".format(i))

        result &= run_single_camera(cam)
        print("Camera {} example complete...\n".format(i))

    # Release reference to camera
    del cam

    # Clear camera list before releasing system
    cam_list.Clear()

    # Release system instance
    system.ReleaseInstance()

    input("Done! Press Enter to exit...")
    return result


if __name__ == "__main__":
    if main():
        sys.exit(0)
    else:
        sys.exit(1)