fix playback timing, apply cam settings, rest wb/exp key binding

This commit is contained in:
ABelliqueux 2024-09-11 20:21:33 +02:00
parent 5aec1e0dac
commit c4e9c00b67
2 changed files with 137 additions and 38 deletions

View File

@ -9,14 +9,10 @@ import signal
import sys import sys
import subprocess import subprocess
import time import time
from timeit import default_timer as timer
import tomllib import tomllib
import numpy as np import numpy as np
import serialutils
# TODO : find camera module version from image size
# https://www.raspberrypi.com/documentation/accessories/camera.html#hardware-specification
# v4l2-ctl --list-framesizes=YU12
# 2592 x 1944 == v1
# 3280 × 2464 == v2
running_from_folder = os.path.realpath(__file__) 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'] 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']
@ -65,6 +61,15 @@ for location in config_locations:
config_found_msg = _("Found configuration file in {}").format(os.path.expanduser(location)) config_found_msg = _("Found configuration file in {}").format(os.path.expanduser(location))
print(config_found_msg) print(config_found_msg)
def reset_wb_exp():
# Flip auto-exposure, white-balance on to adapt to light environment
serialutils.send_serial_cmd(serialutils.find_cam_port(), 'v4l2-ctl -d /dev/video0 -c auto_exposure=0 -c white_balance_auto_preset=1')
# Give some time to the captor to adapt
time.sleep(1)
# Flip back, wb 3 = fluorescent
serialutils.send_serial_cmd(serialutils.find_cam_port(), 'v4l2-ctl -d /dev/video0 -c auto_exposure=1 -c white_balance_auto_preset=2')
def generate_text_image(text:str, screen_w, screen_h, bullets=False): def generate_text_image(text:str, screen_w, screen_h, bullets=False):
text_image = Image.new('RGB', text_image = Image.new('RGB',
(screen_w, screen_h), (screen_w, screen_h),
@ -352,26 +357,26 @@ def remove_frame(img_list, img_index):
else: else:
return img_list, 0, blank_image return img_list, 0, blank_image
def playback_animation(img_list, img_index): # ~ def playback_animation(img_list, img_index):
# save onionskin state # ~ # save onionskin state
if onionskin: # ~ if onionskin:
onionskin = False # ~ onionskin = False
onionskin_was_on = True # ~ onionskin_was_on = True
for file in img_list: # ~ for file in img_list:
img = update_image(img_list, img_list.index(file)) # ~ img = update_image(img_list, img_list.index(file))
print(str(img_list.index(file)) + " : " + file ) # ~ print(str(img_list.index(file)) + " : " + file )
cv2.imshow("StopiCV", img) # ~ cv2.imshow("StopiCV", img)
time.sleep(.5) # ~ time.sleep(.5)
# Restore previous frame # ~ # Restore previous frame
print(img_index) # ~ print(img_index)
frame_before_playback = update_image(img_list, img_index) # ~ frame_before_playback = update_image(img_list, img_index)
# ~ cv2.imshow("StopiCV", img) # ~ cv2.imshow("StopiCV", img)
# Restore onionskin # ~ # Restore onionskin
if 'onionskin_was_on' in locals(): # ~ if 'onionskin_was_on' in locals():
onionskin = True # ~ onionskin = True
# Restore index # ~ # Restore index
return img_index, frame_before_playback # ~ return img_index, frame_before_playback
def testDevice(source): def testDevice(source):
cap = cv2.VideoCapture(source) cap = cv2.VideoCapture(source)
@ -424,7 +429,83 @@ def main(args):
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 900) cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 900)
frame = get_onionskin_frame(savepath, index) frame = get_onionskin_frame(savepath, index)
# TODO : (re)set camera settings on startup
# Using serial to send v4l2-ctl cmd to the camera
# Use v4l-ctl to flip frame
# v4l2-ctl -d /dev/video0 -c vertical_flip=1 -c control=value
# DEPRECATED : Using config file and loading with camera-ctl
# Write config file to /tmp/blah
# ~ write_config_file = '''rm /tmp/blah && echo "power_line_frequency=2
# ~ sharpness=50
# ~ video_bitrate=25000000
# ~ auto_exposure=1
# ~ auto_exposure_bias=2
# ~ white_balance_auto_preset=3
# ~ exposure_metering_mode=1
# ~ " > /tmp/blah'''
# Apply config
# ~ apply_config = '/opt/camera-control/camera-ctl -c /tmp/blah -v /dev/video0 -i repeat_sequence_header -i h264_i_frame_period -i h264_level -i h264_profile -i compression_quality\rl\rq\r'
# ~ serialutils.send_serial_cmd(serialutils.find_cam_port(), write_config_file)
# ~ serialutils.send_serial_cmd(serialutils.find_cam_port(), apply_config)
# ~ User Controls
# ~ brightness 0x00980900 (int) : min=0 max=100 step=1 default=50 value=50 flags=slider
# ~ contrast 0x00980901 (int) : min=-100 max=100 step=1 default=0 value=-2 flags=slider
# ~ saturation 0x00980902 (int) : min=-100 max=100 step=1 default=0 value=5 flags=slider
# ~ red_balance 0x0098090e (int) : min=1 max=7999 step=1 default=1000 value=1000 flags=slider
# ~ blue_balance 0x0098090f (int) : min=1 max=7999 step=1 default=1000 value=1000 flags=slider
# ~ horizontal_flip 0x00980914 (bool) : default=0 value=0
# ~ vertical_flip 0x00980915 (bool) : default=0 value=0
# ~ power_line_frequency 0x00980918 (menu) : min=0 max=3 default=1 value=1
# ~ sharpness 0x0098091b (int) : min=-100 max=100 step=1 default=0 value=10 flags=slider
# ~ color_effects 0x0098091f (menu) : min=0 max=15 default=0 value=0
# ~ rotate 0x00980922 (int) : min=0 max=360 step=90 default=0 value=0 flags=modify-layout
# ~ color_effects_cbcr 0x0098092a (int) : min=0 max=65535 step=1 default=32896 value=32896
# ~ Codec Controls
# ~ video_bitrate_mode 0x009909ce (menu) : min=0 max=1 default=0 value=0 flags=update
# ~ video_bitrate 0x009909cf (int) : min=25000 max=25000000 step=25000 default=10000000 value=25000000
# ~ repeat_sequence_header 0x009909e2 (bool) : default=0 value=0
# ~ h264_i_frame_period 0x00990a66 (int) : min=0 max=2147483647 step=1 default=60 value=60
# ~ h264_level 0x00990a67 (menu) : min=0 max=11 default=11 value=11
# ~ h264_profile 0x00990a6b (menu) : min=0 max=4 default=4 value=4
# ~ Camera Controls
# ~ auto_exposure 0x009a0901 (menu) : min=0 max=3 default=0 value=0
# ~ exposure_time_absolute 0x009a0902 (int) : min=1 max=10000 step=1 default=1000 value=1000
# ~ exposure_dynamic_framerate 0x009a0903 (bool) : default=0 value=0
# ~ auto_exposure_bias 0x009a0913 (intmenu): min=0 max=24 default=12 value=2
# ~ white_balance_auto_preset 0x009a0914 (menu) : min=0 max=9 default=1 value=1
# ~ image_stabilization 0x009a0916 (bool) : default=0 value=0
# ~ iso_sensitivity 0x009a0917 (intmenu): min=0 max=4 default=0 value=0
# ~ iso_sensitivity_auto 0x009a0918 (menu) : min=0 max=1 default=1 value=1
# ~ exposure_metering_mode 0x009a0919 (menu) : min=0 max=3 default=0 value=0
# ~ scene_mode 0x009a091a (menu) : min=0 max=13 default=0 value=0
# ~ JPEG Compression Controls
# ~ compression_quality 0x009d0903 (int) : min=1 max=100 step=1 default=30 value=30
# Make sure we're using max bitrate
serialutils.send_serial_cmd(serialutils.find_cam_port(), 'v4l2-ctl -d /dev/video0 -c video_bitrate=25000000')
# Flip auto-exposure, white-balance on and off to adapt to light environment
reset_wb_exp()
# Flip preview (0 = vert; 1 = hor)
if project_settings['vflip']:
serialutils.send_serial_cmd(serialutils.find_cam_port(), 'v4l2-ctl -d /dev/video0 -c vertical_flip=1')
else:
serialutils.send_serial_cmd(serialutils.find_cam_port(), 'v4l2-ctl -d /dev/video0 -c vertical_flip=0')
if project_settings['hflip']:
serialutils.send_serial_cmd(serialutils.find_cam_port(), 'v4l2-ctl -d /dev/video0 -c horizontal_flip=1')
else:
serialutils.send_serial_cmd(serialutils.find_cam_port(), 'v4l2-ctl -d /dev/video0 -c horizontal_flip=0')
time.sleep(.5)
loop_delta = 0
while True: while True:
start = timer()
if playback: if playback:
if onionskin: if onionskin:
onionskin = False onionskin = False
@ -433,7 +514,11 @@ def main(args):
if playhead < len(img_list)-1: if playhead < len(img_list)-1:
playhead, img = next_frame(playhead, loop_playback) playhead, img = next_frame(playhead, loop_playback)
cv2.imshow("StopiCV", img) cv2.imshow("StopiCV", img)
time.sleep(1.0/project_settings['framerate']) # Calculate framerate according to loop execution time
frame_interval = 1.0/project_settings['framerate']-loop_delta
if frame_interval < 0:
frame_interval = 0
time.sleep(frame_interval)
else: else:
playhead = index playhead = index
img = update_image(img_list, index) img = update_image(img_list, index)
@ -445,24 +530,14 @@ def main(args):
if onionskin: if onionskin:
ret, overlay = cam.read() ret, overlay = cam.read()
og_frame = overlay og_frame = overlay.copy()
# Resize preview # Resize preview
overlay = cv2.resize(overlay, (project_settings['screen_w'], project_settings['screen_h'])) overlay = cv2.resize(overlay, (project_settings['screen_w'], project_settings['screen_h']))
# Apply onionskin # Apply onionskin
alpha = project_settings['onionskin_alpha_default'] alpha = project_settings['onionskin_alpha_default']
beta = (1.0 - alpha) beta = (1.0 - alpha)
overlay = cv2.addWeighted(frame, alpha, overlay, beta, 0) overlay = cv2.addWeighted(frame, alpha, overlay, beta, 0)
# TODO : use v4l-ctl to flip frame
# v4l2-ctl -d /dev/video0 -c vertical_flip=1
# Flip preview (0 = vert; 1 = hor)
if project_settings['vflip'] or project_settings['hflip']:
if project_settings['vflip'] and project_settings['hflip']:
flip_dir = -1
elif project_settings['vflip']:
flip_dir = 0
elif project_settings['hflip']:
flip_dir = 1
overlay = cv2.flip(overlay, flip_dir)
if not ret: if not ret:
print(_("Failed to grab frame.")) print(_("Failed to grab frame."))
break break
@ -502,6 +577,9 @@ def main(args):
if playback: if playback:
playback = False playback = False
index, frame = next_frame(index) index, frame = next_frame(index)
# Key r / keypad 9 - reset wb,exp
elif (k%256 == 114) or (k%256 == 57):
reset_wb_exp()
# Key e / keypad * # Key e / keypad *
elif (k%256 == 101) or (k%256 == 42): elif (k%256 == 101) or (k%256 == 42):
print(_("Export")) print(_("Export"))
@ -509,7 +587,6 @@ def main(args):
# Key Return # Key Return
elif (k%256 == 13): elif (k%256 == 13):
print(_("Playback")) print(_("Playback"))
print(img_list)
playhead = index playhead = index
loop_playback = True loop_playback = True
playback = not playback playback = not playback
@ -546,6 +623,10 @@ def main(args):
continue continue
else: else:
print(k) # else print its value print(k) # else print its value
end = timer()
loop_delta = end - start
print(loop_playback)
if 'ffmpeg_process' in locals(): if 'ffmpeg_process' in locals():
if ffmpeg_process.poll() is None: if ffmpeg_process.poll() is None:
print(_("Ffmpeg is still running.\n Waiting for task to complete.")) print(_("Ffmpeg is still running.\n Waiting for task to complete."))

18
serialutils.py Normal file
View File

@ -0,0 +1,18 @@
import serial
import serial.tools.list_ports
def find_cam_port():
serial_devices = serial.tools.list_ports.comports()
for dev in serial_devices:
if str(dev).find('Piwebcam') != -1:
return str(dev).split(' ')[0]
return None
def send_serial_cmd(cam_port, cmd:str, clear=True):
con = serial.Serial(cam_port, baudrate=115200)
if clear:
append = b'\rclear\r'
else:
append = b'\r'
con.write(str.encode(cmd) + append)
con.close()