From f49d5511efe8f4b69325fdc7bfc4f6f8e8009323 Mon Sep 17 00:00:00 2001 From: Jules Date: Sun, 21 Jun 2026 23:47:06 +0200 Subject: [PATCH] Pellicule : marge de coupe plus large + warmup LiveView apres capture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La coupe physique du K60 n'est jamais parfaitement centree, donc une marge noire trop fine fait perdre la pellicule d'un cote du strip dedouble. Marge de coupe (perforations) elargie de 34 a 55px (~4.6mm) de chaque cote. Ajoute aussi un warmup LiveView (_chauffer_liveview) juste apres la remontee du miroir post-capture : consomme les ~1-2s de stabilisation pendant le spinner de traitement plutot que de laisser le preview du countdown suivant figé sur l'ancienne photo. --- backend/camera.py | 14 ++++++++++++++ backend/collage.py | 18 +++++++++++++----- frontend/index.html | 2 +- frontend/js/camera.js | 2 +- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/backend/camera.py b/backend/camera.py index 6aa8f8e..063740c 100644 --- a/backend/camera.py +++ b/backend/camera.py @@ -172,6 +172,7 @@ class Camera: tmp_path = str(chemin_dest) + ".tmp" fichier_camera.save(tmp_path) self.activer_viewfinder() # Miroir relevé immédiatement après la prise + self._chauffer_liveview() # Absorbe la stabilisation LiveView ici (spinner visible), pas sur le countdown suivant from PIL import ImageOps as PILImageOps img = PILImageOps.exif_transpose(PILImage.open(tmp_path)) if img.width > 4000: @@ -189,6 +190,19 @@ class Camera: log.error(f"Erreur capture DSLR : {e}") return None + def _chauffer_liveview(self, tentatives: int = 8, pause: float = 0.15): + """Consomme les premieres frames LiveView juste apres la remontee du miroir + (souvent en erreur ~1-2s le temps que l'AF/capteur se stabilise), pendant + que le spinner de traitement est affiche — pour que le preview du prochain + countdown soit deja a jour au lieu de rester figé sur l'ancienne photo.""" + for _ in range(tentatives): + try: + with self._gp_lock: + self.camera.capture_preview() + return + except Exception: + time.sleep(pause) + def _capture_webcam(self, chemin_dest: Path) -> Path | None: if not self.webcam or not self.webcam.isOpened(): return None diff --git a/backend/collage.py b/backend/collage.py index 3455fe0..af4c1bb 100644 --- a/backend/collage.py +++ b/backend/collage.py @@ -47,8 +47,13 @@ def creer_strip(photos: list[Path], marge: int = 10, couleur_fond: str = "#00000 bande_largeur = 600 bande_hauteur = 1800 - # Zone perforations pellicule (gauche et droite) - perf_largeur = 34 + # Zone perforations pellicule (gauche et droite). Cette zone noire est aussi + # la marge de securite pour la coupe physique du K60 : la feuille pleine + # (2 bandes cote a cote) est coupee au centre apres impression, et la coupe + # n'est jamais parfaitement precise. Une marge trop fine fait que la coupe + # tombe dans la photo d'un cote (pellicule absente d'un cote, visible de + # l'autre). Elargie a 55px (~4.6mm) pour absorber le jeu mecanique du cutter. + perf_largeur = 55 zone_photos_largeur = bande_largeur - perf_largeur * 2 # Taille de chaque photo dans la bande (separees par bandes noires) @@ -70,16 +75,19 @@ def creer_strip(photos: list[Path], marge: int = 10, couleur_fond: str = "#00000 draw = ImageDraw.Draw(strip) # Bords pellicule noirs (deja le fond) - # Perforations pellicule (gauche) - trous blancs dans le bord noir + # Perforations pellicule (gauche) - trous gris dans le bord noir, centrees + # dans la marge (laisse du noir plein des 2 cotes pour la marge de coupe) + trou_largeur = 26 + trou_x = (perf_largeur - trou_largeur) // 2 for py in range(marge + 10, bande_hauteur - marge, 44): draw.rounded_rectangle( - [8, py, perf_largeur - 8, py + 22], + [trou_x, py, trou_x + trou_largeur, py + 22], radius=5, fill=(60, 60, 60) ) # Perforations pellicule (droite) for py in range(marge + 10, bande_hauteur - marge, 44): draw.rounded_rectangle( - [bande_largeur - perf_largeur + 8, py, bande_largeur - 8, py + 22], + [bande_largeur - perf_largeur + trou_x, py, bande_largeur - perf_largeur + trou_x + trou_largeur, py + 22], radius=5, fill=(60, 60, 60) ) diff --git a/frontend/index.html b/frontend/index.html index 85ddf6e..8851f7f 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -739,7 +739,7 @@ - + diff --git a/frontend/js/camera.js b/frontend/js/camera.js index 71e7ab5..90a3f2d 100644 --- a/frontend/js/camera.js +++ b/frontend/js/camera.js @@ -94,7 +94,7 @@ function getRatioCelluleAttendu() { const mode = config.multi_shot?.mode || 'strip'; if (mode === 'strip') { // Reproduit creer_strip() dans backend/collage.py (bande portrait 600x1800, photos empilees) - const bandeLargeur = 600, bandeHauteur = 1800, marge = 10, sep = 12, perfLargeur = 34; + const bandeLargeur = 600, bandeHauteur = 1800, marge = 10, sep = 12, perfLargeur = 55; const zonePhotosLargeur = bandeLargeur - perfLargeur * 2; const espacePhotos = bandeHauteur - marge * 2 - sep * (nb - 1); const hauteurCellule = Math.floor(espacePhotos / nb);