Cookie Consent by Free Privacy Policy Generator

AI-Powered Image Renamer for Automated Content-Based File Naming

This AI-Powered Image Renamer Python script renames image files based on their contents. It works by first encoding each image into Base64 format and then sending the encoded image to the newer and cheaper OpenAI GPT-4o-mini API. The API analyses the image, reconstructs its features, and identifies key details.

image-renamer-report.jpg


Based on specific prompts, the API carefully selects a new name for the file that accurately reflects the object's attributes, such as its colour, style, type, and material. In the example below, I applied this technique to a batch of over 300 images of furniture.

Code:
# AI-Powered Image Renamer
import os
import pandas as pd
import shutil
import time
import logging
import base64
import requests
import re

# OpenAI API Key (insert your actual API key)
api_key = 'sk-'

# Setup terminal logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
)

# Function to encode the image to base64
def encode_image(image_path):
    try:
        with open(image_path, "rb") as image_file:
            return base64.b64encode(image_file.read()).decode('utf-8')
    except Exception as e:
        logging.error(f"Failed to encode image {image_path}: {e}")
        return None

# Function to calculate API tokens used from response
def calculate_api_tokens(response_json):
    if 'usage' in response_json:
        return response_json['usage'].get('total_tokens', 0)
    return 0

# Step 1: Load Images from Folder and Subfolders
def load_images_from_folder(folder):
    image_data = []
    logging.info(f"Scanning root folder: {folder}")  # Log the root folder being scanned

    for root, dirs, files in os.walk(folder):
        logging.info(f"Checking directory: {root}")  # Log each directory being scanned
        logging.info(f"Found files: {files}")        # Log all the files found in the directory

        for file in files:
            if file.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp')):
                image_path = os.path.join(root, file)
                image_size = os.path.getsize(image_path)
                logging.info(f"Found image: {image_path}")  # Log each image found

                image_data.append({
                    'image_name': file,
                    'image_path': image_path,
                    'image_size': image_size
                })
            else:
                logging.info(f"Skipping non-image file: {file}")  # Log non-image files being skipped

    return pd.DataFrame(image_data)

# Step 2: Analyze Image and Generate Descriptive Name
def analyze_image_and_generate_name(image_path):
    base64_image = encode_image(image_path)
    if base64_image is None:
        return None, 0

    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {api_key}"
    }

    prompt = (
        "Describe the furniture in the image with the following details: colour, style, type "
        "(e.g., sofa, corner sofa, sectional sofa, sofabed, swivel chair, recliner, mattress), and material if it is leather. "
        "Respond in the format: colour style type. "
        "If the image is primarily a photo of a price card, respond with 'price card'."
    )

    payload = {
        "model": "gpt-4o-mini",
        "messages": [
            {
                "role": "user",
                "content": [
                    {"type": "text", "text": prompt},
                    {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}}
                ]
            }
        ],
        "max_tokens": 100
    }

    try:
        response = requests.post("https://api.openai.com/v1/chat/completions", headers=headers, json=payload)
        response_json = response.json()

        # Calculate the API tokens used for this request
        api_tokens_used = calculate_api_tokens(response_json)

        if 'choices' in response_json:
            message_content = response_json['choices'][0]['message']['content'].strip()

            if "price card" in message_content.lower():
                logging.info(f"Skipped image {image_path} because it appears to be a price card.")
                return None, api_tokens_used

            # Simplified parsing: take the first relevant words as colour, style, type
            words = message_content.lower().split()
            leather = "leather" if "leather" in words else ""

            if len(words) >= 3:
                colour = words[0]
                style = words[1]
                type_ = "_".join(words[2:])
                if colour == "gray":
                    colour = "grey"
                if "loveseat" in type_:
                    type_ = type_.replace("loveseat", "two_seater_sofa")

                description = f"{colour}_{style}_{leather}_{type_}".strip("_")
                description = remove_repetitions(description)
                description = sanitize_filename(description[:90])
                logging.info(f"Analyzed {image_path}: Generated name '{description}'")
                return description, api_tokens_used
            else:
                logging.warning(f"Could not extract expected details from: {message_content}")
                return None, api_tokens_used
        else:
            logging.error(f"Unexpected API response: {response_json}")
            return None, api_tokens_used

    except Exception as e:
        logging.error(f"Failed to analyze {image_path}: {e}")
        return None, 0

# Step 3: Rename and Save Images and Generate CSV Report
def rename_and_save_images(df, save_folder, report_file):
    report_data = []

    if not os.path.exists(save_folder):
        os.makedirs(save_folder)

    for _, row in df.iterrows():
        original_name = row['image_name']
        original_path = row['image_path']
        original_size = row['image_size']

        new_name, api_tokens_used = analyze_image_and_generate_name(original_path)
        if new_name:
            extension = os.path.splitext(original_name)[1]
            new_image_name = f"{new_name}{extension}"
            new_image_path = os.path.join(save_folder, new_image_name)
            
            # Convert underscores to spaces for the "Text Only" version
            new_name_text_only = new_name.replace('_', ' ').title()  # Capitalise first letter of each word

            # Handle potential name collisions
            counter = 1
            while os.path.exists(new_image_path):
                new_image_name = f"{new_name}_{counter}{extension}"
                new_image_path = os.path.join(save_folder, new_image_name)
                counter += 1

            shutil.copy(original_path, new_image_path)
            logging.info(f"Saved: {new_image_path}")

            # Append details to report data
            report_data.append({
                'Original Name': original_name,
                'Original Path': original_path,
                'File Size (Bytes)': original_size,
                'New Name': new_image_name,
                'New Path': new_image_path,
                'New Name (Text Only)': new_name_text_only,
                'API Tokens Used': api_tokens_used
            })

        # Add a 2-second delay between API requests
        time.sleep(2)

    # Save the report as CSV
    report_df = pd.DataFrame(report_data)
    report_df.to_csv(report_file, index=False)
    logging.info(f"Report saved: {report_file}")

def remove_repetitions(text):
    """Remove consecutive duplicate phrases in the text."""
    words = text.split('_')
    deduplicated_words = []
    previous_word = None
    for word in words:
        if word != previous_word:
            deduplicated_words.append(word)
            previous_word = word
    return '_'.join(deduplicated_words)

def sanitize_filename(name):
    """Sanitize the filename for safe usage across different filesystems."""
    sanitized_name = "".join(c if c.isalnum() or c in "._-" else "_" for c in name)
    sanitized_name = re.sub(r'_+', '_', sanitized_name)
    return sanitized_name

# Main function to orchestrate the steps
def main():
    # Make sure this path is correctly set to the directory where your images are located
    root_folder = r'C:\Users\chris\Image Renamer'  
    save_folder = os.path.join(root_folder, 'renamed_images')
    report_file = os.path.join(root_folder, 'image_rename_report.csv')

    logging.info(f"Root folder set to: {root_folder}")
    logging.info(f"Save folder set to: {save_folder}")
    logging.info(f"Report file set to: {report_file}")

    # Step 1: Load Images
    df = load_images_from_folder(root_folder)
    logging.info(f"Loaded {len(df)} images.")

    # Step 3: Rename, Save, and Generate Report
    if not df.empty:
        rename_and_save_images(df, save_folder, report_file)
        logging.info("All images processed and saved.")
    else:
        logging.info("No images found to process.")

if __name__ == "__main__":
    main()

Steps to Run:​

  1. Save the Script: Save this updated script in the same directory as your image files.
  2. Run the Script: Execute the script using the Python command in your terminal.

Here’s a quick rundown of how it all works:​

The AI-powered Image Renamer script simplifies the process of organising and cataloguing large collections of images. Instead of manually inspecting and renaming each file, this script automates the process, applying clear, descriptive filenames that are both user-friendly and optimised for searchability.

One key benefit is that the script allows for prompts to be refined, enabling you to focus on specific attributes of the image. This ensures that the generated filenames are as detailed and accurate as possible. For example, the new filenames can easily be used as the image title and alt text, enhancing both image search SEO and compliance with web accessibility best practices.

  • Loading the Images:
    The script scans the specified folder and its subfolders for image files (such as .png, .jpg, .jpeg, .gif, .bmp, and .webp). It logs each file's name and path and stores this data in a pandas DataFrame, making it easy to process and track the images.
  • Analysing the Images:
    Each image is converted into a base64 format and sent along with a refinable prompt to the OpenAI GPT-4o-mini API (remember to include your API key). The API responds with a description detailing attributes such as the furniture’s colour, style, type, and whether it’s made of leather. This description is used to generate a meaningful filename, typically including the item’s colour, material, and type (e.g., grey_modern_sofa_leather).
  • Generating Filenames:
    The script cleans up the generated filenames by removing any repetitive phrases and replacing invalid characters. If the filename exceeds the allowable length, it automatically truncates it, ensuring that the filenames remain neat and usable.
  • Renaming and Saving:
    The renamed images are saved in a new folder, avoiding any risk of overwriting the original files. A CSV report is produced for you at the end to help with matching the old file name against the new file name.
  • Execution:
    The main() function ties everything together, managing the image loading, analysis, renaming, and saving processes. Once the script completes, all renamed files and a CSV report with their details are saved in the designated folder for easy access.

Feel free to improve the script to suit your needs. Let me know how you get on with it, or comment below if you need help. I'm drafting a V2 of the AI-powered image to rename the script and scale it bigger to help with the 2025 web accessibility EU compliance laws and make it useful in migrations.
 

Attachments

  • pythons23.jpg
    pythons23.jpg
    106.2 KB · Views: 324
Last edited:
Back
Top