planarian tracker
This commit is contained in:
@@ -134,11 +134,12 @@ class CameraRecordManager():
|
||||
|
||||
class MultiWellManager:
|
||||
|
||||
def __init__(self, position, feed=None, step=None, proc=None):
|
||||
def __init__(self, position, feed=None, step=None, process=None):
|
||||
self.set_multiwell(position)
|
||||
self._feed = feed
|
||||
self._step = step
|
||||
self.proc = proc
|
||||
self.process = process
|
||||
self.tag = process.tag
|
||||
self.scanner = None
|
||||
|
||||
def set_multiwell(self, position):
|
||||
@@ -159,11 +160,11 @@ class MultiWellManager:
|
||||
xynext.append((0, 0))
|
||||
|
||||
pos = 1
|
||||
self.proc.session = session.id
|
||||
self.tag.session = session.id
|
||||
started = timezone.now()
|
||||
for obs in observations:
|
||||
conf = obs.multiwell.config()
|
||||
self.scanner = grbl.GridScanner(machine, proc=self.proc, **conf)
|
||||
self.scanner = grbl.GridScanner(machine, process=self.process, **conf)
|
||||
obs.started = timezone.now()
|
||||
obs.save()
|
||||
|
||||
@@ -187,8 +188,8 @@ class MultiWellManager:
|
||||
conf['xnext'] = self._xbase
|
||||
conf['ynext'] = self._ybase
|
||||
|
||||
self.proc.session = 0
|
||||
self.scanner = grbl.GridScanner(machine, proc=self.proc, **conf)
|
||||
self.tag.session = 0
|
||||
self.scanner = grbl.GridScanner(machine, process=self.process, **conf)
|
||||
Thread(target=self._start_test, daemon=True).start()
|
||||
|
||||
def scan(self, machine, sid):
|
||||
@@ -293,7 +294,7 @@ class ScannerProcess(Task):
|
||||
self.multiwel = None
|
||||
self.conf = None
|
||||
self.record_queue = Queue()
|
||||
self.proc = ProcTag()
|
||||
self.tag = ProcTag()
|
||||
self.manager = None
|
||||
self.recordDB = CameraRecordManager(cameraDB)
|
||||
|
||||
@@ -325,6 +326,7 @@ class ScannerProcess(Task):
|
||||
|
||||
#self.crop = CircularCrop(radius=self.crop_radius, strategy=CropStrategy.CROP_JPEG, jpeg_quality=self.image_quality)
|
||||
self.crop = self.set_crop_radius(self.crop_radius)
|
||||
'''
|
||||
if not self.conf.use_rpicam:
|
||||
from modules.webcam_capture import WebcamCapture
|
||||
self.cam = WebcamCapture(
|
||||
@@ -342,6 +344,23 @@ class ScannerProcess(Task):
|
||||
height=self.video_height,
|
||||
jpeg_quality=self.video_quality,
|
||||
)
|
||||
'''
|
||||
from modules.videofile_capture import VideoFileCapture
|
||||
self.cam = VideoFileCapture(
|
||||
video_file=settings.MEDIA_ROOT / 'simulation' / 'part2-5fps.mp4',
|
||||
fps=self.video_fps,
|
||||
width=self.video_width,
|
||||
height=self.video_height,
|
||||
jpeg_quality=self.video_quality,
|
||||
video_lists = [
|
||||
settings.MEDIA_ROOT / 'simulation' / 'part1-5fps.mp4',
|
||||
settings.MEDIA_ROOT / 'simulation' / 'part2-5fps.mp4',
|
||||
settings.MEDIA_ROOT / 'simulation' / 'part3-5fps.mp4',
|
||||
settings.MEDIA_ROOT / 'simulation' / 'part4-5fps.mp4',
|
||||
settings.MEDIA_ROOT / 'simulation' / 'part5-5fps.mp4',
|
||||
]
|
||||
)
|
||||
|
||||
self.cam.set_frame_callback(self._on_frame)
|
||||
self.cam.set_median(False)
|
||||
self.cam.set_circular_crop(None)
|
||||
@@ -379,20 +398,29 @@ class ScannerProcess(Task):
|
||||
if self.grbl:
|
||||
self._send(**msg)
|
||||
|
||||
def _on_frame(self, jpeg_bytes: bytes, ts: datetime) -> None:
|
||||
if self.proc.record:
|
||||
def _on_frame(self, jpeg_bytes: bytes, ts: datetime, metrics: dict) -> None:
|
||||
if self.tag.record:
|
||||
# record images
|
||||
self.record_queue.put((self.proc.uuid, ts, jpeg_bytes))
|
||||
if self.proc.play:
|
||||
self.record_queue.put((self.tag.uuid, ts, jpeg_bytes, metrics))
|
||||
if self.tag.play:
|
||||
# play image
|
||||
self._send(ts=ts.timestamp(), jpeg=base64.b64encode(jpeg_bytes).decode(), )
|
||||
self._send(ts=ts.timestamp(), jpeg=base64.b64encode(jpeg_bytes).decode(), **metrics)
|
||||
|
||||
def _recording(self):
|
||||
logger.info(f"Scanner {self.group}: start recorder")
|
||||
while not self.stop_event.is_set():
|
||||
try:
|
||||
(uuid, ts, frame) = self.record_queue.get()
|
||||
labels = dict(fps=self.video_fps, session=self.proc.session)
|
||||
(uuid, ts, frame, metrics) = self.record_queue.get()
|
||||
labels = dict(fps=self.video_fps, session=self.tag.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.record_queue.task_done()
|
||||
except Exception as e:
|
||||
@@ -418,7 +446,7 @@ class ScannerProcess(Task):
|
||||
self.default_multiwell,
|
||||
feed=self.default_feed,
|
||||
step=self.default_step,
|
||||
proc=self.proc
|
||||
process=self
|
||||
)
|
||||
|
||||
for message in pubsub.listen():
|
||||
|
||||
@@ -14,6 +14,14 @@ class ScannerManager {
|
||||
|
||||
init_controls() {
|
||||
this.ts = sId("_ts");
|
||||
this.cx = sId("_cx");
|
||||
this.cy = sId("_cy");
|
||||
this.speed_px_s = sId("_speed_px_s");
|
||||
this.axial_speed = sId("_axial_speed");
|
||||
this.axial_pos = sId("_axial_pos");
|
||||
this.area_px = sId("_area_px");
|
||||
this.frame_count = sId("_count");
|
||||
|
||||
const goto_0 = sId("_goto-0");
|
||||
const goto_xy = sId("_goto-xy");
|
||||
const xy_base = sId("_xy-base");
|
||||
@@ -34,7 +42,7 @@ class ScannerManager {
|
||||
this.debug = sId("_debug");
|
||||
const test = sId("_test");
|
||||
const halt = sId("_halt");
|
||||
|
||||
|
||||
const median = sId("_median");
|
||||
const crop = sId("_crop");
|
||||
const crop_radius = sId("_crop_radius");
|
||||
@@ -76,6 +84,16 @@ class ScannerManager {
|
||||
if (payload.xy) { this.x.textContent=payload.x.toFixed(2); this.y.textContent=payload.y.toFixed(2); }
|
||||
if (payload.state) { this.debug.insertAdjacentHTML('afterbegin', `<li>[ ${++this.debug_count} - ${payload.state} ]: ${payload.msg}</li>`); }
|
||||
if (payload.ts) { this.ts.textContent = timestampToLocalISOString(payload.ts); }
|
||||
|
||||
if (payload.detected) {
|
||||
this.cx.textContent = payload.cx; this.cy.textContent = payload.cy;
|
||||
this.speed_px_s.textContent = payload.speed_px_s;
|
||||
this.axial_speed.textContent = payload.axial_speed;
|
||||
this.axial_pos.textContent = payload.axial_pos;
|
||||
this.area_px.textContent = payload.area_px;
|
||||
this.frame_count.textContent = payload.count;
|
||||
}
|
||||
|
||||
} catch(e) { console.log(e); }
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,14 @@ class ScannerManager {
|
||||
toggle_crop() { this.croping = !this.croping; return this.croping; }
|
||||
|
||||
init_controls() {
|
||||
this.cx = sId("_cx");
|
||||
this.cy = sId("_cy");
|
||||
this.speed_px_s = sId("_speed_px_s");
|
||||
this.axial_speed = sId("_axial_speed");
|
||||
this.axial_pos = sId("_axial_pos");
|
||||
this.area_px = sId("_area_px");
|
||||
this.frame_count = sId("_count");
|
||||
|
||||
this.session= sId("_session");
|
||||
this.ts = sId("_ts");
|
||||
this.x = sId("_x");
|
||||
@@ -40,6 +48,14 @@ class ScannerManager {
|
||||
if (payload.xy) { this.x.textContent=payload.x.toFixed(2); this.y.textContent=payload.y.toFixed(2); }
|
||||
if (payload.state) { this.debug.insertAdjacentHTML('afterbegin', `<li>[ ${++this.debug_count} - ${payload.state} ]: ${payload.msg}</li>`); }
|
||||
if (payload.ts) { this.ts.textContent = timestampToLocalISOString(payload.ts); }
|
||||
if (payload.detected) {
|
||||
this.cx.textContent = payload.cx; this.cy.textContent = payload.cy;
|
||||
this.speed_px_s.textContent = payload.speed_px_s;
|
||||
this.axial_speed.textContent = payload.axial_speed;
|
||||
this.axial_pos.textContent = payload.axial_pos;
|
||||
this.area_px.textContent = payload.area_px;
|
||||
this.frame_count.textContent = payload.count;
|
||||
}
|
||||
} catch(e) { console.log(e); }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
|
||||
class ScannerManager {
|
||||
|
||||
constructor(container, multiwells=null) {
|
||||
this.container = container;
|
||||
this.socket = null;
|
||||
this.multiweels = multiwells;
|
||||
this.axes = 0;
|
||||
this.cropping = 0;
|
||||
}
|
||||
|
||||
toggle_median() { this.axes = !this.axes; return this.axes; }
|
||||
toggle_crop() { this.croping = !this.croping; return this.croping; }
|
||||
|
||||
init_controls() {
|
||||
const goto_0 = sId("_goto-0");
|
||||
const goto_xy = sId("_goto-xy");
|
||||
const xy_base = sId("_xy-base");
|
||||
const xy_step = sId("_xy-step");
|
||||
const up = sId("_up");
|
||||
const down = sId("_down");
|
||||
const left = sId("_left");
|
||||
const right = sId("_right");
|
||||
this.feed = sId("_feed");
|
||||
this.step = sId("_step");
|
||||
this.well = sId("_well");
|
||||
this.x = sId("_x");
|
||||
this.y = sId("_y");
|
||||
this.dx = sId("_dx");
|
||||
this.dy = sId("_dy");
|
||||
this.xbase = sId("_xbase");
|
||||
this.ybase = sId("_ybase");
|
||||
const test = sId("_test");
|
||||
const halt = sId("_halt");
|
||||
|
||||
const median = sId("_median");
|
||||
const crop = sId("_crop");
|
||||
|
||||
up.addEventListener('mousedown', (e) => { this._send({ type: 'calibrate', topic: "up" }); });
|
||||
down.addEventListener('mousedown', (e) => { this._send({ type: 'calibrate', topic: "down" }); });
|
||||
left.addEventListener('mousedown', (e) => { this._send({ type: 'calibrate', topic: "left" }); });
|
||||
right.addEventListener('mousedown', (e) => { this._send({ type: 'calibrate', topic: "right" }); });
|
||||
|
||||
goto_0.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "goto_0" }); });
|
||||
goto_xy.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "goto_xy" }); });
|
||||
xy_base.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "xy_base" }); });
|
||||
xy_step.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "xy_step" }); });
|
||||
|
||||
median.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "median", value: this.toggle_median() }); });
|
||||
crop.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "crop", value: this.toggle_crop() }); });
|
||||
this.well.addEventListener("change", (e) => { this._send({ type: 'calibrate', topic: "well", value: e.target.value }); });
|
||||
this.step.addEventListener("change", (e) => { this._send({ type: 'calibrate', topic: "step", value: e.target.value }); });
|
||||
this.feed.addEventListener("change", (e) => { this._send({ type: 'calibrate', topic: "feed", value: e.target.value }); });
|
||||
|
||||
this.dx.addEventListener("change", (e) => { this._send({ type: 'calibrate', topic: "dx", value: e.target.value }); });
|
||||
this.dy.addEventListener("change", (e) => { this._send({ type: 'calibrate', topic: "dy", value: e.target.value }); });
|
||||
|
||||
test.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "test" }); });
|
||||
halt.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "halt" }); });
|
||||
|
||||
}
|
||||
|
||||
registerSocket(socket) {
|
||||
this.socket = socket;
|
||||
this.init_controls();
|
||||
}
|
||||
|
||||
update(payload) {
|
||||
try {
|
||||
if (payload.jpeg) { this.container.src = `data:image/jpeg;base64,${payload.jpeg}`; }
|
||||
if (payload.xbase) { this.xbase.textContent = payload.xbase; this.ybase.textContent = payload.ybase; }
|
||||
if (payload.dxy) { this.dy.value=payload.dy; this.dx.value=payload.dx; }
|
||||
if (payload.xy) { this.x.textContent=payload.x; this.y.textContent=payload.y; }
|
||||
//if (payload.ts) { console.log(payload.ts); }
|
||||
} catch(e) { console.log(e); }
|
||||
}
|
||||
|
||||
init() {
|
||||
this._send({
|
||||
type: 'scanner',
|
||||
topic: "init",
|
||||
feed: this.feed.value,
|
||||
step: this.step.value,
|
||||
well: this.well.value
|
||||
});
|
||||
}
|
||||
start() { this._send({ type: 'scanner', topic: "start"}); }
|
||||
halt() { this._send({ type: 'scanner', topic: "halt" }); }
|
||||
|
||||
_send(message) { this.socket.send(message); }
|
||||
}
|
||||
|
||||
class MetadataSocket {
|
||||
constructor(url) {
|
||||
this.url = url;
|
||||
this.ws = null;
|
||||
this.manager = null;
|
||||
this.reconnectDelay = 1000;
|
||||
this.shouldReconnect = true;
|
||||
this.reconnect = false;
|
||||
}
|
||||
|
||||
setManager(manager) { this.manager = manager; }
|
||||
|
||||
connect() {
|
||||
this.ws = new WebSocket(this.url);
|
||||
|
||||
this.ws.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
this.manager.update(data);
|
||||
};
|
||||
|
||||
this.ws.onopen = (event) => {
|
||||
if (this.manager && !this.reconnect)
|
||||
this.manager['init']();
|
||||
this.reconnect = false;
|
||||
};
|
||||
|
||||
this.ws.onclose = () => {
|
||||
console.warn(`WebSocket closed...`);
|
||||
if (this.shouldReconnect) {
|
||||
this.reconnect = true;
|
||||
setTimeout(() => {
|
||||
console.log("Reconnect WebSocket...");
|
||||
this.connect();
|
||||
}, this.reconnectDelay);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
send(obj) { if (this.ws?.readyState === WebSocket.OPEN) { this.ws.send(JSON.stringify(obj)); } }
|
||||
}
|
||||
@@ -96,7 +96,9 @@
|
||||
<button id="_halt" class="w3-button w3-red w3-round-large w3-margin-small w3-block"><i class="fa-solid fa-hand"></i> {% trans 'ARRET' %}</button>
|
||||
|
||||
</div>
|
||||
<div class="scanner"><img id="scan-img" class="w3-image"></div>
|
||||
{% include 'scanner/scan-image.html' %}
|
||||
|
||||
|
||||
</div>
|
||||
<ul id="_debug" class="w3-scroll-y" style="height: 30vh"></ul>
|
||||
{% endblock %}
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</button>
|
||||
<button id="_halt" class="w3-button w3-red w3-round-large w3-padding-16 w3-block w3-margin-top"><i class="fa-solid fa-hand"></i><br>{% trans 'ARRET' %}</button>
|
||||
</div>
|
||||
<div class="scanner"><img id="scan-img" class="w3-image"></div>
|
||||
{% include 'scanner/scan-image.html' %}
|
||||
</div>
|
||||
<ul id="_debug" class="w3-scroll-y" style="height: 50vh"></ul>
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
<div class="scanner w3-row">
|
||||
<div class="w3-col w3-small" style="width:180px">
|
||||
<div>Num: <span id="_count"></span></div>
|
||||
<div>Aire: <span id="_area_px"></span></div>
|
||||
|
||||
<div>cx: <span id="_cx"></span></div>
|
||||
<div>cy: <span id="_cy"></span></div>
|
||||
<div>V: <span id="_speed_px_s"></span> px/s</div>
|
||||
<div>V.Ax: <span id="_axial_speed"></span> px/s</div>
|
||||
<div>Ax pos: <span id="_axial_pos"></span></div>
|
||||
</div>
|
||||
<div class="w3-rest">
|
||||
<img id="scan-img" class="w3-image">
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user