User documentation

Using ForensicVision

Everything from installing the application to producing a court-ready PDF. Written against version 1.0.0. If something here disagrees with the application, the application is right — please open an issue.

Read this before using any output in a proceeding

Enhanced imagery is a derivative representation. Neural models can introduce structures — including characters and facial features — that are not present in the source. Read LIMITATIONS.md in full, and see Limitations below for a summary.

Requirements

ComponentMinimumRecommended
Operating systemWindows 10, or Linux with a desktop sessionWindows 11, or Ubuntu 22.04+
Python3.113.11 or 3.12
Memory8 GB16 GB or more for large frames
Disk2 GB for the application+ up to 3 GB if you install every model
GPUNone — everything runs on CPUNVIDIA with 6 GB VRAM or more, CUDA 12.x
Display1366 × 7681920 × 1080 or larger

There is no internet requirement. The only network activity the application ever performs is a model-weight download that you start yourself.

Installation

Install Python 3.11 from python.org with Add python.exe to PATH ticked, then:

PowerShell
git clone https://github.com/SihabSahariar/ForensicVision.git
cd ForensicVision

py -3.11 -m venv .venv
.venv\Scripts\Activate.ps1

python -m pip install --upgrade pip
python -m pip install -r requirements.txt

python main.py --check

If PowerShell refuses to run the activation script, allow it for the current user with Set-ExecutionPolicy -Scope CurrentUser RemoteSigned.

python main.py --check prints the environment report: Python version, whether PyTorch and CUDA were found, the weights folder and how many models are ready. It exits without starting the GUI, so it is safe to run over SSH or in CI.

Verify a build end to end

python main.py --self-test goes further: it creates a real case in a temporary folder, imports evidence, verifies its integrity, runs a classical operator and a neural model, writes a derivative with its provenance sidecar, renders a PDF report and cleans up. Thirteen checks. Use it after any install, upgrade or packaging change.

GPU acceleration

Neural models run on CPU by default. To use an NVIDIA GPU, install a CUDA build of PyTorch before or after the requirements file — the wheel index decides which build you get:

shell
# CUDA 12.6 build (check pytorch.org for the current index URL)
pip install torch --index-url https://download.pytorch.org/whl/cu126

# confirm the GPU is visible
python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))"

The status bar shows the active device, the CUDA version and live VRAM use. If an inference run exhausts VRAM, ForensicVision retries with a smaller tile before giving up, and tells you in the log that it did so. You can pin the tile size yourself in Tools → Preferences.

Full detail, including the CUDA/PyTorch compatibility matrix, is in GPU_SETUP.md.

First run

shell
python main.py

On first launch ForensicVision creates a configuration folder and an empty cases folder, and opens with no case loaded. Nothing is downloaded. The status bar reads Models: N/32 ready — the classical operators are always ready; the rest become ready once you install their weights.

To have something to work with immediately:

shell
python scripts/make_sample.py                             # ten synthetic test images
python main.py --image samples/sample_cctv.jpg --no-case  # open one without a case

The interface

The ForensicVision main window annotated by region: menu bar, toolbar, viewer tabs, inspector and status bar
  1. Menu bar — File, Case, Evidence, Analysis, Restoration, View, Tools, Help.
  2. Toolbar — the eight most frequent actions.
  3. Viewer / Compare tabs — the image, and the before/after workspace.
  4. Inspector — one dock with five tabs: Case, Analysis, Restore, History, Log. Jump with Alt+15, cycle with Ctrl+Tab, hide with F9.
  5. Status bar — the safe-mode lock, the compute device, VRAM, and how many models are ready.
Right-click is the fast path

The image viewer's context menu carries most of the application — analysis, forensic visualisations, auto enhance, selection tools, region actions, zoom presets, compare, detection, OCR, export and pixel copying. It is built from the same actions as the menu bar, so enabled state and shortcuts always agree. F11 hides the Inspector entirely and gives the image 82% of the window.

Viewing and navigation

Creating a case

File → New Case (Ctrl+N). You are asked for a case ID, a title, the investigator, the organisation and a description. All of it is written into the case manifest and printed on the report's title page.

A case is a single self-contained folder with its own SQLite database, so it can be zipped, archived or handed over as a unit. See Case folder layout. File → Recent Cases reopens the last few; Ctrl+Shift+O opens any other.

Importing evidence

File → Import Evidence (Ctrl+O). Accepted formats: .jpg .jpeg .jpe .png .bmp .tif .tiff .webp.

Import performs five things, in this order:

  1. Copies the file into evidence/original/the file you selected is never modified or moved.
  2. Computes SHA-256, SHA-512 and MD5 of the stored copy. SHA-256 is authoritative; MD5 exists only to cross-reference legacy tooling.
  3. Extracts EXIF, IPTC, XMP where present, plus image geometry and — for JPEG — the quantisation tables.
  4. Marks the stored copy read-only on the filesystem while Safe Mode is on.
  5. Writes an evidence.import audit entry recording the source path, the digest and the safe-mode state at the time.

The Case tab then shows the tree and, below it, every metadata field with its value. Copy SHA-256 puts the digest on the clipboard; Verify integrity re-hashes the stored file and compares it against the recorded digest.

What the chain of custody can and cannot say

ForensicVision records custody from the moment of import. It cannot attest to anything before that — how the file was produced, transferred or handled beforehand is outside its knowledge. Import records the source path and the digest of the copy it stores, and nothing more.

Analysing an image

Analysis → Analyse Image (A), or right-click the image. Results appear on the Analysis tab as nine indicators, each scored 0–100 and colour-coded green, amber or red.

The Analysis tab with nine indicators and the dominant-indicator summary
IndicatorWhat it measuresTypical response
BlurCrete perceptual blur, Laplacian variance, spectral high-frequency ratioDeconvolution, or a super-resolution model
Motion blurDirectional gradient-energy anisotropy, gated by blur level — reports an angleWiener with the reported angle, or Restormer
NoiseImmerkaer fast variance and Haar-MAD sigma, luminance and chroma separatelyNon-local means, or DnCNN / Restormer
JPEG artefactsPhase-selective 8×8 block discontinuity, near-edge ringing, and the container's own quantisation tablesDeblocking, or FBCNN
Low resolutionAbsolute size, plus an outer/mid spectral annulus ratio that detects prior upscalingLanczos for a baseline, then Real-ESRGAN or SwinIR
UnderexposureMedian luminance and shadow clipping fractionZero-DCE++ low-light curves, or a hand-set gamma; then CLAHE
OverexposureHighlight clipping fraction per channelExposure correction — clipped detail is gone, not recoverable
Low contrastPercentile dynamic range, RMS contrast, histogram entropyCLAHE
HazeDark-channel lower quartile, saturation, relative local contrast, transmission uniformityDark-channel-prior dehaze

Detailed analysis opens the full report: for every indicator, the estimator's name and each raw measurement it produced. Right-click a bar to copy a single score, all scores, or the raw measurements behind one.

These are indicators, not verdicts

Every score is a heuristic derived from classical image statistics, not the output of a validated classifier. Some degradations are genuinely ambiguous: heavy blur and heavy upscaling produce similar spectra, and the tool cannot always separate them. Read the raw measurements before acting on a score.

The JPEG artefacts indicator is the one exception worth knowing about: when the file is a JPEG, the encoder's quantisation tables are read directly from the container. That is a measurement, not an estimate — encode quality is recovered to within a point.

Forensic visualisations

Analysis → Forensic Visualisations, or right-click the image. Eleven renderings are available, all drawn in place of the image without modifying it. Clear Visualisation returns to the frame.

Statistical

RGB histogram · Luminance histogram · Grayscale · Saturation map · Exposure map · Clipping map

Structural

Edge map · High pass · Noise residual · Frequency spectrum · Error level analysis

Error level analysis deserves a caveat: it highlights regions that recompress differently from their surroundings. That can indicate editing, but it is also produced by ordinary things — resizing, text overlays, differing local detail. ELA is an aid to looking, never a verdict on authenticity.

Regions of interest

Pick a selection tool from Evidence, the toolbar or the context menu: rectangle (Ctrl+1), ellipse (Ctrl+2), polygon (Ctrl+3) or freehand (Ctrl+4). Drag on the image to draw; Esc cancels. For a polygon, right-click closes the shape.

With a region active you can:

Restoring an image

Auto Enhance — the recommended path

Restoration → Auto Enhance (E). The engine reads the analysis, chooses operators for the indicators above threshold, orders them sensibly — artefact removal before contrast, enlargement last — and shows you the proposal.

The pipeline review dialog listing three steps with rationale and a generative-step warning

Nothing has run at this point. In the review dialog you can:

Neural steps are shown in amber, and a banner names the pipeline as containing a generative step. Confirm and the pipeline runs on a worker thread; the progress bar reports each step, and Cancel stops at the next step boundary rather than mid-write.

Running a single operator

The Restore tab groups all thirty operators by task. Expand a group, choose a model, and its description, kind and readiness appear beneath it, followed by the controls for its parameters.

The Restore tab with the Super Resolution group expanded, showing Lanczos Upscale and its parameters

Each step's output becomes the next step's input, and the digests chain: step N's output digest is step N+1's input digest. That makes the route from original to final derivative verifiable link by link, not just end to end. Restoration → Return to Original discards the working derivative from the view; it does not delete anything already recorded.

Face restoration

Never use face restoration for identification

On the standard astronaut benchmark degraded to 128 px, CodeFormer produced a sharp, confident face wearing eyeglasses the subject does not wear, and altered apparent age, face shape and hairline. Nothing in the output separates the measured features from the invented ones.

The application therefore requires a face-specific confirmation before every run, measures and records the inter-ocular distance of each source face (warning below 30 px), exposes the fidelity weight so you can sweep it, and marks every result may synthesise. Treat the output as an illustration of what a model considers plausible — never as a depiction of a person.

Comparing results

View → Compare Original / Enhanced (Ctrl+D) opens the Compare tab.

Side-by-side comparison with the processing history panel
ModeWhat it shows
Side by sideTwo panes, zoom and pan locked together at the same apparent scale even when the derivative is four times larger.
Split viewOne frame with a draggable divider.
OverlayThe two images blended, with an opacity slider.
DifferenceFive renderings — absolute RGB, grayscale, amplified ×8, edge and heatmap — with mean and maximum difference, percentage of pixels changed, and PSNR.
A difference map is a visualisation

Difference maps are rendered with a chosen gain and colour mapping, so their apparent intensity is a display choice, not a measurement. Where the two images differ in size the original is resampled for comparison, which itself introduces small differences. The banner under the view says so, permanently.

Exporting a derivative

File → Export Derivative (Ctrl+S), or Export As… (Ctrl+Shift+S) to choose a path and format.

Prefer a lossless format — .png, .tif, .tiff or .bmp — so the exported pixels are bit-identical to the derivative that was hashed. Exporting to JPEG recompresses the image, and the file you hand over will no longer match the digest in the report.

Every export writes a JSON provenance sidecar next to the image recording the input digest, the model, its version and licence, every parameter, the device, the precision and the timestamp.

Generating a report

Case → Generate Report (Ctrl+P). Choose an output path, name the investigator and organisation, add any case-specific limitations, and pick which optional sections to include — metadata, before/after images, the difference map and the audit trail.

The report's title page beside its model provenance section

The PDF has fourteen sections:

#SectionContents
1Case informationID, title, investigator, organisation, description, counts
2Evidence informationFilename, source path, stored path, import time, geometry, bit depth
3Cryptographic hashesSHA-256, SHA-512, MD5 with the MD5 advisory
4MetadataEXIF, IPTC, XMP and JPEG quantisation tables
5Image analysisEvery indicator with its raw measurements
6Restoration pipelineEach step with task, kind, may synthesise, and the rationale
7Processing parametersEvery parameter actually used
8Model informationAuthors, licence, paper, repository, weight file and digest
9Before / afterSide-by-side thumbnails
10Difference analysisThe map and its statistics
11Processing historyEvery step, device and duration, with the digest chain
12Execution environmentApplication version, Python, platform, device
13Audit trailEvery recorded action in sequence
14Limitations and disclaimerThe standing disclaimer plus anything you added

The mandatory disclaimer is printed on the title page and in the footer of every page, so a printed extract cannot lose it. The finished PDF is hashed and registered in the case database as a report record.

Model Manager

Tools → Model Manager (Ctrl+M).

The Model Manager listing every model with task, kind, status, size and licence

Every model is listed with its task, kind, version, status, download size and licence. Selecting one shows its full detail: registry key, authors, licence, paper, repository, the method it implements, its parameters, and each weight file with its expected SHA-256.

StatusMeaning
InstalledWeights are present and match their expected digest. Ready to run.
Weights missingThe adapter is implemented; the weight file is not on disk. Press Install.
Manual installUpstream publishes no directly downloadable file. Use Install from file… after obtaining it yourself.
Not integratedDeclared for completeness but not implemented. The detail pane says exactly what is missing. It will never return a substitute result.

Installing weights

  1. Select the model and read its licence — several restrict use to non-commercial research.
  2. Press Install. A confirmation shows the source URL, the file size and whether the download can be verified against a published SHA-256.
  3. The download runs on a worker thread and can be cancelled. On completion the digest is checked; a mismatch discards the file.
  4. Verify re-hashes an installed file at any time. Remove weights deletes it.
Air-gapped or restricted machines

Install from file… takes a weight file you obtained elsewhere, verifies it against the expected digest where one is published, and copies it into the weights folder. Or copy files into the folder shown by Open weights folder and restart. Nothing is ever downloaded without you asking for it.

Batch processing

Tools → Batch Processing applies one reviewed pipeline to every image in a folder. Choose the input folder, the output folder and the pipeline, and each image is imported, processed and recorded exactly as it would be individually — with its own hashes, provenance sidecar, history and audit entries.

Batch runs sequentially on a single worker rather than in parallel. GPU inference serialises anyway, so parallelism would only raise peak VRAM and the chance of an out-of-memory failure. Progress is per file, and cancelling stops at the next file boundary, leaving completed work intact and recorded.

OCR and object detection

Both are optional integrations, reached from Analysis or the context menu.

OCR on an enhanced image reads the enhancement

If a generative step invented a character, OCR will read that character confidently. Always run OCR against the original as well as the derivative, and report both. A confidence score describes the recogniser's certainty about the pixels it was given — not whether those pixels reflect the scene.

Forensic Safe Mode

On by default; the status bar shows 🔒 FORENSIC SAFE MODE ENABLED. While it is on:

Turning it off (Tools → Forensic Safe Mode) requires confirming an explicit warning, and the change is itself audited — so a report will show that it was disabled, when, and what happened afterwards. There is no reason to disable it during casework.

Preferences

Tools → Preferences.

GroupSettingNotes
ProcessingCompute deviceauto, cuda or cpu
CUDA device indexWhich GPU, on a multi-GPU machine
Half precision (FP16)Halves VRAM use; recorded in the provenance record
Tile sizeSquare edge in pixels for tiled inference
Tile overlapBlended with a raised-cosine window, so no seams
Auto-reduce on OOMRetry with smaller tiles instead of failing
ForensicForensic Safe ModeLeave this on
Confirm before synthesisPrompt before any step that can invent detail
Allow model downloadsTurn off entirely for an air-gapped machine
FoldersCases folderWhere new cases are created
Model weightsWhere weight files live; can point at shared storage
OCRPreferred engineWhich OCR backend to use
Tesseract binaryNeeded only when it is not on PATH

Settings live in the per-user configuration folder — on Windows %LOCALAPPDATA%\ForensicVision\config, on Linux ~/.config/ForensicVision. Deleting it resets the application to defaults; cases are unaffected.

Keyboard shortcuts

Also available in the application from Help → Keyboard Shortcuts.

KeysAction
Ctrl+NNew case
Ctrl+Shift+OOpen case
Ctrl+OImport evidence
Ctrl+Shift+IOpen image without a case
Ctrl+SExport derivative
Ctrl+Shift+SExport derivative as…
A / Shift+AAnalyse image / analyse ROI
E / Shift+EAuto enhance / enhance ROI
Ctrl+RRun the staged pipeline
Ctrl+DCompare original / enhanced
Ctrl+PGenerate report
Ctrl+MModel manager
F / RFit to window / reset view
1 2 4 8Zoom 100% / 200% / 400% / 800%
Ctrl++ / Ctrl+Zoom in / out
Mouse wheelZoom under the cursor
Middle drag, or Space+dragPan
CToggle crosshair
Right-click on the imageContext menu — most actions live here
F9 / F11Show or hide the inspector / focus mode
Alt+1Alt+5Case / Analysis / Restore / History / Log tab
Ctrl+TabCycle inspector tabs
Ctrl+14Rectangle / ellipse / polygon / freehand ROI
EscCancel ROI drawing
Ctrl+Z / Ctrl+Shift+ZUndo / redo view change

Undo and redo affect the view only. Original evidence is never modified, so there is nothing to undo on it.

Command line

FlagEffect
--case DIROpen this case folder on start
--image FILEOpen this image on start
--no-caseWith --image, inspect the file without creating a case
--device {auto,cuda,cpu}Override the configured compute device for this session
--checkReport the environment and model status, then exit
--self-testRun the functional end-to-end self-test, then exit
--debugEnable debug-level logging
--versionPrint the version string and exit

Case folder layout

CASE-0007/
CASE-0007/
├── case.json            # human-readable manifest
├── case.db              # SQLite: evidence, derivatives, steps, audit
├── evidence/original/   # imported originals, read-only in Safe Mode
├── derivatives/         # every enhanced image + JSON provenance sidecars
├── analysis/            # stored analysis results
├── reports/             # generated PDFs
├── metadata/            # extracted metadata
└── logs/                # per-case log

The folder is self-contained: archive or copy it and the case travels with its database, its provenance and its reports. Derivatives form a tree — a derivative of a derivative records its parent — which is what the History tab renders and what lets any result be traced back to the original.

Scripting the engine

The restoration engine has no Qt dependency, so it can be driven from a plain Python script — for automation, experiments or as the basis of another front end.

python
from analysis import analyze_image
from core.image_io import load_image
from restoration import register_all_models
from restoration.auto_engine import AutoRestorationEngine
from restoration.pipeline import PipelineRunner

register_all_models()
image = load_image("frame.jpg")
report = analyze_image(image)

recommendation = AutoRestorationEngine().recommend(report)
for step in recommendation.pipeline.steps:
    print(step.display_name, step.parameters, step.info().may_synthesise)
# FBCNN (colour)      {'override_quality': False, 'quality_factor': 50}  True
# CLAHE Contrast      {'clip_limit': 2.0, 'tile_grid': 8}                False
# Real-ESRGAN x4plus  {'scale': 4}                                       True

result = PipelineRunner(device="auto").run(image, recommendation.pipeline)
print(result.may_synthesise)                  # True
print(result.steps[-1].output_hashes.sha256)  # the derivative's digest

The module layout and the extension points for adding an indicator or a model are described in ARCHITECTURE.md.

Troubleshooting

“PyTorch not installed” but I installed it

Check that you installed into the same environment you are launching from — python -c "import torch; print(torch.__file__)" from the active virtual environment. If you are running a packaged build, this message can also mean the bundle's PyTorch is broken rather than absent; run --self-test, which fails loudly instead of quietly degrading to CPU-with-classical-only.

CUDA is not detected

The default PyTorch wheel is CPU-only. Install a CUDA build from the index URL on pytorch.org matching your driver, then confirm with python -c "import torch; print(torch.cuda.is_available())".

Out of memory during inference

Lower the tile size in Preferences, enable FP16, close other GPU applications, or set the device to cpu for that run. With auto-reduce enabled the application retries with a smaller tile before failing, and says so in the log.

The application will not start on Linux

Almost always a missing Qt platform library. Run with QT_DEBUG_PLUGINS=1 python main.py to see which one, and install the packages listed under Installation. Over SSH you need X forwarding or a local session; --check and --self-test work headless.

I cannot delete a case folder

Safe Mode marks imported originals read-only, and Windows will refuse to remove them. Clear the read-only attribute first, or delete the case from within the application.

A downloaded weight file fails verification

The download is discarded and nothing is installed — that is the intended behaviour. Retry; if it fails again, download the file manually from the repository shown in the model detail and use Install from file….

Where are the logs?

The Log tab shows the current session live. On disk, each case writes to its own logs/ folder, and the application log is in the per-user configuration folder. --debug raises the level.

Frequently asked questions

Can I use ForensicVision's output in court?

That is a question for your jurisdiction, your instructing authority and your own expertise — not for this documentation. What the software can tell you is exactly what it did: every operation, parameter, model, licence and digest is recorded and printed. ForensicVision has no history of court acceptance and makes no claim of admissibility. Where an established, validated commercial tool is available and appropriate, use it.

Can it read an unreadable licence plate?

No tool can. If the characters were never sampled, they are not in the file and no amount of processing will recover them — a model asked for a plate will produce a plausible plate, which is not the same thing. What enhancement can legitimately do is make marginally-sampled detail easier to see. Compare against the Lanczos baseline: if the neural result shows characters the deterministic upscale does not even hint at, treat them as invented.

Does it ever modify my original file?

No. Import copies the file; the copy is what is hashed and stored read-only. The file you selected is never written to, moved or renamed.

Does it phone home?

No. There is no telemetry, no account, no activation and no licence server. The only network request the application ever makes is a model-weight download you start yourself, to a URL it shows you first — and it can be disabled entirely in Preferences.

Why keep classical operators when neural models exist?

Because they cannot invent detail, and because they sometimes simply win. On motion blur Wiener deconvolution beats Restormer by 4 dB in the bundled benchmark; on defocus blur Richardson–Lucy beats Wiener by 3 dB. They also give you a non-generative baseline to compare any neural result against.

Are results reproducible?

Classical operators are deterministic and reproduce exactly. Neural inference reproduces on the same device, precision and library versions — all of which are recorded — but GPU floating-point accumulation order means a different card or a different PyTorch build can differ in the last decimal places. The provenance record captures everything needed to attempt an exact reproduction.

How do I contribute?

Issues and pull requests are welcome at github.com/SihabSahariar/ForensicVision. The conventions that matter most: never fake a result, every operator declares whether it can invent detail, every estimator names its method, nothing long-running touches the GUI thread, and tests accompany behaviour changes.

Limitations

A summary. The authoritative list is LIMITATIONS.md, and it is the most important file in the repository.

The fundamental limit

Information that was never captured cannot be recovered. Enhancement makes existing detail easier to perceive; it does not add measurements that were not taken.

Analysis scores are heuristics

Not the output of a validated classifier. Heavy blur and heavy upscaling produce similar spectra and cannot always be separated.

Face restoration invents faces

Demonstrated, documented and fenced off. Never usable for identification.

ELA is not an authenticity verdict

ForensicVision does not authenticate images or detect manipulation.

No video

The engine is designed to be reusable for it; the application does not process video today.

No measurement or recognition

No photogrammetry, no 3D reconstruction, and no facial recognition, matching or identification of any kind.

Algorithmic image enhancement modifies image data. AI-based restoration may infer or synthesize structures that are not directly represented in the source image. Enhanced imagery is a derivative representation and should not automatically be interpreted as an exact recovery of information absent from the original evidence.