Но подход, который я использовал требует значительной производительности и на больших коллекциях маленькая утилита разрастается достаточно большую программу со свое базой данных с интерфейсом и всеми прелестями программного проекта.
Более эффективно воспользоваться существующими инструментами, например мощной программой управления фотоколлекцией DigiKam.
Но штука в том, что DigiKam имеет очень мощный инструмент поиска дубликатов, не имеет возможности работы с дубликатами, кроме как мышкой и клавиатурой.
Я провел небольшое расследование и обнаружил, что результаты поиска дубликатов сохраняются в базе данных, которая обычно располагается в $HOME/Pictures/digikam4.db
Как DigiKam сохраняет результаты поиска я покажу на следующем примере.
Результаты поиска сохраняются в таблице searches. Эта таблица содержит строки с результатами поиска.
Каждая строка в поле query хранит xml список содержащий id одинаковых изображений. На приведенном листинге я отформатировал xml, чтобы удобней его было читать.
3 2 1
Имена файлов изображений хранятся в таблице images и доступны по id
Путь
Путь к альбомам хранится в таблице albumroot:
Ниже пример реализации функции на bash получения пути изображения с id = 5
#!/bin/bash DBPATH=digikam4.db getPicture () { FILENAME=$(sqlite3 $DBPATH "SELECT name FROM images WHERE id=$1") IDALBUM=$(sqlite3 $DBPATH "SELECT album FROM images WHERE id=$1") REALTIVEPATH=$(sqlite3 $DBPATH "SELECT relativePath FROM albums WHERE id=$IDALBUM") IDALBUMROOT=$(sqlite3 $DBPATH "SELECT albumRoot FROM albums WHERE id=$IDALBUM") ROOTPATH=$(sqlite3 $DBPATH "SELECT specificPath FROM albumroots WHERE id=$IDALBUMROOT") echo /home/${ROOTPATH}/${REALTIVEPATH}/${FILENAME} } FNAME=$(getPicture 5) echo $FNAME
Ну, собственно, вот и венец творений.
Программа на bash, которая удаляет дубликаты изображений.
Программа удаляет дубликат меньшего размера.
Я ее успешно использую совместно с DigiKam 2.8 под Ubuntu.
Перед запуском скрипта нужно выйти из DigiKam, иначе скрипт не может получить доступ к базе, которая лочится.
#!/bin/bash #------------------------------------------------------------------- # Удаляет дубликаты картинок на основании поиска дубликатов в # программе DigiKam # # Проверено с DigiKam v2.8 COPYRUGHT="\n© http://axa-ru.blogspot.com. Licensed under GPLv3." #------------------------------------------------------------------- #VERSION=0.1 # initial version #VERSION=0.11 # Some DeBugs #VERSION=0.12 # Добавлено приоритет удаления #VERSION=0.13 # Some changes VERSION=0.14 # Испрвавление ошибки в delSmallPic() DEBUG=0 VERBOSE=1 DBPATH=$HOME/Pictures/digikam4.db # Путь до базы даннх DigiKam EXITERROR=192 SAVEPIC="" PATTERN=0_WallPapers99 # Color ANSY sequence BLACK="\033[0;30m" RED="\033[0;31m" GREEN="\033[0;32m" YELLOW="\033[0;33m" BLUE="\033[0;34m" MAGENTA="\033[0;35m" CYAN="\033[0;36m" WHITE="\033[0;37m" GRAY1="\033[38;5;252m" GRAY2="\033[38;5;245m" GRAY3="\033[38;5;238m" NOCOLOR="\033[0;38m" #------------------------------------------------------------------- # Возвращает по id изображения его полный путь # пример использования: # FNAME=$(getPicture 1653) # возвращает путь к картинке №1653 getPicture () { FILENAME=$(sqlite3 $DBPATH "SELECT name FROM images WHERE id=$1") IDALBUM=$(sqlite3 $DBPATH "SELECT album FROM images WHERE id=$1") REALTIVEPATH=$(sqlite3 $DBPATH "SELECT relativePath FROM albums WHERE id=$IDALBUM") IDALBUMROOT=$(sqlite3 $DBPATH "SELECT albumRoot FROM albums WHERE id=$IDALBUM") local ALBUM_TYPE=$(sqlite3 $DBPATH "SELECT type FROM albumroots WHERE id=$IDALBUMROOT") case $ALBUM_TYPE in 1) ROOTPATH=/home$(sqlite3 $DBPATH "SELECT specificPath FROM albumroots WHERE id=$IDALBUMROOT");; 2) echo "External media not supported yet. Exiting"; exit $EXITERROR;; 3) ROOTPATH=$(sqlite3 $DBPATH "SELECT identifier FROM albumroots WHERE id=$IDALBUMROOT" | sed 's/networkshareid:?mountpath=//g' | sed 's/%2F/\//g');; esac echo ${ROOTPATH}/${REALTIVEPATH}/${FILENAME} } #------------------------------------------------------------------- # Удаляет меньшую картинку из двух # delSmallPic() { ((DEBUG)) && echo " ***** $FUNCNAME" if [ "$SAVEPIC" == "" ]; then #((DEBUG)) && echo " $LINENO: Init SAVEPIC" SAVEPIC="$1"; else local W0=$(identify -format '%w' "$SAVEPIC" 2> /dev/null) local H0=$(identify -format '%h' "$SAVEPIC" 2> /dev/null) local W1=$(identify -format '%w' "$1" 2> /dev/null) local H1=$(identify -format '%h' "$1" 2> /dev/null) ((DEBUG)) && echo " $LINENO: PIC1=$SAVEPIC" ((DEBUG)) && echo " $LINENO: PIC2=$1" ((DEBUG)) && echo " $LINENO: $W0 -eq $W1 -a $H0 -eq $H1" # Если картинки одинаковы то удаляет ту, в пути (имени) # которой есть $PATTERN if [ $W0 -eq $W1 -a $H0 -eq $H1 ]; then ((DEBUG)) && echo " $LINENO: PIC1 == PIC2" if [[ "$SAVEPIC" =~ .*$PATTERN.* ]]; then echo "Delete $SAVEPIC" ((DEBUG)) && printf " %s: " $LINENO ! ((DEBUG)) && rm "$SAVEPIC" SAVEPIC="$1" else ((DEBUG)) && printf " %s: " $LINENO echo "Delete $1" ! ((DEBUG)) && rm "$1" fi else if [ $W0 -le $W1 -o $H0 -le $H1 ]; then ((DEBUG)) && echo " $LINENO: PIC1 <= PIC2" echo "Delete $SAVEPIC" ((DEBUG)) && printf " %s: " $LINENO ! ((DEBUG)) && rm "$SAVEPIC" SAVEPIC="$1" else ((DEBUG)) && echo " $LINENO: PIC1 > PIC2" ((DEBUG)) && printf " %s: " $LINENO echo "Delete $1" ! ((DEBUG)) && rm "$1" fi fi fi } #------------------------------------------------------------------- # Удаляет дубликаты картинок из списка # В качестве параметра получает список id картинок с разделителями пробелами delDupPic () { while (( "$#" )); do local PAR=$1 PICURL=$(getPicture "$PAR") if [ ! -f $PICURL ]; then echo -e "${RED}File $PICURL das not exists.${NOCOLOR}" # echo -e "${RED}Use digiKam!!!${NOCOLOR}" # exit $EXITERROR fi ((DEBUG)) && echo " $LINENO: PAR=$PAR, PICURL=$PICURL" delSmallPic $PICURL shift done } #------------------------------------------------------------------- # main code # while getopts "Dd:hVv" SWITCH ; do case $SWITCH in D) DEBUG=1;; d) if [[ $OPTARG == "" ]]; then printf "$SCRIPT:$LINENO: must be argument\n"; exit $ERROR; else PATTERN=$OPTARG; fi;; h) echo -e "usage: ${CYAN}$0 [-OPTION]${NOCOLOR} OPTION is ${CYAN}D${NOCOLOR} - Debug On ${CYAN}d [PAT]${NOCOLOR} - Delete first pic by PATtern Example: deldup.sh -d 00_Picture will delete any picture /mnt/nas3d0/Pictures/Wallpaper/00_Pictures/Pictures_0123.jpg ${CYAN}h${NOCOLOR} - This help ${CYAN}v${NOCOLOR} - Verbose output, No output by default" exit 0 ;; V) echo -e "${CYAN}$0 Version $VERSION\n${COPYRUGHT}${NOCOLOR}"; exit 0;; v) VERBOSE=1;; *) printf "$SCRIPT:$LINENO: script error: unhandled argument\n"; exit $ERROR;; esac done ((DEBUG)) && echo "$LINENO: TOTAL=$TOTAL" # Type=7 - записи после операции поиска дубликатов X=$(sqlite3 $DBPATH "SELECT id FROM searches where type=7 LIMIT 1") TOTAL=$(sqlite3 $DBPATH "SELECT COUNT (*) FROM searches WHERE type=7") echo "$TOTAL Duplicate Images Will be Delete " ((LAST=TOTAL+X)) ((DEBUG)) && echo "$LINENO: X=$X, LAST=$LAST, TOTAL=$TOTAL" while [ $X -lt $LAST ] do SAVEPIC="" # Получаем список записей с дублированными картинками # Ниже показан отформатированный пример. # ## LISTEQPIC=$(sqlite3 $DBPATH "SELECT query FROM searches WHERE id=$X" | sed 's/<\/listitem>/,/g' | sed 's/# ## #5033 #201 #84132 #//g' | sed 's/.*oneof\">//' | sed 's/,<\/.*//' | sed 's/,/ /g' ) ((DEBUG)) && echo "$LINENO: X=$X, LISTEQPIC=$LISTEQPIC" delDupPic $LISTEQPIC ((X++)) done
По хорошему этот скрипт можно оформить в виде плагина в DigiKam и запускать оттуда. Будет функциональней и удобней. Но мне лень ковырять сам DigiKam. Если кто сделает такое добро, то отпишите.
Полезные ссылки: