Remove tk dep, use date for session option
This commit is contained in:
parent
2582da088b
commit
0e49b446c6
Binary file not shown.
|
@ -1,5 +1,6 @@
|
||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
#camera_type = 2
|
#camera_type = 2
|
||||||
|
use_date_for_folder = false
|
||||||
file_extension = 'png'
|
file_extension = 'png'
|
||||||
projects_folder = 'testcv/'
|
projects_folder = 'testcv/'
|
||||||
onion_skin_onstartup = true
|
onion_skin_onstartup = true
|
||||||
|
|
151
frame_opencv.py
151
frame_opencv.py
|
@ -2,6 +2,8 @@ import cv2
|
||||||
import gettext
|
import gettext
|
||||||
from itertools import count
|
from itertools import count
|
||||||
import os
|
import os
|
||||||
|
# Needed for utf-8 text
|
||||||
|
from PIL import ImageFont, ImageDraw, Image
|
||||||
from send2trash import send2trash
|
from send2trash import send2trash
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
|
@ -9,14 +11,14 @@ import subprocess
|
||||||
import time
|
import time
|
||||||
import tomllib
|
import tomllib
|
||||||
# TODO : remove tkinter dep
|
# TODO : remove tkinter dep
|
||||||
import tkinter as tk
|
# ~ import tkinter as tk
|
||||||
from tkinter import filedialog, messagebox
|
# ~ from tkinter import messagebox
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
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']
|
||||||
next_letter = 'A'
|
|
||||||
|
|
||||||
# l10n
|
# l10n
|
||||||
LOCALE = os.getenv('LANG', 'en_EN')
|
LOCALE = os.getenv('LANG', 'en_EN')
|
||||||
|
@ -30,7 +32,7 @@ project_settings_defaults = {
|
||||||
'file_extension':'JPG',
|
'file_extension':'JPG',
|
||||||
'trigger_mode': 'key',
|
'trigger_mode': 'key',
|
||||||
'projects_folder': '',
|
'projects_folder': '',
|
||||||
# ~ 'project_letter': 'A'
|
'use_date_for_folder': False,
|
||||||
'onion_skin_onstartup' : False,
|
'onion_skin_onstartup' : False,
|
||||||
'onionskin_alpha_default' : 0.4,
|
'onionskin_alpha_default' : 0.4,
|
||||||
'onionskin_fx' : False,
|
'onionskin_fx' : False,
|
||||||
|
@ -61,20 +63,85 @@ 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)
|
||||||
|
|
||||||
# Create blank image with 0s
|
def generate_text_image(text:str, screen_w, screen_h, bullets=False):
|
||||||
blank_image = np.zeros((project_settings['screen_h'], project_settings['screen_w'],3), np.uint8)
|
text_image = Image.new('RGB',
|
||||||
# Set all pixels to light grey
|
(screen_w, screen_h),
|
||||||
blank_image[:,0:] = 200
|
(0,0,0)
|
||||||
font = cv2.FONT_HERSHEY_SIMPLEX
|
)
|
||||||
cv2.putText(blank_image,_("Pas d'image"),(int(project_settings['screen_w']/3),int(project_settings['screen_h']/2)), font, 4, (100, 100, 100), 8, cv2.LINE_AA)
|
# ~ text_image = np.zeros((screen_h,screen_w,3),np.uint8)
|
||||||
|
# ~ text_image_pil = Image.fromarray(text_image)
|
||||||
|
text_image_draw = ImageDraw.Draw(text_image)
|
||||||
|
if text is not None:
|
||||||
|
font = ImageFont.truetype("Tuffy_Bold.ttf", (screen_w/32))
|
||||||
|
font_len = font.getlength(text.split('\n')[0])
|
||||||
|
text_image_draw.multiline_text((screen_w/2 - font_len/2, screen_h/2 ),
|
||||||
|
text,
|
||||||
|
fill=(255, 255, 255),
|
||||||
|
font=font,
|
||||||
|
align='center',
|
||||||
|
spacing=20
|
||||||
|
)
|
||||||
|
if bullets:
|
||||||
|
dot_radius = screen_w/24
|
||||||
|
x_unit = (screen_w/32)
|
||||||
|
y_unit = (screen_h/32)
|
||||||
|
green_dot = (x_unit*14, y_unit*24)
|
||||||
|
red_dot = (green_dot[0]+x_unit*4, green_dot[1])
|
||||||
|
# Green dot
|
||||||
|
text_image_draw.circle(green_dot,
|
||||||
|
dot_radius,
|
||||||
|
fill=(0,255,0),
|
||||||
|
outline=None,
|
||||||
|
width=1
|
||||||
|
)
|
||||||
|
text_image_draw.text(green_dot,
|
||||||
|
_("Yes"),
|
||||||
|
fill=(0, 0, 0),
|
||||||
|
font=font,
|
||||||
|
anchor="mm",
|
||||||
|
spacing=20
|
||||||
|
)
|
||||||
|
# Red dot
|
||||||
|
text_image_draw.circle(red_dot,
|
||||||
|
dot_radius,
|
||||||
|
fill=(0,0,255),
|
||||||
|
outline=None,
|
||||||
|
width=1
|
||||||
|
)
|
||||||
|
text_image_draw.text(red_dot,
|
||||||
|
_("No"),
|
||||||
|
fill=(0, 0, 0),
|
||||||
|
font=font,
|
||||||
|
anchor="mm",
|
||||||
|
spacing=20
|
||||||
|
)
|
||||||
|
text_image = np.array(text_image)
|
||||||
|
return text_image
|
||||||
|
|
||||||
def find_letter_after(letter:str):
|
|
||||||
if letter in alphabet and alphabet.index(letter) < len(alphabet) - 1:
|
def askyesno(text:str):
|
||||||
return alphabet[alphabet.index(letter) + 1]
|
blank = generate_text_image(text, project_settings['screen_w'], project_settings['screen_h'], bullets=True)
|
||||||
|
cv2.imshow("StopiCV", blank)
|
||||||
|
# Wait for input to continue
|
||||||
|
answer = cv2.waitKey(0)
|
||||||
|
# Space pressed == yes
|
||||||
|
if answer%256 == 32 or answer%256 == 48 :
|
||||||
|
return True
|
||||||
|
# Any other key == no
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def find_letter_after(letter:str, date=False):
|
||||||
|
if letter in alphabet and alphabet.index(letter) < len(alphabet) - 1 and not date:
|
||||||
|
letter = alphabet[alphabet.index(letter) + 1]
|
||||||
|
else:
|
||||||
|
# Create folder with date
|
||||||
|
year,mon,day,hour,minute,sec,wd,yd,dst = time.localtime()
|
||||||
|
letter = '{}-{}-{}_{}-{}-{}'.format(year,mon,day,hour,minute,sec)
|
||||||
|
return letter
|
||||||
|
|
||||||
|
|
||||||
def get_projects_folder():
|
def get_projects_folder():
|
||||||
if len(projects_folder):
|
if len(projects_folder):
|
||||||
project_folder = projects_folder
|
project_folder = projects_folder
|
||||||
|
@ -99,28 +166,34 @@ def get_projects_folder():
|
||||||
|
|
||||||
|
|
||||||
def get_session_folder():
|
def get_session_folder():
|
||||||
|
global next_letter
|
||||||
project_folder = get_projects_folder()
|
project_folder = get_projects_folder()
|
||||||
if project_folder:
|
if project_folder:
|
||||||
sessions_list = []
|
sessions_list = []
|
||||||
dir_list = os.listdir(project_folder)
|
dir_list = os.listdir(project_folder)
|
||||||
# Filter folders with name only one char long
|
# Filter folders with name only one char long
|
||||||
for dir in dir_list:
|
for folder in dir_list:
|
||||||
if len(dir) == 1 and dir in alphabet:
|
if len(folder) == 1 and folder in alphabet:
|
||||||
sessions_list.append(dir)
|
sessions_list.append(folder)
|
||||||
# If folders exist, find last folder in alphabetical order
|
# If folders exist, find last folder in alphabetical order
|
||||||
if len(sessions_list):
|
if len(sessions_list):
|
||||||
sessions_list.sort()
|
sessions_list.sort()
|
||||||
last_letter = sessions_list[-1]
|
last_letter = sessions_list[-1]
|
||||||
# By default, find next letter for a new session
|
# By default, find next letter for a new session
|
||||||
next_letter = find_letter_after(last_letter)
|
next_letter = find_letter_after(last_letter, project_settings['use_date_for_folder'])
|
||||||
if next_letter is False:
|
if next_letter is False:
|
||||||
return False
|
return False
|
||||||
# A previous session folder was found; ask the user if they wish to resume session
|
# A previous session folder was found; ask the user if they wish to resume session
|
||||||
resume_session = tk.messagebox.askyesno(_("Resume session?"), _("A previous session was found in {}, resume shooting ?").format(os.path.join(project_folder, last_letter)))
|
if not project_settings['use_date_for_folder']:
|
||||||
if resume_session:
|
resume_session = askyesno(_("A previous session was found in {},\n resume shooting ?").format(os.path.join(project_folder, last_letter)))
|
||||||
next_letter = last_letter
|
# ~ resume_session = tk.messagebox.askyesno(_("Resume session?"), _("A previous session was found in {}, resume shooting ?").format(os.path.join(project_folder, last_letter)))
|
||||||
|
if resume_session:
|
||||||
|
next_letter = last_letter
|
||||||
else:
|
else:
|
||||||
next_letter = 'A'
|
if not project_settings['use_date_for_folder']:
|
||||||
|
next_letter = 'A'
|
||||||
|
else:
|
||||||
|
next_letter = find_letter_after('A', project_settings['use_date_for_folder'])
|
||||||
if os.path.exists(os.path.join(project_folder, next_letter)) is False:
|
if os.path.exists(os.path.join(project_folder, next_letter)) is False:
|
||||||
os.mkdir(os.path.join(project_folder, next_letter))
|
os.mkdir(os.path.join(project_folder, next_letter))
|
||||||
print(_("Using {} as session folder.").format(os.path.join(project_folder, next_letter)))
|
print(_("Using {} as session folder.").format(os.path.join(project_folder, next_letter)))
|
||||||
|
@ -346,10 +419,10 @@ def main(args):
|
||||||
cam.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
|
cam.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
|
||||||
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
|
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
|
||||||
|
|
||||||
cv2.namedWindow("StopiCV", cv2.WINDOW_GUI_NORMAL)
|
# ~ cv2.namedWindow("StopiCV", cv2.WINDOW_GUI_NORMAL)
|
||||||
cv2.setWindowProperty("StopiCV", cv2.WND_PROP_OPENGL, cv2.WINDOW_OPENGL)
|
# ~ cv2.setWindowProperty("StopiCV", cv2.WND_PROP_OPENGL, cv2.WINDOW_OPENGL)
|
||||||
cv2.setWindowProperty("StopiCV", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
|
# ~ cv2.setWindowProperty("StopiCV", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
|
||||||
cv2.setWindowProperty("StopiCV", cv2.WND_PROP_ASPECT_RATIO, cv2.WINDOW_KEEPRATIO)
|
# ~ cv2.setWindowProperty("StopiCV", cv2.WND_PROP_ASPECT_RATIO, cv2.WINDOW_KEEPRATIO)
|
||||||
|
|
||||||
frame = get_onionskin_frame(savepath, index)
|
frame = get_onionskin_frame(savepath, index)
|
||||||
while True:
|
while True:
|
||||||
|
@ -403,20 +476,16 @@ def main(args):
|
||||||
# Key left
|
# Key left
|
||||||
elif (k%256 == 81) or (k%256 == 52):
|
elif (k%256 == 81) or (k%256 == 52):
|
||||||
# Displau previous frame
|
# Displau previous frame
|
||||||
print("Left")
|
|
||||||
if len(img_list):
|
if len(img_list):
|
||||||
if playback:
|
if playback:
|
||||||
playback = False
|
playback = False
|
||||||
index, frame = previous_frame(index)
|
index, frame = previous_frame(index)
|
||||||
print("L: {}".format(index))
|
|
||||||
elif (k%256 == 83) or (k%256 == 54):
|
elif (k%256 == 83) or (k%256 == 54):
|
||||||
# Displau next frame
|
# Displau next frame
|
||||||
print("Right")
|
|
||||||
if len(img_list):
|
if len(img_list):
|
||||||
if playback:
|
if playback:
|
||||||
playback = False
|
playback = False
|
||||||
index, frame = next_frame(index)
|
index, frame = next_frame(index)
|
||||||
print("R: {}".format(index))
|
|
||||||
# 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")
|
||||||
|
@ -444,8 +513,8 @@ def main(args):
|
||||||
print(_("Window was closed, exiting..."))
|
print(_("Window was closed, exiting..."))
|
||||||
break
|
break
|
||||||
# Take pic
|
# Take pic
|
||||||
|
# SPACE or numpad 0 pressed
|
||||||
elif (k%256 == 32) or (k%256 == 48):
|
elif (k%256 == 32) or (k%256 == 48):
|
||||||
# SPACE pressed
|
|
||||||
img_name = return_next_frame_number(get_last_frame(savepath))
|
img_name = return_next_frame_number(get_last_frame(savepath))
|
||||||
img_path = os.path.join(savepath, img_name)
|
img_path = os.path.join(savepath, img_name)
|
||||||
cv2.imwrite(img_path, og_frame)
|
cv2.imwrite(img_path, og_frame)
|
||||||
|
@ -455,31 +524,43 @@ def main(args):
|
||||||
else:
|
else:
|
||||||
index += 1
|
index += 1
|
||||||
frame = get_onionskin_frame(savepath, index)
|
frame = get_onionskin_frame(savepath, index)
|
||||||
# TODO : go back to last frame ?
|
|
||||||
# REMOVE : Debug print keycode
|
# REMOVE : Debug print keycode
|
||||||
elif k==-1: # normally -1 returned,so don't print it
|
elif k==-1: # normally -1 returned,so don't print it
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
print(k) # else print its value
|
print(k) # else print its value
|
||||||
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. Waiting for task to complete.")
|
print(_("Ffmpeg is still running.\n Waiting for task to complete."))
|
||||||
|
msg = generate_text_image(_("Ffmpeg is still running.\n Waiting for task to complete."),
|
||||||
|
project_settings['screen_w'], project_settings['screen_h']
|
||||||
|
)
|
||||||
|
cv2.imshow("StopiCV", msg)
|
||||||
|
# Force window refresh
|
||||||
|
cv2.pollKey()
|
||||||
try:
|
try:
|
||||||
ffmpeg_process.wait(timeout=1)
|
ffmpeg_process.wait(timeout=20)
|
||||||
except:
|
except:
|
||||||
print("Terminating running process...")
|
print(_("Terminating running process..."))
|
||||||
ffmpeg_process.terminate()
|
ffmpeg_process.terminate()
|
||||||
cam.release()
|
cam.release()
|
||||||
cv2.destroyAllWindows()
|
cv2.destroyAllWindows()
|
||||||
|
|
||||||
|
cv2.namedWindow("StopiCV", cv2.WINDOW_GUI_NORMAL)
|
||||||
|
cv2.setWindowProperty("StopiCV", cv2.WND_PROP_OPENGL, cv2.WINDOW_OPENGL)
|
||||||
|
cv2.setWindowProperty("StopiCV", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
|
||||||
|
cv2.setWindowProperty("StopiCV", cv2.WND_PROP_ASPECT_RATIO, cv2.WINDOW_KEEPRATIO)
|
||||||
|
|
||||||
ctrlc_pressed = False
|
ctrlc_pressed = False
|
||||||
projects_folder = project_settings['projects_folder']
|
projects_folder = project_settings['projects_folder']
|
||||||
|
next_letter = 'Z'
|
||||||
img_list = []
|
img_list = []
|
||||||
savepath = get_session_folder()
|
savepath = get_session_folder()
|
||||||
onionskin = project_settings['onion_skin_onstartup']
|
onionskin = project_settings['onion_skin_onstartup']
|
||||||
playback = False
|
playback = False
|
||||||
playhead = 0
|
playhead = 0
|
||||||
loop_playback = True
|
loop_playback = True
|
||||||
|
blank_image = generate_text_image(_("No images yet! Start shooting..."), project_settings['screen_w'], project_settings['screen_h'])
|
||||||
|
|
||||||
if len(savepath):
|
if len(savepath):
|
||||||
project_letter = savepath.split(os.sep)[-1]
|
project_letter = savepath.split(os.sep)[-1]
|
||||||
|
|
Binary file not shown.
|
@ -6,14 +6,20 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"POT-Creation-Date: 2024-02-18 11:30+0100\n"
|
"POT-Creation-Date: 2024-02-18 11:30+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: 2024-09-09 12:28+0200\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: arthus <arthus@ateliers>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Generated-By: pygettext.py 1.5\n"
|
"Generated-By: pygettext.py 1.5\n"
|
||||||
|
|
||||||
|
msgid "Yes"
|
||||||
|
msgstr "Oui"
|
||||||
|
|
||||||
|
msgid "No"
|
||||||
|
msgstr "Non"
|
||||||
|
|
||||||
#: ../main_c.py:96
|
#: ../main_c.py:96
|
||||||
msgid "No configuration file found, using defaults."
|
msgid "No configuration file found, using defaults."
|
||||||
msgstr "Aucun fichier de configuration trouvé, utilisation des valeurs par défaut."
|
msgstr "Aucun fichier de configuration trouvé, utilisation des valeurs par défaut."
|
||||||
|
@ -37,8 +43,8 @@ msgid ""
|
||||||
msgstr "\nCaméra introuvable ou occupée."
|
msgstr "\nCaméra introuvable ou occupée."
|
||||||
|
|
||||||
#: ../main_c.py:256
|
#: ../main_c.py:256
|
||||||
msgid "A previous session was found in {}, resume shooting ?"
|
msgid "A previous session was found in {},\n resume shooting ?"
|
||||||
msgstr "Une session précédente à été trouvée dans {}, reprendre la session ?"
|
msgstr "Une session précédente à été trouvée dans {},\n reprendre la session ?"
|
||||||
|
|
||||||
#: ../main_c.py:256
|
#: ../main_c.py:256
|
||||||
msgid "Resume session?"
|
msgid "Resume session?"
|
||||||
|
@ -86,3 +92,9 @@ msgstr "Exportation dans {}"
|
||||||
|
|
||||||
msgid "Mp4 files"
|
msgid "Mp4 files"
|
||||||
msgstr "Fichier Mp4"
|
msgstr "Fichier Mp4"
|
||||||
|
|
||||||
|
msgid "Ffmpeg is still running.\n Waiting for task to complete."
|
||||||
|
msgstr "Ffmpeg est toujours en cours d'éxécution.\n Attente de la fin du processus.'"
|
||||||
|
|
||||||
|
msgid "Terminating running process..."
|
||||||
|
msgstr "Terminaison des processus en cours..."
|
||||||
|
|
|
@ -6,8 +6,8 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"POT-Creation-Date: 2024-02-19 18:47+0100\n"
|
"POT-Creation-Date: 2024-02-19 18:47+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: 2024-09-09 12:28+0200\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: arthus <arthus@ateliers>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
@ -15,6 +15,12 @@ msgstr ""
|
||||||
"Generated-By: pygettext.py 1.5\n"
|
"Generated-By: pygettext.py 1.5\n"
|
||||||
|
|
||||||
|
|
||||||
|
msgid "Yes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "No"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: ../main_c.py:96
|
#: ../main_c.py:96
|
||||||
msgid "No configuration file found, using defaults."
|
msgid "No configuration file found, using defaults."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -38,7 +44,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ../main_c.py:281
|
#: ../main_c.py:281
|
||||||
msgid "A previous session was found in {}, resume shooting ?"
|
msgid "A previous session was found in {},\n resume shooting ?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ../main_c.py:281
|
#: ../main_c.py:281
|
||||||
|
@ -85,3 +91,8 @@ msgstr ""
|
||||||
msgid "Exporting to {}"
|
msgid "Exporting to {}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Ffmpeg is still running.\n Waiting for task to complete."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Terminating running process..."
|
||||||
|
msgstr ""
|
Loading…
Reference in New Issue