metrics
This commit is contained in:
@@ -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",),
|
||||
}),
|
||||
|
||||
@@ -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 <contour>"), 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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}')
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
</div>
|
||||
{% if conf.tracking %}
|
||||
<div class="w3-row w3-row-padding w3-light-grey w3-margin-1 w3-round w3-round-large">
|
||||
<div class="w3-col"><input id="_track" type="checkbox" value="0" onclick="if (this.checked){this.value='1';}else{ this.value='0';}">
|
||||
<div class="w3-col"><input id="_track_it" type="checkbox" value="0" onclick="if (this.checked){this.value='1';}else{ this.value='0';}">
|
||||
{% trans 'Configuration du tracking' %}
|
||||
</div>
|
||||
<div class="w3-half">
|
||||
@@ -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")
|
||||
};
|
||||
</script>
|
||||
<script src="/static/scanner/js/calibration.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user