11from __future__ import annotations
22
33import logging
4- import tkinter as tk
54from pathlib import Path
6- from tkinter import filedialog
75from typing import Any
6+ import gui
87
98import numpy as np
109import pandas as pd
11- import tomli
12- from pyCRT .simpleUI import PCRT , RoiTuple
10+
11+ try :
12+ import tomllib as tomli # Python 3.11+
13+ except ModuleNotFoundError :
14+ import tomli # fallback for older Python
15+
16+ from pyCRT .simpleUI import DATETIME_FORMAT , PCRT , RoiTuple
1317
1418PLAYBACK_FPS_SPEED = {
1519 "fast" : np .inf ,
1620 "normal" : 0 ,
1721 "slow" : 25 ,
1822}
1923
24+ NORM_LEVEL = 25
25+ logging .addLevelName (NORM_LEVEL , "NORM" )
26+ VIDEO_FORMATS = (".mp4" , ".wmv" , ".avi" , ".mov" , ".mkv" )
27+
2028
2129def getLogger (name : str ):
2230 # {{{
@@ -35,21 +43,6 @@ def getLogger(name: str):
3543LOGGER = getLogger ("mauricio" )
3644
3745
38- def selectFile () -> Path :
39- # {{{
40- root = tk .Tk ()
41- root .withdraw () # hide main window
42- root .attributes ("-topmost" , True )
43- selection = filedialog .askopenfilename ()
44- if not selection :
45- raise RuntimeError ("No video selected. Aborting..." )
46- filePath = Path (selection )
47- root .destroy ()
48- return filePath
49-
50-
51- # }}}
52-
5346
5447def loadConfigFile (
5548 tomlPath , defaultsPath : Path | str = "defaults.toml"
@@ -221,7 +214,7 @@ def saveCSV(
221214 overwrite : bool = False ,
222215) -> None :
223216 # {{{
224- colLabels = ["pCRT" , "Unc" , "RelUnc" , "CT" , "ExcCri" , "ExcMet" ]
217+ colLabels = ["pCRT" , "Unc" , "RelUnc" , "CT" , "ExcCri" , "ExcMet" , "Time" ]
225218
226219 row = {
227220 "pCRT" : round (float (pcrtObj .pCRT [0 ]), 3 ),
@@ -230,6 +223,7 @@ def saveCSV(
230223 "CT" : round (float (pcrtObj .criticalTime ), 3 ),
231224 "ExcCri" : pcrtObj .exclusionCriteria ,
232225 "ExcMet" : pcrtObj .exclusionMethod ,
226+ "Time" : pcrtObj .dateTime .strftime (DATETIME_FORMAT ),
233227 }
234228
235229 csvPath = Path (csvPath )
@@ -275,21 +269,11 @@ def saveNpz(
275269 npzFilePath = npzPath / f"{ videoName } .npz"
276270 if not overwrite :
277271 npzFilePath = findUniquePath (npzFilePath )
278- np .savez (
279- str (npzFilePath ),
280- fullTimeScdsArr = pcrtObj .fullTimeScdsArr ,
281- channelsAvgIntensArr = pcrtObj .channelsAvgIntensArr ,
282- channel = pcrtObj .channel ,
283- fromTime = pcrtObj .fromTime ,
284- toTime = pcrtObj .toTime ,
285- expTuple = np .array (pcrtObj .expTuple ),
286- polyTuple = np .array (pcrtObj .polyTuple ),
287- pCRTTuple = np .array (pcrtObj .pCRTTuple ),
288- criticalTime = pcrtObj .criticalTime ,
289- exclusionMethod = pcrtObj .exclusionMethod ,
290- exclusionCriteria = pcrtObj .exclusionCriteria ,
291- )
272+ pcrtObj .name = videoName
273+ pcrtObj .save (npzFilePath )
292274 LOGGER .info (f"pCRT object saved in { npzFilePath } ." )
275+
276+
293277# }}}
294278
295279
@@ -340,16 +324,106 @@ def singleVideoPipeline(
340324
341325 LOGGER .setLevel (configDict ["General" ]["logLevel" ])
342326 LOGGER .info (f"Config loaded from { configPath } ." )
343- crtVideoPath = selectFile ()
327+ crtVideoPath = gui . selectFile ()
344328 videoName = crtVideoPath .stem
345329 pcrtObj , roi = measureCRTVideoFromConfig (crtVideoPath , configDict )
346330
331+ LOGGER .log (NORM_LEVEL , f"{ pcrtObj .plotTitle } : { pcrtObj } " )
332+
347333 plotPath = configDict ["Files" ]["plotPath" ]
348334 csvPath = configDict ["Files" ]["csvPath" ]
349335 npzPath = configDict ["Files" ]["npzPath" ]
350336 overwrite = configDict ["Files" ]["overwrite" ]
351337
338+ showPlots = configDict ["General" ]["showPlots" ]
339+ if showPlots :
340+ pcrtObj .showAvgIntensPlot ()
341+ pcrtObj .showPCRTPlot ()
342+
352343 savePlots (pcrtObj , videoName , plotPath , overwrite )
353344 saveCSV (pcrtObj , videoName , csvPath , overwrite )
354345 saveNpz (pcrtObj , videoName , npzPath , overwrite )
346+
347+
348+ # }}}
349+
350+
351+ def multiVideoPipeline (
352+ configPath : Path | str ,
353+ defaultsPath : Path | str ,
354+ ) -> list [PCRT ]:
355+ # {{{
356+ configDict = loadConfigFile (configPath , defaultsPath )
357+
358+ LOGGER .setLevel (configDict ["General" ]["logLevel" ])
359+ LOGGER .info (f"Config loaded from { configPath } ." )
360+
361+ askConfirmation = configDict ["General" ]["askConfirmation" ]
362+ # Just in case the user decides to read the videos out of order
363+ processedPaths = []
364+
365+ plotPath = configDict ["Files" ]["plotPath" ]
366+ csvPath = configDict ["Files" ]["csvPath" ]
367+ npzPath = configDict ["Files" ]["npzPath" ]
368+ overwrite = configDict ["Files" ]["overwrite" ]
369+ showPlots = configDict ["General" ]["showPlots" ]
370+
371+ dirPath = gui .selectDirectory ()
372+ for candidatePath in dirPath .iterdir ():
373+ if not candidatePath .is_file ():
374+ LOGGER .debug (
375+ f"Skipping { candidatePath .stem } .{ candidatePath .suffix } "
376+ "(not a file)..."
377+ )
378+ continue
379+ if candidatePath .suffix not in VIDEO_FORMATS :
380+ LOGGER .debug (
381+ f"Skipping { candidatePath .stem } .{ candidatePath .suffix } "
382+ "(not a video)..."
383+ )
384+ continue
385+ if candidatePath in processedPaths :
386+ LOGGER .debug (
387+ f"Skipping { candidatePath .stem } .{ candidatePath .suffix } "
388+ "(already processed)..."
389+ )
390+ continue
391+ if askConfirmation :
392+ answer = gui .askNextVideo (candidatePath )
393+ if answer == "skip" :
394+ processedPaths .append (candidatePath )
395+ continue
396+ elif answer == "abort" :
397+ break
398+ elif answer == "select" :
399+ actualPath = gui .selectFile ()
400+ else :
401+ actualPath = candidatePath
402+ else :
403+ actualPath = candidatePath
404+
405+ LOGGER .info (f"Processing video { actualPath } ." )
406+
407+ videoName = actualPath .stem
408+ try :
409+ pcrtObj , _ = measureCRTVideoFromConfig (actualPath , configDict )
410+ processedPaths .append (actualPath )
411+ except (RuntimeError , TypeError , ValueError ):
412+ LOGGER .error ("CRT calculation failed on {actualPath}" )
413+ processedPaths .append (actualPath )
414+ continue
415+
416+ LOGGER .log (NORM_LEVEL , f"{ pcrtObj .plotTitle } : { pcrtObj } " )
417+
418+ if showPlots :
419+ pcrtObj .showAvgIntensPlot ()
420+ pcrtObj .showPCRTPlot ()
421+ savePlots (pcrtObj , videoName , plotPath , overwrite )
422+ saveCSV (pcrtObj , videoName , csvPath , overwrite )
423+ saveNpz (pcrtObj , videoName , npzPath , overwrite )
424+
425+ LOGGER .info (f"Finished processing videos in { dirPath } " )
426+
427+
428+
355429# }}}
0 commit comments