Este script es una aplicación gráfica desarrollada en Python que permite eliminar los metadatos EXIF de imágenes. Los metadatos EXIF contienen información como la fecha de captura, ubicación GPS, configuración de la cámara y otros datos sensibles que pueden comprometer la privacidad.
La aplicación cuenta con una interfaz intuitiva que permite procesar:
El script requiere las siguientes librerías de Python:
PySide6 (para la interfaz gráfica)Pillow (PIL) (para el procesamiento de imágenes)Formatos de imagen admitidos:
pip install PySide6 Pillow
eliminar_metadatos_imagen.py a una carpeta en su equipo.Para ejecutar la aplicación:
eliminar_metadatos_imagen.py.python eliminar_metadatos_imagen.py
Si está utilizando Python 3 explícitamente, use:
python3 eliminar_metadatos_imagen.py
Se abrirá una ventana gráfica con la interfaz de usuario.
La aplicación consta de los siguientes elementos:
Durante el procesamiento, el programa:
Nota: El proceso modifica directamente los archivos originales. No se crean copias de respaldo automáticas.
Este script es software de código abierto distribuido bajo la licencia MIT. Puede ser modificado y distribuido libremente, siempre que se mantenga la atribución original.
Para reportar errores o solicitar mejoras, consulte la documentación del proyecto o contacte al desarrollador.
Versión del manual: 1.0
Última actualización: Septiembre 2025
A continuación se proporciona el código completo del script en Python:
import os
import sys
from PySide6.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit,
QPushButton, QFileDialog, QTextEdit, QProgressBar
)
from PySide6.QtCore import Qt, QThread, Signal
from PIL import Image, ExifTags
class RemoveMetadataThread(QThread):
progress = Signal(int)
log = Signal(str)
def __init__(self, files, directory_name="directorios"):
super().__init__()
self.files = files
self.directory_name = directory_name
def run(self):
total_files = len(self.files)
if total_files == 0:
self.log.emit("No se encontraron imágenes.")
return
self.log.emit(f"Encontradas {total_files} imágenes en {self.directory_name}. Procesando...")
for i, filepath in enumerate(self.files):
filename = os.path.basename(filepath)
try:
self.log.emit(f"Procesando: {filename}")
with Image.open(filepath) as img:
# Extraer metadatos EXIF
exif_data = {}
exif = img._getexif()
if exif:
for tag, value in exif.items():
tag_name = ExifTags.TAGS.get(tag, tag)
exif_data[tag_name] = value
if exif_data:
self.log.emit("Metadatos encontrados:")
for k, v in exif_data.items():
v_str = str(v)
if len(v_str) > 50:
v_str = v_str[:50] + "..."
self.log.emit(f" {k}: {v_str}")
else:
self.log.emit("Sin metadatos EXIF encontrados.")
# Eliminar metadatos
img.save(filepath, exif=b'')
self.log.emit("Metadatos eliminados.")
self.log.emit("-" * 40)
except Exception as e:
self.log.emit(f"Error procesando {filename}: {str(e)}")
self.log.emit("-" * 40)
self.progress.emit(int((i + 1) / total_files * 100))
self.log.emit("Proceso completado.")
class MetadataRemoverApp(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Eliminar Metadatos de Imágenes")
self.setGeometry(300, 300, 600, 400)
layout = QVBoxLayout()
# Etiqueta y campo para el directorio
dir_layout = QHBoxLayout()
dir_layout.addWidget(QLabel("Directorio:"))
self.dir_edit = QLineEdit()
dir_layout.addWidget(self.dir_edit)
browse_btn = QPushButton("Examinar")
browse_btn.clicked.connect(self.browse_directory)
dir_layout.addWidget(browse_btn)
layout.addLayout(dir_layout)
# Etiqueta y campo para imagen suelta
file_layout = QHBoxLayout()
file_layout.addWidget(QLabel("Imagen:"))
self.file_edit = QLineEdit()
file_layout.addWidget(self.file_edit)
browse_file_btn = QPushButton("Examinar Archivo")
browse_file_btn.clicked.connect(self.browse_file)
file_layout.addWidget(browse_file_btn)
layout.addLayout(file_layout)
# Área de log
self.log_text = QTextEdit()
self.log_text.setReadOnly(True)
layout.addWidget(self.log_text)
# Barra de progreso
self.progress_bar = QProgressBar()
layout.addWidget(self.progress_bar)
# Botones
btn_layout = QHBoxLayout()
self.process_btn = QPushButton("Eliminar Metadatos de Directorio")
self.process_btn.clicked.connect(self.remove_metadata_directory)
btn_layout.addWidget(self.process_btn)
self.process_file_btn = QPushButton("Eliminar Metadatos de Imagen")
self.process_file_btn.clicked.connect(self.remove_metadata_file)
btn_layout.addWidget(self.process_file_btn)
self.clear_btn = QPushButton("Limpiar")
self.clear_btn.clicked.connect(self.clear_log)
btn_layout.addWidget(self.clear_btn)
layout.addLayout(btn_layout)
self.setLayout(layout)
# Hilo para procesamiento
self.thread = None
def browse_directory(self):
directory = QFileDialog.getExistingDirectory(self, "Seleccionar Directorio")
if directory:
self.dir_edit.setText(directory)
def browse_file(self):
file_path, _ = QFileDialog.getOpenFileName(self, "Seleccionar Imagen", "", "Imágenes (*.jpg *.jpeg *.png *.bmp *.tiff *.tif)")
if file_path:
self.file_edit.setText(file_path)
def remove_metadata_directory(self):
directory = self.dir_edit.text().strip()
if not directory:
self.log_text.append("Por favor, selecciona un directorio.")
return
if not os.path.isdir(directory):
self.log_text.append(f"El directorio '{directory}' no existe.")
return
image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif'}
files = [os.path.join(directory, f) for f in os.listdir(directory) if os.path.splitext(f)[1].lower() in image_extensions]
self.process_btn.setEnabled(False)
self.process_file_btn.setEnabled(False)
self.log_text.clear()
self.progress_bar.setValue(0)
self.thread = RemoveMetadataThread(files, os.path.basename(directory))
self.thread.progress.connect(self.progress_bar.setValue)
self.thread.log.connect(self.log_text.append)
self.thread.finished.connect(self.enable_buttons)
self.thread.start()
def remove_metadata_file(self):
file_path = self.file_edit.text().strip()
if not file_path:
self.log_text.append("Por favor, selecciona una imagen.")
return
if not os.path.isfile(file_path):
self.log_text.append(f"El archivo '{file_path}' no existe.")
return
image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif'}
if os.path.splitext(file_path)[1].lower() not in image_extensions:
self.log_text.append(f"El archivo '{file_path}' no es una imagen soportada.")
return
self.process_btn.setEnabled(False)
self.process_file_btn.setEnabled(False)
self.log_text.clear()
self.progress_bar.setValue(0)
self.thread = RemoveMetadataThread([file_path], "imagen suelta")
self.thread.progress.connect(self.progress_bar.setValue)
self.thread.log.connect(self.log_text.append)
self.thread.finished.connect(self.enable_buttons)
self.thread.start()
def clear_log(self):
self.log_text.clear()
def enable_buttons(self):
self.process_btn.setEnabled(True)
self.process_file_btn.setEnabled(True)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MetadataRemoverApp()
window.show()
sys.exit(app.exec())