<jupyter_start><jupyter_text>In this notebook we show how to implement a local threshold:<jupyter_code>import torch
from flytracker import run

from flytracker.utils.param_helpers import load_frame, test_mask, test_threshold
from skimage.filters import threshold_local

import matplotlib.pyplot as plt
# Location of a movie
movie_path = "../data/experiments/bruno/videos/seq_1.mp4"
image = load_frame(movie_path, frame=100, color=False)
plt.figure(figsize=(15, 10))
plt.imshow(image, cmap='gray')
mask = torch.ones((1080, 1280), dtype=bool)
mask[:130, :] = 0
mask[-160:, :] = 0
mask[:, :270] = 0
mask[:, -205:] = 0

mask[:190, :350] = 0
mask[:195, -270:] = 0
mask[-220:, :340] = 0
mask[870:, 1010:] = 0

masked_image = test_mask(image, mask)
plt.figure(figsize=(15, 10))
plt.imshow(masked_image, cmap='gray')<jupyter_output><empty_output><jupyter_text>Now we're going to build a local threshold. The idea is that flies are fairly sparse (they make up a small part of all the pixels), and that we can get the local lighting / background by using a big enough median filter. Everything more than 30 away from the local lighting is a fly. Since lighting remains constant, we only need to do this once.You can probably do better filters with some tuning and thinking - the goal here is to show that you can use whatever you want as long as the arrays match shape:<jupyter_code>def local_threshold(image, mask, threshold=30, block_size=31):
    background = threshold_local(image, block_size=block_size, method='median', )
    # we apply the mask after the background to avoid annoying edge effects
    background = test_mask(torch.tensor(background, dtype=torch.uint8), mask)
    return background - threshold
local_threshold = local_threshold(image, mask, 40, 31) # feed in unmasked image!
global_threshold = 120
print(local_threshold.shape, image.shape)
plt.figure(figsize=(15, 10))

plt.subplot(121)
plt.title("Global")
plt.imshow(masked_image < global_threshold, cmap='gray')

plt.subplot(122)
plt.title("Local")
plt.imshow(masked_image < local_threshold, cmap='gray')<jupyter_output><empty_output><jupyter_text>To run we can either specify a specific movie or a folder with movies, which should be numbered.<jupyter_code>single_movie =  "../data/experiments/bruno/videos/split/seq_00.mp4"
multiple_movies = "../data/experiments/bruno/videos/split/"<jupyter_output><empty_output><jupyter_text>max_change sets the maxium distance flies can move or the frame is skipped (more or less, it's an approximation. 40 seems to be a good choice):<jupyter_code>%%time
df = run(
    multiple_movies,
    mask,
    n_arenas=4,
    gpu=False,
    n_ini=100,
    threshold=local_threshold,
    max_change=40
)
for ID in df.ID.unique():
    plt.plot(df.query(f"ID=={ID}")["x"], df.query(f"ID=={ID}")["y"])
df<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11693834160170093, 'fraction_numerical': 0.03579021970233877, 'mean_word_length': 5.519630484988452, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '524576', 'license': 'MIT', 'n_tokens_mistral': 1017, 'n_tokens_neox': 926, 'n_words': 284, 'path': 'examples/example.ipynb', 'repo_name': 'nicolaseberle/flyTracker', 'size': '2822'}