Skip to content

Commit 6f9cb2a

Browse files
committed
UI redesign, Added image background removal, Added discord webhook
1 parent a75324e commit 6f9cb2a

File tree

5 files changed

+191
-23
lines changed

5 files changed

+191
-23
lines changed

app.py

+40-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
from flask import Flask, render_template, request, send_file
22
from PIL import Image
3-
from flask import send_file
43
Image.MAX_IMAGE_PIXELS = None
4+
from rembg import remove
55
import os
6+
import io
7+
from discord_webhook import DiscordWebhook
68

79
app = Flask(__name__)
810

11+
webhook_url = ""
12+
913
@app.route('/')
1014
def index():
15+
client_ip = request.remote_addr
16+
webhook = DiscordWebhook(url=webhook_url, content=f'Un usuario ha abierto la página. IP: {client_ip}')
17+
webhook.execute()
1118
return render_template('index.html')
1219

13-
1420
@app.route('/convert', methods=['POST'])
1521
def convert_image():
1622
file = request.files['file']
@@ -24,6 +30,8 @@ def convert_image():
2430

2531
# Enviar archivo y luego eliminarlo
2632
try:
33+
webhook = DiscordWebhook(url=webhook_url, content='Un usuario ha convertido una imagen')
34+
webhook.execute()
2735
return send_file(filename, as_attachment=True)
2836
finally:
2937
os.remove(filename)
@@ -46,13 +54,41 @@ def resize_image():
4654

4755
# Enviar archivo y luego eliminarlo
4856
try:
57+
webhook = DiscordWebhook(url=webhook_url, content='Un usuario ha redimensionado una imagen')
58+
webhook.execute()
4959
return send_file(filename, as_attachment=True)
5060
finally:
5161
os.remove(filename)
5262
else:
5363
return 'Error: Todos los campos son requeridos.'
54-
64+
65+
@app.route('/remove_background', methods=['POST'])
66+
def remove_background():
67+
file = request.files['file']
68+
69+
if file:
70+
print("Recibido archivo")
71+
img_bytes = file.read()
72+
print("Leídos bytes del archivo")
73+
output_bytes = remove(img_bytes)
74+
print("Fondo removido")
75+
output_img = Image.open(io.BytesIO(output_bytes))
76+
filename = 'background_removed.png'
77+
output_img.save(filename)
78+
print("Imagen guardada")
79+
80+
# Enviar archivo y luego eliminarlo
81+
try:
82+
print("Enviando archivo")
83+
webhook = DiscordWebhook(url=webhook_url, content='Un usuario ha removido el fondo de una imagen')
84+
webhook.execute()
85+
return send_file(filename, as_attachment=True)
86+
finally:
87+
print("Eliminando archivo")
88+
os.remove(filename)
89+
else:
90+
print("Error: Todos los campos son requeridos.")
91+
return 'Error: Todos los campos son requeridos.'
5592

5693
if __name__ == '__main__':
5794
app.run(host='0.0.0.0', debug=True)
58-

backgroundremover.py

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import os
2+
from datetime import datetime
3+
from rembg import remove
4+
5+
class BackgroundRemover:
6+
def __init__(self, input_folder, output_folder):
7+
self.input_folder = input_folder
8+
self.output_folder = output_folder
9+
10+
def process_images(self):
11+
today = datetime.now().strftime('%Y-%m-%d %H-%M-%S')
12+
processed_folder = os.path.join(self.output_folder, today)
13+
os.makedirs(processed_folder, exist_ok=True)
14+
15+
for filename in os.listdir(self.input_folder):
16+
if filename.endswith(('.png', '.jpg', '.jpeg',)):
17+
input_path = os.path.join(self.input_folder, filename)
18+
output_path = os.path.join(processed_folder, filename)
19+
try:
20+
self._remove_background(input_path, output_path)
21+
self._move_originals(input_path, processed_folder)
22+
except Exception as e:
23+
print(f"Error processing {filename}: {e}")
24+
25+
def _remove_background(self, input_p, output_p):
26+
with open(input_p, 'rb') as inp, open(output_p, 'wb') as outp:
27+
background_output = remove(inp.read())
28+
outp.write(background_output)
29+
30+
def _move_originals(self, input_p, dest_p):
31+
originals_folder = os.path.join(dest_p, 'originals')
32+
os.makedirs(originals_folder, exist_ok=True)
33+
34+
filename = os.path.basename(input_p)
35+
new_path = os.path.join(originals_folder, filename)
36+
os.rename(input_p, new_path)
37+
38+
if __name__ == '__main__':
39+
input_folder = "input"
40+
output_folder = "output"
41+
42+
remover = BackgroundRemover(input_folder, output_folder)
43+
remover.process_images()

static/css/styles.css

+31-9
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
.active {
2-
background-color: #0d6efd !important;
2+
background-color: #3498db !important;
33
color: white;
44
}
55

66
body {
77
font-family: 'Arial', sans-serif;
8-
background-color: #f8f9fa;
8+
background-color: #ecf0f1;
99
}
1010

1111
h1 {
12-
color: #343a40;
12+
color: #2c3e50;
1313
}
1414

1515
.card {
@@ -20,33 +20,34 @@ h1 {
2020

2121
.btn {
2222
border-radius: 5px;
23+
transition: background-color 0.3s, color 0.3s;
2324
}
2425

2526
.btn-secondary {
26-
background-color: #343a40;
27+
background-color: #2c3e50;
2728
border: none;
2829
}
2930

3031
.btn-secondary:hover {
31-
background-color: #23272b;
32+
background-color: #1a252f;
3233
}
3334

3435
.btn-secondary.active {
35-
background-color: #0d6efd;
36+
background-color: #3498db;
3637
border: none;
3738
}
3839

3940
.btn-primary {
40-
background-color: #0d6efd;
41+
background-color: #3498db;
4142
border: none;
4243
}
4344

4445
.btn-primary:hover {
45-
background-color: #0056b3;
46+
background-color: #2980b9;
4647
}
4748

4849
.form-label {
49-
color: #343a40;
50+
color: #2c3e50;
5051
}
5152

5253
.form-control {
@@ -80,3 +81,24 @@ h1 {
8081
color: black;
8182
}
8283

84+
85+
.footer {
86+
background-color: #f8f9fa;
87+
border-top: 1px solid #e7e7e7;
88+
}
89+
90+
.footer a {
91+
margin: 0 5px;
92+
}
93+
94+
.footer img {
95+
vertical-align: middle;
96+
}
97+
98+
.footer .text-right {
99+
text-align: right;
100+
margin-left: auto;
101+
}
102+
103+
104+

static/js/scripts.js

+39-5
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,28 @@ function submitForm(formId) {
1212
xhr.upload.onprogress = function(event) {
1313
if (event.lengthComputable) {
1414
var percentage = (event.loaded / event.total) * 100;
15-
document.getElementById('progress').innerText = '' + percentage.toFixed(2) + '%';
15+
document.getElementById('progress').innerText = 'Cargando imagen: ' + percentage.toFixed(2) + '%';
16+
if (percentage === 100) {
17+
document.getElementById('progress').innerText = 'Procesando imagen...';
18+
}
1619
}
1720
};
1821

1922
xhr.onreadystatechange = function() {
2023
if (xhr.readyState == 4 && xhr.status == 200) {
21-
var blob = new Blob([xhr.response], { type: 'application/octet-stream' });
24+
var blob;
25+
if (formId === 'removeBackgroundForm') {
26+
blob = new Blob([xhr.response], { type: 'image/png' });
27+
} else {
28+
blob = new Blob([xhr.response], { type: 'application/octet-stream' });
29+
}
2230
var link = document.createElement('a');
2331
link.href = window.URL.createObjectURL(blob);
24-
var fileInput = formId === 'resizeForm' ? document.getElementById('file') : document.getElementById('convertFile');
32+
var fileInput = formId === 'resizeForm' ? document.getElementById('file') : (formId === 'convertForm' ? document.getElementById('convertFile') : document.getElementById('removeBackgroundFile'));
2533
var fileName = fileInput.files[0].name;
2634
var fileExtension = fileName.split('.').pop();
27-
var format = formId === 'resizeForm' ? 'resizeFormat' : 'convertFormat';
28-
var selectedFormat = document.getElementById(format).value.toLowerCase();
35+
var format = formId === 'resizeForm' ? 'resizeFormat' : (formId === 'convertForm' ? 'convertFormat' : 'png');
36+
var selectedFormat = document.getElementById(format) ? document.getElementById(format).value.toLowerCase() : 'png';
2937
link.download = fileName.replace(fileExtension, selectedFormat);
3038
link.click();
3139
document.getElementById('loading').style.display = 'none';
@@ -41,16 +49,42 @@ function submitForm(formId) {
4149
}
4250

4351

52+
53+
54+
4455
document.getElementById('resizeButton').addEventListener('click', function () {
4556
document.getElementById('resizeDiv').style.display = 'block';
4657
document.getElementById('convertDiv').style.display = 'none';
58+
document.getElementById('removeBackgroundDiv').style.display = 'none';
4759
document.getElementById('resizeButton').classList.add('active');
4860
document.getElementById('convertButton').classList.remove('active');
61+
document.getElementById('removeBackgroundButton').classList.remove('active');
4962
});
5063

5164
document.getElementById('convertButton').addEventListener('click', function () {
5265
document.getElementById('convertDiv').style.display = 'block';
5366
document.getElementById('resizeDiv').style.display = 'none';
67+
document.getElementById('removeBackgroundDiv').style.display = 'none';
5468
document.getElementById('convertButton').classList.add('active');
5569
document.getElementById('resizeButton').classList.remove('active');
70+
document.getElementById('removeBackgroundButton').classList.remove('active');
5671
});
72+
73+
document.getElementById('removeBackgroundButton').addEventListener('click', function () {
74+
document.getElementById('removeBackgroundDiv').style.display = 'block';
75+
document.getElementById('resizeDiv').style.display = 'none';
76+
document.getElementById('convertDiv').style.display = 'none';
77+
document.getElementById('removeBackgroundButton').classList.add('active');
78+
document.getElementById('resizeButton').classList.remove('active');
79+
document.getElementById('convertButton').classList.remove('active');
80+
});
81+
82+
document.getElementById('teramont-logo').addEventListener('mouseover', function () {
83+
this.style.filter = 'grayscale(0%)';
84+
});
85+
86+
document.getElementById('teramont-logo').addEventListener('mouseout', function () {
87+
this.style.filter = 'grayscale(100%)';
88+
});
89+
90+

templates/index.html

+38-5
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@
77
<title>Redimensionador y Convertidor de Imágenes</title>
88
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
99
<link rel="stylesheet" href="/static/css/styles.css">
10+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
1011
</head>
1112

1213
<body class="container mt-5">
1314
<h1 class="text-center">Redimensionador y Convertidor de Imágenes</h1>
1415

1516
<div class="d-flex justify-content-center my-4">
16-
<button id="resizeButton" class="btn btn-secondary mx-2">Redimensionar Imagen</button>
17-
<button id="convertButton" class="btn btn-secondary mx-2">Convertir Formato de Imagen</button>
17+
<button id="resizeButton" class="btn btn-secondary mx-2"><i class="fas fa-expand-arrows-alt"></i> Redimensionar Imagen</button>
18+
<button id="convertButton" class="btn btn-secondary mx-2"><i class="fas fa-exchange-alt"></i> Convertir Formato de Imagen</button>
19+
<button id="removeBackgroundButton" class="btn btn-secondary mx-2"><i class="fas fa-cut"></i> Remover fondo de imagen</button>
1820
</div>
1921

2022
<div id="resizeDiv" class="card p-4 mb-4">
@@ -40,7 +42,7 @@ <h2>Redimensionar Imagen</h2>
4042
<option value="WEBP">WEBP</option>
4143
</select>
4244
</div>
43-
<button type="submit" class="btn btn-primary">Redimensionar</button>
45+
<button type="submit" class="btn btn-primary"><i class="fas fa-expand-arrows-alt"></i> Redimensionar</button>
4446
</form>
4547
</div>
4648

@@ -61,14 +63,45 @@ <h2>Convertir Formato de Imagen</h2>
6163
</div>
6264
<button type="submit" class="btn btn-primary">Convertir</button>
6365
</form>
64-
</div>
66+
</div>
67+
68+
<div id="removeBackgroundDiv" class="card p-4 mb-4" style="display:none;">
69+
<h2>Remover Fondo de Imagen</h2>
70+
<form id="removeBackgroundForm" action="/remove_background" method="POST" enctype="multipart/form-data" onsubmit="return submitForm('removeBackgroundForm')" class="mb-4 needs-validation" novalidate>
71+
<div class="mb-3">
72+
<label for="removeBackgroundFile" class="form-label">Selecciona una imagen:</label>
73+
<input type="file" class="form-control" id="removeBackgroundFile" name="file" required>
74+
</div>
75+
<button type="submit" class="btn btn-primary">Remover Fondo</button>
76+
</form>
77+
</div>
6578

6679
<div id="loading" class="text-center">
6780
<div class="spinner-border text-primary" role="status">
6881
<span class="sr-only"></span>
6982
</div>
7083
<div id="progress" class="progress-text"></div>
71-
</div>
84+
</div>
85+
86+
<footer class="footer mt-auto py-3">
87+
<div class="container">
88+
<div class="row">
89+
<div class="col-md-6">
90+
<span>Herramienta desarrollada por Mizael S.</span>
91+
<a href="https://github.com/MizaelS/Image-Processing-Web-App" target="_blank">
92+
<i class="fab fa-github"></i>
93+
</a>
94+
</div>
95+
<div class="col-md-6 text-right">
96+
<span>Powered by</span>
97+
<a href="https://www.teramont.net/" target="_blank">
98+
<img src="https://www.teramont.net/assets/images/theme/logo/teramont-logo.png" alt="Teramont Logo" width="100">
99+
</a>
100+
</div>
101+
</div>
102+
</div>
103+
</footer>
104+
72105

73106
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
74107
<script src="/static/js/scripts.js"></script>

0 commit comments

Comments
 (0)