Classto
Classto is a Python library for building lightweight, browser-based tools to manually classify images into custom categories - ideal for preparing datasets or sorting visual content.
With just a few lines of Python, Classto spins up a local web interface built on Flask and styled with Tailwind CSS to let you quickly review, label, and organize images - right from your browser.
Interface Previews
Classto in Light and Dark Mode
Features
- Three Operational Modes: Local Folder, Static URL, or Dynamic Database Hooks.
- One-Click Classification: Fast, keyboard-friendly browser UI.
- Flexible Data Management: Moves local files, logs to CSV, or streams directly from cloud/DB pipelines.
- Smart Suffixing: Optionally add unique filename suffixes to avoid naming conflicts.
- Dark Mode Toggle: Easy on the eyes during long labeling sessions.
Installation
You can install Classto via pip:
pip install classto
Quickstart
Classto adapts to your workflow. You can use it in exactly one of the following three modes at a time:
1. Local Folder Mode
Reads images from a local directory, moves them into per-label subfolders, and optionally creates a CSV log.
import classto as ct
labeler = ct.ImageLabeler(
classes=["Cat", "Dog"],
image_folder="images", # Mandatory path to your images
delete_button=True, # Shows a button to delete/skip images
suffix=True, # Add unique suffix to avoid conflicts
log_to_csv=True # Saves results to a local CSV file
)
labeler.launch()
Then open your browser at http://127.0.0.1:5000.
Local Folder Architecture
Place your images in a folder (e.g. images/) relative to your script:
project/
├── images/
│ ├── cat1.jpg
│ ├── cat2.jpg
│ ├── dog1.jpg
│ └── dog2.jpg
├── app.py
After classification, images are moved to:
project/
├── classified/
│ ├── Cat/
│ │ ├── cat1__K8dLs.jpg
│ │ └── cat2__a7JkL.jpg
│ ├── Dog/
│ │ ├── dog1__Xy4Tz.jpg
│ │ └── dog2__Zx9Pm.jpg
│ └── labels-20260522-163400Z.csv
2. Static URL Mode
Streams images directly from a list of web URLs. Classifications are tracked via a local CSV file.
import classto as ct
urls = [
"https://example.com/image1.jpg",
"https://example.com/image2.png"
]
labeler = ct.ImageLabeler(
classes=["Product", "Background Only"],
urls=urls, # Mandatory for URL Mode
log_to_csv=True, # Keeps track of URLs in a CSV file
shuffle=True
)
labeler.launch()
3. Dynamic Hook Mode (Database Integration)
Integrate seamlessly with databases (e.g., MongoDB, Cosmos DB) or cloud storages. Images are streamed on-demand via custom callback hooks without loading datasets into memory or enforcing local file moves.
import classto as ct
# Define your custom database connection logic
def my_next_hook():
# Fetch next document from DB. Must return {"id": str, "url": str} or None
doc = db.images.find_one({"labeled": False})
return {"id": str(doc["_id"]), "url": doc["image_url"]} if doc else None
def my_label_hook(image_id, label):
# Save the label back to your database
db.images.update_one({"_id": ObjectId(image_id)}, {"$set": {"label": label, "labeled": True}})
def my_stats_hook():
# Update live session progress badges in the UI
return {
"total_remaining": db.images.count_documents({"labeled": False}),
"total_labeled": db.images.count_documents({"labeled": True})
}
def my_delete_hook(image_id):
# Optional: Action when the delete/skip button is pressed
db.images.update_one({"_id": ObjectId(image_id)}, {"$set": {"is_deleted": True, "labeled": True}})
labeler = ct.ImageLabeler(
classes=["Valid", "Corrupted"],
delete_button=True,
on_next=my_next_hook, # Mandatory for Hook Mode
on_label=my_label_hook, # Mandatory for Hook Mode
on_get_stats=my_stats_hook, # Optional: Renders live stats UI badges
on_delete=my_delete_hook, # Mandatory if delete_button=True in Hook Mode
log_to_csv=False # Optional: Set to True for a parallel local CSV backup
)
labeler.launch()
Parameters
classes(List[str]): A list of categories for classification (e.g.["Dog", "Cat"]).image_folder(Optional[str]): Path to the folder containing local images. Required for Folder Mode. Defaults to None.urls(Optional[List[str]]): A list of image URLs to stream. Required for URL Mode. Defaults to None.delete_button(bool): IfTrue, shows a delete button to remove or skip images. Defaults toFalse.shuffle(bool): IfTrue, images are presented in a random order (applies to Folder and URL mode only). Defaults toFalse.suffix(bool): IfTrue, appends a random 5-character suffix to local filenames to prevent overwriting. Defaults toFalse.log_to_csv(bool): IfTrue, logs classifications into a local CSV file. Works as a primary tracker for URL mode or as a secondary backup across all modes. Defaults toFalse.log_path(Optional[str]): Custom directory path where the CSV log file should be written.log_file_name(Optional[str]): Custom file name for the CSV log. If omitted, a UTC-timestamped name is automatically generated.on_next(Optional[Callable]): Hook returning{"id": str, "url": str}orNone. Required for Hook Mode.on_label(Optional[Callable]): Hook accepting(image_id: str, label: str). Required for Hook Mode.on_get_stats(Optional[Callable]): Hook returning{"total_remaining": int, "total_labeled": int}to power the UI counter.on_delete(Optional[Callable]): Hook accepting(image_id: str). Required ifdelete_button=Truein Hook Mode.
CSV Logging Format
If log_to_csv=True is enabled, data is written into a CSV file containing the following structure:
| original_filename | new_filename | label | timestamp |
|---|---|---|---|
| img01.jpg | img01__4Fg7T.jpg | Cat | 2026-05-22T16:34:00+00:00 |
| img02.jpg | img02__8Hv2f.jpg | Dog | 2026-05-22T16:34:32+00:00 |
original_filename: The local filename (Folder Mode) or the custom database image_id (Hook Mode).new_filename: The new name after suffixing (Folder Mode) or the streamed image_url (Hook Mode/URL Mode).label: The category selected during classification (orDELETED).timestamp: Execution time in ISO 8601 format (UTC).
Metadata
Release files for classto 2.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| classto-2.1.0.tar.gz | 477.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| classto-2.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 950.9 kB
Release files / classto-2.1.0.tar.gz
| Download URL | classto-2.1.0.tar.gz |
|---|---|
| Size | 477.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
33b4aea9b72e8bd5484c1edc80e49a8e2b0e35a14e63c5bddcc5658408409f6e
|
|
BLAKE2b-256 checksum How to use checksums |
e6797ce0efe6c9e43c2552acf0017865419204cdba776a71f4a91c55d15979c6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.4
|
Release files / classto-2.1.0-py3-none-any.whl
| Download URL | classto-2.1.0-py3-none-any.whl |
|---|---|
| Size | 473.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5fd492243b519a0140bc62fffcc231a77a3fa5303c13289efdbec74598c9d26b
|
|
BLAKE2b-256 checksum How to use checksums |
1d8c16a8d9f034bb3f7651d3d51e2e0dc3b3ca10bfcf8a705e33119e94f6837b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.4
|