# finiSCOPE

**Which of your stacked versions is really the best one?**

*[Deutsche Fassung: README.md](README.md)*

finiSCOPE takes several stacked TIFFs of the same capture — different frame
percentages, different noise-robust settings, with and without drizzle —
measures them all by the same rules and tells you in one sentence which
version won and why.

Instead of clicking back and forth on screen and guessing, you get numbers,
curves and a verdict in plain language.

Built for the sun (H-alpha and white light), the moon and the planets.

> **Free of charge, no warranty, use at your own risk.**
> finiSCOPE is released under the MIT licence (see [LICENSE](LICENSE)) and
> may be shared freely. Liability for damages is excluded.

---

## Contents

1. [Download and run the finished app](#1-download-and-run-the-finished-app)
2. [Using it](#2-using-it)
3. [The measurements — in plain words](#3-the-measurements--in-plain-words)
4. [The two ratings](#4-the-two-ratings)
5. [The score (rank)](#5-the-score-rank)
6. [Object profiles](#6-object-profiles)
7. [Drizzle, .as3 and sessions](#7-drizzle-as3-and-sessions)
8. [Running from source](#8-running-from-source)
9. [Building the programs yourself](#9-building-the-programs-yourself)
10. [When something goes wrong](#10-when-something-goes-wrong)
11. [How the project is laid out](#11-how-the-project-is-laid-out)
12. [Colours](#12-colours)
13. [Assumptions made](#13-assumptions-made)

---

## 1. Download and run the finished app

If you use the finished programs you need **no Python** and install
nothing. Python is bundled inside the app.

### macOS

1. Download `finiSCOPE.app` and drag it into your **Applications** folder.
2. **On the very first launch:** do not double-click. Instead
   **right-click the app → Open → Open again in the dialog**.

   Why? The app is not registered with Apple (that carries a yearly
   developer fee). macOS therefore blocks a plain double-click and reports
   an *unidentified developer*. Confirming once via right-click → Open is
   enough; after that it launches normally.

3. If macOS says the app is **damaged**: that happens with files downloaded
   from the internet. Once in Terminal:

   ```bash
   xattr -dr com.apple.quarantine /Applications/finiSCOPE.app
   ```

Requires macOS 11 or newer on an Apple Silicon Mac (M1 to M4). Older Intel
Macs need a build made on an Intel Mac — see
[section 9](#9-building-the-programs-yourself).

### Windows

1. Download the ZIP file and **extract it completely**, for example to
   `C:\Program Files\finiSCOPE`.
   Important: extract the whole folder; do not just pull the `.exe` out of
   it — it needs the files beside it.
2. Double-click `finiSCOPE.exe`.
3. **On first launch** Windows Defender SmartScreen reports *“Windows
   protected your PC”*. That is normal for programs without a purchased
   signing certificate:

   → click **“More info”**
   → **“Run anyway”**

   From the second launch onwards the message does not appear again.

### Language

finiSCOPE speaks German and English. Switch under **View → Language**; the
choice is remembered.

---

## 2. Using it

**1 · Load images.** Drag the stacked TIFFs into the window — one at a
time, several at once, or a whole folder. Alternatively use *Choose files*
or **File → Open images**. Two or more versions of the **same capture**
make sense.

**2 · Pick the object type.** Sun, moon or planet. This switches the
measurement bands and the size of the measurement window.

**3 · Optional: enter your optics.** Pixel size (µm), focal length (mm) and
aperture (mm).

These figures change **neither the measurement nor the score** — they
unlock additional statements that are impossible without them:

| Entered | What you get in addition |
|---|---|
| pixel size + focal length | **sampling** in arc seconds per pixel and the effective resolution of your image in arc seconds |
| plus the aperture | **% of optimum** — how close the image gets to the theoretical resolving power, and the sampling rated as *good* / *too coarse* / *too fine* |

If the fields stay empty everything else works unchanged; only those lines
are missing. **They cannot be filled automatically** — neither the TIFF nor
AutoStakkert's `.as3` contains such information. finiSCOPE does remember
them, though: type them once and they are there next time.

Typical values: pixel size is in your camera's data sheet (e.g. 2.9 µm for
an ASI 174/178, 3.76 µm for an ASI 183). Focal length including any Barlow.
Aperture is the diameter of the telescope.

**4 · Read the result.**

* **Winner box at the top:** the best version with a one-sentence reason.
* **Image view:** the image with the measurement window as a blue
  rectangle. Beside it, in a fixed place, the content of that window at
  **original size**. Switch version and the same spot from the other image
  appears there. So you compare by eye exactly what was measured — the
  numbers say *what* is better, this crop shows *how* it looks.
* **Chart:** one curve per image, and below it a **sentence in plain
  words** saying what can be seen — for instance “the curves lie almost on
  top of each other, the difference is in the noise”. Especially when two
  versions are nearly equally sharp the curves overlap and you seem to see
  nothing; that sentence is then the actual answer.
* **Table:** two ratings side by side — **quality** (absolute, with a
  light) and **rank** (the comparison among each other). Click a row to
  open the raw values and a longer plain-text verdict. **✕ Remove** at the
  end of a row takes an image out of the comparison; the file on disk is
  left untouched.

The measured values are colour-coded as well: **green** means practically
level with the best image of the run, neutral means slightly behind, warm
means clearly worse. For signal-to-noise the colour is **absolute** — it
does not depend on what else is loaded.

**Switching between versions** works three ways: the thumbnail strip above
the image view, clicking a table row, or the **arrow keys ← →**. Image,
1:1 crop and highlighted curve all follow.

**5 · Adjusting the measurement area.** By default finiSCOPE looks for the
**most detailed region** — not simply the centre of the frame. On a full
disc the window therefore lands on an active region or a spot group by
itself rather than on quiet surface.

It is searched not in the first image but in the **average of all loaded
images**. That way the location does not depend on which file happened to
load first, and it sits where every version shows structure. The window is
then identical for all images.

If one capture differs strongly — different dimensions, a completely
different brightness — it cannot contribute, and finiSCOPE points out in
the notice bar that the images probably do not belong together.

To rate a particular spot, drag a rectangle in the image view. While
dragging, its size in pixels is shown. The window is always **square** —
the frequency analysis requires that — and is placed on the **centre** of
the area you dragged.

Once set, it can be fine-tuned:

* **drag inside it** to move it,
* **drag a corner** to resize,
* **zoom in first** to place it as precisely as you like.

The area applies to **all** images alike — only then is the comparison
fair. The *this image only* tick makes it apply to the selected image as an
exception. *Reset area* returns to automatic placement.

**Offset between captures** is compensated automatically. Two stacks from
the same session are almost never aligned to the pixel — the sun drifts,
AutoStakkert anchors differently. With *Compensate offset* ticked (the
default), the measurement window is moved in every further image to where
the same content sits. The offset found is listed in that row's raw values.

**6 · Zooming.** The scroll wheel or two-finger swipe zooms up to 12×,
always towards the pointer. Pan with the **Alt key** held (or the middle
mouse button); **double-click** shows the whole image again. The zoom is
kept when switching versions — so you can flick between two stacks of the
same spot and see the difference directly.

**7 · Saving.** *Save CSV* writes every raw value (semicolon separated, so
Excel opens it directly). *HTML report* produces a single self-contained
file with images, chart and verdicts — ready to send or archive. Both are
also in the **File** menu.

### The two chart views

Top right you can switch between two presentations of the same
measurement:

* **Sharpness curve** (default) — every curve starts at full detail on the
  left and drops where the image stops carrying detail. **The further right
  a curve falls off, the sharper.** The dot on the dashed line is exactly
  the MTF50 value from the table. This is the view for comparing.
* **Power spectrum** — the technical raw form. It falls off steeply towards
  the right by nature; that is a property of the subject, not of sharpness.
  In exchange you can see the noise plateau here: a curve that stays flat
  and high on the right is noisy.

---

## 3. The measurements — in plain words

Measurement always happens inside the measurement window, never across the
whole image. Every image gets the same window in the same place.

### Sharpness (MTF50)

**What:** how fine the image still shows real detail, in cycles per pixel.
0.5 would be the theoretical ceiling; good solar images land at 0.15–0.25.

**Remember:** higher = sharper.

**Pitfall:** hard sharpening lifts the value artificially while driving up
the noise floor at the same time. Never read it alone — always together
with SNR and noise.

### Amount of detail

**What:** how much real structure the image holds — the power in the detail
band of the spectrum, **after the noise share has been subtracted**.
Normalised to the best image of the run = 1.00.

**Remember:** higher = more real detail.

**Why the subtraction matters:** noise is broadband and would otherwise be
counted as “detail”. A noisy image would then look more detailed than a
clean one — exactly what this tool is meant to prevent.

### Signal-to-noise (SNR)

**What:** how much structure there is compared with the noise, in decibels.
Above 35 dB is very clean, below 20 dB clearly grainy.

**Remember:** higher = cleaner.

**What for:** it shows whether sharpness was bought dearly. High MTF50 with
low SNR almost always means oversharpened.

### Noise level

**What:** how strong the per-pixel noise is — independent of how much
structure the image holds.

**Remember:** lower = better.

**How it is measured:** with a Laplacian-style filter that largely cancels
smooth gradients and even edges; what remains is essentially noise. The
advantage: it works even on crops with **no** dark sky at all, such as a
full-frame active region. In addition — where present — the spread in the
dark part of the image is reported as *background*.

### Contrast and tonal range

**What:** how strong the brightness differences are and how much of the
available tonal range is used.

**Remember:** higher = punchier, low = flat.

**Pitfall:** contrast can be changed at will afterwards. That is why it
only counts 10 % in the score.

### Local sharpness

**What:** a quick sharpness figure in the spatial domain (spread after a
high-pass filter). It usually tracks MTF50 but reacts more strongly to
sharpening.

### Noise plateau

**What:** the frequency at which the curve sinks into the noise (given as a
bin number from 0 to 255).

**Remember:** **early = lots of noise**, late = clean.

### Effective resolution (with optics figures only)

**What:** your sharpness converted to arc seconds, right next to the
theoretical limit of your aperture (Dawes limit, 116 ÷ aperture in mm).

**Remember: smaller is better.** 3.4″ means two details must be at least
3.4 arc seconds apart to still be seen as two.

The value beside it — what the aperture could do in theory — is therefore
always **smaller** than the one reached; you cannot beat physics. The
percentage says how close you get.

Example: *3.44″, 2.90″ would be possible — reached: 84 %.* If the
percentage stays below 60 % it is usually seeing, focus or too few good
frames.

### Sampling (with optics figures only)

**What:** how much sky lands on one pixel, in arc seconds per pixel.

**Rule of thumb:** about **2–3 pixels** per smallest resolvable detail.
Much coarser and detail is lost (a Barlow helps). Much finer and you only
get more noise and longer exposures without any gain.

---

## 4. The two ratings

finiSCOPE answers two different questions, and it matters to keep them
apart:

| | Question | Where |
|---|---|---|
| **Quality** | Is this image good **in its own right**? | light and word in the table |
| **Rank** | Which of the loaded versions is the **best**? | 1., 2., 3. with points |

The difference counts: if you load three mediocre versions, the best of
them still comes first — **but the light stays blue or red**. If every row
is blue, finiSCOPE additionally points this out in the notice bar, because
even the winner is then not worth much.

### What quality is worked out from

Only from quantities that have an absolute meaning:

* **Share of the theoretical resolving power** of your aperture — the most
  honest measure there is. Needs the optics figures.
* **Signal-to-noise** in decibels — fixed thresholds, independent of the
  subject.

From these comes a word: *very good · good · medium · weak · very weak*.
The worse of the two decides — a razor-sharp but heavily noisy image is
simply not good.

**Without optics figures** the rating is provisional and rests on the noise
alone: whether 0.18 cycles/pixel is much or little depends on aperture and
focal length, which cannot be said without them. The tooltip says so.

**Over 100 % is impossible.** If the calculation gives more than the
aperture can physically deliver, finiSCOPE reports *unclear* — usually the
image was sharpened very hard (the measurement then fakes resolution that
never came from the telescope) or the focal length entered does not match.

---

## 5. The score (rank)

The score condenses everything into a number from 0 to 100:

| Metric | Weight | Direction |
|---|---|---|
| Sharpness (MTF50) | 40 % | higher is better |
| Detail energy | 25 % | higher is better |
| SNR | 20 % | higher is better |
| Contrast | 10 % | higher is better |
| Noise floor | −15 % | **lower** is better |

> ### The most important thing about the score
>
> **Scores only apply within one comparison run.**
>
> The best loaded image always gets exactly 100 — even if every loaded
> version is mediocre. So 100 means **“the best of those loaded here”**,
> not “an absolutely good image”.
>
> That is why the winner box says **“best of N images”** rather than
> “score”.
>
> Comparing scores across sessions is meaningless. A 95 from yesterday may
> be better or worse than a 100 from today.

**A single image gets no score at all** — a dash appears instead. A
relative value without anything to compare would be a false statement.

### So what is absolutely meaningful?

Three things — regardless of what else is loaded:

| Value | Absolute meaning |
|---|---|
| **% of optimum** | Needs the optics figures. Says how close the image gets to the theoretical resolving power of the aperture. Above 85 % is maxed out; below 65 % something is usually in the way — seeing or focus. |
| **SNR** | Above 35 dB very clean, 28–35 clean, 22–28 usable, 16–22 visibly grainy, below that heavily noisy. The value is coloured accordingly in the table. |
| **Sampling** | Needs the optics figures. *Good* / *too coarse* / *too fine*. |

Every other figure — score, detail energy, MTF50 — is relative and only to
be read within one run.

The weights live as the constant `WEIGHTS` at the top of
[`analysis/scoring.py`](analysis/scoring.py) and can be changed there.

---

## 6. Object profiles

Each profile decides **where** measurement happens and **which
frequencies** count as real detail (bin limits referring to a 512 px
window):

| Profile | Detail band | Noise from | Window | Where it measures |
|---|---|---|---|---|
| **Sun** | 50–125 | 150 | 512 px | most detailed region |
| **Moon** | 55–135 | 155 | 512 px | most detailed region (usually at the terminator) |
| **Planet** | 40–110 | 140 | 256 px | disc found by brightness threshold, window centred on it |

For small images or small planetary discs the window is shrunk
automatically. All values are in
[`analysis/profiles.py`](analysis/profiles.py).

**What the switch does in practice:** between **sun** and **moon** the
difference is small — both measure at the most detailed spot, the moon's
detail band merely sits slightly higher because crater edges are finer than
H-alpha fibrils. The score usually shifts by a few points, the order almost
never.

**Planet** is markedly different: the disc is first located by a brightness
threshold and the window centred on it (rather than on the most detailed
tile), and at 256 px the window is only half the size — otherwise a small
Jupiter would have black sky measured along with it.

So pick the profile that matches the subject, but do not lose time
wondering whether a lunar image is measured “wrongly” with the solar
profile. What matters above all is that **every image of a run uses the
same profile**, and that is always the case.

---

## 7. Drizzle, .as3 and sessions

### Drizzle is handled fairly

An image stacked with drizzle 1.5× is 1.5 times as large. Measuring it as
is would place its detail at different frequencies and make the comparison
worthless. finiSCOPE therefore scales such images back to their native
pixel size **before measuring** (cubic interpolation).

Detection is deliberately cautious — an image is only scaled back if it is
actually larger than the smallest of the run **and** the ratio matches a
usual factor (1.5× / 2× / 3×), or if file name and size ratio agree. An
image that is larger for some other reason is left alone and triggers a
session warning instead.

The proof that this works is in the test run: the same capture stacked once
natively and once as drizzle 1.5× is measured at **0.1 % identical
sharpness**.

### The .as3 is read if it is there

**You do not have to load the .as3.** finiSCOPE looks for it by itself: if
a `image.as3` from AutoStakkert sits next to `image.tif`, its
`Median quality` is read and shown in the table. AutoStakkert often appends
suffixes to the TIFF names (`_conv`, `_lapl4_ap1274`) — finiSCOPE finds the
matching .as3 anyway.

**Assigning one yourself is possible too.** If the .as3 is named quite
differently from the TIFF, automatic matching has no chance — then there
are two ways:

* click the value in the **as3** column of the table (it reads *“load …”*
  if none was found) and pick the file, or
* simply **drag the .as3 into the window** — it is assigned to the
  currently selected image.

The tooltip in that column shows which file the value came from and whether
it was found automatically or assigned by you.

**Careful:** this value is only comparable **within the same capture**.
Between two sessions it means nothing. If the file is missing, everything
else runs normally and the column shows a dash.

### Session warnings

finiSCOPE checks whether the loaded images plausibly belong together and
warns about strongly differing dimensions, markedly different average
brightness, or files that had to be skipped. The warnings appear below the
table and are included in the HTML report.

---

## 8. Running from source

Needed if you want to change the program. Requires Python 3.9 or newer
(3.11+ recommended).

### macOS

Double-click **`run_mac.command`**. On the first run it creates its own
Python environment and downloads the packages — a few minutes, once.

If macOS refuses to run it, once in Terminal:

```bash
chmod +x run_mac.command build_mac.command
```

### Windows

Double-click **`run_windows.bat`**. It sets everything up on first run too.

If Python is not installed yet: get it from
<https://www.python.org/downloads/> and be sure to tick
**“Add python.exe to PATH”** during installation.

### By hand

```bash
python3 -m venv .venv
source .venv/bin/activate        # Windows:  .venv\Scripts\activate
pip install -r requirements.txt
python app.py
```

Files can also be passed directly:

```bash
python app.py image1.tif image2.tif
```

---

## 9. Building the programs yourself

> **Important:** a Windows `.exe` can only be built **on Windows** and a
> macOS `.app` only **on a Mac**. PyInstaller cannot cross-compile for a
> foreign operating system. To offer both you need both systems — or let
> them be built automatically (see below).

### macOS → `dist/finiSCOPE.app`

```bash
./build_mac.command
```

Takes a minute or two; the result is about 180 MB and runs without any
Python installed. The app is ad-hoc signed so that macOS does not report it
as damaged. It runs on the processor type it was built on (Apple Silicon or
Intel).

**For distribution** run `./paket_mac.command` afterwards. That wraps a
`dist/finiSCOPE-0.1-macOS.dmg` containing the app, a shortcut to the
Applications folder and a short bilingual guide — a single file you can
send.

### Windows → `dist\finiSCOPE\finiSCOPE.exe`

```bat
build_windows.bat
```

For distribution, zip the **whole folder** `dist\finiSCOPE` — the `.exe`
alone does not run.

Step by step with all the pitfalls:
**[WINDOWS_BUILD_EN.md](WINDOWS_BUILD_EN.md)**

### Ready-made transfer folders

```bash
./ordner_erstellen.command
```

Creates `Fertig/Mac-App` (app + DMG) and `Fertig/Windows-Projekt`
(everything needed to build on the laptop, without any macOS leftovers).

### Building both automatically

If you put the project on GitHub,
[`.github/workflows/build.yml`](.github/workflows/build.yml) builds the
macOS and Windows versions on every change — then the laptop is only needed
for testing, not for building.

### Checking a built app for completeness

```bash
./dist/finiSCOPE.app/Contents/MacOS/finiSCOPE --selftest
```

Lists every package and image file and says at the end whether anything is
missing.

---

## 10. When something goes wrong

**The file will not load.**
finiSCOPE reads TIFF (`.tif`, `.tiff`) with 8, 16 or 32 bit, one or three
channels. Colour images are reduced to luminance. FITS, PNG and JPG are not
read. Broken files are skipped and the rest still measured — the notice
line says which.

**“The images have different dimensions.”**
They probably come from different captures. The comparison still runs, but
the power spectrum then compares different subjects — read the scores with
caution.

**Every image gets almost the same score.**
Then they really are almost equally good. Look at the 1:1 view: if you
cannot see a difference there, there is none that matters.

**The measurement window sits somewhere uninteresting.**
Drag your own rectangle over the spot you care about.

**Measuring takes a long time.**
Large drizzle TIFFs (4000 px and more) have to be scaled back. The window
stays responsive; progress runs as a thin line below the status bar.

**macOS: “app is damaged”.**
See [section 1](#macos) — remove the quarantine flag once.

---

## 11. How the project is laid out

```
finiSCOPE/
  app.py                  entry point (also --selftest)
  i18n.py                 all texts, German and English
  analysis/
    core.py               loading, scale-back, window search, PSD,
                          MTF50, SNR, noise, contrast, offset compensation
    profiles.py           sun / moon / planet: bands and windows
    scoring.py            weighted score + plain-text verdicts
    quality.py            absolute rating of a single image
    as3.py                reading the AutoStakkert file
  ui/
    theme.py              colours, fonts, stylesheet, asset paths
    widgets.py            drop zone, cards, winner box, (i) explanations
    imageview.py          image view with measurement window and 1:1 crop
    chart.py              sharpness curve and power spectrum
    table.py              comparison table with expandable raw values
    mainwindow.py         assembly, menu bar, analysis thread, export
  export/
    report.py             producing CSV and HTML
    report.html.j2        template of the HTML report
  assets/                 background, logo, icons (replaceable)
  prototype/preview.html  design prototype (static, opens in a browser)
  tests/
    make_testdata.py      generates synthetic test images
    test_analysis.py      37 checks of the measurement
    screenshot.py         renders the window to an image
  requirements.txt · LICENSE · README.md · README_EN.md
  run_mac.command · run_windows.bat
  build_mac.command · build_windows.bat · finiSCOPE.spec
  paket_mac.command · ordner_erstellen.command
```

The name is the constant `APP_NAME` in [`ui/theme.py`](ui/theme.py) —
change it once there.

Images in `assets/` are found by their **base name**; the extension is
free. So `background.png` can be swapped for `background.jpg` at any time.
Expected are `background`, `favicon`, `logo` and `logo_wordmark`; if one is
missing the app still runs.

All user-visible texts live in [`i18n.py`](i18n.py) as a table
“key → German / English”. No fixed text remains in the program code, so a
third language would only be typing work in that one file.

### Tests

```bash
python tests/make_testdata.py tests/data   # generate test images
python tests/test_analysis.py              # 37 checks
```

The test builds four variants from **one** subject and proves that

* the blurred version shows **less** detail (MTF50 0.128 against 0.221,
  detail energy a fifth),
* the drizzle 1.5× version measures **equivalently** after being scaled
  back (0.1 % deviation),
* the noisy version, despite nominally equal sharpness, gets a tenfold
  noise floor and 18 dB worse SNR — and drops in the ranking accordingly,
* an artificially shifted image is detected to the pixel and the window
  tracked (deviation after compensation: exactly zero).

Plus checks for RGB, 8 bit, missing files, broken files, missing .as3, very
small images and manually set measurement windows.

---

## 12. Colours

The interface deliberately does **without yellow**:

* **Green** is the only all-clear — good, clean, level with the best image.
  The winner is green too, because green means “good” throughout.
* **Blue** is the base colour of the whole application: neutral, medium,
  and every control. It is the same blue as on finisky.com
  (`#4F8DE0` / `#8FC0FF` on `#16263D`). The measurement window in the image
  is blue as well — it stands out equally on grey lunar and orange solar
  images.
* **Warm and red** are reserved for what is genuinely weak.

Yellow and gold appear **nowhere**.

All colour values are constants in [`ui/theme.py`](ui/theme.py).

---

## 13. Assumptions made

Where the brief left room, it was decided like this:

**An aperture field was added.** Without it the theoretical resolution
limit (Dawes) cannot be calculated and the “how close to optimum”
comparison would be impossible. The field is optional.

**MTF50 is determined on a de-noised and “whitened” spectrum.** A subject
has a spectrum that falls towards higher frequencies by nature — that is a
property of the subject, not of sharpness. Looking for the half-power point
of the raw curve gives almost the same value for a sharp and a blurred
image. So the noise floor is subtracted first and then the natural falloff
divided out; what remains is the transfer function of the system. Only that
separates sharp from soft cleanly (0.221 against 0.128 in the test run).

**Noise is measured in the measurement window, not in the dark sky.** A
crop of an active region has no dark sky. The method used (Laplacian filter
with median) works in both cases. The spread in the dark area is reported
additionally when there is one.

**Detail energy has the noise removed.** Without the subtraction, grain
would count as detail and a noisy image would win — exactly what happened
in the first test run.

**Cautious drizzle detection.** Better to skip a scale-back once than to
wrongly shrink an image from another session. Unexplained size differences
produce a warning instead.

**A dragged area applies to all images by default.** Anything else would
not be a fair comparison. The *this image only* tick allows the exception.

**The automatic measurement area is searched on the average of all
images.** Taking the first image would make the location depend on the
order in which files were loaded — and it might sit somewhere that only
looks interesting in the blurriest version.

**A single image gets no score, but does get a quality light.** The
comparison falls away, the absolute assessment remains.

**Offset between images is compensated by phase correlation.** Without it,
slightly shifted stacks would have two different spots compared — the most
likely measurement error in this tool. The method is insensitive to
brightness differences and finds the offset to the pixel (in the test run:
deviation after compensation exactly zero). It can be switched off if the
images are guaranteed to line up.

**The sharpness curve is the chart's default view.** The raw power spectrum
falls off steeply by nature and therefore looks similar for every image —
the differences hide in details that take practice to see. The whitened
curve shows directly what matters: where the image stops carrying detail.
The power spectrum stays one click away.

**Plain-text verdicts take the size of the difference into account.** With
two nearly equally good images it says “just a touch grainier” rather than
“clearly noisy” — even though one is behind on paper.

**The chart is drawn by hand** rather than with pyqtgraph or Matplotlib.
That gives exactly the intended look, starts faster and saves the finished
app about 40 MB.

---

## Author and licence

finiSCOPE is a tool by **Josef Kreidl** — astrophotography at
[finisky.com](https://finisky.com/en/).

**Free of charge, no warranty, use at your own risk.** The software is
released under the MIT licence and may be used, shared and modified as long
as the licence text and copyright notice stay with it. Liability for
damages is excluded. The values shown are guidance for comparing your own
images, not calibrated measurements.

The same information is available inside the app under **Help → About**,
and the full text is in [LICENSE](LICENSE).

The images in `assets/` (sun, moon, logo, icon) are copyrighted photographs
by Josef Kreidl and are **not** covered by the MIT licence.

## Support

finiSCOPE is free and will stay free — every feature works without paying
anything. If you would like to support its development, there is a way to do
so at [finisky.com/en/support](https://finisky.com/en/support),
entirely voluntary. Inside the app the link sits quietly in the footer and
under **Help → Support finiSCOPE**.

*© 2026 Josef Kreidl · finisky.com*
