simulation

This commit is contained in:
2026-05-05 19:38:41 +02:00
parent d946de7b63
commit 3aaa88a1aa
7 changed files with 110 additions and 63 deletions
+3 -7
View File
@@ -21,8 +21,6 @@ print("Django BASE_DIR:", BASE_DIR)
PACKAGE_DIR = BASE_DIR.parent
APP_DATAS = PACKAGE_DIR / config('APP_DATAS')
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
@@ -394,17 +392,15 @@ EXPORT_REMOTE_PATH = config("EXPORT_REMOTE_PATH")
EXPORT_DESTINATIONS = ["local", "remote"]
#EXPORT_DESTINATIONS = ["remote"] # only remote
## tracker default parameters
#
TRACKER_TUBE_AXIS = "vertical"
TRACKER_MIN_AREA = 20 # surface min planaire px x px
TRACKER_MAX_AREA_RATIO = 0.1 # 5% de la frame = surface max acceptable
TRACKER_MAX_PLANARIANS = 1
TRACKER_MAX_PLANARIANS = 4
TRACKER_MERGE_KERNEL_SIZE = 15 # augmenter si fragments résiduels
TRACKER_MIN_CONTOUR_DIST_PX = 40 # augmenter si IDs multiples persistent
CALIBRATION_AUTO_DURATION = 45.0
CALIBRATION_AUTO_TIMEOUT = 2.5
+73 -35
View File
@@ -3,49 +3,79 @@
# Génère 24 vidéos pour simuler le balayage d'un multi-puit de 6x24
# A1..A6, B1..B6, C1..C6, D1..D6
#
MODE="$1"
PATH="./media/simulation"
default_width=1000 # px
default_height=1000 # px
default_diameter=16.0 # mm
declare -A arguments=(
# key count length width fps duration seed bg-color arena-color arena-border shadow-color body-color body-dark body-light head-color thresh-immobile thresh-mobile thigmotaxis photo-mode photo-strength photo-x photo-y photo-sine-freq photo-radius chemo-strength chemo-x chemo-y chemo-radius avoid-strength avoid-radius aggreg-strength aggreg-radius chem-repulsion chem-decay
["A1"]="4 0.40 0.30 15 600 64 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.45 none 0.50 0.50 0.50 0.10 0.30 0.0 0.70 0.70 2.0 1.0 3.0 0.0 6.0 0.0 0.95"
)
#fps duration bg-color arena-color arena-border shadow-color body-color body-dark body-light head-color thresh-immobile thresh-mobile
BASE="15 60 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5"
declare -A arguments_all=(
# key count length width fps duration seed bg-color arena-color arena-border shadow-color body-color body-dark body-light head-color thresh-immobile thresh-mobile thigmotaxis photo-mode photo-strength photo-x photo-y photo-sine-freq photo-radius chemo-strength chemo-x chemo-y chemo-radius avoid-strength avoid-radius aggreg-strength aggreg-radius chem-repulsion chem-decay
["A1"]="3 0.40 0.30 15 60 64 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.45 none 0.50 0.50 0.50 0.10 0.30 0.0 0.70 0.70 2.0 1.0 3.0 0.0 6.0 0.0 0.95"
["A2"]="1 0.42 0.32 15 60 96 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 fixed 0.50 0.50 0.50 0.10 0.30 0.0 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95"
["A3"]="1 0.50 0.40 15 60 128 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 radial 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95"
["A4"]="3 0.60 0.35 15 60 192 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.5 0.95"
["A5"]="1 0.30 0.32 15 60 250 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95"
["A6"]="2 0.55 0.38 15 60 32 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 1.0 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95"
["B1"]="3 0.40 0.30 15 60 64 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.45 none 0.50 0.50 0.50 0.10 0.30 0.0 0.70 0.70 2.0 1.0 3.0 0.0 6.0 0.0 0.95"
["B2"]="1 0.42 0.32 15 60 96 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 fixed 0.50 0.50 0.50 0.10 0.30 0.0 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95"
["B3"]="1 0.50 0.40 15 60 128 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 radial 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95"
["B4"]="3 0.60 0.35 15 60 192 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.5 0.95"
["B5"]="1 0.30 0.32 15 60 250 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95"
["B6"]="2 0.55 0.38 15 60 32 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 1.0 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95"
["C1"]="3 0.40 0.30 15 60 64 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.45 none 0.50 0.50 0.50 0.10 0.30 0.0 0.70 0.70 2.0 1.0 3.0 0.0 6.0 0.0 0.95"
["C2"]="1 0.42 0.32 15 60 96 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 fixed 0.50 0.50 0.50 0.10 0.30 0.0 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95"
["C3"]="1 0.50 0.40 15 60 128 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 radial 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95"
["C4"]="3 0.60 0.35 15 60 192 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.5 0.95"
["C5"]="1 0.30 0.32 15 60 250 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95"
["C6"]="2 0.55 0.38 15 60 32 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 1.0 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95"
["D1"]="3 0.40 0.30 15 60 64 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.45 none 0.50 0.50 0.50 0.10 0.30 0.0 0.70 0.70 2.0 1.0 3.0 0.0 6.0 0.0 0.95"
["D2"]="1 0.42 0.32 15 60 96 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 fixed 0.50 0.50 0.50 0.10 0.30 0.0 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95"
["D3"]="1 0.50 0.40 15 60 128 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 radial 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95"
["D4"]="3 0.60 0.35 15 60 192 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.5 0.95"
["D5"]="1 0.30 0.32 15 60 250 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95"
["D6"]="2 0.55 0.38 15 60 32 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 1.0 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95"
#photo-mode photo-strength photo-x photo-y photo-sine-freq photo-radius
PHOTO_0="none 0.50 0.50 0.50 0.10 0.30"
PHOTO_1="fixed 0.50 0.50 0.50 0.10 0.40"
PHOTO_2="sine 0.50 0.50 0.50 0.10 0.50"
PHOTO_3="radial 0.50 0.50 0.50 0.10 0.60"
)
#chemo-strength chemo-x chemo-y chemo-radius
CHEMO_0="0.0 0.70 0.70 2.0"
CHEMO_1="0.5 0.70 0.70 2.0"
CHEMO_2="1.0 0.70 0.70 2.0"
for key in "${!arguments[@]}"; do
#avoid-strength avoid-radius aggreg-strength aggreg-radius chem-repulsion chem-decay
AVOID_0="0.0 3.0 0.0 6.0 0.0 0.95"
AVOID_1="0.5 3.0 0.0 6.0 0.5 0.75"
AVOID_2="1.0 3.0 0.0 6.0 1.0 0.65"
#length width
SIZE_0="0.40 0.30"
SIZE_1="0.45 0.35"
SIZE_2="0.50 0.40"
SIZE_3="0.55 0.45"
SIZE_4="0.65 0.45"
SIZE_5="0.7 0.25"
declare -A DEFAULT
declare -A ALL
#=========================== count size seed base thigmotaxis photo chemo avoid
DEFAULT[default_simulation]="4 $SIZE_0 32 $BASE 0.45 $PHOTO_0 $CHEMO_0 $AVOID_1"
#======= count size seed base thigmotaxis photo chemo avoid
ALL[A1]="4 $SIZE_0 32 $BASE 0.45 $PHOTO_0 $CHEMO_0 $AVOID_0"
ALL[A2]="4 $SIZE_1 64 $BASE 0.50 $PHOTO_1 $CHEMO_1 $AVOID_1"
ALL[A3]="4 $SIZE_2 96 $BASE 0.55 $PHOTO_1 $CHEMO_2 $AVOID_2"
ALL[A4]="4 $SIZE_3 128 $BASE 0.60 $PHOTO_2 $CHEMO_0 $AVOID_1"
ALL[A5]="4 $SIZE_4 192 $BASE 0.65 $PHOTO_2 $CHEMO_1 $AVOID_0"
ALL[A6]="4 $SIZE_5 240 $BASE 0.75 $PHOTO_3 $CHEMO_2 $AVOID_2"
ALL[B1]="4 $SIZE_0 32 $BASE 0.45 $PHOTO_3 $CHEMO_0 $AVOID_0"
ALL[B2]="4 $SIZE_1 64 $BASE 0.50 $PHOTO_0 $CHEMO_1 $AVOID_1"
ALL[B3]="4 $SIZE_2 96 $BASE 0.55 $PHOTO_0 $CHEMO_2 $AVOID_0"
ALL[B4]="4 $SIZE_3 128 $BASE 0.65 $PHOTO_0 $CHEMO_1 $AVOID_2"
ALL[B5]="4 $SIZE_4 192 $BASE 0.85 $PHOTO_0 $CHEMO_2 $AVOID_0"
ALL[B6]="4 $SIZE_5 240 $BASE 0.95 $PHOTO_0 $CHEMO_0 $AVOID_0"
ALL[C1]="4 $SIZE_0 32 $BASE 0.40 $PHOTO_0 $CHEMO_0 $AVOID_1"
ALL[C2]="4 $SIZE_1 64 $BASE 0.30 $PHOTO_0 $CHEMO_0 $AVOID_0"
ALL[C3]="4 $SIZE_2 96 $BASE 0.55 $PHOTO_0 $CHEMO_1 $AVOID_0"
ALL[C4]="4 $SIZE_3 128 $BASE 0.45 $PHOTO_0 $CHEMO_2 $AVOID_2"
ALL[C5]="4 $SIZE_4 192 $BASE 0.50 $PHOTO_0 $CHEMO_0 $AVOID_0"
ALL[C6]="4 $SIZE_5 240 $BASE 0.65 $PHOTO_0 $CHEMO_1 $AVOID_0"
ALL[D1]="4 $SIZE_0 32 $BASE 0.70 $PHOTO_0 $CHEMO_2 $AVOID_1"
ALL[D2]="4 $SIZE_1 64 $BASE 0.65 $PHOTO_0 $CHEMO_0 $AVOID_0"
ALL[D3]="4 $SIZE_2 96 $BASE 0.75 $PHOTO_0 $CHEMO_1 $AVOID_2"
ALL[D4]="4 $SIZE_3 128 $BASE 0.85 $PHOTO_0 $CHEMO_0 $AVOID_0"
ALL[D5]="4 $SIZE_4 192 $BASE 0.65 $PHOTO_0 $CHEMO_2 $AVOID_0"
ALL[D6]="4 $SIZE_5 240 $BASE 0.45 $PHOTO_0 $CHEMO_0 $AVOID_0"
export_video() {
local -n arguments=$1
for key in "${!arguments[@]}"; do
args="${arguments[$key]}"
read -r count length width fps duration seed bg_color arena_color arena_border shadow_color \
read -r count length width seed fps duration bg_color arena_color arena_border shadow_color \
body_color body_dark body_light head_color thresh_immobile thresh_mobile thigmotaxis \
photo_mode photo_strength photo_x photo_y photo_sine_freq photo_radius chemo_strength chemo_x chemo_y chemo_radius \
avoid_strength avoid_radius aggreg_strength aggreg_radius chem_repulsion chem_decay <<< "$args"
@@ -61,4 +91,12 @@ for key in "${!arguments[@]}"; do
--chemo-strength "$chemo_strength" --chemo-x "$chemo_x" --chemo-y "$chemo_y" --chemo-radius "$chemo_radius" \
--avoid-strength "$avoid_strength" --avoid-radius "$avoid_radius" --aggreg-strength "$aggreg_strength" --aggreg-radius "$aggreg_radius" \
--chem-repulsion "$chem_repulsion" --chem-decay "$chem_decay"
done
done
}
if [ "$MODE" = "all" ]; then
export_video ALL
else
export_video DEFAULT
fi
+2
View File
@@ -175,6 +175,7 @@ def parse_args():
cg.add_argument("--chemo-radius", type=float, default=2.0,
help="Rayon d'influence du chimiotactisme en mm")
# --- Interactions inter-individus ---
ig = parser.add_argument_group(
"Interactions inter-individus",
@@ -186,6 +187,7 @@ def parse_args():
help="Rayon d'évitement en mm")
ig.add_argument("--aggreg-strength", type=float, default=0.0,
help="Force d'agrégation — attraction vers les congénères (0=désactivé)")
ig.add_argument("--aggreg-radius", type=float, default=6.0,
help="Rayon d'agrégation en mm (doit être > --avoid-radius)")
ig.add_argument("--chem-repulsion", type=float, default=0.0,
+2 -2
View File
@@ -73,8 +73,8 @@ class WellPositionAdmin(admin.ModelAdmin):
class ExperimentAdmin(admin.ModelAdmin):
#inlines = (ExperimenConfigInline,)
list_filter = ('session_experiments__session', 'author', )
list_display = ('title', 'author', 'multiwell', 'created', 'started', 'finished')
readonly_fields = ('created', 'started', 'finished', )
list_display = ('title', 'author', 'identifier', 'multiwell', 'created', 'started', 'finished')
readonly_fields = ('created', 'identifier', 'started', 'finished', )
class SessionExperimentInlineAdmin(admin.TabularInline):
+12 -1
View File
@@ -7,7 +7,7 @@ import json
from django_celery_beat.models import PeriodicTask, ClockedSchedule
from django.dispatch import receiver
from django.db.models.signals import post_save, post_delete
from django.utils.text import slugify
from django.utils import timezone
from django.db import models
from django.contrib.auth.models import User
@@ -227,12 +227,23 @@ def create_well_position(sender, instance, created, **kwargs):
class Experiment(models.Model):
title = models.CharField(_("Titre de l'expérience"), max_length=100, null=True, blank=False)
comment = models.TextField(_("Commentaires"), help_text=_("Descriptions de l'expérience"), null=True, blank=True)
identifier = models.CharField(_("Identifiant d'expérience"), unique=True, max_length=100, null=True, blank=False )
author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="Auteur", null=True, blank=True)
multiwell = models.ForeignKey(MultiWell, verbose_name=_("Multi-puits"), on_delete=models.SET_NULL, null=True, blank=True)
created = models.DateTimeField(_("Date de création"), default=timezone.now)
started = models.DateTimeField (_("Date de début"), null=True, blank=True)
finished = models.DateTimeField (_("Date de fin"), null=True, blank=True)
def save(self, *args, **kwargs):
self.identifier = slugify(f'{self.title}')
super().save(*args, **kwargs)
@classmethod
def by_identifier(cls, identifier):
return Experiment.objects.filter(identifier__exact=identifier).first()
class Meta:
ordering = ['-created', ]
+1 -1
View File
@@ -185,7 +185,7 @@ class ScannerProcess(Task):
if capture_type == 'file':
from modules.videofile_capture import VideoFileCapture
self.cam = VideoFileCapture(
video_file=settings.MEDIA_ROOT / 'simulation' / 'default-simulation.mp4',
video_file=settings.MEDIA_ROOT / 'simulation' / 'default_simulation.mp4',
fps=self.video_fps,
width=self.video_width,
height=self.video_height,