Tutorial 01

February 26, 2026 · View on GitHub

What you will learn

  • The minimal structure of a Meshroom node
  • How desc.Node, inputs, outputs, and processChunk work together
  • The __version__ string and cache invalidation
  • Lazy imports for heavy libraries
  • The logManager pattern (try/finally)

Prerequisites

  • Meshroom installed (binary release or built from source)
  • mrHelloWorld plugin installed (see Quick Start)

The code

Open meshroom/helloWorld/HelloWorld.py. Here is a walkthrough of every section.

Version string

__version__ = "1.0"

Every node file must declare __version__. Meshroom uses this to determine if cached results are still valid. If you change the version, all previous computations for this node are invalidated and must be recomputed.

Module-level imports

import os
from meshroom.core import desc
from meshroom.core.utils import VERBOSE_LEVEL

These three imports are standard across all Meshroom nodes:

  • os — lightweight, used for path operations. Not a "heavy" library.
  • desc — the Meshroom descriptor framework. Defines inputs, outputs, and node behavior.
  • VERBOSE_LEVEL — standard verbosity choices for the logging parameter.

Heavy libraries like cv2, numpy, torch are never imported at module level. They go inside processChunk (see below).

Class declaration

class HelloWorld(desc.Node):

The class name must match the filename: HelloWorld.py contains class HelloWorld. Meshroom uses this convention for node discovery.

Class attributes

category = "Hello World"

documentation = """
The simplest Meshroom node. Reads an input image and writes an identical copy.
"""
  • category — determines where the node appears in the creation menu.
  • documentation — displayed in the Meshroom UI when the user selects the node.

Inputs and outputs

inputs = [
    desc.File(name="input", label="Input Image", description="...", value=""),
    desc.ChoiceParam(name="verboseLevel", ...),
]

outputs = [
    desc.File(name="output", label="Output Image", description="...",
              value="{nodeCacheFolder}/output.jpg"),
]

Every parameter needs three attributes:

  • name — internal identifier, used in code (chunk.node.input.value)
  • label — human-readable name shown in the UI
  • description — tooltip text

Output paths use {nodeCacheFolder}, which Meshroom resolves to an isolated cache directory for each computation.

processChunk

def processChunk(self, chunk):
    try:
        chunk.logManager.start(chunk.node.verboseLevel.value)

        import cv2  # Lazy import

        image = cv2.imread(chunk.node.input.value)
        cv2.imwrite(chunk.node.output.value, image)
    finally:
        chunk.logManager.end()

The processChunk method is where the actual computation happens. The pattern is always the same:

  1. try/finally — ensures cleanup happens even if an error occurs.
  2. chunk.logManager.start(chunk.node.verboseLevel.value) — starts logging. The verboseLevel value is a required argument.
  3. Your computation logic.
  4. chunk.logManager.end() — in the finally block, always.

How to test it

A pre-configured project is available: open meshroom/tuto01-hello-world.mg in Meshroom (make sure you have run scripts/setup_projects.sh first). Or set it up manually:

  1. Launch Meshroom with MESHROOM_PLUGINS_PATH set.
  2. Right-click in the graph area and add a HelloWorld node (under "Hello World" category).
  3. Set the Input Image to any image file on your system. For this tutorial, we use the Meshroom logo:
Input image: Meshroom logo
  1. Click Compute on the node.
  2. To view the output in the 2D viewer, double-click on the node or use the dropdown menu at the bottom of the 2D viewer and select Output Image.
  3. The output will be a copy of the input image in the node's cache folder.

HelloWorld node

Expected result

The output is a JPEG-encoded copy of the input. If the input is already JPEG, the result is visually identical (minor recompression artifacts are possible). For PNG inputs, the output loses transparency and gains JPEG compression.

HelloWorld output