Thanks Matteius, been waiting to see my son who does python, he got it working as I could not get windows folders syntax correct and he redirected output to outside the Tonfotos folder.
I had trouble with some filenames with ' eg Men’s Shed had to rename files and remove ' from ini file and some images that had been rotated did not work with padding
FYI My sons mod below
import json
import os
import sys
from PIL import Image
from pathlib import Path
###
#“D:\Tonfoto pics\Mens Shed\2019 pics\” –image-padding=10%
. –image-padding=60%
save to folder and run from there, will create folder extracted faces
Helper function to parse the padding argument
def parse_padding(padding_str):
if padding_str.endswith(‘px’):
return float(padding_str[:-2]), ‘px’
elif padding_str.endswith(‘%’):
return float(padding_str[:-1]), ‘%’
else:
return 0, ‘px’ # Default to 0px if parsing fails or not provided
Helper function to adjust the bounds with padding, ensuring they remain within the image
def adjust_bounds(value, max_value, padding, is_percentage=False):
if is_percentage:
padding = (padding / 100.0) * max_value
new_value = max(0, min(value + padding, max_value))
return new_value, padding - (new_value - value)
Parsing command-line arguments for directory and optional image padding
directory_path = sys.argv[1] if len(sys.argv) > 1 else ‘.’
padding_str = ‘0px’ # Default padding
for arg in sys.argv[2:]:
if arg.startswith(‘–image-padding=’):
padding_str = arg.split(‘=’)[1]
padding, padding_type = parse_padding(padding_str)
ini_file_path = os.path.join(directory_path, ‘.tonfotos.ini’)
#ini_file_path = ‘.tonfotos14.ini’
print(ini_file_path)
Load the tonfotos.ini file
with open(ini_file_path, ‘r’) as file:
data = json.load(file)
files_data = data[“files”]
people_data = data[“data”]
Create a mapping from ID to person name
id_to_name = {}
for person in people_data:
for id in person[“ids”]:
id_to_name[id] = person[“person”][“name”]
Function to extract and save faces with optional padding
def extract_and_save_face(image_path, face_data, person_name):
print(f"Extracting {person_name}")
img = Image.open(image_path)
width, height = img.size
center_x = face_data[“l”] * width
center_y = face_data[“t”] * height
face_width = face_data[“w”] * width
face_height = face_data[“h”] * height
# Determine padding amount
padding_amount_w, padding_amount_h = 0, 0
if padding_type == 'px':
padding_amount_w = padding_amount_h = padding # Same padding on all sides
elif padding_type == '%':
# Calculate padding as a percentage of the face size, not the total image
padding_amount_w = face_width * (padding / 100.0) / 2 # Half on each side for width
padding_amount_h = face_height * (padding / 100.0) / 2 # Half on each side for height
# Apply padding, adjusting to not exceed image boundaries
left = max(0, center_x - face_width / 2 - padding_amount_w)
top = max(0, center_y - face_height / 2 - padding_amount_h)
right = min(width, center_x + face_width / 2 + padding_amount_w)
bottom = min(height, center_y + face_height / 2 + padding_amount_h)
# Crop the face, ensuring the crop is within the image
face = img.crop((left, top, right, bottom))
# Ensure we're not trying to save an empty image
if face.width == 0 or face.height == 0:
print(f"Skipping empty image for {person_name}.")
return
# Create directory for the person if it doesn't exist
# Change [-x:] where x is number of directories to keep i.e [-2:] keeps the last 2 directories on the path.
realpath = os.path.realpath(directory_path).split("\\")[-2:] + [ person_name ]
person_dir = Path("D:\\extracted faces", *realpath)
os.makedirs(person_dir, exist_ok=True)
# Save the face image
face_file_path = person_dir / f"{Path(image_path).stem}_face.jpg"
face.save(face_file_path)
Iterate over each file and face data
for image_name, faces_info in files_data.items():
image_path = os.path.join(directory_path, image_name)
for face_info in faces_info[“faces”].values():
person_id = face_info[“v”][“p”]
if person_id in id_to_name:
person_name = id_to_name[person_id]
extract_and_save_face(image_path, face_info[“v”], person_name)
print(“All faces have been gracefully framed and preserved, with or without the whisper of a border.”)