Fix déformation photos + miroir Canon EOS

- _pfit() : helper scale+crop centré pygame sans déformer
- ImageOps.fit() partout : vignettes, galerie, composite (recadrage centré
  au lieu de resize qui écrase)
- eosremoterelease 'Immediate' : déclenche l'obturateur Canon en restant
  en LV, évite la levée/baisse de miroir à chaque prise

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-11 17:49:46 +02:00
parent 32e83250d2
commit d4ad1dec17

View File

@@ -91,6 +91,18 @@ THEMES = {
},
}
# ── Helpers image ────────────────────────────────────────────────────────────
def _pfit(surf, w, h):
"""Scale + crop centré d'une surface pygame pour remplir (w, h) sans déformer."""
sw, sh = surf.get_size()
if sw == w and sh == h:
return surf
scale = max(w / sw, h / sh)
nw, nh = max(w, int(sw * scale)), max(h, int(sh * scale))
scaled = pygame.transform.scale(surf, (nw, nh))
x, y = (nw - w) // 2, (nh - h) // 2
return scaled.subsurface((x, y, w, h)).copy()
# ── Utilitaire USB ───────────────────────────────────────────────────────────
def _usb_reset_done():
"""Reset USB des DSLR via unbind/bind sysfs pour libérer tout claim obsolète."""
@@ -764,8 +776,18 @@ class PB:
if self.cam is None:
raise RuntimeError("camera None")
if self.cap_demande:
# Capture tethered LV : trigger_capture + wait_for_event
# → le miroir reste levé (déjà en liveview)
# Déclenchement sans mouvement miroir :
# eosremoterelease garde le LV actif sur Canon EOS
triggered = False
try:
cfg_c = self.cam.get_config()
rl = cfg_c.get_child_by_name('eosremoterelease')
rl.set_value('Immediate')
self.cam.set_config(cfg_c)
triggered = True
except Exception:
pass
if not triggered:
self.cam.trigger_capture()
ch = None
deadline = time.time() + 15
@@ -776,6 +798,13 @@ class PB:
self.cam.file_get(ev_data.folder, ev_data.name, gp.GP_FILE_TYPE_NORMAL, cf)
nom = f"photo_{datetime.now().strftime('%Y%m%d_%H%M%S_%f')}.jpg"
ch = str(PHOTOS_DIR/nom); cf.save(ch); break
# Réinitialise le remote release
try:
cfg_c = self.cam.get_config()
rl = cfg_c.get_child_by_name('eosremoterelease')
rl.set_value('None')
self.cam.set_config(cfg_c)
except: pass
self.cap_result=ch; self.cap_demande=False; self.cap_done.set()
errs = 0
else:
@@ -846,7 +875,7 @@ class PB:
if ch:
self.photos.append(ch); self.compteur+=1; self._sav_cpt()
try:
img = ImageOps.exif_transpose(Image.open(ch).convert("RGB")).resize((strip_bw, thumb_h), Image.LANCZOS)
img = ImageOps.fit(ImageOps.exif_transpose(Image.open(ch).convert("RGB")), (strip_bw, thumb_h), Image.LANCZOS)
self._prev_thumbs.append(pygame.image.fromstring(img.tobytes(), img.size, "RGB"))
except:
self._prev_thumbs.append(None)
@@ -867,7 +896,7 @@ class PB:
fw = (W - _PELL_GAP*(nb+1)) // nb
for ch in self.photos[:nb]:
try:
img=ImageOps.exif_transpose(Image.open(ch).convert("RGB")).resize((fw,_PELL_FH),Image.LANCZOS)
img=ImageOps.fit(ImageOps.exif_transpose(Image.open(ch).convert("RGB")),(fw,_PELL_FH),Image.LANCZOS)
surfs.append(pygame.image.fromstring(img.tobytes(),img.size,"RGB"))
except: surfs.append(None)
elif self.format in ("bande3x2","bande4x2"):
@@ -875,14 +904,14 @@ class PB:
pw = W // nb; sh = (H - 150) // 2
for ch in self.photos[:nb]:
try:
img=ImageOps.exif_transpose(Image.open(ch).convert("RGB")).resize((pw,sh),Image.LANCZOS)
img=ImageOps.fit(ImageOps.exif_transpose(Image.open(ch).convert("RGB")),(pw,sh),Image.LANCZOS)
surfs.append(pygame.image.fromstring(img.tobytes(),img.size,"RGB"))
except: surfs.append(None)
else: # grille_4
gw,gh=W//2-15,H//2-75
for ch in self.photos[:4]:
try:
img=Image.open(ch).convert("RGB").resize((gw,gh),Image.LANCZOS)
img=ImageOps.fit(Image.open(ch).convert("RGB"),(gw,gh),Image.LANCZOS)
surfs.append(pygame.image.fromstring(img.tobytes(),img.size,"RGB"))
except: surfs.append(None)
self.gal_surfs=surfs; self.gal_ready=True
@@ -907,7 +936,7 @@ class PB:
top = 80
for i, ch in enumerate(photos[:nb]):
try:
img = ImageOps.exif_transpose(Image.open(ch).convert("RGB")).resize((pw, sh), Image.LANCZOS)
img = ImageOps.fit(ImageOps.exif_transpose(Image.open(ch).convert("RGB")), (pw, sh), Image.LANCZOS)
canvas.paste(img, (i * pw, top))
except Exception as e:
print(f"composite pellicule: {e}")
@@ -916,7 +945,7 @@ class PB:
sh = (H - 150) // 2
for i, ch in enumerate(photos[:nb]):
try:
img = ImageOps.exif_transpose(Image.open(ch).convert("RGB")).resize((pw, sh), Image.LANCZOS)
img = ImageOps.fit(ImageOps.exif_transpose(Image.open(ch).convert("RGB")), (pw, sh), Image.LANCZOS)
canvas.paste(img, (i * pw, 5))
canvas.paste(img, (i * pw, sh + 10))
except Exception as e:
@@ -927,7 +956,7 @@ class PB:
pos = [(5, 5), (W // 2 + 5, 5), (5, H // 2 - 65), (W // 2 + 5, H // 2 - 65)]
for i, ch in enumerate(photos[:4]):
try:
img = Image.open(ch).convert("RGB").resize((gw, gh), Image.LANCZOS)
img = ImageOps.fit(Image.open(ch).convert("RGB"), (gw, gh), Image.LANCZOS)
canvas.paste(img, pos[i])
except Exception as e:
print(f"composite grille: {e}")
@@ -1189,7 +1218,7 @@ class PB:
# Photo prise
th = self._prev_thumbs[i]
if th:
ts = th if th.get_size() == (fw, fh) else pygame.transform.scale(th, (fw, fh))
ts = _pfit(th, fw, fh)
self.scr.blit(ts, (fx, fy))
# Léger grain/vignette pellicule
vig = pygame.Surface((fw, fh), pygame.SRCALPHA)
@@ -1259,7 +1288,7 @@ class PB:
if i < len(self._prev_thumbs):
th = self._prev_thumbs[i]
if th:
ts = th if th.get_size() == (bw, bh) else pygame.transform.scale(th, (bw, bh))
ts = _pfit(th, bw, bh)
self.scr.blit(ts, (bx, by))
pygame.draw.rect(self.scr, T["prim"], (bx, by, bw, bh), 2)
tick = self.F["xs"].render("✓", True, T["prim"])
@@ -1350,7 +1379,7 @@ class PB:
fx=gap+i*(fw+gap); fy=frame_y
surf=self.gal_surfs[i] if i<len(self.gal_surfs) else None
if surf:
ts=pygame.transform.scale(surf,(fw,fh))
ts=_pfit(surf,fw,fh)
self.scr.blit(ts,(fx,fy))
pygame.draw.rect(self.scr,T["prim"],(fx,fy,fw,fh),2)
else: