notebookVb

This commit is contained in:
administrator
2026-05-21 07:11:54 +02:00
parent c14d5c7676
commit b6aba06baa
4 changed files with 1834 additions and 0 deletions
+377
View File
@@ -0,0 +1,377 @@
# FotkyBuzalkovi — návrh systému a poznámky z analýzy
> Pracovní dokument shrnující rozhodnutí a poznatky z prvotní analýzy projektu.
> Datum diskuze: 2026-05-21
---
## 1. Cíl projektu
- Organizovat a tagovat **cca 200 000** rodinných fotografií
- Lokální nasazení (žádný cloud, žádní externí uživatelé)
- Současný stav: prázdný projekt s ukázkovými fotkami v `demo_fotky/`
---
## 2. Architektonické rozhodnutí
### Finální stack: **jen PostgreSQL + filesystem**
```
┌─────────────────────┐
│ Python aplikace │
└──────────┬──────────┘
┌──────┴──────┐
▼ ▼
┌─────────┐ ┌──────────┐
│PostgreSQL│ │Filesystem│
│ metadata │ │ .jpg/.png│
└─────────┘ └──────────┘
```
### Co bylo zvažováno a zavrženo
| Technologie | Verdikt | Důvod |
|-------------|---------|-------|
| MongoDB | ❌ vynecháno | `JSONB` v PostgreSQL nahradí veškerou potřebnou funkcionalitu |
| Redis | ❌ vynecháno (zatím) | Pro lokální 200k fotek bez webových uživatelů zbytečný |
| MySQL | ❌ vynecháno | Projekt používá PostgreSQL (jiná databáze byla v původním plánu) |
### Kdy přidat Redis (pozdější fáze)
- Web UI s rychlým vyhledáváním (cache výsledků)
- Paralelní workery pro import / generování thumbnailů
- Background fronty (zpracování AI tagů)
### Kdy přidat něco dalšího
- **`pgvector`** extension pro PostgreSQL — až budeme chtít sémantické hledání (CLIP embeddings)
- **Elasticsearch** — kdyby PostgreSQL fulltext nestačil (u 200k řádků nestane)
---
## 3. JSONB v PostgreSQL vs MongoDB BSON
### Terminologie
- **BSON** = binární JSON od MongoDB (s typy jako `ObjectId`, `Date`, `Decimal128`)
- **JSONB** = binární JSON v PostgreSQL (vlastní formát, ne BSON, ale funkčně podobný)
### Srovnání vyhledávání
**MongoDB:**
```javascript
db.photos.find({ "exif.camera": "Canon EOS 5D" })
db.photos.find({ "exif.iso": { $gte: 800 } })
db.photos.find({ "tags": { $in: ["dovolená", "moře"] } })
```
**PostgreSQL JSONB:**
```sql
SELECT * FROM photos WHERE exif_data->>'camera' = 'Canon EOS 5D';
SELECT * FROM photos WHERE (exif_data->>'iso')::int >= 800;
SELECT * FROM photos WHERE exif_data @> '{"camera": "Canon EOS 5D"}';
```
### Co umí stejně
| Funkce | MongoDB | PostgreSQL JSONB |
|--------|---------|------------------|
| Filtr podle pole v JSON | ano | ano (`->>`, `@>`) |
| Vnořené cesty | `"a.b.c"` | `data#>>'{a,b,c}'` |
| Existence klíče | `$exists` | `?` operátor |
| Index na JSON cestu | ano | ano (GIN) |
| Fulltext | ano | ano |
### Výhody PostgreSQL JSONB pro náš případ
1. **JOINy** s tabulkami tagů a kamer — v Mongo by to vyžadovalo `$lookup` (pomalé)
2. **Transakce** napříč JSONB a relačními tabulkami
3. **Jeden backup nástroj** (`pg_dump`)
---
## 4. Identita fotky — 4 úrovně hashů
**Klíčový problém:** Když změníte byť jediný keyword v IPTC, SHA256 souboru se kompletně změní. Jak detekovat, že je to stále ta samá fotka?
```
┌─────────────────────────────────────────────────────────────┐
│ Stejná identita Stejný obsah │
│ ←─────────────────────────────────────────────────────→ │
│ │
│ SHA256 Pixel hash Perceptual Embedding │
│ souboru (jen pixely) hash (pHash) (CLIP/DINO) │
│ │
│ byte-by-byte metadata +recomprese +sémantika │
│ identické ignorováno +resize podobnost │
│ +crop │
└─────────────────────────────────────────────────────────────┘
```
### 1. SHA256 souboru (`sha256_file`)
- Detekuje **přesnou kopii** souboru
- Změna IPTC/EXIF, otočení, znovuuložení → jiný hash
### 2. Pixel hash (`sha256_pixels`)
- SHA256 dekódovaných pixelů (po aplikaci EXIF orientation)
- **Stejná fotka po změně metadat** → identický hash
- Toto je odpověď na otázku "jak detekovat, že je to ta samá fotka po změně keywords"
```python
with Image.open(path) as img:
img = ImageOps.exif_transpose(img)
if img.mode != "RGB":
img = img.convert("RGB")
pixel_hash = hashlib.sha256(img.tobytes()).hexdigest()
```
### 3. Perceptual hash (`pHash`, `dHash`, `wHash`)
- 64-bit "otisk obsahu" — vizuálně podobné fotky → blízké hashe
- Porovnává se přes **Hamming distance** (`bin(h1 ^ h2).count("1")`)
- < 10 = velmi podobné, > 20 = odlišné
- Detekuje: recompresi, resize, drobné úpravy, watermark, crop
- Knihovna: [`imagehash`](https://pypi.org/project/ImageHash/)
### 4. Deep learning embedding (budoucnost)
- Vektor 5121024 dimenzí z CLIP / DINOv2
- Sémantická podobnost ("dvě fotky stejné scény z různých úhlů")
- Uložení v PostgreSQL přes `pgvector` extension
### Workflow při importu nové fotky
```
1. Spočítej sha256_file
2. Najdi v DB stejný sha256_file?
ANO → identická kopie, přeskočit
NE → ↓
3. Spočítej sha256_pixels
4. Najdi v DB stejný sha256_pixels?
ANO → stejná fotka, jen jiná metadata → updatuj, nepřidávej duplikát
NE → ↓
5. Spočítej phash, ulož do DB
6. (Volitelně) najdi podobné: WHERE hamming(phash, ?) < 10
```
### Poznámka k JPEG
- Před hashingem **vždy aplikovat EXIF orientation** (`ImageOps.exif_transpose`)
- Konvertovat do RGB pro konzistenci
- Jinak by se rotovaná fotka vs nerotovaná lišila
---
## 5. Tři vrstvy metadat ve fotce
| Standard | Co tam je | Kdo to vyplňuje |
|----------|-----------|-----------------|
| **EXIF** | Technická data (clona, ISO, čas, GPS, model kamery) | Kamera automaticky |
| **IPTC** | Popisná data (titulek, popis, klíčová slova, autor, copyright) | Člověk / software |
| **XMP** | Modernější nástupce IPTC od Adobe, často duplikuje + edits, ratings, regions | Software (Lightroom, Apple Photos) |
### Pro náš případ
- iPhone vyplňuje hlavně **EXIF** a **XMP** (ne IPTC)
- Apple Photos do XMP ukládá **rozpoznané obličeje** (`mwg-rs:Regions`) — i se jmény, pokud je pojmenuješ!
- iOS screenshoty mají v XMP `description: "Screenshot"` → automatická detekce
- Pokud budeme tagovat, je dobré tagy ukládat **i do IPTC `Keywords`** — přežijí export do jiné aplikace
---
## 6. Výsledky exploration na 7 demo fotkách
### Souhrn dat z `explore_photos.py`
| # | Soubor | Mpx | EXIF tagů | IPTC | XMP | GPS | Kamera |
|---|--------|-----|-----------|------|-----|-----|--------|
| 1 | 2026-04-22 09.05.08.jpg | 24.47 | 121 | 0 | 4 | ano | iPhone 16 Pro Max |
| 2 | 2026-04-24 15.15.47.jpg | 12.19 | 107 | 0 | 0 | ne | iPhone 13 Pro Max |
| 3 | 2026-05-02 10.04.44.png | 3.57 | 12 | 0 | 1 | ne | Screenshot |
| 4 | 2026-05-18 06.22.37.jpg | 12.19 | 105 | 0 | 4 | ne | iPhone 13 Pro Max |
| 5 | 2026-05-18 06.22.41.jpg | 12.19 | 105 | 0 | 4 | ne | iPhone 13 Pro Max |
| 6 | 2026-05-18 13.54.47.jpg | 12.19 | 98 | 6 | 0 | ne | iPhone 13 Pro Max |
| 7 | 2026-05-18 14.10.59.jpg | 2.36 | 0 | 0 | 0 | ne | (sirotek) |
### Klíčové objevy
1. **Perceptual hash funguje** — fotky [4] a [5] (pořízené 4 sekundy po sobě) mají Hamming distance pouze **4** = klasický burst snapshot.
2. **Apple face detection v XMP** — fotky 1, 4, 5 mají `face_regions_count: 1`. iPhone už **detekoval obličej** a uložil to do XMP. Můžeme číst přímo, bez vlastní AI.
3. **Screenshot rozpoznán** — foto [3] má v XMP `description: "Screenshot"` → automatická kategorizace.
4. **Foto [7] = sirotek** — žádné metadata. Pravděpodobně přeposlané přes WhatsApp/Messenger, kde se EXIF maže. **Pro 200k fotek bude tato kategorie významná.**
5. **GPS jen u 1/7 fotek** — u většiny iPhone fotek máte vypnuté lokační služby.
6. **Time zone** — fotky mají `OffsetTime: +02:00`. **Datum pořízení ukládat jako `TIMESTAMPTZ`** (s timezone).
7. **ExifRead > Pillow** — Pillow má GPS bug (`'int' object has no attribute 'items'`). **Primární parser bude ExifRead.**
8. **MakerNote (Apple binary blob)** — obsahuje burst ID, HDR příznak, focus distance. Pro rozluštění potřeba [`exiftool`](https://exiftool.org/) (volat přes `pyexiftool`).
---
## 7. Návrh databázového schématu (draft)
```sql
CREATE TABLE photos (
id BIGSERIAL PRIMARY KEY,
-- identita (3 úrovně)
sha256_file CHAR(64) UNIQUE NOT NULL, -- byte identita
sha256_pixels CHAR(64), -- pixel identita
phash BIGINT, -- vizuální podobnost
-- soubor
file_path VARCHAR(1000) NOT NULL,
file_name VARCHAR(255) NOT NULL,
file_size BIGINT,
mime_type VARCHAR(50),
format VARCHAR(20), -- JPEG, PNG, HEIC, ...
width INT,
height INT,
-- pořízení
taken_at TIMESTAMPTZ, -- s timezone (máme OffsetTime!)
taken_at_source VARCHAR(20), -- 'exif' / 'mtime' / 'iptc' / 'unknown'
-- technika (z EXIF)
camera_make VARCHAR(100),
camera_model VARCHAR(255),
lens_model VARCHAR(255),
iso INT,
aperture NUMERIC(4,2),
exposure_time VARCHAR(20), -- "1/500"
focal_length_mm NUMERIC(5,2),
-- GPS (NULL pokud chybí)
gps_lat NUMERIC(10,7),
gps_lon NUMERIC(10,7),
gps_altitude NUMERIC(7,2),
-- klasifikace
is_screenshot BOOLEAN DEFAULT FALSE,
face_count INT, -- z XMP, rozšířit AI
-- flexibilní JSONB pro celý dump
exif_raw JSONB,
iptc_raw JSONB,
xmp_raw JSONB,
-- import / zpracování
imported_at TIMESTAMPTZ DEFAULT NOW(),
processed_at TIMESTAMPTZ,
processing_status VARCHAR(50) DEFAULT 'pending'
);
CREATE INDEX idx_photos_sha256_pixels ON photos(sha256_pixels);
CREATE INDEX idx_photos_phash ON photos(phash);
CREATE INDEX idx_photos_taken_at ON photos(taken_at);
CREATE INDEX idx_photos_camera_model ON photos(camera_model);
CREATE INDEX idx_photos_exif_gin ON photos USING GIN (exif_raw);
CREATE TABLE tags (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
parent_tag_id INT REFERENCES tags(id), -- hierarchie "místo > Praha > Karlův most"
UNIQUE(name, parent_tag_id)
);
CREATE TABLE photo_tags (
photo_id BIGINT REFERENCES photos(id) ON DELETE CASCADE,
tag_id INT REFERENCES tags(id) ON DELETE CASCADE,
source VARCHAR(20), -- 'manual' / 'iptc' / 'xmp' / 'auto'
created_at TIMESTAMPTZ DEFAULT NOW(),
PRIMARY KEY (photo_id, tag_id)
);
```
---
## 8. Otevřené otázky pro příště
### Foto bez EXIF (sirotek typu [7])
- **a)** Importovat (s `taken_at` z `mtime`)
- **b)** Odmítnout jako "nezpracovatelnou"
- **c)** Importovat, ale označit `processing_status = 'no_metadata'`
### Detekce duplikátů (shoda `sha256_pixels`)
- **a)** Přeskočit (nepřidávat duplicitu)
- **b)** Sloučit (aktualizovat metadata u existujícího záznamu)
- **c)** Uložit oba, jen označit jako související
### Storage layout fotek
- **a)** Necháme v aktuálním umístění, do DB jen cestu
- **b)** Kopie do `archiv/YYYY/MM/původní_název.jpg`
- **c)** Kopie do `archiv/{sha[:2]}/{sha}.jpg` (content-addressable = auto-dedup na FS)
---
## 9. Co dál — nápady k prozkoumání
- **`exiftool`** — rozluští Apple MakerNote (burst ID by potvrdilo, že [4] a [5] patří k sobě)
- **Embedded thumbnail z EXIF** — telefon ukládá malou náhledovku přímo v souboru → rychlejší galerie bez generování
- **CLIP embeddings + pgvector** — sémantické vyhledávání ("ukaž fotky pejsků na pláži")
- **Reverse geocoding** — z GPS souřadnic na čitelné místo (Nominatim / Photon, lokálně)
- **Apple Photos jména osob** — z XMP `mwg-rs:Regions` jdou číst i jména, pokud jsou v Photos pojmenovaná
- **Datum z názvu souboru** — fallback když chybí EXIF i sensible `mtime` (regex z "2026-05-18 13.54.47.jpg")
---
## 10. Aktuální stav projektu (k 2026-05-21)
### Soubory v projektu
```
FotkyBuzalkovi/
├── demo_fotky/ # 7 ukázkových fotek
├── explore_photos.py # Explorační skript (hashe, EXIF, IPTC, XMP)
├── photo_exploration.json # Výstup exploreru
├── create_schema.py # ZASTARALÉ - obsahuje MySQL syntaxi a hardcoded hesla
├── test_db_connection.py # Test PG + Mongo + Redis (Mongo/Redis nebudou potřeba)
├── test_mongo.py # ZASTARALÉ - Mongo nepoužijeme
├── README.md
└── NAVRH.md # Tento dokument
```
### Co bude potřeba udělat
- [ ] Smazat nebo přepsat `create_schema.py` (MySQL syntaxe `INDEX` uvnitř `CREATE TABLE` v PG nefunguje)
- [ ] Migrovat hesla do `.env` + `python-dotenv`
- [ ] Smazat / archivovat `test_mongo.py`
- [ ] Vytvořit migraci podle schématu v sekci 7
- [ ] Skript pro import fotek s deduplikací (workflow v sekci 4)
- [ ] Rozhodnout otevřené otázky ze sekce 8
### Nainstalované Python balíčky
```
psycopg2-binary 2.9.12 # PostgreSQL driver
pymongo 4.17.0 # (nepotřebujeme)
redis 7.4.0 # (zatím nepotřebujeme)
pillow 12.2.0 # Základní práce s obrázky
ExifRead 3.5.1 # Primární EXIF parser (lepší než Pillow)
imagehash 4.3.2 # Perceptuální hashe (pHash, dHash, wHash)
+ numpy, scipy, PyWavelets (závislosti imagehash)
```
---
## 11. Užitečné odkazy
- [Pillow](https://pillow.readthedocs.io/) — Python Imaging Library
- [ExifRead](https://github.com/ianare/exif-py) — EXIF parser
- [imagehash](https://github.com/JohannesBuchner/imagehash) — perceptuální hashe
- [exiftool](https://exiftool.org/) — gold standard pro metadata (Perl, ale `pyexiftool` wrapper)
- [pgvector](https://github.com/pgvector/pgvector) — vektory v PostgreSQL pro sémantické hledání
- [PostgreSQL JSONB docs](https://www.postgresql.org/docs/current/datatype-json.html)
- [IPTC Photo Metadata Standard](https://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata)
- [XMP Specification (Adobe)](https://www.adobe.com/devnet/xmp.html)
+153
View File
@@ -0,0 +1,153 @@
# FotkyBuzalkovi
Systém pro zpracování, ukládání a vyhledávání rodinných fotografií s automatickou extrakcí EXIF metadat.
## Architektura
Projekt používá kombinaci tří databází, kde každá řeší specifickou úlohu:
```
┌─────────────────────────────────────────────────────────┐
│ Python aplikace │
│ (zpracování fotek, EXIF, hash) │
└────────────┬──────────────┬──────────────┬───────────────┘
│ │ │
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ PostgreSQL │ │ MongoDB │ │ Redis │
│ (relační) │ │ (EXIF/doc) │ │ (cache) │
└────────────┘ └────────────┘ └────────────┘
192.168.1.76 192.168.1.76 localhost
:5432 :27017 :6379
```
### Proč tři databáze?
| Databáze | Role | Co ukládá |
|----------|------|-----------|
| **PostgreSQL** | Strukturovaná data, relace | `photos`, `cameras`, `photo_tags` - ID, cesty, hashe, FK |
| **MongoDB** | Flexibilní dokumenty | Plná EXIF metadata (různé fotoaparáty = různá pole) |
| **Redis** | Cache + fronty | Miniatury, výsledky vyhledávání, fronta zpracování |
## Datové úložiště
### PostgreSQL - `fotky_buzalkovi`
- **cameras** - seznam fotoaparátů (model, vyrobce)
- **photos** - hlavní tabulka (file_name, file_path, file_hash, taken_at, rozměry, FK na camera)
- **photo_tags** - tagy ke každé fotce (many-to-many)
### MongoDB - `fotky_buzalkovi`
- **photos** kolekce - kompletní EXIF data, GPS souřadnice, nastavení clony, ISO, atd.
### Redis (plánováno)
- **cache:thumb:{photo_id}** - cached miniatury (TTL 1h)
- **queue:process** - fronta nezpracovaných fotek
- **session:{user_id}** - session data
## K čemu Redis
1. **Cache miniatur** - generování miniatur je drahé, Redis je drží v RAM (rychlost ~0.1ms vs ~50ms z disku)
2. **Cache vyhledávání** - "fotky z dovolené 2025" se může opakovat, výsledek se cachuje
3. **Fronta zpracování** - když nahrajete 1000 fotek, Redis funguje jako worker queue
4. **Deduplikace** - rychlá kontrola, zda hash fotky už existuje
5. **Rate limiting** - omezení uploadů
## Instalace
### 1. Python prostředí
```powershell
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install psycopg2-binary pymongo redis pillow exifread python-dotenv
```
### 2. PostgreSQL
Předpoklad: PostgreSQL běží na `192.168.1.76:5432`.
```powershell
python create_schema.py
```
### 3. MongoDB
Předpoklad: MongoDB běží na `192.168.1.76:27017`.
```powershell
python test_mongo.py
```
### 4. Redis (Windows)
Redis oficiálně Windows nepodporuje. Tři možnosti:
**Možnost A - WSL2 (doporučeno):**
```powershell
wsl --install
# v WSL:
sudo apt update
sudo apt install redis-server
sudo service redis-server start
```
**Možnost B - Docker:**
```powershell
docker run -d --name redis -p 6379:6379 redis:latest
```
**Možnost C - Memurai (Windows-native Redis-kompatibilní):**
- Stáhnout z https://www.memurai.com/
Test:
```powershell
python test_db_connection.py
```
## Konfigurace
**⚠️ Hesla v současných skriptech jsou v plain textu - před nasazením přesunout do `.env`:**
```env
PG_HOST=192.168.1.76
PG_PORT=5432
PG_USER=vladimir.buzalka
PG_PASSWORD=...
PG_DB=fotky_buzalkovi
MONGO_URI=mongodb://192.168.1.76:27017/
MONGO_DB=fotky_buzalkovi
REDIS_HOST=localhost
REDIS_PORT=6379
```
## Struktura projektu
```
FotkyBuzalkovi/
├── demo_fotky/ # Testovací fotografie
├── create_schema.py # Vytvoření PostgreSQL schématu
├── test_db_connection.py # Test všech tří databází
├── test_mongo.py # Test MongoDB + vytvoření kolekcí
├── .gitignore
└── README.md
```
## Známé problémy
- `create_schema.py` používá MySQL syntaxi `INDEX idx_x` uvnitř `CREATE TABLE` - v PostgreSQL je potřeba `CREATE INDEX` zvlášť po `CREATE TABLE`
- Hesla jsou hardcodovaná v Python souborech - migrovat do `.env` + `python-dotenv`
## Roadmap
- [ ] Opravit PostgreSQL schéma (INDEX syntaxe)
- [ ] Migrace hesel do `.env`
- [ ] Instalace Redis
- [ ] Skript pro hromadný import fotek z `demo_fotky/`
- [ ] EXIF parser (pillow + exifread)
- [ ] Generování miniatur s Redis cache
- [ ] Web UI pro prohlížení galerie
+398
View File
@@ -0,0 +1,398 @@
"""
Explorační skript: projde všechny fotky v demo_fotky/ a vytáhne maximum dat.
Výstup do konzole + JSON soubor pro detailní analýzu.
"""
import hashlib
import json
import os
import re
import sys
from datetime import datetime
from pathlib import Path
# Windows konzole - vynutit UTF-8
if sys.stdout.encoding.lower() != "utf-8":
sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")
import exifread
import imagehash
from PIL import Image, ImageOps, IptcImagePlugin
from PIL.ExifTags import TAGS, GPSTAGS
PHOTOS_DIR = Path(__file__).parent / "demo_fotky"
OUTPUT_JSON = Path(__file__).parent / "photo_exploration.json"
def file_hash_sha256(path: Path, chunk_size: int = 65536) -> str:
"""Hash celého souboru - detekce přesné kopie."""
h = hashlib.sha256()
with open(path, "rb") as f:
while chunk := f.read(chunk_size):
h.update(chunk)
return h.hexdigest()
def pixel_hash_sha256(path: Path) -> str | None:
"""Hash dekódovaných pixelů - identita fotky nezávisle na metadatech.
Aplikuje EXIF orientation pro konzistenci."""
try:
with Image.open(path) as img:
img = ImageOps.exif_transpose(img)
if img.mode != "RGB":
img = img.convert("RGB")
return hashlib.sha256(img.tobytes()).hexdigest()
except Exception as e:
return None
def perceptual_hashes(path: Path) -> dict:
"""Perceptuální hashe - detekce vizuálně podobných fotek.
Každý hash je 64-bit, porovnává se Hamming distance."""
out = {}
try:
with Image.open(path) as img:
img = ImageOps.exif_transpose(img)
out["phash"] = str(imagehash.phash(img))
out["dhash"] = str(imagehash.dhash(img))
out["ahash"] = str(imagehash.average_hash(img))
out["whash"] = str(imagehash.whash(img))
except Exception as e:
out["_error"] = str(e)
return out
def iptc_info(path: Path) -> dict:
"""IPTC metadata - keywords, title, description, author atd."""
out = {}
# Mapování IPTC numerických tagů na čitelné názvy
iptc_names = {
(2, 5): "ObjectName", # Title
(2, 10): "Urgency",
(2, 15): "Category",
(2, 20): "SupplementalCategories",
(2, 25): "Keywords",
(2, 40): "SpecialInstructions",
(2, 55): "DateCreated",
(2, 60): "TimeCreated",
(2, 80): "Byline", # Creator/Author
(2, 85): "BylineTitle",
(2, 90): "City",
(2, 92): "SubLocation",
(2, 95): "ProvinceState",
(2, 100): "CountryCode",
(2, 101): "CountryName",
(2, 103): "OriginalTransmissionReference",
(2, 105): "Headline",
(2, 110): "Credit",
(2, 115): "Source",
(2, 116): "Copyright",
(2, 118): "Contact",
(2, 120): "Caption", # Description
(2, 122): "WriterEditor",
}
try:
with Image.open(path) as img:
raw = IptcImagePlugin.getiptcinfo(img)
if not raw:
return {}
for key, value in raw.items():
name = iptc_names.get(key, f"IPTC{key}")
if isinstance(value, bytes):
value = value.decode("utf-8", errors="replace")
elif isinstance(value, list):
value = [v.decode("utf-8", errors="replace") if isinstance(v, bytes) else v for v in value]
out[name] = value
except Exception as e:
out["_error"] = str(e)
return out
def xmp_info(path: Path) -> dict:
"""XMP metadata - moderní alternativa IPTC, často keywords/rating/regions."""
out = {}
try:
with Image.open(path) as img:
xmp_raw = img.info.get("xmp")
if not xmp_raw:
return {}
if isinstance(xmp_raw, bytes):
xmp_raw = xmp_raw.decode("utf-8", errors="replace")
# Velmi jednoduchý parser - vytáhne nejčastější pole regexem
patterns = {
"creator_tool": r'xmp:CreatorTool="([^"]+)"',
"create_date": r'xmp:CreateDate="([^"]+)"',
"modify_date": r'xmp:ModifyDate="([^"]+)"',
"rating": r'xmp:Rating="([^"]+)"',
"label": r'xmp:Label="([^"]+)"',
"title": r'<dc:title[^>]*>.*?<rdf:li[^>]*>([^<]+)</rdf:li>',
"description": r'<dc:description[^>]*>.*?<rdf:li[^>]*>([^<]+)</rdf:li>',
"creator": r'<dc:creator[^>]*>.*?<rdf:li[^>]*>([^<]+)</rdf:li>',
"subject_keywords": r'<dc:subject[^>]*>(.*?)</dc:subject>',
}
for name, pat in patterns.items():
m = re.search(pat, xmp_raw, re.DOTALL)
if m:
out[name] = m.group(1).strip()
# Keywords z dc:subject - vytáhnout jednotlivé rdf:li
if "subject_keywords" in out:
kws = re.findall(r'<rdf:li[^>]*>([^<]+)</rdf:li>', out["subject_keywords"])
out["subject_keywords"] = kws
# Apple regions (rozpoznané obličeje s pozicí)
face_count = len(re.findall(r'mwg-rs:Type="Face"', xmp_raw))
if face_count:
out["face_regions_count"] = face_count
# Délka raw XMP pro představu
out["_xmp_length_bytes"] = len(xmp_raw)
except Exception as e:
out["_error"] = str(e)
return out
def filesystem_info(path: Path) -> dict:
stat = path.stat()
return {
"file_name": path.name,
"file_path": str(path),
"file_size_bytes": stat.st_size,
"file_size_mb": round(stat.st_size / 1024 / 1024, 2),
"mtime": datetime.fromtimestamp(stat.st_mtime).isoformat(),
"ctime": datetime.fromtimestamp(stat.st_ctime).isoformat(),
"extension": path.suffix.lower(),
}
def pillow_info(path: Path) -> dict:
info = {}
try:
with Image.open(path) as img:
info["format"] = img.format
info["mode"] = img.mode
info["width"] = img.width
info["height"] = img.height
info["megapixels"] = round((img.width * img.height) / 1_000_000, 2)
info["has_transparency"] = img.mode in ("RGBA", "LA") or "transparency" in img.info
info["dpi"] = img.info.get("dpi")
info["icc_profile_present"] = "icc_profile" in img.info
info["exif_present"] = bool(img.getexif())
# XMP (často v JPG od Adobe)
if "xmp" in img.info:
xmp_raw = img.info["xmp"]
if isinstance(xmp_raw, bytes):
xmp_raw = xmp_raw[:500].decode("utf-8", errors="ignore")
info["xmp_snippet"] = str(xmp_raw)[:500]
# Thumbnail embedded?
info["has_embedded_thumbnail"] = "thumbnail" in img.info
except Exception as e:
info["error"] = str(e)
return info
def pillow_exif(path: Path) -> dict:
"""Pillow EXIF — čitelné názvy."""
out = {}
try:
with Image.open(path) as img:
exif = img.getexif()
if not exif:
return {}
for tag_id, value in exif.items():
tag = TAGS.get(tag_id, f"Tag{tag_id}")
# GPS info jako vnořený dict
if tag == "GPSInfo":
gps = {}
for gps_tag_id, gps_value in value.items():
gps_tag = GPSTAGS.get(gps_tag_id, f"GPSTag{gps_tag_id}")
gps[gps_tag] = _serializable(gps_value)
out[tag] = gps
else:
out[tag] = _serializable(value)
except Exception as e:
out["_error"] = str(e)
return out
def exifread_tags(path: Path) -> dict:
"""ExifRead — často víc tagů než Pillow, mj. detailní MakerNote."""
out = {}
try:
with open(path, "rb") as f:
tags = exifread.process_file(f, details=True)
for k, v in tags.items():
# přeskočit binární thumbnail
if "Thumbnail" in k and "JPEGInterchangeFormat" not in k:
continue
out[k] = str(v)
except Exception as e:
out["_error"] = str(e)
return out
def _serializable(v):
"""Pillow vrací občas IFDRational, bytes apod. → převést na JSON-friendly."""
if isinstance(v, bytes):
return v[:200].decode("utf-8", errors="replace")
if isinstance(v, (tuple, list)):
return [_serializable(x) for x in v]
if isinstance(v, dict):
return {str(k): _serializable(val) for k, val in v.items()}
if hasattr(v, "numerator") and hasattr(v, "denominator"):
try:
return float(v)
except Exception:
return str(v)
try:
json.dumps(v)
return v
except (TypeError, ValueError):
return str(v)
def explore_photo(path: Path) -> dict:
return {
"filesystem": filesystem_info(path),
"hashes": {
"sha256_file": file_hash_sha256(path),
"sha256_pixels": pixel_hash_sha256(path),
**perceptual_hashes(path),
},
"pillow": pillow_info(path),
"exif_pillow": pillow_exif(path),
"exif_exifread": exifread_tags(path),
"iptc": iptc_info(path),
"xmp": xmp_info(path),
}
def hamming_distance(h1: str, h2: str) -> int:
"""Hamming distance mezi dvěma hex perceptual hashes."""
return bin(int(h1, 16) ^ int(h2, 16)).count("1")
def print_summary(photos: list[dict]) -> None:
print(f"\n{'=' * 70}")
print(f"PŘEHLED: {len(photos)} fotek")
print(f"{'=' * 70}\n")
# Které EXIF tagy existují napříč fotkami?
all_pillow_keys = set()
all_exifread_keys = set()
for p in photos:
all_pillow_keys.update(p["exif_pillow"].keys())
all_exifread_keys.update(p["exif_exifread"].keys())
print(f"Unikátní EXIF tagy (Pillow): {len(all_pillow_keys)}")
print(f"Unikátní EXIF tagy (ExifRead): {len(all_exifread_keys)}")
print()
for i, p in enumerate(photos, 1):
fs = p["filesystem"]
pi = p["pillow"]
h = p["hashes"]
er = p["exif_exifread"]
print(f"[{i}] {fs['file_name']}")
print(f" Velikost: {fs['file_size_mb']} MB ({pi.get('width')}x{pi.get('height')}, {pi.get('megapixels')} Mpx)")
print(f" Formát: {pi.get('format')} / mode={pi.get('mode')}")
print(f" sha256_file: {h['sha256_file'][:16]}...")
print(f" sha256_pixels: {(h.get('sha256_pixels') or 'N/A')[:16]}...")
print(f" phash: {h.get('phash')} (perceptual)")
print(f" EXIF tagů: ExifRead={len(er)}, Pillow={len(p['exif_pillow'])}")
print(f" IPTC polí: {len([k for k in p['iptc'] if not k.startswith('_')])}")
print(f" XMP polí: {len([k for k in p['xmp'] if not k.startswith('_')])}")
# ExifRead je spolehlivější (Pillow má GPS bug)
interesting = {
"Kamera": f"{er.get('Image Make', '')} {er.get('Image Model', '')}".strip(),
"Objektiv": er.get("EXIF LensModel"),
"Datum": er.get("EXIF DateTimeOriginal") or er.get("Image DateTime"),
"TZ offset": er.get("EXIF OffsetTimeOriginal") or er.get("EXIF OffsetTime"),
"Clona": er.get("EXIF FNumber"),
"ISO": er.get("EXIF ISOSpeedRatings"),
"Expozice": er.get("EXIF ExposureTime"),
"Ohnisko mm": er.get("EXIF FocalLength"),
"Flash": er.get("EXIF Flash"),
"GPS lat": er.get("GPS GPSLatitude"),
"GPS lon": er.get("GPS GPSLongitude"),
"Software": er.get("Image Software"),
}
for k, v in interesting.items():
if v and str(v).strip():
print(f" {k:12s}: {v}")
# IPTC / XMP — vypsat všechno, co je
if p["iptc"]:
for k, v in p["iptc"].items():
if not k.startswith("_"):
print(f" IPTC.{k:8s}: {v}")
if p["xmp"]:
for k, v in p["xmp"].items():
if not k.startswith("_"):
print(f" XMP.{k:9s}: {v}")
print()
# Tabulka perceptuálních podobností (Hamming distance phash)
print(f"{'=' * 70}")
print("PERCEPTUÁLNÍ PODOBNOST (phash Hamming distance)")
print("Hodnota 0-10 = vizuálně velmi podobné, >20 = odlišné")
print(f"{'=' * 70}")
n = len(photos)
header = " " + "".join(f" [{i+1}]" for i in range(n))
print(header)
for i in range(n):
row = f" [{i+1}] "
for j in range(n):
if i == j:
row += " -"
else:
h1 = photos[i]["hashes"].get("phash")
h2 = photos[j]["hashes"].get("phash")
if h1 and h2:
d = hamming_distance(h1, h2)
marker = "*" if d <= 10 and i != j else " "
row += f" {d:3d}{marker}"
else:
row += " N/A "
print(row)
print("\n * = vizuálně podobné fotky (možná duplikát po editaci)")
print()
def main():
if not PHOTOS_DIR.exists():
print(f"[ERROR] Složka neexistuje: {PHOTOS_DIR}")
return
files = sorted([p for p in PHOTOS_DIR.iterdir()
if p.is_file() and p.suffix.lower() in {".jpg", ".jpeg", ".png", ".heic", ".tiff", ".tif", ".webp"}])
if not files:
print(f"[WARN] Žádné fotky v {PHOTOS_DIR}")
return
print(f"Nalezeno {len(files)} fotek v {PHOTOS_DIR}\n")
photos = []
for f in files:
print(f" zpracovávám: {f.name} ...", end=" ", flush=True)
try:
photos.append(explore_photo(f))
print("OK")
except Exception as e:
print(f"FAIL: {e}")
print_summary(photos)
# Uložit do JSON pro detailní analýzu
with open(OUTPUT_JSON, "w", encoding="utf-8") as f:
json.dump(photos, f, indent=2, ensure_ascii=False, default=str)
print(f"\n[OK] Detailní data uložena: {OUTPUT_JSON}")
if __name__ == "__main__":
main()
+906
View File
@@ -0,0 +1,906 @@
[
{
"filesystem": {
"file_name": "2026-04-22 09.05.08.jpg",
"file_path": "U:\\PycharmProjects\\FotkyBuzalkovi\\demo_fotky\\2026-04-22 09.05.08.jpg",
"file_size_bytes": 10186179,
"file_size_mb": 9.71,
"mtime": "2026-04-22T09:05:08",
"ctime": "2026-05-19T06:22:12.659966",
"extension": ".jpg"
},
"hashes": {
"sha256_file": "4ea38e22f815c4512e6bfe06d9203180e5920ac7324d2e0e9271e918d53adff5",
"sha256_pixels": "b95b72d63a300cde544091c11f8e884896d583afa0dc380cdebc47b3ade8d05f",
"phash": "f7a2c8606d4c76d4",
"dhash": "c4884c4c4c4cc010",
"ahash": "feffe6a702246098",
"whash": "feffe7a702006098"
},
"pillow": {
"format": "JPEG",
"mode": "RGB",
"width": 5712,
"height": 4284,
"megapixels": 24.47,
"has_transparency": false,
"dpi": [
"72.0",
"72.0"
],
"icc_profile_present": true,
"exif_present": true,
"xmp_snippet": "<?xpacket begin=\"\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?> <x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"XMP Core 6.0.0\"> <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"> <rdf:Description rdf:about=\"\" xmlns:exif=\"http://ns.adobe.com/exif/1.0/\" xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\" xmlns:tiff=\"http://ns.adobe.com/tiff/1.0/\" xmlns:exifEX=\"http://cipa.jp/exif/1.0/\" xmlns:mwg-rs=\"http://www.metadataworkinggroup.com/schemas/regions/\" xmlns:stDim=\"http://ns.adobe.com/xap/1.0/sType/Dimens",
"has_embedded_thumbnail": false
},
"exif_pillow": {
"_error": "'int' object has no attribute 'items'"
},
"exif_exifread": {
"Image Make": "Apple",
"Image Model": "iPhone 16 Pro Max",
"Image Orientation": "Rotated 90 CW",
"Image XResolution": "72",
"Image YResolution": "72",
"Image ResolutionUnit": "Pixels/Inch",
"Image Software": "26.3.1",
"Image DateTime": "2026:04:22 09:05:07",
"Image HostComputer": "iPhone 16 Pro Max",
"Image ExifOffset": "232",
"GPS GPSLatitudeRef": "N",
"GPS GPSLatitude": "[50, 5, 1009/100]",
"GPS GPSLongitudeRef": "E",
"GPS GPSLongitude": "[14, 24, 3089/100]",
"GPS GPSAltitudeRef": "0",
"GPS GPSAltitude": "299875/1571",
"GPS GPSTimeStamp": "[7, 5, 6]",
"GPS GPSSpeedRef": "K",
"GPS GPSSpeed": "0",
"GPS GPSImgDirectionRef": "T",
"GPS GPSImgDirection": "379301/2482",
"GPS GPSDestBearingRef": "T",
"GPS GPSDestBearing": "379301/2482",
"GPS GPSDate": "2026:04:22",
"GPS Tag 0x001F": "43090/3283",
"Image GPSInfo": "2808",
"Thumbnail JPEGInterchangeFormat": "3224",
"Thumbnail JPEGInterchangeFormatLength": "10108",
"EXIF ExposureTime": "1/715",
"EXIF FNumber": "1244236/699009",
"EXIF ExposureProgram": "Program Normal",
"EXIF ISOSpeedRatings": "80",
"EXIF ExifVersion": "0232",
"EXIF DateTimeOriginal": "2026:04:22 09:05:07",
"EXIF DateTimeDigitized": "2026:04:22 09:05:07",
"EXIF OffsetTime": "+02:00",
"EXIF OffsetTimeOriginal": "+02:00",
"EXIF OffsetTimeDigitized": "+02:00",
"EXIF ComponentsConfiguration": "YCbCr",
"EXIF ShutterSpeedValue": "18849/1988",
"EXIF ApertureValue": "42657/25639",
"EXIF BrightnessValue": "72829/10013",
"EXIF ExposureBiasValue": "0",
"EXIF MeteringMode": "Pattern",
"EXIF Flash": "Flash did not fire, compulsory flash mode",
"EXIF FocalLength": "251773/37217",
"EXIF SubjectArea": "[1375, 2179, 413, 416]",
"EXIF MakerNote": "[65, 112, 112, 108, 101, 32, 105, 79, 83, 0, 0, 1, 77, 77, 0, 59, 0, 1, 0, 9, ... ]",
"EXIF SubSecTimeOriginal": "565",
"EXIF SubSecTimeDigitized": "565",
"EXIF FlashPixVersion": "0100",
"EXIF ColorSpace": "Uncalibrated",
"EXIF ExifImageWidth": "5712",
"EXIF ExifImageLength": "4284",
"EXIF SensingMethod": "One-chip color area",
"EXIF SceneType": "Directly Photographed",
"EXIF ExposureMode": "Auto Exposure",
"EXIF WhiteBalance": "Auto",
"EXIF FocalLengthIn35mmFilm": "24",
"EXIF SceneCaptureType": "Standard",
"EXIF LensSpecification": "[253126/37417, 2052196/131047, 1244236/699009, 14/5]",
"EXIF LensMake": "Apple",
"EXIF LensModel": "iPhone 16 Pro Max back triple camera 6.765mm f/1.78",
"EXIF Tag 0xA460": "2",
"MakerNote MakerNoteVersion": "16",
"MakerNote Tag 0x0002": "[161, 0, 127, 0, 112, 0, 61, 0, 40, 0, 49, 0, 45, 0, 10, 0, 31, 0, 213, 1, ... ]",
"MakerNote Tag 0x0003": "[6, 7, 8, 85, 102, 108, 97, 103, 115, 85, 118, 97, 108, 117, 101, 89, 116, 105, 109, 101, ... ]",
"MakerNote AEStable": "No",
"MakerNote AETarget": "188",
"MakerNote AEAverage": "204",
"MakerNote AFStable": "Yes",
"MakerNote Tag 0x0008": "[-856227839/729350144, -1321/103, 64/3]",
"MakerNote Tag 0x000C": "[279109/1162032945, 876031301/809906477]",
"MakerNote Tag 0x000D": "41",
"MakerNote Tag 0x000E": "0",
"MakerNote Tag 0x0010": "1",
"MakerNote Tag 0x0011": "4E88-B357-FC9658AA6BCC",
"MakerNote ImageCaptureType": "Scene",
"MakerNote Tag 0x0019": "19013666",
"MakerNote Tag 0x001A": "8D88B1",
"MakerNote Tag 0x001D": "842542391/1128675373",
"MakerNote Tag 0x001F": "1",
"MakerNote Tag 0x0020": "497E-A420-FA5685058AE3",
"MakerNote Tag 0x0021": "106496/43",
"MakerNote Tag 0x0023": "[277217292, 1443889152]",
"MakerNote Tag 0x0026": "3",
"MakerNote Tag 0x0027": "909192513/926431021",
"MakerNote Tag 0x002B": "459C-B603-F499EADDB45A",
"MakerNote Tag 0x002D": "5227",
"MakerNote CameraType": "Back Normal",
"MakerNote Tag 0x002F": "51",
"MakerNote Tag 0x0030": "71686300/454712541",
"MakerNote Tag 0x0036": "13797",
"MakerNote Tag 0x0037": "8",
"MakerNote Tag 0x0038": "121",
"MakerNote Tag 0x0039": "1",
"MakerNote Tag 0x003A": "164",
"MakerNote Tag 0x003B": "0",
"MakerNote Tag 0x003C": "4",
"MakerNote Tag 0x003D": "100",
"MakerNote Tag 0x003F": "0",
"MakerNote Tag 0x0041": "0",
"MakerNote Tag 0x0042": "0",
"MakerNote Tag 0x0043": "0",
"MakerNote Tag 0x0044": "0",
"MakerNote Tag 0x0045": "0",
"MakerNote Tag 0x0046": "0",
"MakerNote Tag 0x0048": "0",
"MakerNote Tag 0x0049": "0",
"MakerNote Tag 0x004A": "2",
"MakerNote Tag 0x004D": "808505857/33752145",
"MakerNote Tag 0x004E": "[49, 81, 50, 16, 3, 162, 5, 10, 210, 6, 7, 8, 9, 83, 50, 46, 49, 83, 50, 46, ... ]",
"MakerNote Tag 0x004F": "[0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 98, 112, 108, 105, 115, 116, 48, 48, 216, 1, 2, 3, 4, 5]",
"MakerNote Tag 0x0052": "3",
"MakerNote Tag 0x0053": "2",
"MakerNote Tag 0x0054": "[6, 7, 8, 9, 10, 11, 11, 11, 12, 13, 12, 81, 55, 81, 51, 81, 52, 81, 48, 81, ... ]",
"MakerNote Tag 0x0055": "126",
"MakerNote Tag 0x0058": "2307",
"MakerNote Tag 0x005A": "[6, 7, 8, 7, 9, 81, 51, 81, 49, 81, 52, 81, 50, 81, 48, 34, 60, 189, 98, 49, ... ]",
"MakerNote Tag 0x0060": "2048",
"MakerNote Tag 0x0061": "24"
},
"iptc": {},
"xmp": {
"creator_tool": "26.3.1",
"create_date": "2026-04-22T09:05:07.565",
"modify_date": "2026-04-22T09:05:07",
"face_regions_count": 1,
"_xmp_length_bytes": 6047
}
},
{
"filesystem": {
"file_name": "2026-04-24 15.15.47.jpg",
"file_path": "U:\\PycharmProjects\\FotkyBuzalkovi\\demo_fotky\\2026-04-24 15.15.47.jpg",
"file_size_bytes": 5928247,
"file_size_mb": 5.65,
"mtime": "2026-04-24T15:15:47",
"ctime": "2026-05-19T06:22:06.110438",
"extension": ".jpg"
},
"hashes": {
"sha256_file": "500ac874b758feb80f02d5c95d7cb60fd3d651de6220631e9e836c0e9efcf9bf",
"sha256_pixels": "48e05ab5ee5e532117c76d7f6000ede03c1e382ebdb90e22b3df158a90e3933e",
"phash": "e99c96b8b493a4b4",
"dhash": "182062829393cb4e",
"ahash": "fff8f8f0c1c1e0e3",
"whash": "fff8f8e0c0c0e0e1"
},
"pillow": {
"format": "JPEG",
"mode": "RGB",
"width": 4032,
"height": 3024,
"megapixels": 12.19,
"has_transparency": false,
"dpi": [
"72.0",
"72.0"
],
"icc_profile_present": true,
"exif_present": true,
"has_embedded_thumbnail": false
},
"exif_pillow": {
"ResolutionUnit": 2.0,
"ExifOffset": 232.0,
"Make": "Apple",
"Model": "iPhone 13 Pro Max",
"Software": "26.4.1",
"Orientation": 1.0,
"DateTime": "2026:04:24 15:15:46",
"YCbCrPositioning": 1.0,
"XResolution": 72.0,
"YResolution": 72.0,
"HostComputer": "iPhone 13 Pro Max"
},
"exif_exifread": {
"Image Make": "Apple",
"Image Model": "iPhone 13 Pro Max",
"Image Orientation": "Horizontal (normal)",
"Image XResolution": "72",
"Image YResolution": "72",
"Image ResolutionUnit": "Pixels/Inch",
"Image Software": "26.4.1",
"Image DateTime": "2026:04:24 15:15:46",
"Image HostComputer": "iPhone 13 Pro Max",
"Image YCbCrPositioning": "Centered",
"Image ExifOffset": "232",
"Thumbnail JPEGInterchangeFormat": "2750",
"Thumbnail JPEGInterchangeFormatLength": "7338",
"EXIF ExposureTime": "1/184",
"EXIF FNumber": "3/2",
"EXIF ExposureProgram": "Program Normal",
"EXIF ISOSpeedRatings": "40",
"EXIF ExifVersion": "0232",
"EXIF DateTimeOriginal": "2026:04:24 15:15:46",
"EXIF DateTimeDigitized": "2026:04:24 15:15:46",
"EXIF OffsetTime": "+02:00",
"EXIF OffsetTimeOriginal": "+02:00",
"EXIF OffsetTimeDigitized": "+02:00",
"EXIF ComponentsConfiguration": "YCbCr",
"EXIF ShutterSpeedValue": "83635/11111",
"EXIF ApertureValue": "27767/23734",
"EXIF BrightnessValue": "80499/13364",
"EXIF ExposureBiasValue": "0",
"EXIF MeteringMode": "Pattern",
"EXIF Flash": "Flash did not fire, compulsory flash mode",
"EXIF FocalLength": "57/10",
"EXIF SubjectArea": "[2014, 1508, 2218, 1328]",
"EXIF MakerNote": "[65, 112, 112, 108, 101, 32, 105, 79, 83, 0, 0, 1, 77, 77, 0, 60, 0, 1, 0, 9, ... ]",
"EXIF SubSecTimeOriginal": "964",
"EXIF SubSecTimeDigitized": "964",
"EXIF FlashPixVersion": "0100",
"EXIF ColorSpace": "Uncalibrated",
"EXIF ExifImageWidth": "4032",
"EXIF ExifImageLength": "3024",
"EXIF SensingMethod": "One-chip color area",
"EXIF SceneType": "Directly Photographed",
"EXIF ExposureMode": "Auto Exposure",
"EXIF WhiteBalance": "Auto",
"EXIF FocalLengthIn35mmFilm": "26",
"EXIF SceneCaptureType": "Standard",
"EXIF LensSpecification": "[299253/190607, 9, 3/2, 14/5]",
"EXIF LensMake": "Apple",
"EXIF LensModel": "iPhone 13 Pro Max back triple camera 5.7mm f/1.5",
"EXIF Tag 0xA460": "2",
"MakerNote MakerNoteVersion": "16",
"MakerNote Tag 0x0002": "[171, 1, 108, 1, 146, 1, 148, 1, 102, 1, 61, 1, 15, 1, 225, 0, 191, 0, 107, 2, ... ]",
"MakerNote Tag 0x0003": "[6, 7, 8, 85, 102, 108, 97, 103, 115, 85, 118, 97, 108, 117, 101, 89, 116, 105, 109, 101, ... ]",
"MakerNote AEStable": "Yes",
"MakerNote AETarget": "199",
"MakerNote AEAverage": "208",
"MakerNote AFStable": "Yes",
"MakerNote Tag 0x0008": "[401975979/194379776, 22723/23, 256/9]",
"MakerNote Tag 0x000C": "[16384/1061, 67112487/105295456]",
"MakerNote Tag 0x000D": "34",
"MakerNote Tag 0x000E": "0",
"MakerNote Tag 0x0010": "1",
"MakerNote ImageCaptureType": "Photo",
"MakerNote Tag 0x0019": "2",
"MakerNote Tag 0x001A": "-9193-",
"MakerNote Tag 0x001F": "0",
"MakerNote Tag 0x0020": "4018-B96D-3A65CF0E00D6",
"MakerNote Tag 0x0021": "2228224/0",
"MakerNote Tag 0x0023": "[8781826, -950272000]",
"MakerNote Tag 0x0026": "3",
"MakerNote Tag 0x0027": "163616959/158636587",
"MakerNote Tag 0x0028": "1",
"MakerNote Tag 0x002B": "443A-AA33-717906FF9D4E",
"MakerNote Tag 0x002D": "5365",
"MakerNote CameraType": "Back Normal",
"MakerNote Tag 0x002F": "71",
"MakerNote Tag 0x0030": "808506369/33752069",
"MakerNote Tag 0x0033": "4096",
"MakerNote Tag 0x0034": "4",
"MakerNote Tag 0x0035": "3",
"MakerNote Tag 0x0036": "4214",
"MakerNote Tag 0x0037": "4",
"MakerNote Tag 0x0038": "121",
"MakerNote Tag 0x0039": "0",
"MakerNote Tag 0x003A": "0",
"MakerNote Tag 0x003B": "0",
"MakerNote Tag 0x003C": "4",
"MakerNote Tag 0x003D": "34",
"MakerNote Tag 0x003F": "45",
"MakerNote Tag 0x0040": "[6, 6, 7, 81, 51, 81, 49, 81, 50, 81, 48, 16, 0, 34, 0, 0, 0, 0, 16, 1, ... ]",
"MakerNote Tag 0x0041": "0",
"MakerNote Tag 0x0042": "0",
"MakerNote Tag 0x0043": "0",
"MakerNote Tag 0x0044": "0",
"MakerNote Tag 0x0045": "0",
"MakerNote Tag 0x0046": "0",
"MakerNote Tag 0x0048": "570",
"MakerNote Tag 0x0049": "0",
"MakerNote Tag 0x004A": "2",
"MakerNote Tag 0x004D": "808505857/33752145",
"MakerNote Tag 0x004E": "[49, 81, 50, 16, 3, 162, 5, 10, 210, 6, 7, 8, 9, 83, 50, 46, 49, 83, 50, 46, ... ]",
"MakerNote Tag 0x004F": "[0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 4, 144, 245, 0, 2, 232, 143, 0, 0, 0, 9, 0, 0]",
"MakerNote Tag 0x0052": "3",
"MakerNote Tag 0x0053": "1",
"MakerNote Tag 0x0055": "0",
"MakerNote Tag 0x0058": "1795",
"MakerNote Tag 0x0060": "11057",
"MakerNote Tag 0x0061": "26"
},
"iptc": {},
"xmp": {}
},
{
"filesystem": {
"file_name": "2026-05-02 10.04.44.png",
"file_path": "U:\\PycharmProjects\\FotkyBuzalkovi\\demo_fotky\\2026-05-02 10.04.44.png",
"file_size_bytes": 652043,
"file_size_mb": 0.62,
"mtime": "2026-05-02T10:04:44",
"ctime": "2026-05-19T06:22:01.483987",
"extension": ".png"
},
"hashes": {
"sha256_file": "bfbebc9c3db1b1c50af4925c7208c422425e637da1b497271096e298382ca2c3",
"sha256_pixels": "53bb4fd7cb92bb3e709f3bef385324034ed608a0fb23f54de1f5b5d1c3b3fa67",
"phash": "8f4be06bd2f0161e",
"dhash": "4b6b755d7b6f6fea",
"ahash": "f9b91fff17839fff",
"whash": "b8980fef030303ff"
},
"pillow": {
"format": "PNG",
"mode": "RGB",
"width": 1284,
"height": 2778,
"megapixels": 3.57,
"has_transparency": false,
"dpi": [
143.99259999999998,
143.99259999999998
],
"icc_profile_present": false,
"exif_present": true,
"xmp_snippet": "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"XMP Core 6.0.0\">\n <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n <rdf:Description rdf:about=\"\"\n xmlns:exif=\"http://ns.adobe.com/exif/1.0/\"\n xmlns:photoshop=\"http://ns.adobe.com/photoshop/1.0/\"\n xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\"\n xmlns:tiff=\"http://ns.adobe.com/tiff/1.0/\"\n xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n <exif:UserComment>Screenshot</exif:User",
"has_embedded_thumbnail": false
},
"exif_pillow": {
"ResolutionUnit": 2.0,
"ExifOffset": 146.0,
"ImageDescription": "Screenshot",
"Orientation": 1.0,
"DateTime": "2026:05:02 10:04:44",
"XResolution": 144.0,
"YResolution": 144.0
},
"exif_exifread": {
"Image ImageDescription": "Screenshot",
"Image Orientation": "Horizontal (normal)",
"Image XResolution": "144",
"Image YResolution": "144",
"Image ResolutionUnit": "Pixels/Inch",
"Image DateTime": "2026:05:02 10:04:44",
"Image ExifOffset": "146",
"EXIF DateTimeOriginal": "2026:05:02 10:04:44",
"EXIF UserComment": "Screenshot",
"EXIF ColorSpace": "sRGB",
"EXIF ExifImageWidth": "1284",
"EXIF ExifImageLength": "2778"
},
"iptc": {},
"xmp": {
"description": "Screenshot",
"_xmp_length_bytes": 909
}
},
{
"filesystem": {
"file_name": "2026-05-18 06.22.37.jpg",
"file_path": "U:\\PycharmProjects\\FotkyBuzalkovi\\demo_fotky\\2026-05-18 06.22.37.jpg",
"file_size_bytes": 9331138,
"file_size_mb": 8.9,
"mtime": "2026-05-18T06:22:37",
"ctime": "2026-05-19T06:21:53.390486",
"extension": ".jpg"
},
"hashes": {
"sha256_file": "9b9395396fd7893cdcdc69703a9e8e331bd93ce64da53ef9b5b25f3d786036ed",
"sha256_pixels": "47d95e29f8972b2d4d8f4f28b42a335bac4a629248486bc2ad1694467d1d4630",
"phash": "edcc9e48e832b5a4",
"dhash": "a2e2c68383d6d002",
"ahash": "f8f8f3f1f0600080",
"whash": "f8f8fbf1f06000f0"
},
"pillow": {
"format": "JPEG",
"mode": "RGB",
"width": 4032,
"height": 3024,
"megapixels": 12.19,
"has_transparency": false,
"dpi": [
"72.0",
"72.0"
],
"icc_profile_present": true,
"exif_present": true,
"xmp_snippet": "<?xpacket begin=\"\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?> <x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"XMP Core 6.0.0\"> <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"> <rdf:Description rdf:about=\"\" xmlns:exif=\"http://ns.adobe.com/exif/1.0/\" xmlns:exifEX=\"http://cipa.jp/exif/1.0/\" xmlns:tiff=\"http://ns.adobe.com/tiff/1.0/\" xmlns:photoshop=\"http://ns.adobe.com/photoshop/1.0/\" xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\" xmlns:mwg-rs=\"http://www.metadataworkinggroup.com/schemas/regions/\" ",
"has_embedded_thumbnail": false
},
"exif_pillow": {
"ResolutionUnit": 2.0,
"ExifOffset": 220.0,
"Make": "Apple",
"Model": "iPhone 13 Pro Max",
"Software": "26.4.2",
"Orientation": 6.0,
"DateTime": "2026:05:18 06:22:37",
"XResolution": 72.0,
"YResolution": 72.0,
"HostComputer": "iPhone 13 Pro Max"
},
"exif_exifread": {
"Image Make": "Apple",
"Image Model": "iPhone 13 Pro Max",
"Image Orientation": "Rotated 90 CW",
"Image XResolution": "72",
"Image YResolution": "72",
"Image ResolutionUnit": "Pixels/Inch",
"Image Software": "26.4.2",
"Image DateTime": "2026:05:18 06:22:37",
"Image HostComputer": "iPhone 13 Pro Max",
"Image ExifOffset": "220",
"Thumbnail JPEGInterchangeFormat": "2776",
"Thumbnail JPEGInterchangeFormatLength": "7602",
"EXIF ExposureTime": "1/50",
"EXIF FNumber": "3/2",
"EXIF ExposureProgram": "Program Normal",
"EXIF ISOSpeedRatings": "200",
"EXIF ExifVersion": "0232",
"EXIF DateTimeOriginal": "2026:05:18 06:22:37",
"EXIF DateTimeDigitized": "2026:05:18 06:22:37",
"EXIF OffsetTime": "+02:00",
"EXIF OffsetTimeOriginal": "+02:00",
"EXIF OffsetTimeDigitized": "+02:00",
"EXIF ComponentsConfiguration": "YCbCr",
"EXIF ShutterSpeedValue": "48307/8559",
"EXIF ApertureValue": "27767/23734",
"EXIF BrightnessValue": "22485/14578",
"EXIF ExposureBiasValue": "0",
"EXIF MeteringMode": "Pattern",
"EXIF Flash": "Flash did not fire, compulsory flash mode",
"EXIF FocalLength": "57/10",
"EXIF SubjectArea": "[1000, 1528, 190, 191]",
"EXIF MakerNote": "[65, 112, 112, 108, 101, 32, 105, 79, 83, 0, 0, 1, 77, 77, 0, 59, 0, 1, 0, 9, ... ]",
"EXIF SubSecTimeOriginal": "375",
"EXIF SubSecTimeDigitized": "375",
"EXIF FlashPixVersion": "0100",
"EXIF ColorSpace": "Uncalibrated",
"EXIF ExifImageWidth": "4032",
"EXIF ExifImageLength": "3024",
"EXIF SensingMethod": "One-chip color area",
"EXIF SceneType": "Directly Photographed",
"EXIF ExposureMode": "Auto Exposure",
"EXIF WhiteBalance": "Auto",
"EXIF FocalLengthIn35mmFilm": "26",
"EXIF SceneCaptureType": "Standard",
"EXIF LensSpecification": "[299253/190607, 9, 3/2, 14/5]",
"EXIF LensMake": "Apple",
"EXIF LensModel": "iPhone 13 Pro Max back triple camera 5.7mm f/1.5",
"EXIF Tag 0xA460": "2",
"MakerNote MakerNoteVersion": "16",
"MakerNote Tag 0x0002": "[22, 2, 104, 2, 90, 1, 152, 0, 121, 0, 109, 0, 108, 0, 118, 0, 121, 0, 139, 0, ... ]",
"MakerNote Tag 0x0003": "[6, 7, 8, 85, 102, 108, 97, 103, 115, 85, 118, 97, 108, 117, 101, 89, 116, 105, 109, 101, ... ]",
"MakerNote AEStable": "Yes",
"MakerNote AETarget": "192",
"MakerNote AEAverage": "184",
"MakerNote AFStable": "Yes",
"MakerNote Tag 0x0008": "[653568683/78381056, 13267/125, 256/55]",
"MakerNote Tag 0x000C": "[2103323/437786648, 314292673/392696335]",
"MakerNote Tag 0x000D": "24",
"MakerNote Tag 0x000E": "0",
"MakerNote Tag 0x0010": "1",
"MakerNote Tag 0x0011": "4FAB-8319-C67E13D5DB53",
"MakerNote ImageCaptureType": "Scene",
"MakerNote Tag 0x0019": "8194",
"MakerNote Tag 0x001A": "-BB21-",
"MakerNote Tag 0x001F": "0",
"MakerNote Tag 0x0020": "497C-BF6F-D6B9ACA3BAEA",
"MakerNote Tag 0x0021": "2818048/0",
"MakerNote Tag 0x0023": "[344850434, -1100677120]",
"MakerNote Tag 0x0026": "3",
"MakerNote Tag 0x0027": "910503235/1177825837",
"MakerNote Tag 0x002B": "45D3-801F-A154352C61C6",
"MakerNote Tag 0x002D": "3476",
"MakerNote CameraType": "Back Normal",
"MakerNote Tag 0x002F": "50",
"MakerNote Tag 0x0030": "808506369/33752069",
"MakerNote Tag 0x0033": "12288",
"MakerNote Tag 0x0034": "5",
"MakerNote Tag 0x0035": "4",
"MakerNote Tag 0x0036": "179",
"MakerNote Tag 0x0037": "4",
"MakerNote Tag 0x0038": "201",
"MakerNote Tag 0x0039": "1",
"MakerNote Tag 0x003A": "133",
"MakerNote Tag 0x003B": "0",
"MakerNote Tag 0x003C": "4",
"MakerNote Tag 0x003D": "33",
"MakerNote Tag 0x0040": "[6, 6, 7, 81, 51, 81, 49, 81, 50, 81, 48, 16, 0, 34, 0, 0, 0, 0, 16, 1, ... ]",
"MakerNote Tag 0x0041": "0",
"MakerNote Tag 0x0042": "0",
"MakerNote Tag 0x0043": "0",
"MakerNote Tag 0x0044": "0",
"MakerNote Tag 0x0045": "0",
"MakerNote Tag 0x0046": "0",
"MakerNote Tag 0x0048": "569",
"MakerNote Tag 0x0049": "0",
"MakerNote Tag 0x004A": "2",
"MakerNote Tag 0x004D": "808505857/33752145",
"MakerNote Tag 0x004E": "[49, 81, 50, 16, 1, 162, 5, 10, 210, 6, 7, 8, 9, 83, 50, 46, 49, 83, 50, 46, ... ]",
"MakerNote Tag 0x004F": "[0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 4, 144, 245, 0, 2, 232, 143, 0, 0, 0, 9, 0]",
"MakerNote Tag 0x0052": "3",
"MakerNote Tag 0x0053": "0",
"MakerNote Tag 0x0055": "174",
"MakerNote Tag 0x0058": "1795",
"MakerNote Tag 0x0060": "6424",
"MakerNote Tag 0x0061": "26"
},
"iptc": {},
"xmp": {
"creator_tool": "26.4.2",
"create_date": "2026-05-18T06:22:37.375",
"modify_date": "2026-05-18T06:22:37",
"face_regions_count": 1,
"_xmp_length_bytes": 5345
}
},
{
"filesystem": {
"file_name": "2026-05-18 06.22.41.jpg",
"file_path": "U:\\PycharmProjects\\FotkyBuzalkovi\\demo_fotky\\2026-05-18 06.22.41.jpg",
"file_size_bytes": 8328001,
"file_size_mb": 7.94,
"mtime": "2026-05-18T06:22:41",
"ctime": "2026-05-19T06:21:46.033863",
"extension": ".jpg"
},
"hashes": {
"sha256_file": "e22b409fecfd997585b8bb16850c8568e876b780d54d091fc086803c5e884c80",
"sha256_pixels": "3fb2350a514b8840984fbb8e85e0aad834a670f14d01aef56ce07d1dbb1d6c62",
"phash": "e9cc9e496a32b5a4",
"dhash": "a2e2c68383d2d002",
"ahash": "f8f8f3f1f0600080",
"whash": "f8f8fbf1f16000b0"
},
"pillow": {
"format": "JPEG",
"mode": "RGB",
"width": 4032,
"height": 3024,
"megapixels": 12.19,
"has_transparency": false,
"dpi": [
"72.0",
"72.0"
],
"icc_profile_present": true,
"exif_present": true,
"xmp_snippet": "<?xpacket begin=\"\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?> <x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"XMP Core 6.0.0\"> <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"> <rdf:Description rdf:about=\"\" xmlns:exif=\"http://ns.adobe.com/exif/1.0/\" xmlns:exifEX=\"http://cipa.jp/exif/1.0/\" xmlns:tiff=\"http://ns.adobe.com/tiff/1.0/\" xmlns:photoshop=\"http://ns.adobe.com/photoshop/1.0/\" xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\" xmlns:mwg-rs=\"http://www.metadataworkinggroup.com/schemas/regions/\" ",
"has_embedded_thumbnail": false
},
"exif_pillow": {
"ResolutionUnit": 2.0,
"ExifOffset": 220.0,
"Make": "Apple",
"Model": "iPhone 13 Pro Max",
"Software": "26.4.2",
"Orientation": 6.0,
"DateTime": "2026:05:18 06:22:41",
"XResolution": 72.0,
"YResolution": 72.0,
"HostComputer": "iPhone 13 Pro Max"
},
"exif_exifread": {
"Image Make": "Apple",
"Image Model": "iPhone 13 Pro Max",
"Image Orientation": "Rotated 90 CW",
"Image XResolution": "72",
"Image YResolution": "72",
"Image ResolutionUnit": "Pixels/Inch",
"Image Software": "26.4.2",
"Image DateTime": "2026:05:18 06:22:41",
"Image HostComputer": "iPhone 13 Pro Max",
"Image ExifOffset": "220",
"Thumbnail JPEGInterchangeFormat": "2776",
"Thumbnail JPEGInterchangeFormatLength": "7639",
"EXIF ExposureTime": "1/50",
"EXIF FNumber": "3/2",
"EXIF ExposureProgram": "Program Normal",
"EXIF ISOSpeedRatings": "200",
"EXIF ExifVersion": "0232",
"EXIF DateTimeOriginal": "2026:05:18 06:22:41",
"EXIF DateTimeDigitized": "2026:05:18 06:22:41",
"EXIF OffsetTime": "+02:00",
"EXIF OffsetTimeOriginal": "+02:00",
"EXIF OffsetTimeDigitized": "+02:00",
"EXIF ComponentsConfiguration": "YCbCr",
"EXIF ShutterSpeedValue": "48307/8559",
"EXIF ApertureValue": "27767/23734",
"EXIF BrightnessValue": "5765/3682",
"EXIF ExposureBiasValue": "0",
"EXIF MeteringMode": "Pattern",
"EXIF Flash": "Flash did not fire, compulsory flash mode",
"EXIF FocalLength": "57/10",
"EXIF SubjectArea": "[1025, 1438, 190, 191]",
"EXIF MakerNote": "[65, 112, 112, 108, 101, 32, 105, 79, 83, 0, 0, 1, 77, 77, 0, 59, 0, 1, 0, 9, ... ]",
"EXIF SubSecTimeOriginal": "023",
"EXIF SubSecTimeDigitized": "023",
"EXIF FlashPixVersion": "0100",
"EXIF ColorSpace": "Uncalibrated",
"EXIF ExifImageWidth": "4032",
"EXIF ExifImageLength": "3024",
"EXIF SensingMethod": "One-chip color area",
"EXIF SceneType": "Directly Photographed",
"EXIF ExposureMode": "Auto Exposure",
"EXIF WhiteBalance": "Auto",
"EXIF FocalLengthIn35mmFilm": "26",
"EXIF SceneCaptureType": "Standard",
"EXIF LensSpecification": "[299253/190607, 9, 3/2, 14/5]",
"EXIF LensMake": "Apple",
"EXIF LensModel": "iPhone 13 Pro Max back triple camera 5.7mm f/1.5",
"EXIF Tag 0xA460": "2",
"MakerNote MakerNoteVersion": "16",
"MakerNote Tag 0x0002": "[9, 2, 186, 2, 164, 1, 174, 0, 132, 0, 109, 0, 107, 0, 118, 0, 120, 0, 136, 0, ... ]",
"MakerNote Tag 0x0003": "[6, 7, 8, 85, 102, 108, 97, 103, 115, 85, 118, 97, 108, 117, 101, 89, 116, 105, 109, 101, ... ]",
"MakerNote AEStable": "Yes",
"MakerNote AETarget": "192",
"MakerNote AEAverage": "187",
"MakerNote AFStable": "Yes",
"MakerNote Tag 0x0008": "[-1233846271/1143537664, -1825/49, 64/187]",
"MakerNote Tag 0x000C": "[16790593/909130039, 1093741891/1127760685]",
"MakerNote Tag 0x000D": "24",
"MakerNote Tag 0x000E": "0",
"MakerNote Tag 0x0010": "1",
"MakerNote Tag 0x0011": "4345-8E58-D5B765E42693",
"MakerNote ImageCaptureType": "Scene",
"MakerNote Tag 0x0019": "8194",
"MakerNote Tag 0x001A": "-3ED5-",
"MakerNote Tag 0x001F": "0",
"MakerNote Tag 0x0020": "42CB-8A14-2D09F1435647",
"MakerNote Tag 0x0021": "2228224/0",
"MakerNote Tag 0x0023": "[344850436, -1787625472]",
"MakerNote Tag 0x0026": "3",
"MakerNote Tag 0x0027": "1178873142/875704621",
"MakerNote Tag 0x002B": "4B9E-8FE9-F6739C66DC97",
"MakerNote Tag 0x002D": "3476",
"MakerNote CameraType": "Back Normal",
"MakerNote Tag 0x002F": "50",
"MakerNote Tag 0x0030": "808506369/33752069",
"MakerNote Tag 0x0033": "12288",
"MakerNote Tag 0x0034": "5",
"MakerNote Tag 0x0035": "4",
"MakerNote Tag 0x0036": "180",
"MakerNote Tag 0x0037": "4",
"MakerNote Tag 0x0038": "203",
"MakerNote Tag 0x0039": "1",
"MakerNote Tag 0x003A": "133",
"MakerNote Tag 0x003B": "0",
"MakerNote Tag 0x003C": "4",
"MakerNote Tag 0x003D": "66",
"MakerNote Tag 0x0040": "[6, 6, 7, 81, 51, 81, 49, 81, 50, 81, 48, 16, 0, 34, 0, 0, 0, 0, 16, 1, ... ]",
"MakerNote Tag 0x0041": "0",
"MakerNote Tag 0x0042": "0",
"MakerNote Tag 0x0043": "0",
"MakerNote Tag 0x0044": "0",
"MakerNote Tag 0x0045": "0",
"MakerNote Tag 0x0046": "0",
"MakerNote Tag 0x0048": "569",
"MakerNote Tag 0x0049": "0",
"MakerNote Tag 0x004A": "2",
"MakerNote Tag 0x004D": "808505857/33752145",
"MakerNote Tag 0x004E": "[49, 81, 50, 16, 1, 162, 5, 10, 210, 6, 7, 8, 9, 83, 50, 46, 49, 83, 50, 46, ... ]",
"MakerNote Tag 0x004F": "[0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 4, 144, 245, 0, 2, 232, 143, 0, 0, 0, 9, 0]",
"MakerNote Tag 0x0052": "3",
"MakerNote Tag 0x0053": "0",
"MakerNote Tag 0x0055": "185",
"MakerNote Tag 0x0058": "1795",
"MakerNote Tag 0x0060": "10127",
"MakerNote Tag 0x0061": "26"
},
"iptc": {},
"xmp": {
"creator_tool": "26.4.2",
"create_date": "2026-05-18T06:22:41.023",
"modify_date": "2026-05-18T06:22:41",
"face_regions_count": 1,
"_xmp_length_bytes": 5345
}
},
{
"filesystem": {
"file_name": "2026-05-18 13.54.47.jpg",
"file_path": "U:\\PycharmProjects\\FotkyBuzalkovi\\demo_fotky\\2026-05-18 13.54.47.jpg",
"file_size_bytes": 3149514,
"file_size_mb": 3.0,
"mtime": "2026-05-18T13:54:47",
"ctime": "2026-05-19T06:21:40.360916",
"extension": ".jpg"
},
"hashes": {
"sha256_file": "be41557d3ef6822c24a00905e65ad9fba5950bf03ed5c2a434d25c893ed0a87d",
"sha256_pixels": "417d6fc06052eba388922d61af72674becb044b8650f2ac528a59aa3109d0408",
"phash": "f8124da5f87a0e27",
"dhash": "0b926d3902230203",
"ahash": "000000dcfffffbf8",
"whash": "000000dcfffff9f8"
},
"pillow": {
"format": "JPEG",
"mode": "RGB",
"width": 4032,
"height": 3024,
"megapixels": 12.19,
"has_transparency": false,
"dpi": [
"72.0",
"72.0"
],
"icc_profile_present": true,
"exif_present": true,
"has_embedded_thumbnail": false
},
"exif_pillow": {
"ResolutionUnit": 2.0,
"ExifOffset": 220.0,
"Make": "Apple",
"Model": "iPhone 13 Pro Max",
"Software": "26.4.2",
"Orientation": 6.0,
"DateTime": "2026:05:18 13:54:46",
"XResolution": 72.0,
"YResolution": 72.0,
"HostComputer": "iPhone 13 Pro Max"
},
"exif_exifread": {
"Image Make": "Apple",
"Image Model": "iPhone 13 Pro Max",
"Image Orientation": "Rotated 90 CW",
"Image XResolution": "72",
"Image YResolution": "72",
"Image ResolutionUnit": "Pixels/Inch",
"Image Software": "26.4.2",
"Image DateTime": "2026:05:18 13:54:46",
"Image HostComputer": "iPhone 13 Pro Max",
"Image ExifOffset": "220",
"EXIF ExposureTime": "1/100",
"EXIF FNumber": "3/2",
"EXIF ExposureProgram": "Program Normal",
"EXIF ISOSpeedRatings": "40",
"EXIF DateTimeOriginal": "2026:05:18 13:54:46",
"EXIF DateTimeDigitized": "2026:05:18 13:54:46",
"EXIF OffsetTime": "+02:00",
"EXIF OffsetTimeOriginal": "+02:00",
"EXIF OffsetTimeDigitized": "+02:00",
"EXIF ShutterSpeedValue": "135889/20452",
"EXIF ApertureValue": "27767/23734",
"EXIF BrightnessValue": "99413/19450",
"EXIF ExposureBiasValue": "0",
"EXIF MeteringMode": "Pattern",
"EXIF Flash": "Flash did not fire, compulsory flash mode",
"EXIF FocalLength": "57/10",
"EXIF SubjectArea": "[2014, 1508, 2218, 1328]",
"EXIF MakerNote": "[65, 112, 112, 108, 101, 32, 105, 79, 83, 0, 0, 1, 77, 77, 0, 58, 0, 1, 0, 9, ... ]",
"EXIF SubSecTimeOriginal": "334",
"EXIF SubSecTimeDigitized": "334",
"EXIF ColorSpace": "Uncalibrated",
"EXIF ExifImageWidth": "4032",
"EXIF ExifImageLength": "3024",
"EXIF SensingMethod": "One-chip color area",
"EXIF SceneType": "Directly Photographed",
"EXIF ExposureMode": "Auto Exposure",
"EXIF WhiteBalance": "Auto",
"EXIF FocalLengthIn35mmFilm": "26",
"EXIF LensSpecification": "[299253/190607, 9, 3/2, 14/5]",
"EXIF LensMake": "Apple",
"EXIF LensModel": "iPhone 13 Pro Max back triple camera 5.7mm f/1.5",
"EXIF Tag 0xA460": "2",
"MakerNote MakerNoteVersion": "16",
"MakerNote Tag 0x0002": "[9, 1, 5, 1, 252, 0, 245, 0, 237, 0, 223, 0, 199, 0, 177, 0, 163, 0, 136, 0, ... ]",
"MakerNote Tag 0x0003": "[6, 7, 8, 85, 102, 108, 97, 103, 115, 85, 118, 97, 108, 117, 101, 85, 101, 112, 111, 99, ... ]",
"MakerNote AEStable": "Yes",
"MakerNote AETarget": "207",
"MakerNote AEAverage": "202",
"MakerNote AFStable": "Yes",
"MakerNote Tag 0x0008": "[-1352269823/1435631616, 22289/49, 128/101]",
"MakerNote Tag 0x000C": "[1048576/1061, 536888888/959919927]",
"MakerNote Tag 0x000D": "24",
"MakerNote Tag 0x000E": "0",
"MakerNote Tag 0x0010": "1",
"MakerNote ImageCaptureType": "Photo",
"MakerNote Tag 0x0019": "2",
"MakerNote Tag 0x001F": "0",
"MakerNote Tag 0x0020": "4C30-A38C-5D8C18006FEF",
"MakerNote Tag 0x0021": "2621440/0",
"MakerNote Tag 0x0023": "[9043971, -445775872]",
"MakerNote Tag 0x0026": "3",
"MakerNote Tag 0x0027": "61983868/46144959",
"MakerNote Tag 0x0028": "1",
"MakerNote Tag 0x002B": "4C1B-ADDB-46C891A19178",
"MakerNote Tag 0x002D": "6004",
"MakerNote CameraType": "Back Normal",
"MakerNote Tag 0x002F": "143",
"MakerNote Tag 0x0030": "17549364/151570847",
"MakerNote Tag 0x0033": "4096",
"MakerNote Tag 0x0034": "4",
"MakerNote Tag 0x0035": "3",
"MakerNote Tag 0x0036": "2044",
"MakerNote Tag 0x0037": "4",
"MakerNote Tag 0x0038": "19",
"MakerNote Tag 0x0039": "0",
"MakerNote Tag 0x003A": "0",
"MakerNote Tag 0x003B": "0",
"MakerNote Tag 0x003C": "4",
"MakerNote Tag 0x003D": "11",
"MakerNote Tag 0x003F": "1",
"MakerNote Tag 0x0041": "0",
"MakerNote Tag 0x0042": "0",
"MakerNote Tag 0x0043": "0",
"MakerNote Tag 0x0044": "0",
"MakerNote Tag 0x0045": "0",
"MakerNote Tag 0x0046": "0",
"MakerNote Tag 0x0048": "19",
"MakerNote Tag 0x0049": "100",
"MakerNote Tag 0x004A": "2",
"MakerNote Tag 0x004D": "808505857/33752145",
"MakerNote Tag 0x004E": "[49, 81, 50, 16, 1, 162, 5, 10, 210, 6, 7, 8, 9, 83, 50, 46, 49, 83, 50, 46, ... ]",
"MakerNote Tag 0x004F": "[0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 4, 144, 245, 0, 2, 232, 143, 0, 0, 0, 9, 0, 0]",
"MakerNote Tag 0x0052": "3",
"MakerNote Tag 0x0053": "1",
"MakerNote Tag 0x0055": "0",
"MakerNote Tag 0x0058": "1795",
"MakerNote Tag 0x0060": "2856",
"MakerNote Tag 0x0061": "26"
},
"iptc": {
"IPTC(1, 90)": "\u001b%G",
"IPTC(2, 0)": "\u0000\u0002",
"IPTC(2, 63)": "135446",
"IPTC(2, 62)": "20260518",
"DateCreated": "20260518",
"TimeCreated": "135446"
},
"xmp": {}
},
{
"filesystem": {
"file_name": "2026-05-18 14.10.59.jpg",
"file_path": "U:\\PycharmProjects\\FotkyBuzalkovi\\demo_fotky\\2026-05-18 14.10.59.jpg",
"file_size_bytes": 304048,
"file_size_mb": 0.29,
"mtime": "2026-05-18T14:10:59",
"ctime": "2026-05-19T06:21:38.476009",
"extension": ".jpg"
},
"hashes": {
"sha256_file": "a4b20aa73cbc2fe845f12709ccc7e1d227e8fc1c84605a538e86f4a00b899754",
"sha256_pixels": "5b5e19daae775d21822891aef4558ae2c241bf082dfb54ae62cd0be1a045f223",
"phash": "c7226934de856937",
"dhash": "e74fc3ec6c3fc663",
"ahash": "0000217eb683fff9",
"whash": "000060feb6c3fff9"
},
"pillow": {
"format": "JPEG",
"mode": "RGB",
"width": 1153,
"height": 2048,
"megapixels": 2.36,
"has_transparency": false,
"dpi": null,
"icc_profile_present": false,
"exif_present": false,
"has_embedded_thumbnail": false
},
"exif_pillow": {},
"exif_exifread": {},
"iptc": {},
"xmp": {}
}
]