Но подход, который я использовал требует значительной производительности и на больших коллекциях маленькая утилита разрастается достаточно большую программу со свое базой данных с интерфейсом и всеми прелестями программного проекта.
Более эффективно воспользоваться существующими инструментами, например мощной программой управления фотоколлекцией 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=""
# Получаем список записей с дублированными картинками
# Ниже показан отформатированный пример.
#
#
#
#
# 5033
# 201
# 84132
#
#
#
LISTEQPIC=$(sqlite3 $DBPATH "SELECT query FROM searches WHERE id=$X" | sed 's/<\/listitem>/,/g' | sed 's///g' | sed 's/.*oneof\">//' | sed 's/,<\/.*//' | sed 's/,/ /g' )
((DEBUG)) && echo "$LINENO: X=$X, LISTEQPIC=$LISTEQPIC"
delDupPic $LISTEQPIC
((X++))
done
По хорошему этот скрипт можно оформить в виде плагина в DigiKam и запускать оттуда. Будет функциональней и удобней. Но мне лень ковырять сам DigiKam. Если кто сделает такое добро, то отпишите.
Полезные ссылки:




