diff --git a/__init__.py b/__init__.py index 699b2cd..151441d 100644 --- a/__init__.py +++ b/__init__.py @@ -1,6 +1,6 @@ from flask import Flask, send_from_directory, session import os -from . import db, config, routes, utils +from . import db, config, routes, utils, leds app = Flask(__name__) @@ -25,7 +25,7 @@ def inject(): conn = db.get() return { 'calibration': utils.get_calibration(conn), - 'leds': config.GPIO_LEDS.leds, + 'leds': leds.get().leds, 'CalibrationState': db.CalibrationState, } diff --git a/camera.py b/camera.py index bec4ce1..f95434a 100644 --- a/camera.py +++ b/camera.py @@ -1,42 +1,47 @@ import gphoto2 as gp import shutil -from . import leds +from . import leds, config class Camera: + def capture(self, output_path: str) -> bool: + return False + + +class RealCamera(Camera): def __init__(self): self.inner = gp.Camera() def __enter__(self): self.inner.init() + return self - def __exit__(self): + def __exit__(self, *args): self.inner.exit() def capture(self, output_path: str) -> bool: try: - with self: - file_path = self.inner.capture(gp.GP_CAPTURE_IMAGE) - preview = self.inner.file_get(file_path.folder, file_path.name[:-3] + '.JPG', gp.GP_FILE_TYPE_NORMAL) - raw = self.inner.file_get(file_path.folder, file_path.name, gp.GP_FILE_TYPE_RAW) + file_path = self.inner.capture(gp.GP_CAPTURE_IMAGE) + preview = self.inner.file_get(file_path.folder, file_path.name[:-3] + '.JPG', gp.GP_FILE_TYPE_NORMAL) + raw = self.inner.file_get(file_path.folder, file_path.name, gp.GP_FILE_TYPE_RAW) - preview.sve(output_path + '.jpg') - raw.save(output_path + '.cr2') - return True + preview.sve(output_path + '.jpg') + raw.save(output_path + '.cr2') + return True except Exception as e: print('An error occured when capturing photo', e) return False -class DummyCamera: +class DummyCamera(Camera): def __init__(self, leds: leds.DummyLeds): self.leds = leds def __enter__(self): - pass + return self - def __exit__(self): + def __exit__(self, *args): pass def capture(self, output_path: str) -> bool: @@ -50,9 +55,17 @@ class DummyCamera: else: all_on = True - if found: + if all_on: + shutil.copyfile('data-keep/small/all_on.jpg', output_path + '.jpg') + elif found is not None: shutil.copyfile('data-keep/small/' + str(found) + '.jpg', output_path + '.jpg') - elif all_on: - shutil.copyfile('data-keep/small/LED17.jpg', output_path + '.jpg') else: - shutil.copyfile('data-keep/small/LED17.jpg', output_path + '.jpg') + print('ALL_OFF') + shutil.copyfile('data-keep/small/all_off.jpg', output_path + '.jpg') + + +camera = DummyCamera(leds.get()) if config.CAMERA == "dummy" else RealCamera() + + +def get(): + return camera diff --git a/config.darkroom.py b/config.darkroom.py index 09ca3b1..dae0118 100644 --- a/config.darkroom.py +++ b/config.darkroom.py @@ -1,5 +1,4 @@ from os.path import join -from . import camera, leds DATA_DIR = 'data' BACKUPS_DIR = 'data-backups' @@ -10,5 +9,4 @@ DATABASE_PATH = join(DATA_DIR, 'db.sqlite') AUTO_USE_LAST_CALIBRATION = True GPIO_CHIP = 'gpiochip0' LEDS_UUIDS = [17, 18, 22, 23, 24, 27] -GPIO_LEDS = leds.GpioLeds(LEDS_UUIDS) -CAMERA = camera.Camera() +CAMERA = 'real' diff --git a/config.local.py b/config.local.py index d0ed701..5442bf4 100644 --- a/config.local.py +++ b/config.local.py @@ -1,5 +1,4 @@ from os.path import join -from . import camera, leds DATA_DIR = 'data' BACKUPS_DIR = 'data-backups' @@ -10,5 +9,4 @@ DATABASE_PATH = join(DATA_DIR, 'db.sqlite') AUTO_USE_LAST_CALIBRATION = False GPIO_CHIP = None LEDS_UUIDS = [17, 18, 22, 23, 24, 27] -GPIO_LEDS = leds.DummyLeds(LEDS_UUIDS) -CAMERA = camera.DummyCamera(GPIO_LEDS) +CAMERA = 'dummy' diff --git a/leds.py b/leds.py index 54ecd09..f5eb9b0 100644 --- a/leds.py +++ b/leds.py @@ -1,5 +1,21 @@ import gpiod +from . import config + + +class Leds: + def __enter__(self): + pass + + def __exit__(self, *args): + pass + + def on(self): + pass + + def off(self): + pass + class GpioLed: def __init__(self, gpio_pin: int): @@ -26,7 +42,7 @@ class GpioLed: return f'LED{self.gpio_pin:02}' -class GpioLeds: +class GpioLeds(Leds): def __init__(self, chip: str, gpio_pins: list[int]): self.chip = gpiod.Chip(chip) self.leds = [] @@ -36,6 +52,7 @@ class GpioLeds: def __enter__(self): for led in self.leds: led.enter(self.chip) + return self def __exit__(self, *args): for led in self.leds: @@ -71,7 +88,7 @@ class DummyLed: return f'LED{self.gpio_pin:02}' -class DummyLeds: +class DummyLeds(Leds): def __init__(self, gpio_pins: list[int]): self.leds = [] for pin in gpio_pins: @@ -80,6 +97,7 @@ class DummyLeds: def __enter__(self): for led in self.leds: led.enter() + return self def __exit__(self, *args): for led in self.leds: @@ -92,3 +110,10 @@ class DummyLeds: def on(self): for led in self.leds: led.on() + + +_leds = GpioLeds(config.GPIO_CHIP, config.LEDS_UUIDS) if config.GPIO_CHIP is not None else DummyLeds(config.LEDS_UUIDS) + + +def get() -> Leds: + return _leds diff --git a/routes/acquisition.py b/routes/acquisition.py index fa26dd7..8896f0c 100644 --- a/routes/acquisition.py +++ b/routes/acquisition.py @@ -26,7 +26,7 @@ def scan_existing(id: int): conn = db.get() calibrated = session.get('calibration_id', None) is not None acquisition = db.Acquisition.get_from_id(id, conn) - object = acquisition.object(conn) + object = acquisition.object(conn) if acquisition is not None else None return render_template('scan.html', object=object, acquisition=acquisition, calibrated=calibrated) @@ -48,7 +48,7 @@ def run(object_id: int): def generate(): yield str(acquisition.id) - length = len(config.LEDS_UUIDS) + length = len(config.LEDS_UUIDS) + 2 # with all_on and all_off for index, led_uuid in enumerate(scanner.scan(join(config.OBJECT_DIR, str(object.id), str(acquisition.id)))): yield f"{led_uuid},{(index+1)/length}\n" @@ -74,7 +74,7 @@ def rescan(acquisition_id: int): object = acquisition.object(conn) def generate(): - length = len(config.LEDS_UUIDS) + length = len(config.LEDS_UUIDS) + 2 # with all_on and all_off for index, led_uuid in enumerate(scanner.scan(join(config.OBJECT_DIR, str(object.id), str(acquisition.id)))): yield f"{led_uuid},{(index+1)/length}\n" @@ -87,7 +87,7 @@ def validate(acquisition_id: int): acquisition = db.Acquisition.get_from_id(acquisition_id, conn) if acquisition is None: - raise f"Aucune acquisition d'id {acquisition_id}" + raise Exception(f"Aucune acquisition d'id {acquisition_id}") object = acquisition.object(conn) diff --git a/routes/calibration.py b/routes/calibration.py index dab4736..0f58911 100644 --- a/routes/calibration.py +++ b/routes/calibration.py @@ -57,7 +57,7 @@ def scan(): def generate(): length = len(config.LEDS_UUIDS) - for index, led_uuid in enumerate(scanner.scan(join(config.CALIBRATION_DIR, calibration_id))): + for index, led_uuid in enumerate(scanner.scan(join(config.CALIBRATION_DIR, calibration_id), False)): yield f"{led_uuid},{(index+1)/length}\n" with conn: diff --git a/scanner.py b/scanner.py index 5ecd274..f70bc32 100644 --- a/scanner.py +++ b/scanner.py @@ -1,42 +1,51 @@ import os from os.path import join import time -from . import config +from . import leds, camera # Delay between to captures DELAY = 0.5 -def scan(output_dir: str): +def delay_capture(cam, output_path): + # Measure the time it takes to capture + start = time.time() + output = cam.capture(output_path) + delta = time.time() - start + + # Wait for at least one second between each capture + if delta < DELAY: + time.sleep(DELAY - delta) + + return output + + +def scan(output_dir: str, on_and_off: bool = True): os.makedirs(output_dir, exist_ok=True) - with config.GPIO_LEDS: - for count, led in enumerate(config.GPIO_LEDS.leds): - print(f'Turn on {led}') - img = join(output_dir, f'{led}') + with leds.get() as gpio_leds: + for count, led in enumerate(gpio_leds.leds): + with camera.get() as cam: + print(f'Turn on {led}') + img = join(output_dir, f'{led}') - # Measure the time it takes to capture - start = time.time() + led.on() + delay_capture(cam, img) + led.off() - led.on() - config.CAMERA.capture(img) - led.off() + print(f'Turn off {led}') + yield str(led) - delta = time.time() - start - - # Wait for at least one second between each capture - if delta < DELAY: - time.sleep(DELAY - delta) - - print(f'Turn off {led}') - if count == len(config.LEDS_UUIDS) - 1: - # capture with all leds ON OFF - config.GPIO_LEDS.on() + # capture with all leds ON OFF + if on_and_off: + with camera.get() as cam: + gpio_leds.on() img = join(output_dir, 'all_on') - config.CAMERA.capture(img) + delay_capture(cam, img) + yield 'all_on' - config.GPIO_LEDS.off() + with camera.get() as cam: + gpio_leds.off() img = join(output_dir, 'all_off') - config.CAMERA.capture(img) - - yield led + delay_capture(cam, img) + yield 'all_off' diff --git a/templates/scan.html b/templates/scan.html index 303d5ba..27d70ea 100644 --- a/templates/scan.html +++ b/templates/scan.html @@ -102,6 +102,22 @@ cell.appendChild(img); grid.appendChild(cell); {% endfor %} + + cell = document.createElement('div'); + cell.classList.add('cell'); + img = document.createElement('img'); + img.classList.add('is-loading'); + img.src = '/data/objects/{{ object.id }}/' + acquisitionId + '/all_on.jpg?v=0'; + cell.appendChild(img); + grid.appendChild(cell); + + cell = document.createElement('div'); + cell.classList.add('cell'); + img = document.createElement('img'); + img.classList.add('is-loading'); + img.src = '/data/objects/{{ object.id }}/' + acquisitionId + '/all_off.jpg?v=0'; + cell.appendChild(img); + grid.appendChild(cell); } scanButton.addEventListener('click', async () => {