{"slug": "show-hn-image-categorizer-sort-a-photo-library-with-local-vision-models", "title": "Show HN: Image Categorizer – sort a photo library with local vision models", "summary": "A developer released Image Categorizer, an open-source tool that sorts a photo library into categories using local vision and text models served by Ollama, keeping all photos on the user's own network. The tool describes each photo with a vision model (qwen3.6:35b) and flags accidental shots, then sorts photos into user-editable categories with a text model (mistral-small3.2:24b), exporting a bash script that moves files into folders or writes the category into each photo's caption. The developer reports that 1,195 photos took about 80 minutes to describe and 8 minutes to sort on an RTX 4090, and the tool never moves, renames or deletes a photo itself.", "body_md": "Sorts a folder of photos into categories using local AI models served by\n[Ollama](https://ollama.com). A vision model describes every photo and flags\naccidental shots; a text model sorts the photos into categories you keep and\nrefine across runs. You review the result in a browser report and export a script\nthat moves the files into folders, writes each category into the photo's caption,\nor both. No photo leaves your network.\n\nBuilt for camera rolls full of accidental shots, near-duplicates and screenshots, where reviewing every photo by hand is not practical.\n\n1. **Describe.** A vision model looks at each photo, writes a short description,\nand decides whether it is an accidental or failed shot (motion blur, pocket\nshot, no subject). With`--describe-only` (see below), descriptions are saved\nas they go and an interrupted run resumes where it stopped.\n2. **Sort.** A text model assigns every photo to a category using the descriptions\nonly, so re-sorting takes minutes and never looks at the images again. Each\ncategory has a rule describing what belongs in it, and the list is saved and\nreused for every library.\n3. **Review.** A report in the browser lets you move photos between categories,\ntrash them, and rename, merge or create categories.\n4. **Apply.** The report exports a bash script: move each photo into a folder named\nafter its category, write`Category: <name>` into its caption (searchable in the\nPhotos app on Mac and iPhone), or both. Nothing is deleted.\n\nThe tool itself never moves, renames or deletes a photo.\n\n**With an AI coding agent.** Open the repository in Claude Code (or a similar\nagent) and ask it to follow [SETUP.md](https://github.com/DrBenedictPorkins/image-categorizer/blob/main/SETUP.md). It checks the machine, installs\nwhat is missing, configures Ollama and the models, and runs a smoke test on the\nimages in [`samples/`](https://github.com/DrBenedictPorkins/image-categorizer/blob/main/samples).\n\n**By hand.** You need [uv](https://docs.astral.sh/uv/) and an Ollama server, on\nthis machine or another one on your network.\n\n```\ngit clone https://github.com/DrBenedictPorkins/image-categorizer.git\ncd image-categorizer\nuv sync\n\nollama pull qwen3.6:35b            # vision model (describe)\nollama pull mistral-small3.2:24b   # text model (sort)\ncp .env.example .env               # set OLLAMA_HOST if Ollama runs elsewhere\n\nuv run python main.py samples --provider ollama\n```\n\nThe last command sorts the eleven sample images and opens the report. Smaller\nmodel pairs for 8-16 GB GPUs are listed in [SETUP.md](https://github.com/DrBenedictPorkins/image-categorizer/blob/main/SETUP.md#5-ollama-and-models).\n\nDescribe once, then sort as often as you like:\n\n```\n# Describe every photo (hours for thousands; rerun the same command to resume)\nuv run python main.py \"/path/to/photos\" --description-provider ollama --describe-only\n\n# Optional: build the category list and stop, to edit it before sorting\nuv run python main.py \"/path/to/photos\" --categorization-provider ollama \\\n    --categorize-from \"/path/to/photos/descriptions_only.json\" --plan-categories --max-categories 12\n\n# Sort and open the report (minutes)\nuv run python main.py \"/path/to/photos\" --categorization-provider ollama \\\n    --categorize-from \"/path/to/photos/descriptions_only.json\" --max-categories 12\n```\n\nFor reference: 1,195 photos took about 80 minutes to describe and 8 minutes to\nsort with `qwen3.6:35b` and `mistral-small3.2:24b` on an RTX 4090.\n\n- Photos grouped by category; the rail on the left shows counts and accepts drops\n- Move a photo by dragging it or with its category menu, which also offers the model's suggested categories\n- Rename, merge (rename to an existing name), create and remove categories; edit each category's rule\n- Trash with restore; move a whole category to Trash in one step\n- Full-size preview with the model's description; arrow keys browse the category\n- **Save categories** downloads the category list, with your edits, for future runs\n- **Export script** writes the organize script\n\nWriting captions requires [exiftool](https://exiftool.org). An existing caption is\nkept; a previous `Category:` part is replaced, so the script can be run again.\n\nCategories are saved in `~/.config/image-categorizer/categories.yaml` (override\nwith `CATEGORIES_FILE`) and reused for every library:\n\n```\ncategories:\n  - name: Portraits\n    rule: Photos of people looking at the camera, including selfies. Not group photos.\n    status: kept\n```\n\nThe first run builds the list from the photos. Later runs sort into the saved\nlist; only when enough photos fit none of the rules does the model propose a new\ncategory, marked `new` in the report until you save the list. `--max-categories`\ncaps the list: once it is full, photos that fit nothing go to `Unsorted`.\n\nWhen a category holds photos that belong elsewhere, click the re-sort button on that category and describe what went wrong.\n\n```\nuv run python main.py --resort ~/Downloads/resort.json\n```\n\nThe text model turns the note into rule changes and asks you to approve them,\nshows how 10 sample photos would move, then re-sorts only that category. Photos\nthat fit no other category stay put. Moved photos are marked **Re-sorted** in the\nrebuilt report, and edits made in the report before exporting are kept.\n\nSettings live in `.env`; [`.env.example`](https://github.com/DrBenedictPorkins/image-categorizer/blob/main/.env.example) lists them.\n\n| Variable | Purpose | \n|---|---|\n| `OLLAMA_HOST` | Ollama server, default `http://localhost:11434` | \n| `OLLAMA_MODEL` | Vision model for describing | \n| `OLLAMA_TEXT_MODEL` | Text model for sorting and re-sorting | \n| `CATEGORIES_FILE` | Saved category list | \n| `MAX_CATEGORIES` | Category cap | \n\nAll command-line options: `uv run python main.py --help` and\n[SETUP.md](https://github.com/DrBenedictPorkins/image-categorizer/blob/main/SETUP.md#12-reference). Supported formats: `.jpg`, `.jpeg`, `.png`,\n`.gif`, `.bmp`, `.webp`, `.heic`, `.heif`; videos are skipped. The report shows HEIC\nphotos through JPEG previews kept in a hidden `.image-categorizer-previews` folder.\n\n`--provider huggingface` runs BLIP-2, LLaVA or Flan-T5 in-process without Ollama\n([docs/HUGGINGFACE_PROVIDER.md](https://github.com/DrBenedictPorkins/image-categorizer/blob/main/docs/HUGGINGFACE_PROVIDER.md)), and\n`--categorization-provider keyword` sorts by keyword matching. Both predate the\nsaved categories and re-sort features, and are less maintained.\n\n| Path | Contents | \n|---|---|\n| `main.py` | Command line, workflows, re-sort | \n| `providers/ollama_provider.py` | Describing, sorting, category proposals, rule rewrites | \n| `core/categories.py` | Saved category list | \n| `core/html_generator.py` ,`template.html` | Report | \n| `models/image_data.py` | Result data model | \n| `SETUP.md` | Install and first run, written for AI coding agents | \n| `samples/` | Public-domain sample images ( [sources](https://github.com/DrBenedictPorkins/image-categorizer/blob/main/samples/README.md) ) | \n| `docs/` | Provider guides, dependency audit, design notes, README images | \n| `sundry/` | Archive of one-off scripts, not used by the app | \n\nMIT. See [LICENSE](https://github.com/DrBenedictPorkins/image-categorizer/blob/main/LICENSE).", "url": "https://wpnews.pro/news/show-hn-image-categorizer-sort-a-photo-library-with-local-vision-models", "canonical_source": "https://github.com/DrBenedictPorkins/image-categorizer", "published_at": "2026-09-29 23:17:25+00:00", "updated_at": "2026-09-29 23:47:55.912304+00:00", "lang": "en", "topics": ["ai-tools", "computer-vision", "ai-products", "artificial-intelligence"], "entities": ["Image Categorizer", "Ollama", "qwen3.6:35b", "mistral-small3.2:24b", "Claude Code", "uv", "RTX 4090", "GitHub"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-image-categorizer-sort-a-photo-library-with-local-vision-models", "markdown": "https://wpnews.pro/news/show-hn-image-categorizer-sort-a-photo-library-with-local-vision-models.md", "text": "https://wpnews.pro/news/show-hn-image-categorizer-sort-a-photo-library-with-local-vision-models.txt", "jsonld": "https://wpnews.pro/news/show-hn-image-categorizer-sort-a-photo-library-with-local-vision-models.jsonld"}}