Cullergrader
Cullergrader is a simple Java GUI made for photographers that groups and exports images based on perceptual similarity (and timestamps), allowing users to select the best shots from each set of similar, consecutively taken photos.
Install / Use
/learn @PenguinPush/CullergraderREADME
Cullergrader
Cullergrader is a simple Java GUI made for photographers that groups and exports images based on <a href="https://en.wikipedia.org/wiki/Perceptual_hashing" target="_blank">perceptual similarity</a> (and timestamps), allowing users to select the best shots from each set of similar, consecutively taken photos.
Like many photographers, I have the habit of taking the same shot multiple times and selecting the best one to keep. However, when going through thousands of photos, this process of culling images is time-consuming, and tools such as <a href="https://github.com/qarmin/czkawka" target="_blank">Czkawka</a> (a large inspiration for this project) can detect a few very similar images, but isn't sensitive enough to group somewhat similar bursts.
Cullergrader is named for being a tool that culls and grades* photos. Please note that it doesn't actually colour grade photos.
<sub>* grading photos has yet to be implemented...</sub>
<div style="display: flex; justify-content: center; align-items: center; gap: 20px;"> <img src="images/exampleA.JPG" alt="DSC07442.JPG" width="300"/> <img src="images/exampleB.JPG" alt="DSC07443.JPG" width="300"/> </div> <p align="left">For example: Cullergrader would mark these two images as "similar" (at similarity threshold >= 37%)</p>Table of Contents
Features
- 100% free and open-source!
- Configurable options for calibrating your perceptual hash
- Hash similarity
- Timestamp difference
- Exports the best takes to any folder on your computer
- Runs on Windows, Mac, Linux, and anything else that supports Java GUIs
- Blazingly-fast thanks to configurable multithreading support
- Caches images -- future scans should be incredibly fast!
- Extra information about images available on hover
- Runs completely offline, and never connects to the internet
- Logs information to .txt files
- Light/Dark themes from FlatLaf
- Configurable:
- Multithreading
- Hashing settings
- Cache options
- Grouping settings
- Dark theme

Installation
Prebuilt Executable
- Cullergrader requires a Java 8 JRE or newer to run
- A prebuilt executable
.jarwith all libraries bundled is available for download at GitHub Releases - Extract the
.zipfile to any folder and runcullergrader-<version>.jarfile to begin using Cullergrader
Note:
- If you want to view logs in console, please use
run.batorrun.shdepending on your operating system - Using console is highly recommended for large batches of images (or on slow drives), as there is no other way to monitor slow hashing progress!
Compiling from Source
- Ensure you have the following installed:
- Java Development Kit (JDK) 8 or newer
- Apache Maven
- Clone the repository with:
git clone https://github.com/PenguinPush/cullergrader.git cd cullergrader - Build the project with the following command:
mvn clean install - Extract the generated
cullergrader-version.zipand run as described above
How to Use
1. Open a Folder of Images
Folders can be opened from File > Open Folder or with Ctrl + O. The first time images are computed, it may take a few minutes (but often less) to hash the images, depending on image count and disk speed. Hashes are cached for future use in cache.json, so as long as this file stays intact, future computations of the same images will be nearly instant.

2. Calibrate Grouping Settings
Although the default settings are designed to work fine out of the box, depending on many factors in your photo, and your style of photography, manual calibration is often recommended. By adjusting the timestamp and similarity thresholds and hitting Reload Groups, you can adjust how your images are grouped until the grouping behaves as expected.
Timestamp Thresholdis the amount of seconds between two photos before it counts it as no longer a part of the same photo group, and creates a new groupSimilarity Thresholdis the percentage of similarity between the hash of two photos. A higher threshold means more tolerance for less similar photos to be in the same group.

3. View Photos and Select Best Takes
By clicking on a photo, users can access the Photo Viewer, bringing up all individual photos in a group, with the best take marked by a star (which by default is the first image in group). By navigating using either mouse or arrow keys (left and right to move between photos, up and down to move between groups) to a photo, they can use the spacebar or Controls > Set Best Take to change a photo to the best take.

Tip: by hovering on a photo in the photo viewer, you can view its name, seconds between the last photo, and similarity % to the last photo. Use this information to help you calibrate the grouper.

4. Export Your Best Takes
Best takes can be exported to a folder using File > Export Best Takes or with Ctrl + S. After choosing an export folder, the selected best takes will begin copying to that folder!

Config
Default Config
{
"DARK_THEME": true,
"CACHE_FILE": "hashes.json",
"LOG_DIRECTORY": "logs",
"DEFAULT_FOLDER_PATH": "",
"HASHING_ENABLED": true,
"EXECUTOR_TIMEOUT_MINUTES": 60,
"MAX_THREADS_RATIO": 2,
"HASHED_WIDTH": 8,
"HASHED_HEIGHT": 8,
"TIME_THRESHOLD_SECONDS": 15,
"SIMILARITY_THRESHOLD_PERCENT": 45
}
Config Settings Explained
| Setting | Description | Variable Type |
|--------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|
| DARK_THEME | Toggles the dark and light FlatLaf themes. On by default (obviously) | boolean |
| CACHE_FILE | The name of the file used to store hash caches. Passed as a pathname in a File constructor | String |
| LOG_DIRECTORY | The name of the folder used to store logs. Passed as a pathname in a File constructor | String |
| DEFAULT_FOLDER_PATH | The default folder opened when importing images. Passed as a pathname in a File constructor. When empty, uses the systems user.home property | String |
| HASHING_ENABLED | Whether or not hashing is used in grouping photos | boolean |
| EXECUTOR_TIMEOUT_MINUTES | The amount of time, in minutes, before the hash manager times out and stops hashing photos | int |
| MAX_THREADS_RATIO | The fraction of CPU threads the hasher is allowed to multithread. 2 means half your threads, 3 means a third, etc. | int |
| HASHED_WIDTH | The width that images are computed at before hashing, higher values mean more accurate similarity checks at the cost of performance | int |
| HASHED_HEIGHT | The height that images are computed at before hashing, higher values mean more accurate similarity checks at the cost of performance | int |
| TIME_THRESHOLD_SECONDS | The default amount of seconds between photos (from the timestamp) before they're counted as a new group. Editable in-app, but will not change the default stored here | float |
| SIMILARITY_THRESHOLD_PERCENT | The default similarit
Related Skills
node-connect
347.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
108.0kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
347.2kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
347.2kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
