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);