Add pi camera v3 support

This commit is contained in:
ABelliqueux 2024-12-15 12:28:40 +01:00
parent 3c00380b0d
commit 0bf7fb9d98
3 changed files with 137 additions and 54 deletions

View File

@ -1,3 +1,4 @@
#!/bin/env python
import cv2
import gettext
from itertools import count
@ -14,6 +15,10 @@ import tomllib
import numpy as np
import serialutils
# Run from SSH
if not os.getenv('DISPLAY'):
os.putenv('DISPLAY', ':0')
running_from_folder = os.path.realpath(__file__)
alphabet = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
@ -25,6 +30,7 @@ _ = gettext.translation('template', localedir='locales', languages=[LOCALE]).get
# Config
# defaults
project_settings_defaults = {
'cam_is_picam': True,
'cam_is_showmewebcam': False,
'use_date_for_folder': False,
'file_extension':'png',
@ -65,13 +71,21 @@ for location in config_locations:
config_found_msg = _("Found configuration file in {}").format(os.path.expanduser(location))
print(config_found_msg)
camera_current_settings = {
if project_settings['cam_is_showmewebcam']:
camera_current_settings = {
'auto_exposure': dict(min=0, max=1, default=camera_settings['auto_exposure'], value=camera_settings['auto_exposure']),
'white_balance_auto_preset': dict(min=0, max=9, default=camera_settings['white_balance_auto_preset'], value=camera_settings['white_balance_auto_preset']),
'horizontal_flip': dict(min=0, max=1, default=camera_settings['hflip'], value=camera_settings['hflip']),
'vertical_flip': dict(min=0, max=1, default=camera_settings['vflip'], value=camera_settings['vflip']),
'video_bitrate': dict(min=25000000, max=25000000, default=camera_settings['video_bitrate'], value=camera_settings['video_bitrate']),
}
else:
camera_current_settings = {
'auto_exposure': dict(min=0, max=4, default=camera_settings['auto_exposure'], value=camera_settings['auto_exposure']),
'white_balance_auto_preset': dict(min=0, max=7, default=camera_settings['white_balance_auto_preset'], value=camera_settings['white_balance_auto_preset']),
'horizontal_flip': dict(min=0, max=1, default=camera_settings['hflip'], value=camera_settings['hflip']),
'vertical_flip': dict(min=0, max=1, default=camera_settings['vflip'], value=camera_settings['vflip']),
}
def apply_cam_setting(cam_settings:dict, to_set:list=None):
@ -113,7 +127,7 @@ def generate_text_image(text:str, screen_w, screen_h, bullets=False):
)
text_image_draw = ImageDraw.Draw(text_image)
if text is not None:
font = ImageFont.truetype("Tuffy_Bold.ttf", (screen_w/32))
font = ImageFont.truetype("Tuffy_Bold.ttf", int(screen_w/32))
lines = text.split('\n')
longest_line = lines[0]
for line in lines:
@ -435,12 +449,37 @@ def main(args):
global onionskin, liveview_only, playback, loop_playback, playhead, index, img_list, first_playback, camera_current_settings
if not testDevice(0):
print(_("No camera device found. Exiting..."))
return 1
cam = cv2.VideoCapture(0)
cam.set(cv2.CAP_PROP_FRAME_WIDTH, camera_settings['cam_w'])
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, camera_settings['cam_h'])
if not project_settings['cam_is_picam']:
if not testDevice(0):
print(_("No camera device found. Exiting..."))
return 1
cam = cv2.VideoCapture(0)
cam.set(cv2.CAP_PROP_FRAME_WIDTH, camera_settings['cam_w'])
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, camera_settings['cam_h'])
else:
# Pi Cam V3 setup
from picamera2 import Picamera2
from libcamera import Transform
cam = Picamera2()
picam_config = cam.create_video_configuration(main={"format": 'RGB888',"size": (camera_settings['cam_w'], camera_settings['cam_h'])})
# ~ picam_config["transform"] = Transform(hflip=camera_settings['hflip'], vflip=camera_settings['vflip'])
picam_config["transform"] = Transform(vflip=camera_current_settings['vertical_flip']['value'],hflip=camera_current_settings['horizontal_flip']['value'])
cam.configure(picam_config)
# Autofocus, get lens position and switch to manual mode
# Set Af mode to Manual (0). Default is Continuous (2), Auto is 1
cam.set_controls({'AfMode':1})
cam.start()
cam.autofocus_cycle()
cam_lenspos = cam.capture_metadata()['LensPosition']
# Set focus, wb, exp to manual
cam.set_controls({'AfMode': 0,
'AwbEnable': 1,
'AwbMode': camera_current_settings['white_balance_auto_preset']['default'],
'AeEnable': 1,
'AeExposureMode': camera_current_settings['auto_exposure']['default']
})
# ~ cam.stop()
frame = get_onionskin_frame(savepath, index)
@ -480,10 +519,13 @@ def main(args):
if liveview_only:
# ~ onionskin = False
ret, overlay = cam.read()
if not ret:
print(_("Failed to grab frame."))
break
if not project_settings['cam_is_picam']:
ret, overlay = cam.read()
if not ret:
print(_("Failed to grab frame."))
break
else:
overlay = cam.capture_array("main")
# Resize preview
overlay = cv2.resize(overlay, (project_settings['screen_w'], project_settings['screen_h']))
cv2.imshow("StopiCV", overlay)
@ -491,7 +533,13 @@ def main(args):
# ~ onionskin = True
if onionskin:
ret, overlay = cam.read()
if not project_settings['cam_is_picam']:
ret, overlay = cam.read()
if not ret:
print(_("Failed to grab frame."))
break
else:
overlay = cam.capture_array("main")
og_frame = overlay.copy()
# Resize preview
overlay = cv2.resize(overlay, (project_settings['screen_w'], project_settings['screen_h']))
@ -499,95 +547,115 @@ def main(args):
alpha = project_settings['onionskin_alpha_default']
beta = (1.0 - alpha)
overlay = cv2.addWeighted(frame, alpha, overlay, beta, 0)
if not ret:
print(_("Failed to grab frame."))
break
cv2.imshow("StopiCV", overlay)
if not playback and not onionskin and not liveview_only:
cv2.imshow("StopiCV", frame)
k = cv2.waitKey(1)
# TODO : Show liveview only
# Key l / 5
if (k%256 == 108) or (k%256 == 53):
# Key l / kp 5
if (k%256 == 108) or (k%256 == 53) or (k%256 == 181):
print(_("Liveview only"))
# Toggle liveview
liveview_only = not liveview_only
onionskin = not onionskin
# Key o / 9
if (k%256 == 111) or (k%256 == 47):
# Key o / kp slash
elif (k%256 == 111) or (k%256 == 47) or (k%256 == 175):
print(_("Onionskin toggle"))
# Toggle onionskin
onionskin = not onionskin
liveview_only = False
# Key w / 7 - cycle wb
if (k%256 == 119) or (k%256 == 55):
elif (k%256 == 119) or (k%256 == 55) or (k%256 == 183):
print(_("White balance mode"))
camera_current_settings = apply_cam_setting(camera_current_settings, ['white_balance_auto_preset'])
if project_settings['cam_is_picam']:
cam.set_controls({'AwbMode': camera_current_settings['white_balance_auto_preset']['value']})
# Key x / 1 - cycle exposure
if (k%256 == 120) or (k%256 == 49):
elif (k%256 == 120) or (k%256 == 49) or (k%256 == 177):
print(_("Exp. mode"))
camera_current_settings = apply_cam_setting(camera_current_settings, ['auto_exposure'])
if project_settings['cam_is_picam']:
print(camera_current_settings['auto_exposure']['value'])
if camera_current_settings['auto_exposure']['value'] == 4:
cam.set_controls({'AeEnable': 0})
else:
cam.set_controls({'AeEnable': 1})
cam.set_controls({"AeExposureMode": camera_current_settings['auto_exposure']['value']})
# Key f / 3 - flip image
if (k%256 == 102) or (k%256 == 51):
elif (k%256 == 102) or (k%256 == 51) or (k%256 == 179):
print(_("Flip image"))
camera_current_settings = apply_cam_setting(camera_current_settings, ['vertical_flip','horizontal_flip'])
# Key up
elif (k%256 == 82) or (k%256 == 56):
if project_settings['cam_is_picam']:
cam.stop()
picam_config["transform"] = Transform(vflip=camera_current_settings['vertical_flip']['value'],hflip=camera_current_settings['horizontal_flip']['value'])
cam.configure(picam_config)
cam.start()
# Key up, kp 8
elif (k%256 == 82) or (k%256 == 56) or (k%256 == 184):
print(_("Last frame"))
if len(img_list):
if playback:
playback = False
index, frame = last_frame(index)
# Key down
elif (k%256 == 84) or (k%256 == 50):
# Key down , kp 2
elif (k%256 == 84) or (k%256 == 50) or (k%256 == 178):
print(_("First frame"))
if len(img_list):
if playback:
playback = False
index, frame = first_frame(index)
# Key left
elif (k%256 == 81) or (k%256 == 52):
# Key left, kp 4
elif (k%256 == 81) or (k%256 == 52) or (k%256 == 180):
print(_("Prev. frame"))
# Displau previous frame
if len(img_list):
if playback:
playback = False
index, frame = previous_frame(index)
# Key right
elif (k%256 == 83) or (k%256 == 54):
# Key right, kp 6
elif (k%256 == 83) or (k%256 == 54) or (k%256 == 182):
print(_("Next frame"))
# Displau next frame
if len(img_list):
if playback:
playback = False
index, frame = next_frame(index)
# Key r / keypad 9 - reset wb,exp
elif (k%256 == 114) or (k%256 == 57):
elif (k%256 == 114) or (k%256 == 57) or (k%256 == 185) :
print(_("Reset camera settings"))
camera_current_settings = apply_cam_setting(camera_current_settings)
if project_settings['cam_is_picam']:
if camera_current_settings['auto_exposure']['default'] == 4:
cam.set_controls({'AeEnable': 0})
else:
cam.set_controls({'AeEnable': 1})
cam.set_controls({"AeExposureMode": camera_current_settings['auto_exposure']['default']})
cam.set_controls({'AwbMode': camera_current_settings['white_balance_auto_preset']['default']})
cam.stop()
picam_config["transform"] = Transform(vflip=camera_current_settings['vertical_flip']['default'],hflip=camera_current_settings['horizontal_flip']['default'])
cam.configure(picam_config)
cam.start()
# Key e / keypad *
elif (k%256 == 101) or (k%256 == 42):
elif (k%256 == 101) or (k%256 == 42) or (k%256 == 170) :
print(_("Export"))
ffmpeg_process = export_animation(input_filename, export_filename)
# Key Return
elif (k%256 == 13):
# Key Return, kp return
elif (k%256 == 13) or (k%256 == 141) :
print(_("Playback"))
playhead = index
loop_playback = True
playback = not playback
# Key remove frame - backspace, del, numpad_minus
elif (k%256 == 8) or (k%256 == 45) or (k == 255):
elif (k%256 == 8) or (k%256 == 45) or (k == 255) or (k%256 == 173) :
# Remove frame
print(_("Remove frame"))
img_list, index, frame = remove_frame(img_list, index)
# Quit app
elif k%256 == 27:
# ESC pressed
print(_("Escape hit, exiting..."))
break
elif ctrlc_pressed:
print(_("Ctrl-C hit, exiting..."))
break
elif cv2.getWindowProperty("StopiCV", cv2.WND_PROP_VISIBLE) < 1:
print(_("Window was closed, exiting..."))
break
# Take pic
# SPACE or numpad 0 pressed
elif (k%256 == 32) or (k%256 == 48):
elif (k%256 == 32) or (k%256 == 48) or (k%256 == 176):
print(_("Capture frame"))
img_name = return_next_frame_number(get_last_frame(savepath))
img_path = os.path.join(savepath, img_name)
cv2.imwrite(img_path, og_frame)
@ -597,6 +665,18 @@ def main(args):
else:
index += 1
frame = get_onionskin_frame(savepath, index)
# Quit app
elif k%256 == 27:
# ESC pressed
print(_("Escape hit, exiting..."))
break
elif ctrlc_pressed:
print(_("Ctrl-C hit, exiting..."))
break
elif cv2.getWindowProperty("StopiCV", cv2.WND_PROP_AUTOSIZE) == -1:
print(_("Window was closed, exiting..."))
# ~ pass
break
# REMOVE : Debug print keycode
elif k==-1: # normally -1 returned,so don't print it
continue
@ -619,7 +699,10 @@ def main(args):
except:
print(_("Terminating running process..."))
ffmpeg_process.terminate()
cam.release()
if not project_settings["cam_is_picam"]:
cam.release()
else:
cam.close()
cv2.destroyAllWindows()
cv2.namedWindow("StopiCV", cv2.WINDOW_GUI_NORMAL)

View File

@ -28,8 +28,8 @@ Dans un terminal :
1. Installer les dépendances suivantes :
```
# Avec une distribution basée sur Debian (Ubuntu, Mint...)
sudo apt install --no-install-recommends --no-install-suggests git ffmpeg python3-tk python3-pip python3-venv libtiff5-dev libtopenjp2 libopenjp2-7-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev libharfbuzz-dev libfribidi-dev libxcb1-dev python3-tk python3-dev
```
sudo apt install --no-install-recommends --no-install-suggests git ffmpeg python3-tk python3-pip python3-venv libtiff5-dev libtopenjp2 libopenjp2-7-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev libharfbuzz-dev libfribidi-dev libxcb1-dev python3-tk python3-dev libopenblas-dev libatlas-base-dev libhdf5-dev libhdf5-serial-dev libatlas-base-dev libjasper-dev libqtgui4 libqt4-test
```
- (Optionnel) Pour installer un environnement graphique minimal sur une [installation console](https://debian-facile.org/doc:install:installation-minimale) : `sudo apt install --no-install-recommends --no-install-suggests openbox xserver-xorg xinit pcmanfm gmrun lxterminal hsetroot unclutter plymouth plymouth-themes`
2. Cloner le dépôt dans le dossier de votre choix : `git clone https://git.arthus.net/arthus/stopi2.git`
3. Aller dans répertoire du projet : `cd stopi2`

View File

@ -1,5 +1,5 @@
Send2Trash
opencv-python
numpy
pyserial
pillow
opencv-python