Your own mask method
How to make TraCell use a mask method that you wrote. You need a little Python; you do not need to know how TraCell works inside.
A mask method is one function: it gets a frame and returns the mask of that frame. Contours, tracking, export and Live Tracking then work as with the built-in methods.
Two ways
| Mask script | Mask service | |
|---|---|---|
| What it is | One Python file that TraCell loads | A small program of yours that runs next to TraCell |
| Libraries you can use | NumPy and OpenCV | Any: scikit-image, SciPy, PyTorch, a GPU |
| Python to install | None | Your own |
| Settings in the Mask window | Yes, a form is built for them | No, they are in your file |
| Start with it | Yes | Only when you need another library |
What your method gets and returns
The same for both ways:
- You get a frame: 8-bit gray, already cut and adjusted by TraCell's Region and Adjustment settings.
- You return a mask of the same size: 255 where a cell is, 0 elsewhere.
- Frames come one after the other, in order, so your method may remember something between them, for example a background.
learnisFalsewhile the video is paused on a frame. Return the mask then, but do not change what you remember.
Way 1: a mask script
1. Copy the example
In TraCell choose Help > Example Scripts. A folder opens. Copy mask_script.py to a folder of your own,
for example Documents\my-mask, and give it a name of your own.
2. Put your method in
Open the file in an editor. It holds one class. This is the example's: it marks what differs from the first frame.
import cv2
import numpy as np
from pydantic import Field
from tracell.model.mask_methods import MaskMethod
from tracell.registry import Params
class DifferenceToFirstFrame(MaskMethod):
kind = "difference_to_first_frame" # the name stored in presets
label = "Difference to first frame" # the name shown in the Mask window
class Params(Params):
threshold: int = Field(25, ge=1, le=255, description="Gray-level difference that counts as a cell")
blur: int = Field(3, ge=1, le=15, description="Size of the median blur applied first (odd number)")
invert: bool = Field(False, description="Tick when the cells are the unchanged part")
def __init__(self, params=None) -> None:
super().__init__(params)
self.background: np.ndarray | None = None
def apply(self, frame: np.ndarray, learn: bool = True) -> np.ndarray:
blur = self.params.blur | 1 # the blur size must be odd
frame = cv2.medianBlur(np.asarray(frame, dtype=np.uint8), blur)
if self.background is None or self.background.shape != frame.shape:
self.background = frame.copy()
difference = cv2.absdiff(frame, self.background)
mask = cv2.threshold(difference, self.params.threshold, 255, cv2.THRESH_BINARY)[1]
return cv2.bitwise_not(mask) if self.params.invert else mask
What to change:
| Part | What it is for |
|---|---|
kind and label |
The name of your method: kind in presets (no spaces), label in the Mask window. |
Params |
Your settings, each with a type, a default and limits. TraCell shows them as a form in the Mask window and saves them in the preset. In apply they are self.params.threshold and so on. |
__init__ |
What your method remembers between frames. |
apply |
Your method: frame in, mask out. |
3. Load it
- Tools > Mask > Settings. Press the folder button next to the Method list and choose your file.
- Your method is now in the Method list, and its settings are below it.
- Tools > Mask > Show Mask shows your mask next to the video.
File > Save stores the method, its settings and the path of your file in the preset. Loading the preset loads your file again.
When you change the code, save the file and load it again with the folder button.
Way 2: a mask service
Use this when your method needs a library that TraCell does not have. Your method then runs in a program of its own, with a Python that you install, on the same computer. You start that program; TraCell hands it every frame and takes the mask back.
1. A folder and a Python for it
Install Python 3.9 or newer (python.org). Then make a folder and, in it, a Python environment with the libraries your method needs:
cd Documents\my-mask
py -m venv env
env\Scripts\pip install numpy scikit-image
cd ~/my-mask
python3 -m venv env
env/bin/pip install numpy scikit-image
2. Copy the example
Copy mask_server.py from Help > Example Scripts into the folder and give it a name of your own, for
example my_mask_service.py.
3. Put your method in
Near the top of the file is the class Method. Replace it with yours, and add the imports it needs below
import numpy as np. Leave everything below the class as it is.
This example uses scikit-image. It is here to show the steps; it is not a better method than MOG2.
from skimage.filters import threshold_triangle
from skimage.morphology import disk, opening
class Method:
BACKGROUND_FRAMES = 50 # the still background is the average of the first frames
MIN_DIFFERENCE = 10 # a frame that differs less than this everywhere has no cell
SPECK = disk(1) # what is thinner than this shape is noise
def __init__(self, height: int, width: int) -> None:
self.background = np.zeros((height, width), np.float32)
self.seen = 0
def apply(self, frame: np.ndarray, learn: bool) -> np.ndarray:
if learn and self.seen < self.BACKGROUND_FRAMES:
self.seen += 1
self.background += (frame - self.background) / self.seen
difference = np.abs(frame - self.background)
if self.seen == 0 or difference.max() < self.MIN_DIFFERENCE:
return np.zeros(frame.shape, np.uint8)
cells = np.where(difference > threshold_triangle(difference), 255, 0).astype(np.uint8)
return opening(cells, self.SPECK)
| Part | What it is for |
|---|---|
__init__(height, width) |
Called once when a run starts. Prepare here what takes long, such as loading a network. |
apply(frame, learn) |
Called once per frame. Your method: frame in, mask out. |
One thing differs from a script: frame is overwritten by the next frame. To keep a frame, copy it
(frame.copy()).
4. Start it
env\Scripts\python my_mask_service.py
env/bin/python my_mask_service.py
It answers mask service on port 52750; Ctrl+C stops it. Leave this window open.
To start it with a double click on Windows, put a file Start mask service.cmd next to it with this line:
"%~dp0env\Scripts\python.exe" "%~dp0my_mask_service.py"
5. Use it in TraCell
- Tools > Mask > Settings, method Mask service. The port field shows 52750.
- Tools > Mask > Show Mask shows your mask next to the video.
File > Save stores the method and the port in the preset. From then on, start your mask service first and TraCell second.
When you change the code, stop the service with Ctrl+C and start it again. TraCell connects again with the next frame.
Check your mask
- Tools > Mask > Show Mask and play the video: cells should be white, everything else black.
- Set the area limits in Contours so that specks and clumps are left out, and turn on Show Contours.
- Press Track on a short frame range (Tracker) and look at the traces.
When it does not work
| What you see | What to do |
|---|---|
| … did not load: … (script) | An error in your file; the text names it. Fix it and load the file again. |
| … defines no MaskMethod class (script) | The class must be derived from MaskMethod, as in the example. |
ModuleNotFoundError (script) |
Your script imports a library TraCell does not have. Use a mask service. |
| No mask service answers on port … | Start your mask service, and check that the port in the Mask window is the one it printed. |
| Mask service: … | The text after the colon is an error of your code. The window of your service shows more. |
| The mask service on port … did not answer | Your method took longer than 30 seconds for one frame, or your program stopped. |
ModuleNotFoundError in the window of your service |
The library is not installed in the environment you started it with (step 1). |
| The mask is black | Your method found no cell. Try a lower threshold, or check it on one frame outside TraCell. |
For a mask service in another programming language, the Mask API lists what TraCell sends and expects.