Suggestion: rename photos in batches in a more specific way.
Hello, my name is Anderson.
This text was originally written
in Portuguese and translated to English
using an AI, as English is not my native language.
I hope it is understandable.
I want to contribute to DigiKam by suggesting a feature to rename photos based on sorting
algorithms like InsertionSort, QuickSort, MergeSort, etc.
We don’t always want to sort photos by date, resolution, or size. Sometimes we want to sort by
subjective values that only make sense personally.
For this reason, I created my own Python code for this task, as I often need to put photos in a
specific, personalized order.
I hope you can review my Python code (the only language I know) and, if you find it useful,
consider adapting it to DigiKam.
I hope I am not breaking any rules and that this can help someone.
Thank you very much!
Below is my Python code. Comments were added by AI to help understanding.
python code
Code Commented
# Sorting by pictures
import os # Importing the os module for interacting with the operating system
from PIL import Image # Importing the Image class from the PIL library for image processing
import tkinter as tk # Importing tkinter for creating GUI applications
from tkinter import filedialog # Importing filedialog for file selection dialogs
from PIL import ImageTk # Importing ImageTk for displaying images in tkinter
def buscar_imagens(diretorio):
imagens = [] # Initializing an empty list to store image paths
for arquivo in os.listdir(diretorio): # Iterating through files in the specified directory
if arquivo.lower().endswith(('.jpg', '.jpeg', '.png')): # Checking if the file is an image
imagens.append(os.path.join(diretorio, arquivo)) # Adding the image path to the list
return imagens # Returning the list of image paths
def mostrar_imagens_comparacao(img1_path, img2_path):
janela = tk.Tk() # Creating a new tkinter window
janela.title("Comparar Imagens") # Setting the window title
img1 = Image.open(img1_path) # Opening the first image
img2 = Image.open(img2_path) # Opening the second image
img1 = img1.resize((600, 600)) # Resizing the first image to 600x600 pixels
img2 = img2.resize((600, 600)) # Resizing the second image to 600x600 pixels
tk_img1 = ImageTk.PhotoImage(img1) # Converting the first image to a format suitable for tkinter
tk_img2 = ImageTk.PhotoImage(img2) # Converting the second image to a format suitable for tkinter
lbl1 = tk.Label(janela, image=tk_img1) # Creating a label for the first image
lbl1.grid(row=0, column=0, padx=10, pady=10) # Placing the first image label in the grid
lbl2 = tk.Label(janela, image=tk_img2) # Creating a label for the second image
lbl2.grid(row=0, column=1, padx=10, pady=10) # Placing the second image label in the grid
lbl1.image = tk_img1 # Keeping a reference to the first image to prevent garbage collection
lbl2.image = tk_img2 # Keeping a reference to the second image to prevent garbage collection
resultado = [0] # Initializing a list to store the user's choice
def escolher_1(): # Function to handle the selection of the first image
resultado[0] = 1 # Setting the result to indicate the first image was chosen
janela.destroy() # Closing the window
def escolher_2(): # Function to handle the selection of the second image
resultado[0] = 2 # Setting the result to indicate the second image was chosen
janela.destroy() # Closing the window
btn1 = tk.Button(janela, text="Escolher Imagem 1", command=escolher_1) # Button for selecting the first image
btn1.grid(row=1, column=0, pady=10) # Placing the button in the grid
btn2 = tk.Button(janela, text="Escolher Imagem 2", command=escolher_2) # Button for selecting the second image
btn2.grid(row=1, column=1, pady=10) # Placing the button in the grid
janela.mainloop() # Starting the tkinter event loop
return resultado[0] # Returning the user's choice
# --- Rotate Merge Sort adapted for interactive comparisons ---
def rotate(arr, start, mid, end):
def rev(a, l, r): # Helper function to reverse a portion of the array
i, j = l, r - 1 # Initializing pointers for the reversal
while i < j: # While the pointers do not cross
a[i], a[j] = a[j], a[i] # Swapping elements
i += 1 # Moving the left pointer to the right
j -= 1 # Moving the right pointer to the left
rev(arr, start, mid) # Reversing the first half
rev(arr, mid, end) # Reversing the second half
rev(arr, start, end) # Reversing the entire segment
def binary_search_left_imagens(arr, start, end, value_path):
"""Finds the first position in [start, end) where arr[pos] >= value using visual comparisons.
Returns the index pos such that arr[pos] is the first image the user considers >= value.
"""
lo, hi = start, end # Initializing low and high pointers for binary search
while lo < hi: # While the search space is valid
m = (lo + hi) // 2 # Finding the middle index
resposta = mostrar_imagens_comparacao(arr[m], value_path) # Comparing the middle image with the value
# If the user prefers arr[m] (resposta==1), we consider arr[m] <= value_path (arr[m] 'comes before' or equal)
# We need to decide direction: treated here as arr[m] < value => lo = m+1
if resposta == 1: # If the user prefers the middle image
lo = m + 1 # Search to the right
else: # If the user prefers the value path
hi = m # Search to the left
return lo # Returning the position found
def inplace_merge_imagens(arr, start, mid, end):
if start >= mid or mid >= end: # If the indices are invalid, return
return
if (mid - start) + (end - mid) <= 16: # If the segment is small enough, use a simple insertion sort
i = start # Pointer for the left segment
j = mid # Pointer for the right segment
while i < j and j < end: # While both pointers are valid
resposta = mostrar_imagens_comparacao(arr[i], arr[j]) # Comparing the images
if resposta == 1: # If the user prefers the left image
i += 1 # Move the left pointer
else: # If the user prefers the right image
val = arr[j] # Storing the value to be inserted
k = j # Pointer for shifting elements
while k > i: # Shifting elements to the right
arr[k] = arr[k-1] # Moving elements
k -= 1 # Moving the pointer
arr[i] = val # Inserting the value
i += 1 # Moving the left pointer
j += 1 # Moving the right pointer
return # Returning after merging
left_mid = (start + mid) // 2 # Finding the middle of the left segment
# Finding the position in the right segment for arr[left_mid]
right_pos = binary_search_left_imagens(arr, mid, end, arr[left_mid]) # Performing binary search
rotate(arr, left_mid, mid, right_pos) # Rotating the segments
new_mid = left_mid + (right_pos - mid) # Calculating the new middle index
inplace_merge_imagens(arr, start, left_mid, new_mid) # Merging the left segment
inplace_merge_imagens(arr, new_mid, right_pos, end) # Merging the right segment
def rotate_mergesort_imagens(arr, start=0, end=None):
if end is None: # If end is not specified, set it to the length of the array
end = len(arr)
if end - start <= 1: # If the segment is too small, return
return
mid = (start + end) // 2 # Finding the middle index
rotate_mergesort_imagens(arr, start, mid) # Recursively sorting the left segment
rotate_mergesort_imagens(arr, mid, end) # Recursively sorting the right segment
inplace_merge_imagens(arr, start, mid, end) # Merging the two sorted segments
return arr # Returning the sorted array
# --- end Rotate Merge Sort ---
# Part of Tim
def insertion_sort_run(arr, left, right):
for i in range(left + 1, right + 1): # Iterating through the segment
chave = arr[i] # Storing the current value
j = i - 1 # Pointer for the sorted segment
while j >= left: # While the pointer is valid
res = mostrar_imagens_comparacao(arr[j], chave) # Comparing the images
if res == 1: # If the user prefers the current value
break # Break the loop
else: # If the user prefers the image at j
arr[j + 1] = arr[j] # Shifting the element to the right
j -= 1 # Moving the pointer
arr[j + 1] = chave # Inserting the current value in the correct position
# Part of Timsort
def merge_timsort(arr, left, mid, right):
left_run = arr[left:mid + 1] # Creating a left run
right_run = arr[mid + 1:right + 1] # Creating a right run
i = j = 0 # Initializing pointers for both runs
k = left # Pointer for the main array
while i < len(left_run) and j < len(right_run): # While both runs have elements
res = mostrar_imagens_comparacao(left_run[i], right_run[j]) # Comparing the images
if res == 1: # If the user prefers the left run
arr[k] = left_run[i] # Adding the left run element to the main array
i += 1 # Moving the left pointer
else: # If the user prefers the right run
arr[k] = right_run[j] # Adding the right run element to the main array
j += 1 # Moving the right pointer
k += 1 # Moving the main array pointer
while i < len(left_run): # If there are remaining elements in the left run
arr[k] = left_run[i] # Adding the remaining elements to the main array
i += 1 # Moving the left pointer
k += 1 # Moving the main array pointer
while j < len(right_run): # If there are remaining elements in the right run
arr[k] = right_run[j] # Adding the remaining elements to the main array
j += 1 # Moving the right pointer
k += 1 # Moving the main array pointer
# Tim Sort - Main
def timsort_adaptativo(arr):
n = len(arr) # Getting the length of the array
min_run = 4 # Setting the minimum run size
for start in range(0, n, min_run): # Iterating through the array in chunks
end = min(start + min_run - 1, n - 1) # Defining the end of the current run
insertion_sort_run(arr, start, end) # Sorting the current run
size = min_run # Initializing the size for merging
while size < n: # While the size is less than the array length
for left in range(0, n, 2 * size): # Iterating through the array in pairs of runs
mid = min(n - 1, left + size - 1) # Finding the middle index
right = min((left + 2 * size - 1), (n - 1)) # Finding the right index
if mid < right: # If the runs are valid
merge_timsort(arr, left, mid, right) # Merging the runs
size *= 2 # Doubling the size for the next iteration
return arr # Returning the sorted array
# Binary Insertion Sort
def binary_insertion_sort_imagens(arr):
size = len(arr) # Getting the length of the array
for i in range(1, size): # Iterating through the array
chave = arr[i] # Storing the current value
left = 0 # Initializing the left pointer
right = i # Initializing the right pointer
while left < right: # While the pointers are valid
meio = (left + right) // 2 # Finding the middle index
res = mostrar_imagens_comparacao(arr[meio], chave) # Comparing the images
if res == 1: # If the user prefers the middle image
left = meio + 1 # Searching to the right
else: # If the user prefers the current value
right = meio # Searching to the left
arr[left+1:i+1] = arr[left:i] # Shifting elements to the right
arr[left] = chave # Inserting the current value in the correct position
return arr # Returning the sorted array
# Part of Merge Sort
def merge(left, right):
resultado = [] # Initializing an empty list for the merged result
i = j = 0 # Initializing pointers for both lists
while i < len(left) and j < len(right): # While both lists have elements
resposta = mostrar_imagens_comparacao(left[i], right[j]) # Comparing the images
if resposta == 1: # If the user prefers the left image
resultado.append(left[i]) # Adding the left image to the result
i += 1 # Moving the left pointer
else: # If the user prefers the right image
resultado.append(right[j]) # Adding the right image to the result
j += 1 # Moving the right pointer
resultado.extend(left[i:]) # Adding any remaining elements from the left list
resultado.extend(right[j:]) # Adding any remaining elements from the right list
return resultado # Returning the merged result
# Mergesort - main
def merge_sort_imagens(imagens):
if len(imagens) <= 1: # If the list has one or no elements, return it
return imagens
meio = len(imagens) // 2 # Finding the middle index
left = imagens[:meio] # Splitting the list into left half
right = imagens[meio:] # Splitting the list into right half
left = merge_sort_imagens(left) # Recursively sorting the left half
right = merge_sort_imagens(right) # Recursively sorting the right half
return merge(left, right) # Merging the sorted halves
# Quick Sort
def quick_sort_imagens(imagens):
size = len(imagens) # Getting the length of the list
if size <= 1: # If the list has one or no elements, return it
return imagens
else:
less = [] # List for elements less than the pivot
meio = [] # List for elements equal to the pivot
more = [] # List for elements greater than the pivot
num = size * 6 // 10 # Choosing a pivot index (60% of the list)
pivo = imagens[num] # Selecting the pivot element
for cada in imagens: # Iterating through the list
if cada == pivo: # If the element is equal to the pivot
meio.append(cada) # Adding it to the middle list
else:
resposta = mostrar_imagens_comparacao(cada, pivo) # Comparing the element with the pivot
if resposta == 1: # If the user prefers the current element
less.append(cada) # Adding it to the less list
else:
more.append(cada) # Adding it to the more list
return quick_sort_imagens(less) + meio + quick_sort_imagens(more) # Returning the sorted list
# End of sorting blocks
def processamento(imagens, algoritmo):
if algoritmo == 1: # If the user chose Merge Sort
return merge_sort_imagens(imagens.copy()) # Sorting and returning the images
elif algoritmo == 2: # If the user chose Quick Sort
return quick_sort_imagens(imagens.copy()) # Sorting and returning the images
elif algoritmo == 3: # If the user chose Binary Insertion Sort
return binary_insertion_sort_imagens(imagens.copy()) # Sorting and returning the images
elif algoritmo == 4: # If the user chose Adaptive Tim Sort
return timsort_adaptativo(imagens.copy()) # Sorting and returning the images
elif algoritmo == 5: # If the user chose Rotate Merge Sort
return rotate_mergesort_imagens(imagens.copy()) # Sorting and returning the images
# Functions for output and user interaction remain
def saida_renomeado(imagens_ordenadas):
for i, img_path in enumerate(imagens_ordenadas): # Iterating through the sorted images
dir_atual = os.path.dirname(img_path) # Getting the directory of the image
nome_arquivo_original = os.path.basename(img_path) # Getting the original file name
nome_novo = f"{i+1:03d}_{nome_arquivo_original}" # Creating a new file name with a prefix
novo_caminho = os.path.join(dir_atual, nome_novo) # Constructing the new file path
if not os.path.exists(novo_caminho): # If the new file does not exist
os.rename(img_path, novo_caminho) # Renaming the file
else: # If the new file already exists
print(f"Arquivo já existe: {novo_caminho}. Renomeando com sufixo extra.") # Informing the user
novo_caminho = os.path.join(dir_atual, f"{i+1:03d}_dup_{nome_arquivo_original}") # Creating a new name with a suffix
os.rename(img_path, novo_caminho) # Renaming the file
def pergunta_algoritmo():
while True: # Loop until a valid input is received
texto = "Algoritmo de ordenação:\n" # Prompting the user for sorting algorithm
# texto += "1. Merge Sort\n2. Quick Sort\n3. Binary Insertion Sort\n4. Timsort Adaptativo\n"
texto += "1: Merge Sort.\n" # Adding Merge Sort option
texto += "2: Quick Sort.\n" # Adding Quick Sort option
texto += "3: Binary Insertion Sort.\n" # Adding Binary Insertion Sort option
texto += "4: Tim Sort.\n" # Adding Tim Sort option
texto += "5: Rotate Merge Sort.\n" # Adding Rotate Merge Sort option
print(texto) # Displaying the options
res = input("Digite: ") # Getting user input
try:
res = int(res) # Converting input to integer
if res in [1, 2, 3, 4, 5]: # Checking if the input is valid
break # Breaking the loop if valid
else:
print("Digite 1 até 5: ") # Prompting for valid input
except ValueError: # Handling non-integer input
print("Digite um número válido") # Prompting for valid input
return res # Returning the selected algorithm
def entrada():
diretorio = filedialog.askdirectory(title="Selecione a pasta com as imagens") # Prompting user to select a directory
if not diretorio: # If no directory is selected
return None, None # Returning None values
imagens = buscar_imagens(diretorio) # Fetching images from the selected directory
print(f"Encontradas {len(imagens)} imagens") # Informing the user of the number of images found
return imagens, diretorio # Returning the list of images and the directory
if __name__ == "__main__": # Checking if the script is run directly
op = pergunta_algoritmo() # Asking the user for the sorting algorithm
imagens, diretorio = entrada() # Getting the images and directory
if not imagens: # If no images were found
print("Nenhuma imagem encontrada ou operação cancelada") # Informing the user
exit() # Exiting the program
imagens_ordenadas = processamento(imagens, op) # Processing the images with the selected algorithm
saida_renomeado(imagens_ordenadas) # Renaming the sorted images