Retrait verrou camera et threading : revert approche simple sans deadlock
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
import io
|
import io
|
||||||
import logging
|
import logging
|
||||||
import subprocess
|
import subprocess
|
||||||
import threading
|
|
||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
@@ -67,7 +66,6 @@ class Camera:
|
|||||||
self.webcam = None
|
self.webcam = None
|
||||||
self.webcam_index = -1
|
self.webcam_index = -1
|
||||||
self.preview_dslr_ok = True # True si le dernier preview DSLR a reussi
|
self.preview_dslr_ok = True # True si le dernier preview DSLR a reussi
|
||||||
self._lock = threading.Lock() # Verrou pour eviter acces gphoto2 concurrent
|
|
||||||
|
|
||||||
def connecter(self, source=None) -> bool:
|
def connecter(self, source=None) -> bool:
|
||||||
"""Connecte la camera. source: 'gphoto2', 'webcam:0', 'webcam:2', etc."""
|
"""Connecte la camera. source: 'gphoto2', 'webcam:0', 'webcam:2', etc."""
|
||||||
@@ -147,33 +145,32 @@ class Camera:
|
|||||||
def _capture_gphoto2(self, chemin_dest: Path) -> Path | None:
|
def _capture_gphoto2(self, chemin_dest: Path) -> Path | None:
|
||||||
from PIL import Image as PILImage
|
from PIL import Image as PILImage
|
||||||
|
|
||||||
with self._lock:
|
for attempt in range(3):
|
||||||
for attempt in range(3):
|
try:
|
||||||
try:
|
chemin_camera = self.camera.capture(gp.GP_CAPTURE_IMAGE)
|
||||||
chemin_camera = self.camera.capture(gp.GP_CAPTURE_IMAGE)
|
fichier_camera = gp.CameraFile()
|
||||||
fichier_camera = gp.CameraFile()
|
self.camera.file_get(
|
||||||
self.camera.file_get(
|
chemin_camera.folder, chemin_camera.name, gp.GP_FILE_TYPE_NORMAL, fichier_camera
|
||||||
chemin_camera.folder, chemin_camera.name, gp.GP_FILE_TYPE_NORMAL, fichier_camera
|
)
|
||||||
)
|
tmp_path = str(chemin_dest) + ".tmp"
|
||||||
tmp_path = str(chemin_dest) + ".tmp"
|
fichier_camera.save(tmp_path)
|
||||||
fichier_camera.save(tmp_path)
|
|
||||||
|
|
||||||
img = PILImage.open(tmp_path)
|
img = PILImage.open(tmp_path)
|
||||||
if img.width > 4000:
|
if img.width > 4000:
|
||||||
ratio = 4000 / img.width
|
ratio = 4000 / img.width
|
||||||
img = img.resize((4000, int(img.height * ratio)), PILImage.LANCZOS)
|
img = img.resize((4000, int(img.height * ratio)), PILImage.LANCZOS)
|
||||||
img.save(str(chemin_dest), "JPEG", quality=92)
|
img.save(str(chemin_dest), "JPEG", quality=92)
|
||||||
Path(tmp_path).unlink(missing_ok=True)
|
Path(tmp_path).unlink(missing_ok=True)
|
||||||
|
|
||||||
log.info(f"Photo capturee (DSLR) : {chemin_dest} ({img.width}x{img.height})")
|
log.info(f"Photo capturee (DSLR) : {chemin_dest} ({img.width}x{img.height})")
|
||||||
return chemin_dest
|
return chemin_dest
|
||||||
except gp.GPhoto2Error as e:
|
except gp.GPhoto2Error as e:
|
||||||
if "I/O in progress" in str(e) and attempt < 2:
|
if "I/O in progress" in str(e) and attempt < 2:
|
||||||
log.warning(f"DSLR occupe, retry {attempt + 1}/3...")
|
log.warning(f"DSLR occupe, retry {attempt + 1}/3...")
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
continue
|
continue
|
||||||
log.error(f"Erreur capture DSLR : {e}")
|
log.error(f"Erreur capture DSLR : {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _capture_webcam(self, chemin_dest: Path) -> Path | None:
|
def _capture_webcam(self, chemin_dest: Path) -> Path | None:
|
||||||
if not self.webcam or not self.webcam.isOpened():
|
if not self.webcam or not self.webcam.isOpened():
|
||||||
@@ -201,49 +198,46 @@ class Camera:
|
|||||||
return None # Pas de simulation ni webcam en dehors du mode dedie
|
return None # Pas de simulation ni webcam en dehors du mode dedie
|
||||||
|
|
||||||
def activer_viewfinder(self):
|
def activer_viewfinder(self):
|
||||||
"""Active le LiveView sur les Canon (nécessaire avant capture_preview)."""
|
"""Active le LiveView sur les Canon."""
|
||||||
if self.mode != "gphoto2" or not self.connectee:
|
if self.mode != "gphoto2" or not self.connectee:
|
||||||
return
|
return
|
||||||
with self._lock:
|
try:
|
||||||
try:
|
cfg = self.camera.get_config()
|
||||||
cfg = self.camera.get_config()
|
vf = cfg.get_child_by_name("viewfinder")
|
||||||
vf = cfg.get_child_by_name("viewfinder")
|
vf.set_value(1)
|
||||||
vf.set_value(1)
|
self.camera.set_config(cfg)
|
||||||
self.camera.set_config(cfg)
|
log.info("Viewfinder activé")
|
||||||
log.info("Viewfinder activé")
|
except Exception as e:
|
||||||
except Exception as e:
|
log.warning(f"Viewfinder non supporté : {e}")
|
||||||
log.warning(f"Viewfinder non supporté : {e}")
|
|
||||||
|
|
||||||
def desactiver_viewfinder(self):
|
def desactiver_viewfinder(self):
|
||||||
"""Désactive le LiveView."""
|
"""Désactive le LiveView."""
|
||||||
if self.mode != "gphoto2" or not self.connectee:
|
if self.mode != "gphoto2" or not self.connectee:
|
||||||
return
|
return
|
||||||
with self._lock:
|
try:
|
||||||
try:
|
cfg = self.camera.get_config()
|
||||||
cfg = self.camera.get_config()
|
vf = cfg.get_child_by_name("viewfinder")
|
||||||
vf = cfg.get_child_by_name("viewfinder")
|
vf.set_value(0)
|
||||||
vf.set_value(0)
|
self.camera.set_config(cfg)
|
||||||
self.camera.set_config(cfg)
|
log.info("Viewfinder désactivé")
|
||||||
log.info("Viewfinder désactivé")
|
except Exception as e:
|
||||||
except Exception as e:
|
log.warning(f"Viewfinder non supporté : {e}")
|
||||||
log.warning(f"Viewfinder non supporté : {e}")
|
|
||||||
|
|
||||||
def _preview_gphoto2(self) -> bytes | None:
|
def _preview_gphoto2(self) -> bytes | None:
|
||||||
with self._lock:
|
try:
|
||||||
try:
|
fichier = self.camera.capture_preview()
|
||||||
fichier = self.camera.capture_preview()
|
donnees = bytes(fichier.get_data_and_size())
|
||||||
donnees = bytes(fichier.get_data_and_size())
|
img = cv2.imdecode(np.frombuffer(donnees, dtype=np.uint8), cv2.IMREAD_COLOR)
|
||||||
img = cv2.imdecode(np.frombuffer(donnees, dtype=np.uint8), cv2.IMREAD_COLOR)
|
if img is not None:
|
||||||
if img is not None:
|
h, w = img.shape[:2]
|
||||||
h, w = img.shape[:2]
|
if w > 960:
|
||||||
if w > 960:
|
img = cv2.resize(img, (960, int(h * 960 / w)), interpolation=cv2.INTER_LINEAR)
|
||||||
img = cv2.resize(img, (960, int(h * 960 / w)), interpolation=cv2.INTER_LINEAR)
|
_, buf = cv2.imencode(".jpg", img, [cv2.IMWRITE_JPEG_QUALITY, 75])
|
||||||
_, buf = cv2.imencode(".jpg", img, [cv2.IMWRITE_JPEG_QUALITY, 75])
|
return buf.tobytes()
|
||||||
return buf.tobytes()
|
return donnees
|
||||||
return donnees
|
except gp.GPhoto2Error as e:
|
||||||
except gp.GPhoto2Error as e:
|
log.error(f"Erreur preview DSLR : {e}")
|
||||||
log.error(f"Erreur preview DSLR : {e}")
|
return None
|
||||||
return None
|
|
||||||
|
|
||||||
def _preview_webcam(self) -> bytes | None:
|
def _preview_webcam(self) -> bytes | None:
|
||||||
if not self.webcam or not self.webcam.isOpened():
|
if not self.webcam or not self.webcam.isOpened():
|
||||||
|
|||||||
Reference in New Issue
Block a user