Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

image-dedup

Rust

Прореживание похожих изображений в датасете по перцептивному хешу. Написано для подготовки выборок под обучение Stable Diffusion / WAN / FLUX.

Задача

В папке с картинками для обучения обычно много почти одинаковых кадров: соседние кадры из видео, серийная съёмка, слегка перекадрированные варианты одного снимка. Модель переобучается на такой многократно повторённый ракурс, а разнообразие датасета страдает.

Утилита находит наиболее похожие друг на друга изображения и удаляет их, оставляя по одному представителю от каждой группы дублей.

Как работает

Шаг 1 — хеширование. Каждый файл в директории открывается и прогоняется через перцептивный хеш (crate img_hash). Хеш — 64-битная подпись картинки, устойчивая к масштабированию, пережатию JPEG и мелким правкам: похожие изображения дают похожие хеши. Мера похожести — расстояние Хэмминга между хешами: 0 — хеши идентичны, 64 — противоположны. Хеши считаются один раз при загрузке.

Файлы, которые не удалось открыть как изображение, пропускаются с сообщением в stderr. Подпапки не обходятся.

Шаг 2 — жадное удаление. Пока не исчерпан лимит -n и находятся пары ближе порога -t:

  1. Выбирается кандидат на удаление (см. стратегии ниже).
  2. Считается расстояние от кандидата до ближайшего к нему изображения.
  3. Если это расстояние больше порога — похожих пар в наборе не осталось, работа заканчивается досрочно.
  4. Иначе файл удаляется, и следующая итерация пересчитывает картину уже без него.

Стратегии

--strategy Кандидат на удаление
pairwise (по умолчанию) изображение, у которого есть самый близкий двойник в наборе
central «самое среднее» изображение — с минимальной суммой расстояний до всех остальных

pairwise бьёт точечно по дублирующимся парам, оставляя второго участника пары. central вычищает центр масс датасета — полезно, когда надо не убрать дубли, а увеличить разброс выборки.

Порог -t в обеих стратегиях проверяется по расстоянию до ближайшего соседа: сумма расстояний в central не сравнима с порогом напрямую.

Установка и запуск

cargo build --release
image-dedup <директория> [ОПЦИИ]

ОПЦИИ:
    -n, --max-deletions <N>   максимум удаляемых файлов (по умолчанию 20)
    -t, --threshold <D>       максимальное расстояние Хэмминга, при котором пара
                              считается дублем (по умолчанию 10)
        --strategy <S>        pairwise (по умолчанию) | central
        --apply               действительно удалять файлы
    -h, --help                справка

Сухой прогон по умолчанию

Без --apply утилита ничего не удаляет — только печатает список файлов, которые были бы удалены, и в каком порядке. Удаление необратимо и в корзину не кладёт, так что сначала всегда смотрите сухой прогон:

image-dedup ./dataset -t 6          # посмотреть, что уйдёт
image-dedup ./dataset -t 6 --apply  # удалить

Как подбирать порог

Хеш 64-битный, осмысленный диапазон порога — примерно 0..20.

-t Что попадает под удаление
0 только изображения с полностью совпавшим хешем
2–5 пережатые/отмасштабированные копии одного кадра
6–10 соседние кадры видео, серийная съёмка
>12 начинают попадать просто похожие по композиции снимки

Начинайте с сухого прогона на -t 5 и поднимайте, глядя на выводимые расстояния.

-n — верхний предел, страховка от того, чтобы утилита не выкосила полдатасета за один заход. Если порог достигнут раньше лимита, работа останавливается сама.

Пример вывода

Хеширование файлов в ./dataset:
  1jbF0aHiSyE=  ./dataset/frame_001.png
  1jbF0aHiSyE=  ./dataset/frame_002.png
  fJRchAmDkxQ=  ./dataset/portrait.jpg
  пропущен (не изображение): ./dataset/notes.txt

Загружено изображений: 3, пропущено файлов: 1

Режим сухого прогона: файлы не удаляются. Для удаления добавьте --apply.

Стратегия: pairwise, порог: 10, лимит удалений: 20

Было бы удалено (расстояние 0): ./dataset/frame_001.png
Осталось 2 изображений, ближайшая пара на расстоянии 27 — это больше порога 10. Останавливаемся.

Итог: к удалению 1 файл(ов), осталось изображений: 2
Ничего не удалено — это был сухой прогон. Повторите с --apply.

Стек

Rust 2021, без CLI-фреймворков. Зависимости: img_hash 3.2 (хеширование), image 0.23.14 (декодирование).

Известные ограничения

Алгоритм хеширования — не pHash. HasherConfig::new() даёт алгоритм по умолчанию, HashAlg::Gradient (родственник dHash). Он быстрее pHash и хорошо ловит пережатие и масштабирование, но хуже переносит поворот и сильную коррекцию яркости. Настоящий pHash включается через .preproc_dct() в load_images().

Сложность O(N · n²). Матрица расстояний пересчитывается на каждой итерации удаления. Хеши, к счастью, считаются один раз. Для сотен и тысяч картинок это незаметно, для десятков тысяч при большом -n — ощутимо; выполнение однопоточное.

img_hash 3.2 больше не поддерживается (последний релиз ~2020) и тянет за собой устаревший image 0.23. Актуальный форк — image_hasher, он работает со свежим image.

Обход не рекурсивный — файлы берутся только из указанной директории, подпапки игнорируются.

About

Finds the images in the directory that are most similar to the others and deletes the N most similar ones. Use to remove similar images before training stable diffusion models.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages