Timelapse extension

I made a little timelapse extension following the documentation and the example there (OpenFlexure eV GUI — OpenFlexure Microscope Software documentation)

However, I got some sort of error that the .camera.new_image() method didn’t exist, so I modified my code a bit. It takes timelapse images now, but the images/experiment folders are not shown in the gallery on microscope.local. Also I’d like to add the metadata, but not sure how to use the .put_metadata() method

import time  # Used in our timelapse function
import os # Used to make the experiment folders

from labthings import current_action, fields, find_component, update_action_progress
from labthings.extensions import BaseExtension
from labthings.views import ActionView

# Used in our timelapse function
from openflexure_microscope.captures.capture_manager import generate_basename

# Used to convert our GUI dictionary into a complete eV extension GUI
from openflexure_microscope.api.utilities.gui import build_gui



# Create the extension class
class TimelapseExtension(BaseExtension):
    def __init__(self):
        # Superclass init function
        super().__init__("org.openflexure.timelapse-extension", version="0.1")

        # Add our API views
        self.add_view(TimelapseAPIView, "/timelapse")

        # Add our GUI description
        gui_description = {
            "icon": "timelapse",  # Name of an icon from https://material.io/resources/icons/
            "forms": [  # List of forms. Each form is a collapsible accordion panel
                {
                    "name": "Start a timelapse",  # Form title
                    "route": "/timelapse",  # The URL rule (as given by "add_view") of your submission view
                    "isTask": True,  # This forms submission starts a background task
                    "isCollapsible": False,  # This form cannot be collapsed into an accordion
                    "submitLabel": "Start",  # Label for the form submit button
                    "schema": [  # List of dictionaries. Each element is a form component.
                        {
                            "fieldType": "numberInput",
                            "name": "n_images",  # Name of the view arg this value corresponds to
                            "label": "Number of images",
                            "min": 1,  # HTML number input attribute
                            "placeholder": "Enter number of images",
                            #"default": 5,  # HTML number input attribute
                        },
                        {
                            "fieldType": "numberInput",
                            "name": "t_between",
                            "label": "Time (seconds) between images",
                            "min": 0.1,  # HTML number input attribute
                            "step": 1,  # HTML number input attribute
                            "placeholder": "Enter time",
                            #"default": 1,  # HTML number input attribute
                        },
                        {
                            "fieldType": "textInput",
                            "name": "experiment_name",
                            "label": "Experiment Name",
                            "placeholder": "Enter folder name for experiment",
                            #"default": "test",
                        },
                    ],
                }
            ],
        }
        self.add_meta("gui", build_gui(gui_description, self))

    def timelapse(self, microscope, n_images, t_between, experiment_name):
        """
        Save a set of images in a timelapse

        Args:
            microscope: Microscope object
            n_images (int): Number of images to take
            t_between (int/float): Time, in seconds, between sequential captures
            experiment_name: Name for the folder in which the images shall be saved
        """

        os.makedirs(f"/var/openflexure/data/micrographs/{experiment_name}", exist_ok=False)

        # Take exclusive control over both the camera and stage
        with microscope.camera.lock, microscope.stage.lock:
            for n in range(n_images):
                # Elegantly handle action cancellation
                if current_action() and current_action().stopped:
                    return
                # Generate a filename, basename creates a name based on the current time
                base_file_name = generate_basename()
                filename = f"/var/openflexure/data/micrographs/{experiment_name}/{base_file_name}_image{n}.jpg"

                # Capture
                microscope.camera.capture(filename)

                # Add system metadata
                #output.put_metadata(microscope.metadata, system=True)

                # Update task progress (only does anything if the function is running in a LabThings task)
                progress_pct = ((n + 1) / n_images) * 100  # Progress, in percent
                update_action_progress(progress_pct)

                # Wait for the specified time
                time.sleep(t_between)


## Extension views


class TimelapseAPIView(ActionView):
    """
    Take a series of images in a timelapse
    """

    args = {
        "n_images": fields.Integer(
            required=True, metadata={"example": 5, "description": "Number of images"}
        ),
        "t_between": fields.Number(
            load_default=1,
            metadata={"example": 1, "description": "Time (seconds) between images"},
        ),
        "experiment_name": fields.String(
            required=True, metadata={"example": "test", "description": "Folder name for experiment"}
        )
    }

    def post(self, args):
        # Find our microscope component
        microscope = find_component("org.openflexure.microscope")

        # Start "timelapse"
        return self.extension.timelapse(
            microscope, args.get("n_images"), args.get("t_between"), args.get("experiment_name")
        )


LABTHINGS_EXTENSIONS = (TimelapseExtension,)
2 Likes

This would be a very useful extension. I have not got solutions to your issues as I have not got in to the extensions part of the project. The Gallery only looks at one particular directory I think, which may be your problem with viewing images. This is written in a settings file somewhere.
The scan extension also writes multiple images with metadata, maybe looking at that would give pointers for capture and adding metadata.

1 Like

Thanks for the tip with the scan extension, William! I adapted my script so it uses the same method as that extension for image capturing and now metadata and organizing the images into folders works. However, a small quirk remains: The folders will only appear in the gallery after I manually click the refresh button on the gallery page/ reload the whole page. I guess after image taking or scanning actions somehow a gallery refresh is triggered, but I didn’t find out how.

Also I’d like to ask if there currently is a way to control the LED with the Sangaboard or if that is planned for the upcoming ofm version 3 (In my understanding the hardware should be capable of that, but I didn’t manage to get Filips Sangaboard extension to work). As we are planning some experiments with plant roots, I’d like to minimize light exposure. Probably the easiest and quickest way for now is to simply use some free GPIO pins and a transistor module?

And of course I’m happy to share the timelapse extension if it is useful for others as well. Uploaded it here on gitlab: Simon Pree / Timelapse Extension for Openflexure Microscope · GitLab

1 Like

That is good news. The Sangaboard extension should let you turn the lamp off. It should work on the server v2 I believe. @filip.ayazi ?

As William said this is built into the Sangaboard extension, to use it you need the modified pysangaboard module as described in the extensions README but it should work.

However, if you are writing your own extension you can either interact directly with the modified pysangaboard instance illumination module, or you can send commands to the sangaboard directly using the stock version
Something like

   microscope = find_component("org.openflexure.microscope") 
   sangaboard = microscope.stage.board
   sangaboard.query("led_cc <power_float>")
2 Likes