Convertire foto in bianco e nero ad alto contrasto con Python
Pacchetto Python (.zip) · versione 1.0.0 · verificato
bn-foto-locale.zip
Script autonomo che gira solo sul tuo computer. Nessun upload, nessuna telemetria.
- Formato
- Archivio ZIP (.zip)
- Dimensione
- 8,2 KB
- Python
- 3.9 o superiore
- Licenza
- MIT
- Richiede
- opencv-python, numpy, Pillow
- Rete
- Offline
Crea, rinomina o sposta file solo se lo chiedi tu (per esempio per il report o la quarantena). Il codice è leggibile riga per riga nel file scaricato.
Verifica integrità
SHA-256 di bn-foto-locale.zip
eb5d657837cab4e8391b4a0afcbdb89658f15f2b671094cef57902363887974a Calcola lo SHA-256 del file scaricato e confrontalo con questo: Confronta hash · Come si verifica
Trasforma intere cartelle di foto in bianco e nero con Python, OpenCV e una pipeline a 7 stadi (Rec.709, istogramma, CLAHE). Pacchetto ZIP per Windows e Mac.
Hai una cartella con trenta o quaranta foto — ritratti di famiglia, scatti di un evento o immagini per un progetto — e vuoi trasformarle in un bianco e nero pulito, tridimensionale e coerente. Se usi il comando «Scala di grigi» del visualizzatore di sistema ottieni quasi sempre un grigio piatto e spento; se invece le carichi su un convertitore online gratuito, finisci per spedire foto personali e volti su un server esterno solo per applicare un filtro.
Risposta breve: con il pacchetto Python di questo Lab trasformi un’intera cartella di foto in bianco e nero sul tuo computer, con un doppio clic su Windows o Mac e senza caricare nulla online. A differenza di una conversione standard, lo script converti_bn.py legge l’istogramma di ogni singola immagine e applica una pipeline fotografica adattiva in 7 stadi (bilanciamento del bianco, luminanza Rec.709, livelli sui percentili, curva Gamma, micro-contrasto locale CLAHE, maschera di nitidezza e curva tonale a S), adattando i parametri se lo scatto di partenza è scuro, sovraesposto o a basso contrasto.
ℹ️ Nota / Prerequisito operativo: Ti basta avere Python 3.9 o superiore installato sul computer. Il pacchetto
.zipscaricabile qui sopra include già lo scriptconverti_bn.py, il filerequirements.txt, le due cartelle di lavoro e i file di avvio automaticoAvvia conversione.bat(Windows) eAvvia conversione.command(macOS e Linux), che al primo lancio creano da soli l’ambiente virtuale.venve installano le librerie necessarie.
Perché una normale «scala di grigi» appiattisce le foto
Quando un programma converte una foto a colori in bianco e nero facendo una semplice media tra Rosso, Verde e Blu — oppure azzerando la saturazione — tratta allo stesso modo uno scatto controluce, un ritratto in ombra e una foto scattata sotto una lampadina gialla. Il risultato è quella patina grigia “slavata” in cui mancano sia un nero profondo sia un bianco luminoso.
Lo script di questo progetto lavora invece come una piccola camera oscura automatica: prima misura la foto, poi decide come svilupparla. Ogni immagine attraversa sette passaggi consecutivi in memoria (su matrici float32 con NumPy e OpenCV) prima di essere salvata su disco alla massima qualità.
| Stadio della pipeline | Tipo di intervento | Cosa fa concretamente sui pixel |
|---|---|---|
1. Bilanciamento del bianco
Funzione bilanciamento()
|
Pre-filtro RGB | Applica l'ipotesi Grey-World ai tre canali colore per eliminare le dominanti calde o fredde prima ancora di calcolare i grigi. |
2. Luminanza percettiva Rec.709
Funzione luminanza()
|
Pesi occhi umani | Converte in monocromatico pesando il Verde al 71,52%, il Rosso al 21,26% e il Blu al 7,22% (standard ITU-R BT.709), restituendo incarnati e fogliame naturali. |
3. Analisi dell'istogramma
Funzione analizza()
|
Adattivo | Calcola mediana (P50), escursione dinamica (P95 - P5) e percentuale di ombre chiuse o luci bruciate, classificando lo scatto come scuro, bilanciato o chiaro. |
4. Espansione dei livelli
Funzione livelli()
|
Punto nero/bianco | Rimappa l'istogramma sui percentili scelti dall'analisi (tra 0,5% e 99,5%), evitando che un singolo riflesso blocchi il contrasto dell'intera foto. |
5. Correzione Gamma via LUT
Funzione gamma()
|
Mezzi toni | Usa una Look-Up Table a 256 valori per schiarire i mezzi toni nelle foto sottoesposte (gamma 0.92) o dare densità a quelle troppo chiare (gamma 1.08). |
6. Micro-contrasto CLAHE
Funzione clahe()
|
Locale 8×8 | Divide l'immagine in una griglia 8×8 ed equalizza il contrasto locale con un limite anti-rumore dinamico (da 1.8 fino a 4.0 per le foto piatte). |
7. Nitidezza e «Ritocchino»
nitidezza() + ritocchino()
|
Finitura S-Curve | Applica una maschera di contrasto (raggio 1,2 px), recupera morbidamente ombre estreme (<52) e alte luci (>212) e chiude con una curva a S sui mezzi toni. |
Cosa contiene il pacchetto ZIP e cosa fa sul tuo computer
Quando scarichi ed estrai bn-foto-locale.zip dal riquadro in cima alla pagina, ottieni una cartella chiamata bn-foto-locale già pronta con questa struttura:
converti_bn.py— lo script Python completo e commentato.requirements.txt— l’elenco delle tre librerie richieste (opencv-python>=4.8.0,numpy>=1.24.0,Pillow>=10.0.0).Avvia conversione.bat— il file di avvio con doppio clic per Windows.Avvia conversione.command— il file di avvio con doppio clic per macOS e Linux.foto da trasformare/— la cartella in cui incollare o trascinare le foto a colori (jpg,jpeg,png,webp,tif,tiff,bmp,gif,heic,heif,avif).foto in bianco e nero/— la cartella dove compaiono i file convertiti.LEGGIMI.txt— un promemoria sintetico dei comandi anche offline.
Cosa non fa e quando usa la rete. Le foto originali dentro foto da trasformare/ vengono aperte solo in lettura e non vengono mai modificate né cancellate. La connessione a internet serve esclusivamente la prima volta in cui avvii il programma, per scaricare da PyPI le tre librerie dentro la sottocartella .venv. Dal secondo avvio in poi puoi usarlo anche con il Wi-Fi spento. Se oltre a convertire le foto in bianco e nero vuoi assicurarti di cancellare le coordinate GPS prima di pubblicarle, puoi abbinare questo flusso allo script per rimuovere i metadati EXIF dalle foto in locale.
Metodo 1 — Avvio rapido con doppio clic (Windows e macOS)
Questo è il modo più comodo per usare il tool ogni volta che hai nuove foto da convertire, senza dover aprire il terminale e riscrivere i comandi a mano.
Su Windows: usare Avvia conversione.bat
- Fai clic destro su
bn-foto-locale.zipe scegli Estrai tutto…, poi apri la cartellabn-foto-locale. - Copia una o più foto a colori dentro la sottocartella
foto da trasformare. - Torna nella cartella
bn-foto-localee fai doppio clic suAvvia conversione.bat. - Se compare l’avviso blu di Windows SmartScreen («PC protetto da Windows», normale per qualsiasi file
.batscaricato dal browser): clicca su Ulteriori informazioni e poi sul pulsante Esegui comunque. - Cosa succede al primo avvio: si apre la finestra nera del Prompt, lo script crea in automatico la cartella
.venv, installaopencv-python,numpyePillow(richiede circa 15–30 secondi la prima volta) e subito dopo converte tutte le foto. Dalle volte successive salta l’installazione e converte decine di foto in pochi secondi. - Al termine premi un tasto qualsiasi per chiudere la finestra e apri la cartella
foto in bianco e nero.
Su macOS: attivare e usare Avvia conversione.command
Su macOS i file con estensione .command si avviano con un doppio clic nel Finder, ma quando vengono scaricati da internet il sistema applica l’attributo di quarantena di Gatekeeper. Ecco come sbloccarlo una volta sola per sempre:
- Estrai
bn-foto-locale.zip(basta un doppio clic sul file.zip) e copia le tue foto dentrofoto da trasformare. - Apri il Terminale direttamente nella cartella
bn-foto-locale(nel Finder fai clic destro sulla cartellabn-foto-localee scegli Nuovo terminale nella cartella). - Esegui questi due comandi per rendere eseguibile il file e rimuovere il blocco di quarantena del download:
chmod +x "Avvia conversione.command" "converti_bn.py"
xattr -d com.apple.quarantine "Avvia conversione.command" 2>/dev/null
- Da questo momento in poi, ogni volta che vorrai convertire nuove foto ti basterà fare doppio clic su
Avvia conversione.commandnel Finder: si aprirà una finestra del Terminale che al primo giro prepara.venv, converte tutte le immagini e si chiude da sola dopo 6 secondi.
💡 Consiglio pratico — Perché il launcher crea una cartella
.venvnascosta: Su macOS (con Python installato via Homebrew) e sulle distribuzioni Linux recenti, installare pacchetti conpipfuori da un ambiente virtuale viene bloccato con l’errore externally-managed-environment (PEP 668). I due fileAvvia conversione.bateAvvia conversione.commandcreano automaticamente un.venvlocale dentro la cartella del progetto: così non sporcano il sistema e non si bloccano mai.
Metodo 2 — Avvio manuale da Terminale o PowerShell
Se preferisci avere il pieno controllo da riga di comando o integrare lo script in una tua automazione, puoi eseguire i passaggi manualmente. Per approfondire come funzionano l’ambiente isolato e il file delle dipendenze puoi leggere le guide su ambienti virtuali Python (venv) e su come usare requirements.txt.
1. Entra nella cartella e prepara l’ambiente virtuale
Su Windows (apri PowerShell dentro la cartella bn-foto-locale):
python -m venv .venv
.venv\Scripts\python.exe -m pip install --upgrade pip
.venv\Scripts\python.exe -m pip install -r requirements.txt
.venv\Scripts\python.exe -c "import cv2, numpy, PIL; print('OpenCV:', cv2.__version__, '| NumPy:', numpy.__version__)"
Su macOS e Linux (apri il Terminale dentro la cartella bn-foto-locale):
python3 -m venv .venv
.venv/bin/python -m pip install --upgrade pip
.venv/bin/python -m pip install -r requirements.txt
.venv/bin/python -c "import cv2, numpy, PIL; print('OpenCV:', cv2.__version__, '| NumPy:', numpy.__version__)"
L’ultimo comando di verifica deve stampare i numeri di versione senza errori, per esempio:
OpenCV: 4.10.0 | NumPy: 2.1.2
2. Lancia la conversione e leggi il report
Metti qualche immagine dentro foto da trasformare ed esegui lo script.
Su Windows:
.venv\Scripts\python.exe converti_bn.py
Su macOS e Linux:
.venv/bin/python converti_bn.py
Per ogni immagine vedrai in tempo reale il profilo di esposizione rilevato dall’istogramma (scura, bilanciata o chiara), la mediana P50 e l’ampiezza dinamica P95 - P5:
==============================================================
CONVERSIONE FOTO IN BIANCO E NERO — PIPELINE A 7 STADI
==============================================================
Trovate 3 immagini. Elaborazione in corso...
→ architettura-milano.jpg ... OK → architettura-milano.jpg [esposizione bilanciata, P50=118, dinamica=174]
→ cortile-ombra.png ... OK → cortile-ombra.png [esposizione scura, P50=74, dinamica=112]
→ ritratto-controluce.webp ... OK → ritratto-controluce.webp [esposizione chiara, P50=172, dinamica=148]
--------------------------------------------------------------
Completate: 3 | Errori: 0 | Tempo: 0.41s
Cartella di output: /Users/alex/bn-foto-locale/foto in bianco e nero
--------------------------------------------------------------
Il codice completo di converti_bn.py, spiegato pezzo per pezzo
Qui sotto trovi il codice integrale contenuto nel pacchetto .zip, commentato in ogni funzione così puoi verificarlo o personalizzare le soglie a tuo gusto:
#!/usr/bin/env python3
"""
converti_bn.py — Conversione fotografica adattiva in Bianco e Nero (7 stadi).
Autore: Alex Lunalbi (ai riparo)
Licenza: MIT
"""
from __future__ import annotations
import argparse
import time
from pathlib import Path
import cv2
import numpy as np
from PIL import Image, ImageOps
# Supporto opzionale per file .heic / .heif (iPhone) se pillow-heif è installato
try:
import pillow_heif # type: ignore
pillow_heif.register_heif_opener()
except ImportError:
pass
BASE_DIR = Path(__file__).resolve().parent
DEFAULT_INPUT_DIR = BASE_DIR / "foto da trasformare"
DEFAULT_OUTPUT_DIR = BASE_DIR / "foto in bianco e nero"
SUPPORTED = {
".jpg", ".jpeg", ".png", ".tif", ".tiff",
".bmp", ".webp", ".gif", ".heic", ".heif", ".avif",
}
# Coefficienti di luminanza percettiva ITU-R BT.709
LUMA_R, LUMA_G, LUMA_B = 0.2126, 0.7152, 0.0722
def crea_cartelle(input_dir: Path, output_dir: Path) -> None:
"""Crea le cartelle di ingresso e di uscita se non esistono ancora."""
input_dir.mkdir(parents=True, exist_ok=True)
output_dir.mkdir(parents=True, exist_ok=True)
def trova_immagini(origine: Path) -> list[Path]:
"""Restituisce l'elenco ordinato delle immagini supportate (da cartella o file singolo)."""
if origine.is_file():
return [origine] if origine.suffix.lower() in SUPPORTED else []
if not origine.is_dir():
return []
return sorted(
(
p for p in origine.iterdir()
if p.is_file() and not p.name.startswith(".") and p.suffix.lower() in SUPPORTED
),
key=lambda p: p.name.lower(),
)
def carica(percorso: Path) -> np.ndarray | None:
"""
Carica un'immagine come matrice BGR a 8 bit.
Usa Pillow prima per rispettare il tag EXIF Orientation (foto verticali da smartphone);
fallback su OpenCV imdecode (compatibile con percorsi Windows accentati/Unicode).
"""
try:
with Image.open(percorso) as pil_img:
pil_corretta = ImageOps.exif_transpose(pil_img)
rgb = np.array(pil_corretta.convert("RGB"), dtype=np.uint8)
return cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR)
except Exception:
pass
try:
buffer = np.fromfile(str(percorso), dtype=np.uint8)
img = cv2.imdecode(buffer, cv2.IMREAD_COLOR)
if img is not None:
return img
except Exception:
pass
return None
def bilanciamento(bgr: np.ndarray) -> np.ndarray:
"""Stadio 1 — Bilanciamento del bianco automatico (ipotesi Grey-World)."""
img = bgr.astype(np.float32)
media = img.reshape(-1, 3).mean(axis=0)
img *= media.mean() / (media + 1e-6)
return np.clip(img, 0, 255).astype(np.uint8)
def luminanza(bgr: np.ndarray) -> np.ndarray:
"""Stadio 2 — Conversione in scala di grigi secondo lo standard Rec.709."""
b, g, r = cv2.split(bgr.astype(np.float32))
luma = LUMA_R * r + LUMA_G * g + LUMA_B * b
return np.clip(luma, 0, 255).astype(np.uint8)
def analizza(gray: np.ndarray) -> dict:
"""Stadio 3 — Analisi statistica dell'istogramma (P5, P50, P95, range dinamico)."""
hist = cv2.calcHist([gray], [0], None, [256], [0, 256]).flatten()
tot = float(hist.sum()) or 1.0
cum = np.cumsum(hist) / tot
p5 = int(np.searchsorted(cum, 0.05))
p50 = int(np.searchsorted(cum, 0.50))
p95 = int(np.searchsorted(cum, 0.95))
rd = max(p95 - p5, 1)
if p50 < 90:
sp, hp, valore_gamma, clip_clahe, profilo = 1.0, 99.5, 0.92, 2.8, "scura"
elif p50 > 165:
sp, hp, valore_gamma, clip_clahe, profilo = 0.5, 99.0, 1.08, 1.8, "chiara"
else:
sp, hp, valore_gamma, clip_clahe, profilo = 1.5, 98.5, 1.0, 2.2, "bilanciata"
if rd < 120:
clip_clahe = min(clip_clahe + 1.0, 4.0)
return {
"shadow_pct": sp,
"highlight_pct": hp,
"clahe_clip": clip_clahe,
"gamma": valore_gamma,
"range_dyn": rd,
"p50": p50,
"profilo": profilo,
"ombre_clip": float(hist[:10].sum()) / tot,
"luci_clip": float(hist[245:].sum()) / tot,
}
def livelli(gray: np.ndarray, sp: float, hp: float) -> np.ndarray:
"""Stadio 4 — Rimappatura adattiva dei livelli sui percentili."""
flat = gray.astype(np.float32)
nero, bianco = float(np.percentile(flat, sp)), float(np.percentile(flat, hp))
if bianco - nero < 5:
return gray
riscalato = (flat - nero) * (255.0 / (bianco - nero))
return np.clip(riscalato, 0, 255).astype(np.uint8)
def gamma(gray: np.ndarray, g: float) -> np.ndarray:
"""Stadio 5 — Correzione Gamma tramite Look-Up Table (LUT a 256 valori)."""
if abs(g - 1.0) < 0.01:
return gray
inv = 1.0 / g
tab = np.array([((i / 255.0) ** inv) * 255.0 for i in range(256)], dtype=np.uint8)
return cv2.LUT(gray, tab)
def clahe(gray: np.ndarray, clip: float) -> np.ndarray:
"""Stadio 6 — Micro-contrasto locale CLAHE su griglia 8x8."""
equalizzatore = cv2.createCLAHE(clipLimit=clip, tileGridSize=(8, 8))
return equalizzatore.apply(gray)
def nitidezza(gray: np.ndarray) -> np.ndarray:
"""Stadio 7a — Maschera di contrasto (Unsharp Mask con sigma=1.2)."""
blur = cv2.GaussianBlur(gray, (0, 0), sigmaX=1.2)
return np.clip(cv2.addWeighted(gray, 1.25, blur, -0.25, 0), 0, 255).astype(np.uint8)
def ritocchino(gray: np.ndarray, p: dict) -> np.ndarray:
"""Stadio 7b — Chiarezza locale, protezione ombre/luci e curva tonale a S."""
img = gray.astype(np.float32)
rd, p50 = p["range_dyn"], p["p50"]
if rd < 130:
blur = cv2.GaussianBlur(img, (0, 0), 16)
img += (img - blur) * (0.07 + (130 - rd) / 1000.0)
if p["ombre_clip"] > 0.03 or p50 < 88:
maschera_ombre = cv2.GaussianBlur(np.clip((52.0 - img) / 52.0, 0, 1), (0, 0), 7)
img += maschera_ombre * 3.5
if p["luci_clip"] > 0.02 or p50 > 168:
maschera_luci = cv2.GaussianBlur(np.clip((img - 212.0) / 43.0, 0, 1), (0, 0), 9)
img -= maschera_luci * 4.0
t = np.clip(img / 255.0, 0, 1)
boost = 0.05 if rd < 125 else 0.03
t = t + boost * t * (1.0 - t) * (t - 0.5) * 4.0
return np.clip(t * 255.0, 0, 255).astype(np.uint8)
def elabora(bgr: np.ndarray) -> tuple[np.ndarray, dict]:
"""Esegue l'intera pipeline a 7 stadi e restituisce immagine B/N + metriche."""
g = luminanza(bilanciamento(bgr))
info = analizza(g)
g = livelli(g, info["shadow_pct"], info["highlight_pct"])
g = gamma(g, info["gamma"])
g = clahe(g, info["clahe_clip"])
g = nitidezza(g)
finale = ritocchino(g, info)
return finale, info
def salva(gray: np.ndarray, out: Path, qualita: int = 98) -> None:
"""Salva l'immagine con imencode + tofile (supporto pieno percorsi Unicode su Windows)."""
ext = out.suffix.lower()
if ext in {".jpg", ".jpeg"}:
parametri = [cv2.IMWRITE_JPEG_QUALITY, qualita, cv2.IMWRITE_JPEG_OPTIMIZE, 1]
formato = ".jpg"
elif ext == ".png":
parametri = [cv2.IMWRITE_PNG_COMPRESSION, 3]
formato = ".png"
elif ext == ".webp":
parametri = [cv2.IMWRITE_WEBP_QUALITY, qualita]
formato = ".webp"
elif ext in {".tif", ".tiff"}:
parametri = []
formato = ".tiff"
else:
out = out.with_suffix(".png")
parametri = [cv2.IMWRITE_PNG_COMPRESSION, 3]
formato = ".png"
successo, buffer = cv2.imencode(formato, gray, parametri)
if not successo:
raise RuntimeError(f"Impossibile codificare il file {out.name}")
buffer.tofile(str(out))
def percorso_output(percorso_in: Path, cartella_out: Path) -> Path:
if percorso_in.suffix.lower() in {".gif", ".heic", ".heif", ".avif", ".bmp"}:
return cartella_out / f"{percorso_in.stem}.png"
return cartella_out / percorso_in.name
def configura_argomenti() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Converte una cartella di foto (o un singolo file) in bianco e nero."
)
parser.add_argument("-i", "--input", type=Path, default=DEFAULT_INPUT_DIR,
help='Cartella o file di origine (predefinito: "foto da trasformare")')
parser.add_argument("-o", "--output", type=Path, default=DEFAULT_OUTPUT_DIR,
help='Cartella di destinazione (predefinito: "foto in bianco e nero")')
parser.add_argument("-q", "--qualita", type=int, default=98,
help="Qualità di salvataggio JPEG/WebP da 70 a 100 (predefinito: 98)")
return parser.parse_args()
def main() -> int:
args = configura_argomenti()
qualita = max(70, min(100, args.qualita))
print("=" * 62)
print(" CONVERSIONE FOTO IN BIANCO E NERO — PIPELINE A 7 STADI")
print("=" * 62)
if args.input == DEFAULT_INPUT_DIR and args.output == DEFAULT_OUTPUT_DIR:
crea_cartelle(DEFAULT_INPUT_DIR, DEFAULT_OUTPUT_DIR)
else:
args.output.mkdir(parents=True, exist_ok=True)
immagini = trova_immagini(args.input)
if not immagini:
print(f"\nNessuna immagine trovata in:\n {args.input}")
print("\nCome procedere:")
print(" 1. Copia le tue foto dentro la cartella «foto da trasformare»")
print(" 2. Rilancia questo programma")
print("-" * 62)
return 0
ok, errori = 0, 0
inizio = time.perf_counter()
print(f"\nTrovate {len(immagini)} immagini. Elaborazione in corso...\n")
for percorso in immagini:
print(f" → {percorso.name} ... ", end="", flush=True)
bgr = carica(percorso)
if bgr is None:
print("ERRORE (file non leggibile o formato non supportato)")
errori += 1
continue
try:
out = percorso_output(percorso, args.output)
bn, info = elabora(bgr)
salva(bn, out, qualita=qualita)
print(
f"OK → {out.name} "
f"[esposizione {info['profilo']}, P50={info['p50']}, dinamica={info['range_dyn']}]"
)
ok += 1
except Exception as exc:
print(f"ERRORE ({exc})")
errori += 1
durata = time.perf_counter() - inizio
print("\n" + "-" * 62)
print(f"Completate: {ok} | Errori: {errori} | Tempo: {durata:.2f}s")
print(f"Cartella di output: {args.output}")
print("-" * 62)
return 1 if errori else 0
if __name__ == "__main__":
raise SystemExit(main())
Tre dettagli tecnici che evitano i problemi più frequenti
- Foto verticali dello smartphone sempre dritte (
ImageOps.exif_transpose): molte fotocamere e telefoni salvano lo scatto in orizzontale e annotano la rotazione solo nel tag EXIFOrientation. La funzionecarica()applica primaexif_transposedi Pillow: così un ritratto verticale resta verticale anche dopo il passaggio in OpenCV. - Nomi file e cartelle con accenti su Windows (
np.fromfile+cv2.imencode): su Windows le funzioni classichecv2.imread()ecv2.imwrite()di OpenCV usano le API C ANSI e falliscono se il percorso contiene lettere accentate (comeC:\Users\Niccolò\...oCittà.jpg). Passare danp.fromfileebuffer.tofilerisolve il problema alla radice su tutti i sistemi operativi. - Conversione automatica in
.pngper formati contenitore: se nella cartella di ingresso metti un file.heic,.avif,.gifo.bmp,percorso_output()salva il risultato come.pnglossless nella cartella di uscita, mentre mantiene invariata l’estensione originale per.jpg,.png,.webpe.tiff.
I flag da riga di comando uno per uno
Anche se lo script nasce per funzionare senza argomenti usando le due cartelle predefinite, accetta tre opzioni gestite con argparse:
-i/--input: indica una cartella diversa da"foto da trasformare"oppure un singolo file immagine (es.-i "C:\Foto\Vacanze"o-i ritratto.jpg).-o/--output: indica la cartella in cui salvare le foto convertite (se non esiste viene creata automaticamente).-q/--qualita: regola la compressione per i file.jpge.webpsu una scala da70a100(il valore predefinito è98, pensato per la stampa e l’archiviazione fotografica; se le foto devono andare su una pagina web puoi scendere a-q 85per dimezzare il peso in KB).
Esempio — Convertire una cartella esterna salvando per il web (-q 85):
.venv\Scripts\python.exe converti_bn.py -i "C:\Users\Alex\Pictures\Evento" -o "C:\Users\Alex\Pictures\Evento_BN" -q 85
Errori comuni e come risolverli
Su macOS compare «“Avvia conversione.command” non può essere aperto perché proviene da uno sviluppatore non identificato».
Perché succede: macOS applica il flag com.apple.quarantine agli script scaricati dal browser.
Come risolvere: apri il Terminale nella cartella bn-foto-locale e lancia chmod +x "Avvia conversione.command" seguito da xattr -d com.apple.quarantine "Avvia conversione.command". In alternativa, fai clic destro sul file tenendo premuto il tasto Ctrl e scegli Apri.
I file .heic dell’iPhone danno ERRORE (file non leggibile).
Perché succede: Pillow e OpenCV non includono di serie il decoder brevettato HEIF/HEIC.
Come risolvere: lo script è già predisposto per agganciare automaticamente pillow-heif se presente. Ti basta installarlo una volta sola nel .venv con .venv\Scripts\python.exe -m pip install pillow-heif (Windows) oppure .venv/bin/python -m pip install pillow-heif (macOS/Linux).
No module named 'cv2' quando lanci python converti_bn.py dal terminale.
Perché succede: stai chiamando il Python globale del sistema invece di quello dentro .venv dove sono installate le librerie.
Come risolvere: chiama l’interprete del venv (.venv\Scripts\python.exe converti_bn.py su Windows, .venv/bin/python converti_bn.py su macOS/Linux) oppure fai doppio clic sui launcher Avvia conversione.bat / Avvia conversione.command.
Domande Frequenti (FAQ)
Le mie foto originali vengono modificate o sovrascritte?
No. Lo script apre i file dentro foto da trasformare/ esclusivamente in lettura e scrive le nuove immagini dentro foto in bianco e nero/. Anche se rilanci il programma più volte, i tuoi scatti a colori originali restano intatti.
Quante foto posso mettere insieme nella cartella?
Non c’è un limite fisso: lo script elabora un’immagine alla volta e libera la memoria RAM subito dopo il salvataggio. Nei miei test su foto da 12–24 megapixel impiega circa 0,10–0,25 secondi a scatto; una cartella da 100 foto viene completata in meno di mezzo minuto.
Perché il bianco e nero di questo script sembra più nitido e tridimensionale?
Perché combina due interventi complementari: i livelli globali (che assicurano la presenza di un vero punto di nero e di un vero punto di bianco) e l’equalizzazione CLAHE su griglia 8×8 (che esalta il micro-contrasto locale su trame, tessuti, architetture e volti senza bruciare le alte luci).
Posso regolare l’intensità dell’effetto se lo trovo troppo contrastato?
Sì. Ti basta aprire converti_bn.py con qualsiasi editor di testo e guardare la funzione analizza(): abbassando il valore di clip_clahe (per esempio da 2.2 a 1.4) otterrai un bianco e nero più morbido e delicato; aumentando boost nella funzione ritocchino() da 0.05 a 0.08 otterrai invece una curva a S ancora più marcata in stile pellicola ad alto contrasto.
Cosa succede ai metadati EXIF nelle foto convertite?
Durante il passaggio in matrice NumPy e la ricodifica finale con cv2.imencode, i metadati EXIF (inclusi gli eventuali dati GPS dello smartphone) non vengono copiati nel nuovo file generato in foto in bianco e nero/. Se invece vuoi pulire gli EXIF dalle tue foto mantenendole a colori, usa il progetto dedicato per rimuovere i metadati EXIF dalle foto.