diff --git a/test_tube_scanner/make_videos.sh b/test_tube_scanner/make_videos.sh index d85bcac..6205a29 100755 --- a/test_tube_scanner/make_videos.sh +++ b/test_tube_scanner/make_videos.sh @@ -11,12 +11,37 @@ 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"]="3 0.40 0.30 5 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 5 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 5 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" - + ["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" ) +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" + +) for key in "${!arguments[@]}"; do args="${arguments[$key]}" diff --git a/test_tube_scanner/modules/capture_interface.py b/test_tube_scanner/modules/capture_interface.py index bf93fba..4547a94 100644 --- a/test_tube_scanner/modules/capture_interface.py +++ b/test_tube_scanner/modules/capture_interface.py @@ -99,13 +99,13 @@ class VideoCaptureInterface(abc.ABC): def on_test_well_change(self, **cfg): if self.use_tracking and cfg: - logger.warning(f"tracking conf: {cfg}") self._tracker = PlanarianTracker(**cfg) + logger.warning(f"New tracker conf: {cfg}") def on_well_change(self, cfg): """ - Appelé par le CNC lors du changement de puits. + Appelé par la CNC lors du changement de puits. Réinitialise le fond appris et l'état inter-frame du tracker. Construit les métriques aussi """ @@ -119,8 +119,8 @@ class VideoCaptureInterface(abc.ABC): min_area_px = self._params.min_area_px, max_area_ratio = self._params.max_area_ratio, max_planarians = self._params.planarian_count, - merge_kernel_size = settings.TRACKER_MERGE_KERNEL_SIZE, - min_contour_dist_px = settings.TRACKER_MIN_CONTOUR_DIST_PX, + merge_kernel_size = self._params.merge_kernel_size, + min_contour_dist_px = self._params.min_contour_dist_px, ) diff --git a/test_tube_scanner/modules/planarian_metrics.py b/test_tube_scanner/modules/planarian_metrics.py index 5a653d2..acee2ce 100644 --- a/test_tube_scanner/modules/planarian_metrics.py +++ b/test_tube_scanner/modules/planarian_metrics.py @@ -34,7 +34,7 @@ Métriques résumé (summary) : Created on 25 avr. 2026 @author: denis """ - +import asyncio import csv import io import json @@ -659,13 +659,30 @@ class ReductStoreClient: url: str = "http://localhost:8383", token: str = "", bucket: str = "planarian_metrics", + quota_type=None, + quota_size=1000_000_000 ): self.url = url self.token = token self.bucket_name = bucket + self.quota_type = quota_type + self.quota_size = quota_size self._client = None - self._bucket = None + self._bucket = asyncio.run(self.create_bucket()) + + async def create_bucket(self): + from reduct import Client, BucketSettings + self._client = Client(self.url, api_token=self.token) + + settings = BucketSettings( + quota_type=self.quota_type, + quota_size=self.quota_size, + exist_ok=True, + ) + return await self._client.create_bucket(self.bucket_name, settings, exist_ok=True) + + ''' async def connect(self): """Initialise la connexion et crée le bucket si nécessaire.""" from reduct import Client, BucketSettings, QuotaType @@ -676,14 +693,17 @@ class ReductStoreClient: exist_ok=True, ) logger.info(f"ReductStore connecté : {self.url} / {self.bucket_name}") + + ''' async def store_metric( self, record: dict, experiment: str, well: str, + entry_name: str = "metrics", planarian: int = 0, - record_type: str = "frame", + record_type: str = "metrics", ts_us: Optional[int] = None, ): """Stocke un enregistrement dans ReductStore.""" @@ -691,7 +711,7 @@ class ReductStoreClient: await self.connect() ts_us = ts_us or int(time.time() * 1_000_000) await self._bucket.write( - entry_name = "metrics", + entry_name = entry_name, data = json.dumps(record).encode("utf-8"), timestamp = ts_us, labels = { diff --git a/test_tube_scanner/modules/reductstore.py b/test_tube_scanner/modules/reductstore.py index 3318269..225ab84 100644 --- a/test_tube_scanner/modules/reductstore.py +++ b/test_tube_scanner/modules/reductstore.py @@ -27,8 +27,8 @@ class ReductStoreBase(ABC): self.bucket: Bucket = asyncio.run(self.create_bucket()) logger.info(f"==== {url} token:{api_token}") - async def create_bucket(self): + settings = BucketSettings( quota_type=self.quota_type, quota_size=self.quota_size, diff --git a/test_tube_scanner/planarian/admin.py b/test_tube_scanner/planarian/admin.py index a4e3df3..a4fc7ac 100644 --- a/test_tube_scanner/planarian/admin.py +++ b/test_tube_scanner/planarian/admin.py @@ -30,7 +30,7 @@ class ExperimentConfigAdmin(admin.ModelAdmin): "classes": ("collapse",), }), (_("Tracker"), { - "fields": ("tube_axis", "min_area_px", "max_area_ratio", "planarian_count"), + "fields": ("tube_axis", "min_area_px", "max_area_ratio", "planarian_count", "merge_kernel_size", "min_contour_dist_px"), "classes": ("collapse",), }), (_("Thigmotactisme"), { diff --git a/test_tube_scanner/planarian/models.py b/test_tube_scanner/planarian/models.py index 0e88347..eaad200 100644 --- a/test_tube_scanner/planarian/models.py +++ b/test_tube_scanner/planarian/models.py @@ -75,6 +75,18 @@ class ExperimentConfig(models.Model): verbose_name=_("Nombre de planaires"), ) + merge_kernel_size = models.PositiveIntegerField( + _("Taille du kernel"), + help_text=_("taille du kernel elliptique de fusion des fragments (px)"), + default=15 + ) + + min_contour_dist_px = models.PositiveIntegerField( + _("Distance "), + help_text=_("Distance min entre deux contours pour les considérer comme individus distincts. Défaut : 40px."), + default=40 + ) + # --- Thigmotactisme --- thigmotaxis_wall_dist_mm = models.FloatField( default=1.0, @@ -133,6 +145,8 @@ class ExperimentConfig(models.Model): "tube_axis": self.tube_axis, "min_area_px": self.min_area_px, "max_area_ratio": self.max_area_ratio, + "merge_kernel_size": self.merge_kernel_size, + "min_contour_dist_px": self.min_contour_dist_px, "planarian_count": self.planarian_count, "thigmotaxis_wall_dist_mm": self.thigmotaxis_wall_dist_mm, "photo_mode": self.photo_mode, diff --git a/test_tube_scanner/planarian/templates/planarian/experiment_form.html b/test_tube_scanner/planarian/templates/planarian/experiment_form.html index dd3f7a5..be2c48a 100644 --- a/test_tube_scanner/planarian/templates/planarian/experiment_form.html +++ b/test_tube_scanner/planarian/templates/planarian/experiment_form.html @@ -188,7 +188,22 @@ {{ form.planarian_count.errors|join:", " }} {% endif %} - + +
+ + {{ form.min_contour_dist_px }} + {% if form.min_contour_dist_px.errors %} + {{ form.min_contour_dist_px.errors|join:", " }} + {% endif %} +
+ +
+ + {{ form.merge_kernel_size }} + {% if form.merge_kernel_size.errors %} + {{ form.merge_kernel_size.errors|join:", " }} + {% endif %} +
diff --git a/test_tube_scanner/scanner/admin.py b/test_tube_scanner/scanner/admin.py index eacd3d7..9b0f6f6 100644 --- a/test_tube_scanner/scanner/admin.py +++ b/test_tube_scanner/scanner/admin.py @@ -33,7 +33,7 @@ class ConfigurationAdmin(admin.ModelAdmin): "fields": ("calibration_crop_radius", "calibration_default_multiwell", "calibration_default_feed", "calibration_default_step", "calibration_default_duration"), "classes": ("collapse",), }), - (_("Tracking"), { + (_("Tracking: valeurs par défaut"), { "fields": ("tracking", "min_area_px", "max_area_ratio", "max_planarians", "merge_kernel_size", "min_contour_dist_px"), "classes": ("collapse",), }), diff --git a/test_tube_scanner/scanner/models.py b/test_tube_scanner/scanner/models.py index e6980fd..b4958dc 100644 --- a/test_tube_scanner/scanner/models.py +++ b/test_tube_scanner/scanner/models.py @@ -66,13 +66,11 @@ class Configuration(models.Model): calibration_default_duration = models.FloatField(_("Duruée calibration"), help_text=_("Durée de pose entre chaque puits en s"), default=3.0) # tracking tracking = models.BooleanField(_("Suivi"), help_text=_("Suivi et analyse des planaires"), default=False) - min_area_px = models.PositiveIntegerField(_("Surface minimale"), help_text=_("surface minimale d'un contour pour être considéré valide (px²)"), default=20) max_area_ratio = models.FloatField(_("surface maximale "), help_text=_("surface maximale d'un contour en fraction de la frame (défaut 10%)"), default=0.10) max_planarians = models.PositiveIntegerField(_("Max planaire"), help_text=_("nombre maximum de planaires à suivre simultanément (1-10)"), default=1) merge_kernel_size = models.PositiveIntegerField(_("Taille du kernel"), help_text=_("taille du kernel elliptique de fusion des fragments (px)"), default=15) min_contour_dist_px = models.PositiveIntegerField(_("Distance "), help_text=_("Distance min entre deux contours pour les considérer comme individus distincts. Défaut : 40px."), default=40) - # active = models.BooleanField(_("Actif"), default=False) diff --git a/test_tube_scanner/scanner/multiwell.py b/test_tube_scanner/scanner/multiwell.py index 2e06696..5772737 100644 --- a/test_tube_scanner/scanner/multiwell.py +++ b/test_tube_scanner/scanner/multiwell.py @@ -135,11 +135,11 @@ class MultiWellManager: def _grid_scanning_capture(self, experiment, well_position, simulate=False): well = well_position.well multiwell = experiment.multiwell - - # Paramètres d'une expérience PlanarianScanner - cfg = ExperimentConfig.objects.get(experiment_id=experiment.id, well_id=well.id) - # reset PlanarianTracker => on_well_change - self.process.cam.on_well_change(cfg) + if self.process.use_tracking: + # Paramètres d'une expérience PlanarianScanner + cfg = ExperimentConfig.objects.get(experiment_id=experiment.id, well_id=well.id) + # reset PlanarianTracker => on_well_change + self.process.cam.on_well_change(cfg) uuid = f'{self.process.data.session}-{multiwell.position}-{well.name}' ## start recording diff --git a/test_tube_scanner/scanner/process.py b/test_tube_scanner/scanner/process.py index 8079371..80eb4c6 100644 --- a/test_tube_scanner/scanner/process.py +++ b/test_tube_scanner/scanner/process.py @@ -21,7 +21,7 @@ from celery.exceptions import Ignore from celery.utils.log import get_task_logger from redis import Redis from dataclasses import dataclass -from modules import reductstore, grbl, utils +from modules import reductstore, grbl, utils, planarian_metrics ## camera devices from modules.circular_crop import CircularCrop, CropStrategy @@ -42,6 +42,7 @@ class ProcessData: logger = get_task_logger(__name__) redisDB = Redis(host=settings.REDIS_HOST, port=settings.REDIS_PORT, db=0, decode_responses=True) cameraDB = reductstore.ReductStore(name='camera') +planarianDB = planarian_metrics.ReductStoreClient(url=settings.REDUCTSTORE_URL, token=settings.REDUCTSTORE_TOKEN) class CameraRecordManager(): @@ -183,14 +184,16 @@ class ScannerProcess(Task): capture_type = self.conf.capture_type if capture_type == 'file': video_lists = [] + wells = models.Well.objects.all() for wl in wells: - video_lists.append(str( settings.MEDIA_ROOT / 'simulation' / f'{wl.name}.mp4') ) - + if exists := (settings.MEDIA_ROOT / 'simulation' / f'{wl.name}.mp4').exists(): + video_lists.append(str( settings.MEDIA_ROOT / 'simulation' / f'{wl.name}.mp4') ) + from modules.videofile_capture import VideoFileCapture self.cam = VideoFileCapture( - video_file=settings.MEDIA_ROOT / 'simulation' / 'D6.mp4', + video_file=settings.MEDIA_ROOT / 'simulation' / 'A1.mp4', fps=self.video_fps, width=self.video_width, height=self.video_height, @@ -271,39 +274,51 @@ class ScannerProcess(Task): def _display(self, **msg): if self.grbl: self._send(**msg) - + + + def _store_metrics(self, uuid, metrics, ts): + for r in metrics: + pid = r["planarian_id"] + record = self.cam._metrics[pid].update(r, well_radius_mm=self.cam._params.well_radius_mm) + logger.warning(f"{record}") + + async_to_sync(planarianDB.store_metrics)( + record, + self.cam._params.experiment, + self.cam._params.well_name, + entry_name=uuid, + planarian=pid, + record_type='metrics', + ts_us=ts, + ) + + def _store_frame(self, uuid, frame, ts, frame_count): + labels = { + "fps": self.video_fps, + "record_type": 'frame', + "frame_count": frame_count, + } + self.recordDB.write(uuid, frame, ts=ts, labels=labels) + def _on_frame(self, jpeg_bytes: bytes, ts: datetime, metrics: dict, frame_count: int = 0) -> None: + if self.data.record: self.record_queue.put((self.data.uuid, ts, jpeg_bytes, metrics, frame_count)) if self.data.play: try: jpeg=base64.b64encode(jpeg_bytes).decode() - #logger.warning(f"{jpeg[:100]}") self._send(ts=ts.timestamp(), jpeg=jpeg, frame_count=frame_count) except Exception as e: logger.error(f"----_on_frame: {e}") - + def _recording(self): logger.info(f"Scanner {self.group}: start recorder") while not self.stop_event.is_set(): try: (uuid, ts, frame, metrics, frame_count) = self.record_queue.get() - - - labels = dict(fps=self.video_fps, session=self.data.session, detected="1" if metrics.get("detected") else "0") - - if metrics.get("detected"): - labels.update({ - "cx" : str(metrics["cx"]), - "cy" : str(metrics["cy"]), - "area_px" : str(metrics["area_px"]), - "speed_px_s" : str(metrics["speed_px_s"]), - "axial_pos" : str(metrics["axial_pos"]), - "axial_speed" : str(metrics["axial_speed"]), - }) - - self.recordDB.write(uuid, frame, labels, ts=ts) + self._store_metrics(uuid, metrics, ts) + self._frame_store(uuid, frame, ts, frame_count) self.record_queue.task_done() except Exception as e: logger.error(f'recorder: {e}') diff --git a/test_tube_scanner/scanner/static/scanner/js/calibration.js b/test_tube_scanner/scanner/static/scanner/js/calibration.js index 8f1e59b..6f8424f 100644 --- a/test_tube_scanner/scanner/static/scanner/js/calibration.js +++ b/test_tube_scanner/scanner/static/scanner/js/calibration.js @@ -49,7 +49,6 @@ class ScannerManager { this.merge_kernel_size = options.merge_kernel_size; this.min_contour_dist_px = options.min_contour_dist_px; this.track = options.track; - } init_controls() { diff --git a/test_tube_scanner/scanner/templates/scanner/calibration.html b/test_tube_scanner/scanner/templates/scanner/calibration.html index c7ffe0c..9c8874e 100644 --- a/test_tube_scanner/scanner/templates/scanner/calibration.html +++ b/test_tube_scanner/scanner/templates/scanner/calibration.html @@ -150,7 +150,7 @@ {% if conf.tracking %}
-
+
{% trans 'Configuration du tracking' %}
@@ -260,7 +260,7 @@ max_planarians : sId("_max_planarians"), merge_kernel_size : sId("_merge_kernel_size"), min_contour_dist_px : sId("_min_contour_dist_px"), - track : sId("_track") + track : sId("_track_it") };