mirror of
https://github.com/ChrisSewell/TeamsLogoAdder.git
synced 2025-07-01 18:47:28 -04:00
Initial commit
This commit is contained in:
52
.gitignore
vendored
Normal file
52
.gitignore
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# Ignore contents of input, logo, and output folders but keep the folders
|
||||||
|
input/*
|
||||||
|
logo/*
|
||||||
|
output/*
|
||||||
|
|
||||||
|
# Keep the folder structure by not ignoring .gitkeep files
|
||||||
|
!input/.gitkeep
|
||||||
|
!logo/.gitkeep
|
||||||
|
!output/.gitkeep
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# IDEs
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
ehthumbs.db
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
109
README.md
Normal file
109
README.md
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
# TeamsFlipper - Image Processing Tool
|
||||||
|
|
||||||
|
TeamsFlipper is a Python script that processes images for video conferencing backgrounds by cropping, resizing, and adding logo overlays.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Batch Processing**: Automatically processes all `.jpg` and `.png` files in the input folder
|
||||||
|
- **Smart Cropping**: Crops images to 16:9 aspect ratio while preserving the center content
|
||||||
|
- **Standard Sizing**: Resizes all images to 2560x1440 pixels for consistent output
|
||||||
|
- **Logo Overlay**: Adds PNG logo from the logo directory to the upper-right corner
|
||||||
|
- **High Quality Output**: Saves processed images as high-quality JPEGs
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Clone this repository or download the files
|
||||||
|
2. Install required dependencies:
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
TeamsFlipper/
|
||||||
|
├── flip_image.py # Main processing script
|
||||||
|
├── input/ # Place your source images here
|
||||||
|
├── logo/ # Place your logo PNG file here
|
||||||
|
│ └── cornerlogo.png # Example logo file
|
||||||
|
├── output/ # Processed images appear here
|
||||||
|
├── README.md # This file
|
||||||
|
├── requirements.txt # Python dependencies
|
||||||
|
└── .gitignore # Git ignore rules
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. **Prepare your files:**
|
||||||
|
- Place your source images (`.jpg` or `.png`) in the `input/` folder
|
||||||
|
- Ensure your logo PNG file is in the `logo/` folder
|
||||||
|
|
||||||
|
2. **Run the script:**
|
||||||
|
```bash
|
||||||
|
python3 flip_image.py
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Get your results:**
|
||||||
|
- Processed images will be saved in the `output/` folder
|
||||||
|
- Output files are named `{original_filename}_logo.jpg`
|
||||||
|
|
||||||
|
## Processing Pipeline
|
||||||
|
|
||||||
|
The script performs the following operations on each image:
|
||||||
|
|
||||||
|
1. **Crop to 16:9**: Centers and crops the image to 16:9 aspect ratio
|
||||||
|
2. **Resize**: Scales the image to exactly 2560x1440 pixels
|
||||||
|
3. **Logo Overlay**: Places the logo (480px tall) in the exact upper-right corner
|
||||||
|
4. **Save**: Outputs as high-quality JPEG (95% quality)
|
||||||
|
|
||||||
|
## Logo Requirements
|
||||||
|
|
||||||
|
- **Format**: PNG with transparency support
|
||||||
|
- **Sizing**: Logo will be automatically resized to 480 pixels tall while maintaining aspect ratio
|
||||||
|
- **Position**: Logo is placed in the exact upper-right corner with no padding
|
||||||
|
|
||||||
|
## Supported Input Formats
|
||||||
|
|
||||||
|
- JPEG (`.jpg`, `.jpeg`)
|
||||||
|
- PNG (`.png`)
|
||||||
|
- Case-insensitive file extensions
|
||||||
|
|
||||||
|
## Output Format
|
||||||
|
|
||||||
|
- All processed images are saved as JPEG files
|
||||||
|
- Quality: 95% (high quality)
|
||||||
|
- Naming: `{original_filename}_logo.jpg`
|
||||||
|
- Size: 2560x1440 pixels (16:9 aspect ratio)
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Input
|
||||||
|
- `my_photo.jpg` (any size/aspect ratio)
|
||||||
|
- Logo: `logo/company_logo.png`
|
||||||
|
|
||||||
|
### Output
|
||||||
|
- `output/my_photo_logo.jpg` (2560x1440 with logo overlay)
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Python 3.6+
|
||||||
|
- Pillow (PIL) library for image processing
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### No images found
|
||||||
|
- Ensure images are placed in the `input/` folder
|
||||||
|
- Check that file extensions are `.jpg`, `.jpeg`, or `.png`
|
||||||
|
|
||||||
|
### Logo not appearing
|
||||||
|
- Verify logo file exists in the `logo/` folder
|
||||||
|
- Ensure logo is in PNG format
|
||||||
|
- Check that logo file is not corrupted
|
||||||
|
|
||||||
|
### Processing errors
|
||||||
|
- Ensure input images are valid image files
|
||||||
|
- Check that you have write permissions for the `output/` folder
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is open source and available under standard terms.
|
141
flip_image.py
Normal file
141
flip_image.py
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
import os
|
||||||
|
import glob
|
||||||
|
from PIL import Image, ImageDraw
|
||||||
|
|
||||||
|
def crop_to_16_9(image):
|
||||||
|
"""Crop image to 16:9 aspect ratio, centering the crop."""
|
||||||
|
width, height = image.size
|
||||||
|
target_ratio = 16 / 9
|
||||||
|
current_ratio = width / height
|
||||||
|
|
||||||
|
if current_ratio > target_ratio:
|
||||||
|
# Image is too wide, crop width
|
||||||
|
new_width = int(height * target_ratio)
|
||||||
|
left = (width - new_width) // 2
|
||||||
|
right = left + new_width
|
||||||
|
cropped = image.crop((left, 0, right, height))
|
||||||
|
else:
|
||||||
|
# Image is too tall, crop height
|
||||||
|
new_height = int(width / target_ratio)
|
||||||
|
top = (height - new_height) // 2
|
||||||
|
bottom = top + new_height
|
||||||
|
cropped = image.crop((0, top, width, bottom))
|
||||||
|
|
||||||
|
return cropped
|
||||||
|
|
||||||
|
def resize_to_target(image, target_width=2560, target_height=1440):
|
||||||
|
"""Resize image to target dimensions."""
|
||||||
|
return image.resize((target_width, target_height), Image.Resampling.LANCZOS)
|
||||||
|
|
||||||
|
def overlay_logo(image, logo_path):
|
||||||
|
"""Overlay logo on the upper-right corner of the image."""
|
||||||
|
if not os.path.exists(logo_path):
|
||||||
|
print(f"Warning: Logo file {logo_path} not found. Skipping logo overlay.")
|
||||||
|
return image
|
||||||
|
|
||||||
|
try:
|
||||||
|
logo = Image.open(logo_path).convert("RGBA")
|
||||||
|
|
||||||
|
# Resize logo to be exactly 480 pixels tall while maintaining aspect ratio
|
||||||
|
logo_height = 480
|
||||||
|
logo_ratio = logo.size[0] / logo.size[1] # width / height
|
||||||
|
logo_width = int(logo_height * logo_ratio)
|
||||||
|
logo = logo.resize((logo_width, logo_height), Image.Resampling.LANCZOS)
|
||||||
|
|
||||||
|
# Get image dimensions
|
||||||
|
img_width, img_height = image.size
|
||||||
|
|
||||||
|
# Position logo in exact upper-right corner with no padding
|
||||||
|
x = img_width - logo_width
|
||||||
|
y = 0
|
||||||
|
|
||||||
|
# Create a copy of the image to work with
|
||||||
|
result = image.copy().convert("RGBA")
|
||||||
|
|
||||||
|
# Paste logo with transparency
|
||||||
|
result.paste(logo, (x, y), logo)
|
||||||
|
|
||||||
|
# Convert back to RGB
|
||||||
|
return result.convert("RGB")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error overlaying logo: {e}")
|
||||||
|
return image
|
||||||
|
|
||||||
|
def process_images():
|
||||||
|
"""Process all .jpg and .png images in the input folder."""
|
||||||
|
input_folder = "input"
|
||||||
|
output_folder = "output"
|
||||||
|
logo_folder = "logo"
|
||||||
|
|
||||||
|
# Create output folder if it doesn't exist
|
||||||
|
os.makedirs(output_folder, exist_ok=True)
|
||||||
|
|
||||||
|
# Find logo file in logo directory
|
||||||
|
logo_files = glob.glob(os.path.join(logo_folder, "*.png"))
|
||||||
|
if not logo_files:
|
||||||
|
# Fallback to cornerlogo.png in root directory
|
||||||
|
if os.path.exists("cornerlogo.png"):
|
||||||
|
logo_path = "cornerlogo.png"
|
||||||
|
print("Using cornerlogo.png from root directory")
|
||||||
|
else:
|
||||||
|
logo_path = None
|
||||||
|
print("Warning: No logo file found")
|
||||||
|
else:
|
||||||
|
logo_path = logo_files[0] # Use first PNG found in logo directory
|
||||||
|
print(f"Using logo: {logo_path}")
|
||||||
|
|
||||||
|
# Find all image files in input folder
|
||||||
|
image_extensions = ["*.jpg", "*.jpeg", "*.png", "*.JPG", "*.JPEG", "*.PNG"]
|
||||||
|
image_files = []
|
||||||
|
|
||||||
|
for extension in image_extensions:
|
||||||
|
image_files.extend(glob.glob(os.path.join(input_folder, extension)))
|
||||||
|
|
||||||
|
if not image_files:
|
||||||
|
print(f"No image files found in {input_folder} folder.")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"Found {len(image_files)} image(s) to process:")
|
||||||
|
|
||||||
|
for image_path in image_files:
|
||||||
|
try:
|
||||||
|
print(f"Processing: {os.path.basename(image_path)}")
|
||||||
|
|
||||||
|
# Open and process the image
|
||||||
|
with Image.open(image_path) as img:
|
||||||
|
# Convert to RGB if necessary
|
||||||
|
if img.mode != 'RGB':
|
||||||
|
img = img.convert('RGB')
|
||||||
|
|
||||||
|
# Step 1: Crop to 16:9 aspect ratio
|
||||||
|
cropped_img = crop_to_16_9(img)
|
||||||
|
|
||||||
|
# Step 2: Resize to 2560x1440
|
||||||
|
resized_img = resize_to_target(cropped_img)
|
||||||
|
|
||||||
|
# Step 3: Overlay logo
|
||||||
|
if logo_path:
|
||||||
|
logo_img = overlay_logo(resized_img, logo_path)
|
||||||
|
else:
|
||||||
|
logo_img = resized_img
|
||||||
|
|
||||||
|
# Step 4: Save as JPG in output folder
|
||||||
|
base_name = os.path.splitext(os.path.basename(image_path))[0]
|
||||||
|
output_path = os.path.join(output_folder, f"{base_name}_logo.jpg")
|
||||||
|
|
||||||
|
logo_img.save(output_path, "JPEG", quality=95)
|
||||||
|
print(f"Saved: {output_path}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error processing {image_path}: {e}")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Main function to process images."""
|
||||||
|
print("TeamsFlipper - Image Processing Tool")
|
||||||
|
print("=" * 40)
|
||||||
|
process_images()
|
||||||
|
print("Processing complete!")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Pillow>=9.0.0
|
Reference in New Issue
Block a user