Add frame interpolation presets in Filters

This commit is contained in:
Stu Leak 2025-12-20 20:25:21 -05:00
parent 2f9995d8f1
commit 28e2f40b75
3 changed files with 80 additions and 4 deletions

View File

@ -835,6 +835,7 @@ This file tracks completed features, fixes, and milestones.
- ✅ Reduced module video pane minimum sizes to allow GNOME window snapping - ✅ Reduced module video pane minimum sizes to allow GNOME window snapping
- ✅ Added cache/temp directory setting with SSD recommendation and override - ✅ Added cache/temp directory setting with SSD recommendation and override
- ✅ Snippet defaults now use conversion settings (not Match Source) - ✅ Snippet defaults now use conversion settings (not Match Source)
- ✅ Added frame interpolation presets to Filters and wired filter chain to Upscale
- ✅ Stabilized video seeking and embedded rendering - ✅ Stabilized video seeking and embedded rendering
- ✅ Improved player window positioning - ✅ Improved player window positioning
- ✅ Fixed clear video functionality - ✅ Fixed clear video functionality

View File

@ -67,6 +67,7 @@ This file tracks upcoming features, improvements, and known issues.
- Snippet UI rearranged into Convert Snippet / Batch / Options with context-sensitive visibility - Snippet UI rearranged into Convert Snippet / Batch / Options with context-sensitive visibility
- Reduce module video pane min sizes to allow GNOME snapping - Reduce module video pane min sizes to allow GNOME snapping
- Cache/temp directory setting with SSD recommendation - Cache/temp directory setting with SSD recommendation
- Frame interpolation presets in Filters with Upscale linkage
*Last Updated: 2025-12-20* *Last Updated: 2025-12-20*

82
main.go
View File

@ -801,6 +801,9 @@ type appState struct {
filterFlipV bool filterFlipV bool
filterGrayscale bool filterGrayscale bool
filterActiveChain []string // Active filter chain filterActiveChain []string // Active filter chain
filterInterpEnabled bool
filterInterpPreset string
filterInterpFPS string
// Upscale module state // Upscale module state
upscaleFile *videoSource upscaleFile *videoSource
@ -12563,6 +12566,37 @@ func buildFiltersView(state *appState) fyne.CanvasObject {
state.filterSharpness = 0.0 // 0.0 to 5.0 state.filterSharpness = 0.0 // 0.0 to 5.0
state.filterDenoise = 0.0 // 0.0 to 10.0 state.filterDenoise = 0.0 // 0.0 to 10.0
} }
if state.filterInterpPreset == "" {
state.filterInterpPreset = "Balanced"
}
if state.filterInterpFPS == "" {
state.filterInterpFPS = "60"
}
buildFilterChain := func() {
var chain []string
if state.filterInterpEnabled {
fps := state.filterInterpFPS
if fps == "" {
fps = "60"
}
var filter string
switch state.filterInterpPreset {
case "Ultra Fast":
filter = fmt.Sprintf("minterpolate=fps=%s:mi_mode=blend", fps)
case "Fast":
filter = fmt.Sprintf("minterpolate=fps=%s:mi_mode=duplicate", fps)
case "High Quality":
filter = fmt.Sprintf("minterpolate=fps=%s:mi_mode=mci:mc_mode=aobmc:me_mode=bidir:vsbmc=1:search_param=32", fps)
case "Maximum Quality":
filter = fmt.Sprintf("minterpolate=fps=%s:mi_mode=mci:mc_mode=aobmc:me_mode=bidir:vsbmc=1:search_param=64", fps)
default: // Balanced
filter = fmt.Sprintf("minterpolate=fps=%s:mi_mode=mci:mc_mode=obmc:me_mode=bidir:me=epzs:search_param=16:vsbmc=0", fps)
}
chain = append(chain, filter)
}
state.filterActiveChain = chain
}
// File label // File label
fileLabel := widget.NewLabel("No file loaded") fileLabel := widget.NewLabel("No file loaded")
@ -12607,8 +12641,8 @@ func buildFiltersView(state *appState) fyne.CanvasObject {
upscaleNavBtn := widget.NewButton("Send to Upscale →", func() { upscaleNavBtn := widget.NewButton("Send to Upscale →", func() {
if state.filtersFile != nil { if state.filtersFile != nil {
state.upscaleFile = state.filtersFile state.upscaleFile = state.filtersFile
// TODO: Transfer active filter chain to upscale buildFilterChain()
// state.upscaleFilterChain = state.filterActiveChain state.upscaleFilterChain = append([]string{}, state.filterActiveChain...)
} }
state.showUpscaleView() state.showUpscaleView()
}) })
@ -12656,14 +12690,50 @@ func buildFiltersView(state *appState) fyne.CanvasObject {
widget.NewCheck("Grayscale", func(b bool) { state.filterGrayscale = b }), widget.NewCheck("Grayscale", func(b bool) { state.filterGrayscale = b }),
)) ))
// Frame Interpolation Section
interpEnabledCheck := widget.NewCheck("Enable Frame Interpolation", func(checked bool) {
state.filterInterpEnabled = checked
buildFilterChain()
})
interpEnabledCheck.SetChecked(state.filterInterpEnabled)
interpPresetSelect := widget.NewSelect([]string{"Ultra Fast", "Fast", "Balanced", "High Quality", "Maximum Quality"}, func(val string) {
state.filterInterpPreset = val
buildFilterChain()
})
interpPresetSelect.SetSelected(state.filterInterpPreset)
interpFPSSelect := widget.NewSelect([]string{"24", "30", "50", "59.94", "60"}, func(val string) {
state.filterInterpFPS = val
buildFilterChain()
})
interpFPSSelect.SetSelected(state.filterInterpFPS)
interpHint := widget.NewLabel("Balanced preset is recommended; higher presets are CPU-intensive.")
interpHint.TextStyle = fyne.TextStyle{Italic: true}
interpHint.Wrapping = fyne.TextWrapWord
interpSection := widget.NewCard("Frame Interpolation (Minterpolate)", "", container.NewVBox(
widget.NewLabel("Generate smoother motion by interpolating new frames"),
interpEnabledCheck,
container.NewGridWithColumns(2,
widget.NewLabel("Preset:"),
interpPresetSelect,
widget.NewLabel("Target FPS:"),
interpFPSSelect,
),
interpHint,
))
buildFilterChain()
// Apply button // Apply button
applyBtn := widget.NewButton("Apply Filters", func() { applyBtn := widget.NewButton("Apply Filters", func() {
if state.filtersFile == nil { if state.filtersFile == nil {
dialog.ShowInformation("No Video", "Please load a video first.", state.window) dialog.ShowInformation("No Video", "Please load a video first.", state.window)
return return
} }
// TODO: Implement filter application buildFilterChain()
dialog.ShowInformation("Coming Soon", "Filter application will be implemented soon.", state.window) dialog.ShowInformation("Filters", "Filters are now configured and will be applied when sent to Upscale.", state.window)
}) })
applyBtn.Importance = widget.HighImportance applyBtn.Importance = widget.HighImportance
@ -12680,6 +12750,7 @@ func buildFiltersView(state *appState) fyne.CanvasObject {
colorSection, colorSection,
enhanceSection, enhanceSection,
transformSection, transformSection,
interpSection,
creativeSection, creativeSection,
applyBtn, applyBtn,
) )
@ -12743,6 +12814,9 @@ func buildUpscaleView(state *appState) fyne.CanvasObject {
if !state.upscaleAIAvailable { if !state.upscaleAIAvailable {
state.upscaleAIAvailable = checkAIUpscaleAvailable() state.upscaleAIAvailable = checkAIUpscaleAvailable()
} }
if len(state.filterActiveChain) > 0 {
state.upscaleFilterChain = append([]string{}, state.filterActiveChain...)
}
// File label // File label
fileLabel := widget.NewLabel("No file loaded") fileLabel := widget.NewLabel("No file loaded")