Jak automatycznie wycinać tło ze zdjęć za pomocą AI i Pythona [Poradnik]
Tworzenie spójnej i profesjonalnej galerii zdjęć na stronę internetową — na przykład dla salonu fryzjerskiego czy sklepu e-commerce — bywa czasochłonne. Ręczne wycinanie krawędzi, nakładanie nowego tła i dopasowywanie rozmiarów w programach graficznych potrafi zająć wiele godzin.
Na szczęście rozwój sztucznej inteligencji pozwala całkowicie zautomatyzować ten proces. W tym poradniku pokazujemy, jak za pomocą darmowego modelu AI RMBG 2.0 oraz prostego skryptu w Pythonie stworzyć automatyczny system, który:
- Precyzyjnie wytnie tło (nawet ze skomplikowanych krawędzi, takich jak włosy).
- Wycentruje obiekt i ustali spójną skalę dla całej galerii.
- Podmieni tło na estetyczny wzór z miękkim cieniem.
- Nałoży logo / znak wodny.
- Zmieni nazwy plików na uporządkowany ciąg i skompresuje je do lekkiego formatu WebP.
1. Wymagania wstępne i instalacja komponentów
Wszystkie potrzebne narzędzia są całkowicie darmowe i dają pełną kontrolę nad przetwarzaniem plików bezpośrednio na Twoim komputerze.
Krok 1: Instalacja potrzebnych bibliotek
Otwórz terminal (lub wiersz poleceń) i zainstaluj niezbędne pakiety Pythona:
pip install torch torchvision transformers pillow kornia timm huggingface_hubZa co odpowiadają poszczególne pakiety?
torchitorchvision— silnik do obsługi modeli uczenia maszynowego.transformers— biblioteka do łatwego ładowania modeli AI z platformy HuggingFace.pillow— biblioteka do obróbki obrazów (przycinanie, skalowanie, nakładanie logo).timmorazkornia— pomocnicze biblioteki wymagane przez architekturę modelu.huggingface_hub— narzędzie do autoryzacji i pobierania wag modeli.
2. Jednorazowa autoryzacja na HuggingFace (Model RMBG 2.0)
Model RMBG 2.0 stworzony przez BRIA AI to obecnie jedna z najlepszych open-source’owych sieci neuronowych do detekcji pierwszego planu. Dostęp do wag modelu wymaga jednorazowego zatwierdzenia darmowej licencji.
- Wejdź na stronę huggingface.co/briaai/RMBG-2.0 i zaakceptuj warunki (przycisk Agree and access repository).
- W ustawieniach swojego profilu (
Settings -> Access Tokens) wygeneruj token typu Read. - Zaloguj się w terminalu komendą:
hf auth loginWklej wygenerowany token i zatwierdź Enterem. Zostanie on zapisany w systemie.
3. Kompletny skrypt w Pythonie
Utwórz plik obrobka_galerii.py w folderze ze swoimi zdjęciami i wklej do niego poniższy kod:
import os
import io
import urllib.request
import torch
import numpy as np
from PIL import Image, ImageFilter, ImageEnhance
from torchvision import transforms
from transformers import AutoModelForImageSegmentation
# --- USTAWIENIA KONFIGURACYJNE ---
TARGET_SIZE = (1080, 1080) # Rozdzielczość wyjściowa (kwadrat)
MAX_FILE_SIZE_KB = 250 # Maksymalna waga pliku w KB
OUTPUT_FOLDER = "gotowe_do_galerii" # Podfolder na przetworzone zdjęcia
BG_IMAGE_PATH = "salon_bg.jpg" # Plik tła (np. beton, tekstura)
LOGO_URL = "https://questfryzjer.pl/wp-content/uploads/2025/12/quest-lt.png"
LOGO_LOCAL_PATH = "quest-logo.png"
# 1. Inicjalizacja modelu AI
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"--> Używam urządzenia: {device}")
print("--> Ładowanie modelu RMBG 2.0...")
model = AutoModelForImageSegmentation.from_pretrained(
'briaai/RMBG-2.0',
trust_remote_code=True
)
model.to(device)
model.eval()
def get_or_download_logo():
"""Pobiera logo z podanego adresu URL, jeśli plik nie istnieje lokalnie."""
if not os.path.exists(LOGO_LOCAL_PATH):
print("--> Pobieranie logo ze strony...")
try:
req = urllib.request.Request(LOGO_URL, headers={'User-Agent': 'Mozilla/5.0'})
with urllib.request.urlopen(req) as response, open(LOGO_LOCAL_PATH, 'wb') as out_file:
out_file.write(response.read())
except Exception as e:
print(f"[OSTRZEŻENIE] Nie udało się pobrać logo: {e}")
return None
return Image.open(LOGO_LOCAL_PATH).convert("RGBA")
def prepare_background(size):
"""Przygotowuje tło – wczytuje i rozmywa obrazek lokalny."""
if not os.path.exists(BG_IMAGE_PATH):
raise FileNotFoundError(f"Brak pliku '{BG_IMAGE_PATH}' w folderze ze skryptem!")
bg = Image.open(BG_IMAGE_PATH).convert("RGB")
# Kadrowanie centralne do wymaganego formatu
bg_ratio = bg.width / bg.height
target_ratio = size[0] / size[1]
if bg_ratio > target_ratio:
new_width = int(bg.height * target_ratio)
left = (bg.width - new_width) // 2
bg = bg.crop((left, 0, left + new_width, bg.height))
else:
new_height = int(bg.width / target_ratio)
top = (bg.height - new_height) // 2
bg = bg.crop((0, top, bg.width, top + new_height))
bg = bg.resize(size, Image.Resampling.LANCZOS)
return bg.filter(ImageFilter.GaussianBlur(radius=8))
def add_logo_watermark(base_image, logo_img, target_width_ratio=0.22, opacity=0.85, margin=40):
"""Nakłada logo w prawym dolnym rogu."""
if logo_img is None:
return base_image
img_w, img_h = base_image.size
logo_w = int(img_w * target_width_ratio)
aspect_ratio = logo_img.height / logo_img.width
logo_h = int(logo_w * aspect_ratio)
resized_logo = logo_img.resize((logo_w, logo_h), Image.Resampling.LANCZOS)
r, g, b, alpha = resized_logo.split()
alpha = alpha.point(lambda p: int(p * opacity))
resized_logo.putalpha(alpha)
pos_x = img_w - logo_w - margin
pos_y = img_h - logo_h - margin
combined = base_image.convert("RGBA")
combined.paste(resized_logo, (pos_x, pos_y), mask=resized_logo)
return combined.convert("RGB")
def process_photo(input_path, output_path, bg_template, logo_img):
orig_image = Image.open(input_path).convert("RGB")
transform_image = transforms.Compose([
transforms.Resize((1024, 1024)),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
input_tensor = transform_image(orig_image).unsqueeze(0).to(device)
# Wycięcie tła przez model AI
with torch.no_grad():
preds = model(input_tensor)[-1].sigmoid().cpu()
mask = transforms.ToPILImage()(preds[0].squeeze()).resize(orig_image.size)
no_bg_img = orig_image.copy()
no_bg_img.putalpha(mask)
# Korekta ostrości i kontrastu
no_bg_img = no_bg_img.filter(ImageFilter.UnsharpMask(radius=2, percent=130, threshold=3))
enhancer = ImageEnhance.Contrast(no_bg_img)
no_bg_img = enhancer.enhance(1.08)
# Centrowanie pierwszego planu
bbox = no_bg_img.getbbox()
if bbox:
no_bg_img = no_bg_img.crop(bbox)
target_h = int(TARGET_SIZE[1] * 0.85)
aspect_ratio = no_bg_img.width / no_bg_img.height
target_w = int(target_h * aspect_ratio)
if target_w > int(TARGET_SIZE[0] * 0.85):
target_w = int(TARGET_SIZE[0] * 0.85)
target_h = int(target_w / aspect_ratio)
no_bg_img = no_bg_img.resize((target_w, target_h), Image.Resampling.LANCZOS)
mask_resized = no_bg_img.split()[3]
final_img = bg_template.copy()
paste_x = (TARGET_SIZE[0] - no_bg_img.width) // 2
paste_y = (TARGET_SIZE[1] - no_bg_img.height) // 2
# Generowanie miękkiego cienia
shadow_mask = mask_resized.filter(ImageFilter.GaussianBlur(radius=20))
shadow = Image.new("RGBA", TARGET_SIZE, (0, 0, 0, 0))
shadow_layer = Image.new("RGBA", no_bg_img.size, (0, 0, 0, 80))
shadow.paste(shadow_layer, (paste_x + 5, paste_y + 10), mask=shadow_mask)
final_img.paste(shadow, (0, 0), mask=shadow)
# Połączenie warstw
final_img.paste(no_bg_img, (paste_x, paste_y), mask=mask_resized)
final_img = add_logo_watermark(final_img, logo_img)
# Optymalizacja rozmiaru i zapis w formacie WebP
quality = 92
while quality > 10:
buffer = io.BytesIO()
final_img.save(buffer, format="WEBP", quality=quality, optimize=True)
size_kb = buffer.tell() / 1024
if size_kb <= MAX_FILE_SIZE_KB:
break
quality -= 4
with open(output_path, "wb") as f:
f.write(buffer.getvalue())
print(f"[OK] {os.path.basename(input_path)} -> {os.path.basename(output_path)} ({size_kb:.1f} KB)")
# --- PĘTLA GŁÓWNA ---
if __name__ == "__main__":
os.makedirs(OUTPUT_FOLDER, exist_ok=True)
try:
bg_template = prepare_background(TARGET_SIZE)
except FileNotFoundError as e:
print(f"\n[BŁĄD] {e}")
exit(1)
logo_img = get_or_download_logo()
ignored_files = {BG_IMAGE_PATH, LOGO_LOCAL_PATH}
valid_extensions = ('.jpg', '.jpeg', '.png', '.bmp', '.webp')
files = sorted([f for f in os.listdir('.') if f.lower().endswith(valid_extensions) and f not in ignored_files])
if not files:
print("Nie znaleziono zdjęć do przetworzenia!")
else:
print(f"Rozpoczynam przetwarzanie {len(files)} plików...\n")
for idx, file_name in enumerate(files, start=1):
input_path = file_name
output_filename = f"questfryzjer_{idx:02d}.webp"
output_path = os.path.join(OUTPUT_FOLDER, output_filename)
try:
process_photo(input_path, output_path, bg_template, logo_img)
except Exception as e:
print(f"[BŁĄD] Nie udało się przetworzyć {file_name}: {e}")
print(f"\nPrzetwarzanie zakończone! Pliki zapisano w: /{OUTPUT_FOLDER}")4. Podstawowe komendy do uruchamiania
Gdy pliki graficzne oraz skrypt znajdują się w jednym folderze, wystarczy jedno polecenie w terminalu:
python obrobka_galerii.pyDlaczego warto wdrożyć to rozwiązanie?
- Format WebP: Zapewnia znacznie mniejszą wagę plików przy wysokiej jakości, przyspieszając ładowanie witryny.
- Spójność wizualna: Wszystkie kadry są idealnie wycentrowane i zachowują stałą skalę (85% wysokości kadru).
- Porządek w plikach: Automatyczna zmiana nazw (np.
questfryzjer_01.webp) ułatwia zarządzanie zasobami w bibliotece mediów WordPressa.