Repository navigation
feat(engines): offline AI image upscaling and super-resolution up to 4x (NCNN) - #1064
rohitLovesKrishna wants to merge 8 commits into
Conversation
01fe528 to
a66f29d
Compare
KnifeLemon
left a comment
There was a problem hiding this comment.
Thanks, @rohitLovesKrishna. Offline AI upscaling is a good fit for Filee: it runs locally, nothing leaves the PC, and the model choice with its live size preview is well thought out. The direction is good. Before it can merge, a few parts need to follow how Filee already handles engines, downloads and presets.
Timing: the next release is planned for around 17:00 UTC today (November 3). To make it into that release, the changes below would need to be pushed by 16:20 UTC. If that's too tight (it's a big PR), no problem: it goes into the following release.
Must fix
1. Pin every download with a SHA-256
UpscaleModelService downloads an executable (upscayl-bin) and ten models without checking what arrives. The models also come from the main branch of upscayl/custom-models, which can change at any time. Every other engine Filee downloads is pinned by version and SHA-256:
- programs in
src/Filee.Engines/Infrastructure/engines.json, installed byEngineInstaller; - per-item data (the Tesseract languages) in
ocr-languages.json, with a URL pinned to a tag or commit and a SHA-256 per file.
Please do the same:
- The binary: add
upscayl-binas an engine package (one component per platform) inengines.json. - The models: add an
upscale-models.jsonpinned to a commit ofcustom-models(notmain), with the size and SHA-256 of each.binand.param.
2. Use the existing engine installer, not a second one
The new download service and model-manager window duplicate EngineInstaller and Settings → Engines. As a package there, the upscaler gets what the rest already has:
- the install / remove / update buttons and their progress;
- locking against a second Filee;
- clean removal of an engine in use, and no leftover
.partfiles (#1058); - the busy message in every language;
filee engineson the command line.
The models can then work like the OCR languages: downloaded on first use, or from a small list in the engine's card.
3. Run the program through ProcessRunner
UpscalerEngine builds one argument string ($"-i \"{actualInput}\" …") and starts Process itself. Please use ProcessRunner.RunAsync with an argument list instead. It escapes each argument (a path with a quote in it breaks today's string), and it applies a time limit, cancellation that ends the whole process tree, lower priority and captured output.
Also:
- Work folder: put the temporary PNGs in
step.WorkDirectory, notPath.GetTempPath(). The job cleans its work folder up; the system temp folder keeps whatever a crash leaves. - Thread safety:
outputLinesis a plainListwritten from two output threads.
4. Read pictures with ImageReader
new MagickImage(inputPath) skips what ImageReader.Read does for camera RAW, layered PSD/XCF, HEIC, LIVP, KRA and the rest. Please read the source through it (with ImageEncoder.SelectFrames), as the other picture tools do.
5. Licences, per model
upscayl-bin: it's AGPL-3.0. That's fine as a separate program downloaded on demand, which is how Ghostscript comes in. Please say so inTHIRD-PARTY-NOTICES.mdthe same way, without the "/ BSD-3-Clause".- The models: "various open-source licenses" isn't enough. Some upscaling models are non-commercial (CC BY-NC…), which Filee shouldn't offer as a plain download. Please list each model's licence in the catalog and in the notices, and leave out any that aren't free to use.
6. Existing users: a settings schema step
UpscalePresets() only reaches new libraries. Existing users need a migration step that adds the tool to the library, palette only, no donut slot (#806). main is at 63 (#1062), so take 64.
7. No special case in DonutMenu
The change in DonutMenu.cs makes the label smaller only when the id is tool-upscale or the text is "Upscale". Labels fit by their length in every language, so please drop this. If "Upscale" doesn't fit somewhere, the label sizing should be fixed for every slice, in its own PR.
Smaller
- ARM64: the release has
windows,macosandlinuxzips. Please check which architectures they hold. Where there's no build (Windows ARM64, Linux ARM64), the engine should say "not available on this PC" (EngineDownloads.UnavailableReason) rather than fail. - Tests: keep anything that needs the real program or network behind
Assert.SkipUnless(...), as the other engine tests do, so CI doesn't download it.
Happy to look again as soon as it's pushed.
a66f29d to
1fca295
Compare
|
Thanks for the thorough review and guidance, @KnifeLemon! All requested changes have been addressed:
|
KnifeLemon
left a comment
There was a problem hiding this comment.
Thanks, @rohitLovesKrishna, a big step forward. Much of the review is done well:
- Pins: I downloaded every pinned file (the three upscayl-bin zips and all 18 model files at commit
4b6d2cfa…). Each size and SHA-256 matches, and the zips hold x64 Windows, x64 Linux and universal macOS binaries. - ProcessRunner, ImageReader and schema 64: in place. Schema 64 is palette only, with tests, and merges cleanly with
main. - Cleanup: the non-commercial model and the
DonutMenuspecial case are gone, and CI is green.
It won't make today's 2.3.0 release, though: a few things still need to change, one of which breaks an existing feature.
Must fix
- Whisper is broken by the installer change. In
EngineInstaller,case "model":now falls into the newncnn-param/ncnn-binblock (onmainit fell into"bin").whisper-modelhas"kind": "model", so it gets a.whisper-modelmarker and returns early.Marker()then looks for.filee-component, so Whisper never counts as installed and re-downloads its 148 MB every time. Please keep"model"with"bin"and give the NCNN files their own kinds only. A test that installs akind: "model"component would catch it. - The Windows binary needs
VCOMP140.DLL, which isn't in the zip. On a PC without the VC++ Redistributable it won't start. Add thevcruntimecomponent to the package (as Ghostscript does) and make sure that DLL is copied next toupscayl-bin.exe. - Upscaling isn't a real conversion step.
UpscalerConverter's edges are plainDirectOnlyedges with no planner flag, so a preset withUpscale.Enabledthat goes throughJobQueueroutes via ImageMagick and is never upscaled. The window also callsUpscalerEngineitself, with its own output path, so there's no toast, no history and no output rules. Please hand the job to the conversion service like the other tools do (XTool.ConversionOf(...)), with a flag on the edge (as Translate, OCR and Captions have) so only upscale presets take it. - Format ids: "Same format" uses the file's extension as the format id (
UpscaleViewModel.cs:214)..jpeg,.tifand camera RAW extensions then reachImageReader.Read/ImageEncoder.ToMagickFormat, which throws. UseFormatRegistry.Detect(...).Id, andUpscaleTool.TargetFor(PNG for sources Filee can't write). The converter's format list also has"jpeg", which isn't a format id. - Deleting a model can crash Filee:
UpscaleWindow.axaml.csdeletes in anasync voidhandler with notry/catch. AnEngineBusyExceptionorIOExceptionthere ends the app. - Closing the window doesn't stop the job: the title-bar ✕ (and opening a second window) leaves
upscayl-binrunning hidden until its 15-minute limit. Cancel on close and dispose the view model. - Notices and docs: the NMKD models are by nmkd (not Philip Hofmann), and their two OpenModelDB links return 404.
docs/ENGINES.mdstill listsuniscale_restoreand the removed model manager. - Leftover keys: 17 keys from the removed manager window are unused in all 19 files (
engines.upscaler.manage,modal_title,search_placeholder,hardware_*,recommended_*,download*,delete_*,no_models,upscale.no_models_installed,engine.reason.needs_models). Please remove them.
Smaller
- Windows on ARM: on Windows 11 ARM64 the x64 zip is offered (the runtime maps to win-x64), not "unavailable" as the PR says. Whether it runs there under emulation with Vulkan is untested, so please say which you intend.
- Scale: the binary takes 2, 3 or 4 (
-s), but the UI allows 1×. - Output: the output is written with a bare
MagickImage.Write, so the preset's quality is ignored. Use the image encoder, which also handles ICO's size limit. - Small fixes: the model download shows no progress, and the window hard-codes colours instead of theme resources.
Happy to look again for the next release.
ea61387 to
0d630cb
Compare
# Conflicts: # src/Filee.Core/Presets/BuiltInData.cs # src/Filee.Core/Presets/Preset.cs # src/Filee.Core/Settings/UserDataStore.cs # tests/Filee.App.Tests/ToolbarPaletteViewTests.cs # Conflicts: # src/Filee.Core/Settings/UserDataStore.cs
0d630cb to
a2749a8
Compare
|
Thanks for the thorough review, @KnifeLemon! All points have been addressed in the latest commit:
|
80195a8 to
f51e07f
Compare
- Restore case 'model' to bin extraction in EngineInstaller to prevent Whisper regression - Add unit test verifying kind 'model' component installation - Add vcruntime to upscayl-bin on Windows and ensure VCOMP140.DLL is copied next to binary - Add Upscale flag to ConversionEdge, RoutePlanner, and route jobs via JobQueue - Use FormatRegistry.Detect and UpscaleTool.TargetFor to avoid raw extensions as format IDs - Remove 'jpeg' from upscaler supported format IDs - Safely handle model deletion with try/catch and error dialog - Add Material trash icon StreamGeometry and fix delete button visibility for installed models - Cancel token and dispose view model on window closing - Use ImageEncoder to respect preset quality and icon constraints - Show live progress percentage during model download in dialog - Use dynamic theme brushes in UpscaleWindow instead of hardcoded hex values - Update NMKD notices author to nmkd and clean up docs/ENGINES.md - Remove 17 leftover keys across all 19 language files
KnifeLemon
left a comment
There was a problem hiding this comment.
Thanks, @rohitLovesKrishna. A lot of this is much better now: everything in the first review is done, the schema step (v69) sits cleanly on main's 68, the zip pins match, the arguments use list form, and all 22 keys are in all 20 languages. CI is green too, but these need fixing, because CI can't reach any of them.
Must fix
1. VCOMP140.DLL is still not installed (EngineDownloads.cs:57)
This is the one from my last review. vc14_runtime leaves it out on purpose: conda-forge's packaging skips vcomp*.dll, and the package depends on vcomp14 for it. So on a PC without the VC++ Redistributable, upscayl-bin won't start. Add vcomp14 (the -h818238b_41 build that goes with the runtime you pin) and copy it in CopyRuntime.
2. Portrait photos come out sideways (UpscalerEngine.cs:52-58)
The frame is written to the temp PNG without AutoOrient, and ImageReader doesn't rotate. PNG has no orientation for ApplyOptions to see later, so a phone's portrait JPEG ends up on its side. EXIF and ICC are dropped there too, whatever KeepMetadata says. Please orient first and copy the profiles to the result.
3. The default "Same format" fails for many pictures (UpscaleTool.cs:29, UpscalerConverter.cs:13)
TargetFor returns any writable format, but the converter has edges for only 8. GIF, JXL, TGA, JP2 and PSD are writable, get their own id back and end in error.no_route. Fall back to PNG unless the id is one of the targets, or add the edges.
4. The window says it worked before anything ran (UpscaleViewModel.cs:214-221)
_start only enqueues, and straight after it the window shows upscale.success, even if the job fails later. Clicking again queues the same pictures again. Please close the window after starting and let the toast report the result.
5. Removing Upscayl can break Ghostscript's status (EngineDownloads.cs:57, EngineInstaller.cs:174)
vcruntime is now shared. Removing one package moves that folder away, so IsInstalled for the other turns false, and Ghostscript shows as not installed (and the other way round). Components that another installed package still uses have to stay.
6. No Vulkan means raw English stderr (UpscalerEngine.cs:93, docs/ENGINES.md:1919)
From its source (main.cpp:1193), upscayl-bin has no CPU mode: with no Vulkan device it exits -1. The user then sees its stderr untranslated, and the docs still claim a CPU route. Refuse with a translated ConversionRefusedException and correct the docs.
Smaller
UpscaleViewModel.cs:114:common.files_countdoesn't exist, so several pictures show the raw key.home.filesis the one.PresetEditorView.axaml:302: the slider allows 1×, which saves 1 and reopens at 4×. Minimum 2.UpscalerEngine.cs:48-49: fixed temp names in the job's work folder. If a later file exits 0 without output, it gets the previous file's picture. Use unique names.UpscalerEngine.cs:17: a 15-minute total limit can kill slow runs that are still working. A stall timeout fits better, like calibre's.upscale-models.json:10,25: the author still says "Philip Hofmann (NMKD)", from the last review.- Unused:
GpuId(Preset.cs:1700) andupscale.processingin the language files. - Comments say schema 65 (
BuiltInData.cs:334,338,Preset.cs:285,UpscaleToolTests.cs:2). - Unrelated to this PR:
ClipWindowTests.cs:919triples a wait, and there are stray blank lines inAppHost.cs,EnginesPageViewModel.csandEnginesPage.axaml.cs. UpscalerTests:Assert.NotNull(status)proves nothing. Items 2 and 3 and the PNG fallback need real tests.
Question still open
Is Windows on ARM meant to work? Please say either way, so we know whether it needs a refusal or an arm64 build.
Most of this was found by reading the code, not by running upscayl-bin, so tell me if something behaves differently on your machine.
- Add vcomp14 conda package to upscayl-bin Windows components and copy OpenMP runtime in CopyRuntime - Auto-orient frames before upscaling and preserve EXIF and ICC profiles on results - Fall back unsupported writable target formats to PNG in UpscaleTool.TargetFor - Connect UpscalerConverter edges from all readable image sources to supported target formats - Close upscale dialog immediately upon job start and delegate progress/completion to conversion toast - Retain shared package components (like vcruntime) during package uninstallation in EngineInstaller - Check AppLocalRuntime for missing DLLs and refuse cleanly when Vulkan GPU is absent - Replace fixed timeout with stall/inactivity timeout and use unique temp file names - Set PresetEditorView scale slider minimum to 2 and use home.files localization key in UpscaleViewModel - Remove unused GpuId property, remove unused upscale.processing key, and update schema 69 comments - Clarify offline Vulkan requirement in docs/ENGINES.md and remove inaccurate CPU claims - Add unit tests for shared component uninstallation, auto-orient, profile preservation, and format fallback - Revert out-of-scope edits in ClipWindowTests, AppHost, EnginesPageViewModel, and EnginesPage
2026-11-04.20-14-31_compressed.mp4Thanks for the thorough review, @KnifeLemon! All points have been addressed in the latest commit: Must Fix
Smaller Items & Cleanups
Regarding Windows on ARM (win-arm64):
|
2026-11-03.19-49-47_compressed.mp4
Hi @KnifeLemon,
This PR adds offline AI image upscaling and super-resolution (up to 4x) using local Vulkan/NCNN neural network models, bringing high-fidelity local image enhancement to Filee while strictly upholding Filee's offline privacy promise.
What this adds
100% Offline AI Super-Resolution Engine (
Filee.Engines.Upscale):upscayl-bin(NCNN).Curated Model Catalog (
UpscaleModelInfo):upscayl/custom-models):4xLSDIRCompactC3,RealESRGAN_General_x4_v3,RealESRGAN_General_WDN_x4_v3.4x_NMKD-Siax_200k,4x_NMKD-Superscale-SP_178000_G,4xLSDIR,4xLSDIRplusC,4xNomos8kSC,4xHFA2k,uniscale_restore.%LOCALAPPDATA%\Filee\engines\upscaler-models\.Engines Page Integration (
Settings → Engines):WindowService.ConfirmAsync) and localized error handling.Donut Wheel & Interactive Workflow (
UpscaleWindow):≤ 11.0 px) so text never clips.300 x 400 px → 1200 x 1600 px (4x).Presets Page & Preset Editor (
Settings → Presets):"tool-upscale"(UpscaleTool) so it can be managed and configured in Settings.PresetEditorViewdisplays an Upscale options card allowing users to configure default models and scales with full model descriptions and a shortcut to the model manager.Localisation & Attribution:
src/Filee.App/Assets/i18n/*.json), maintaining exact formatting conventions (it.jsonnon-indented, Korean in standard polite~해요tone).THIRD-PARTY-NOTICES.mdforupscayl-bin / upscayl-ncnnandUpscayl Custom Models.docs/ENGINES.mdunder## Image Quality Models (Offline AI Upscaler).Tests & Verification
tests/Filee.Engines.Tests/UpscalerTests.csverifying catalog metadata, GPU detection heuristics, argument generation, CLI path resolution, cancellation tokens, and full upscale cycles.dotnet testpassing (20/20).dotnet format --verify-no-changes --severity warnclean with 0 warnings.upstream/main(d4d4c066) with zero conflicts.