Update app.py
Browse files
app.py
CHANGED
@@ -14,10 +14,9 @@ HTML_TEMPLATE = """
|
|
14 |
<!DOCTYPE html>
|
15 |
<html>
|
16 |
<head>
|
17 |
-
<title>ZeroIG Enhancement
|
18 |
<style>
|
19 |
body { font-family: Arial, sans-serif; max-width: 1000px; margin: 0 auto; padding: 20px; }
|
20 |
-
.debug { background: #f0f0f0; padding: 15px; margin: 10px 0; border-radius: 5px; font-family: monospace; }
|
21 |
.container { text-align: center; }
|
22 |
.upload-area { border: 2px dashed #ccc; padding: 40px; margin: 20px 0; border-radius: 10px; }
|
23 |
.result { margin-top: 20px; }
|
@@ -30,23 +29,14 @@ HTML_TEMPLATE = """
|
|
30 |
</head>
|
31 |
<body>
|
32 |
<div class="container">
|
33 |
-
<h1
|
34 |
-
|
35 |
-
<div class="debug">
|
36 |
-
<h3>π File Check:</h3>
|
37 |
-
{{ file_status }}
|
38 |
-
</div>
|
39 |
-
|
40 |
-
<div class="debug">
|
41 |
-
<h3>π Import Status:</h3>
|
42 |
-
{{ import_status }}
|
43 |
-
</div>
|
44 |
|
45 |
<form method="post" enctype="multipart/form-data">
|
46 |
<div class="upload-area">
|
47 |
<input type="file" name="image" accept="image/*" required>
|
48 |
<br><br>
|
49 |
-
<button type="submit" style="padding: 10px 20px; font-size: 16px;"
|
50 |
</div>
|
51 |
</form>
|
52 |
|
@@ -63,145 +53,150 @@ HTML_TEMPLATE = """
|
|
63 |
<h3>Results:</h3>
|
64 |
<div class="comparison">
|
65 |
<div class="image-container">
|
66 |
-
<h4>Original</h4>
|
67 |
<img src="data:image/png;base64,{{ original_image }}" alt="Original">
|
68 |
</div>
|
69 |
<div class="image-container">
|
70 |
-
<h4>Enhanced</h4>
|
71 |
<img src="data:image/png;base64,{{ result_image }}" alt="Enhanced">
|
|
|
|
|
|
|
|
|
|
|
72 |
</div>
|
73 |
</div>
|
74 |
</div>
|
75 |
{% endif %}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
76 |
</div>
|
77 |
</body>
|
78 |
</html>
|
79 |
"""
|
80 |
|
81 |
-
|
82 |
-
|
83 |
-
|
|
|
|
|
84 |
|
85 |
-
|
86 |
-
files_in_dir = os.listdir('.')
|
87 |
-
status.append(f"Files in current directory: {files_in_dir}")
|
88 |
-
|
89 |
-
# Check specific ZeroIG files
|
90 |
-
required_files = ['model.py', 'loss.py', 'utils.py']
|
91 |
-
for file in required_files:
|
92 |
-
if os.path.exists(file):
|
93 |
-
status.append(f"β
{file} - EXISTS")
|
94 |
-
# Check file size
|
95 |
-
size = os.path.getsize(file)
|
96 |
-
status.append(f" Size: {size} bytes")
|
97 |
-
else:
|
98 |
-
status.append(f"β {file} - MISSING")
|
99 |
-
|
100 |
-
# Check weights directory
|
101 |
-
if os.path.exists('weights'):
|
102 |
-
weights_files = os.listdir('weights')
|
103 |
-
status.append(f"π weights/ directory: {weights_files}")
|
104 |
-
else:
|
105 |
-
status.append("π weights/ directory: NOT FOUND")
|
106 |
-
|
107 |
-
return "<br>".join(status)
|
108 |
-
|
109 |
-
def check_imports():
|
110 |
-
"""Try importing ZeroIG modules and report results"""
|
111 |
-
status = []
|
112 |
-
|
113 |
-
try:
|
114 |
-
# Add current directory to path
|
115 |
-
import sys
|
116 |
-
sys.path.insert(0, '.')
|
117 |
-
status.append("β
Added current directory to Python path")
|
118 |
-
|
119 |
-
# Try importing each module
|
120 |
try:
|
121 |
-
|
122 |
-
|
123 |
|
124 |
-
#
|
125 |
-
|
126 |
-
|
|
|
|
|
|
|
|
|
127 |
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
except Exception as e:
|
140 |
-
status.append(f"β Failed to import model: {e}")
|
141 |
-
|
142 |
-
try:
|
143 |
-
import loss
|
144 |
-
status.append("β
Successfully imported 'loss' module")
|
145 |
-
except Exception as e:
|
146 |
-
status.append(f"β Failed to import loss: {e}")
|
147 |
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
|
|
|
|
|
|
|
|
153 |
|
154 |
-
|
155 |
-
|
|
|
|
|
|
|
|
|
|
|
156 |
|
157 |
-
|
158 |
-
|
159 |
-
def try_create_model():
|
160 |
-
"""Try to create ZeroIG model and report what happens"""
|
161 |
-
try:
|
162 |
-
sys.path.insert(0, '.')
|
163 |
-
|
164 |
-
from model import Network, Finetunemodel
|
165 |
-
|
166 |
-
# Try creating Network
|
167 |
-
network = Network()
|
168 |
-
print("β
Successfully created Network model")
|
169 |
-
|
170 |
-
# Try creating Finetunemodel (this will fail without weights, but should show the error)
|
171 |
try:
|
172 |
-
|
173 |
-
|
174 |
-
return finetuned, "Finetunemodel"
|
175 |
-
except Exception as e:
|
176 |
-
print(f"β οΈ Finetunemodel failed (expected without weights): {e}")
|
177 |
-
print("β
Using Network model instead")
|
178 |
-
return network, "Network"
|
179 |
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
print("
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
196 |
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
enhanced = np.clip(arr * 1.5, 0, 255).astype(np.uint8)
|
201 |
-
return Image.fromarray(enhanced)
|
202 |
|
203 |
def image_to_base64(image):
|
204 |
-
"""Convert PIL image to base64"""
|
205 |
img_buffer = io.BytesIO()
|
206 |
image.save(img_buffer, format='PNG')
|
207 |
img_str = base64.b64encode(img_buffer.getvalue()).decode()
|
@@ -218,56 +213,34 @@ def index():
|
|
218 |
try:
|
219 |
file = request.files['image']
|
220 |
if file:
|
221 |
-
print(f"Processing: {file.filename}")
|
222 |
|
|
|
223 |
image = Image.open(file.stream).convert('RGB')
|
|
|
|
|
|
|
224 |
original_image = image_to_base64(image)
|
225 |
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
transform = transforms.ToTensor()
|
234 |
-
input_tensor = transform(image).unsqueeze(0).to(device)
|
235 |
-
|
236 |
-
with torch.no_grad():
|
237 |
-
if hasattr(model, 'enhance') and hasattr(model, 'denoise_1'):
|
238 |
-
enhanced, denoised = model(input_tensor)
|
239 |
-
result_tensor = denoised
|
240 |
-
else:
|
241 |
-
outputs = model(input_tensor)
|
242 |
-
result_tensor = outputs[13]
|
243 |
-
|
244 |
-
result_tensor = result_tensor.squeeze(0).cpu().clamp(0, 1)
|
245 |
-
enhanced_image = transforms.ToPILImage()(result_tensor)
|
246 |
-
result_image = image_to_base64(enhanced_image)
|
247 |
-
status = f"β
Used {model_status} model successfully!"
|
248 |
-
|
249 |
-
except Exception as e:
|
250 |
-
print(f"Model processing error: {e}")
|
251 |
-
enhanced_image = simple_enhance(image)
|
252 |
-
result_image = image_to_base64(enhanced_image)
|
253 |
-
status = f"β οΈ Model failed, used simple enhancement: {e}"
|
254 |
-
else:
|
255 |
-
enhanced_image = simple_enhance(image)
|
256 |
-
result_image = image_to_base64(enhanced_image)
|
257 |
-
status = "β οΈ No model available, used simple enhancement"
|
258 |
-
|
259 |
except Exception as e:
|
260 |
-
error = f"Error: {e}"
|
261 |
print(f"Error: {e}")
|
|
|
|
|
262 |
|
263 |
-
return render_template_string(HTML_TEMPLATE,
|
264 |
-
file_status=file_check,
|
265 |
-
import_status=import_check,
|
266 |
original_image=original_image,
|
267 |
-
result_image=result_image,
|
268 |
-
status=status,
|
269 |
error=error)
|
270 |
|
271 |
if __name__ == '__main__':
|
272 |
-
print("π
|
273 |
app.run(host='0.0.0.0', port=7860)
|
|
|
14 |
<!DOCTYPE html>
|
15 |
<html>
|
16 |
<head>
|
17 |
+
<title>ZeroIG Enhancement</title>
|
18 |
<style>
|
19 |
body { font-family: Arial, sans-serif; max-width: 1000px; margin: 0 auto; padding: 20px; }
|
|
|
20 |
.container { text-align: center; }
|
21 |
.upload-area { border: 2px dashed #ccc; padding: 40px; margin: 20px 0; border-radius: 10px; }
|
22 |
.result { margin-top: 20px; }
|
|
|
29 |
</head>
|
30 |
<body>
|
31 |
<div class="container">
|
32 |
+
<h1>π ZeroIG: Zero-Shot Low-Light Enhancement</h1>
|
33 |
+
<p><strong>CVPR 2024</strong> - Upload a low-light image for professional enhancement!</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
|
35 |
<form method="post" enctype="multipart/form-data">
|
36 |
<div class="upload-area">
|
37 |
<input type="file" name="image" accept="image/*" required>
|
38 |
<br><br>
|
39 |
+
<button type="submit" style="padding: 10px 20px; font-size: 16px;">π Enhance with ZeroIG</button>
|
40 |
</div>
|
41 |
</form>
|
42 |
|
|
|
53 |
<h3>Results:</h3>
|
54 |
<div class="comparison">
|
55 |
<div class="image-container">
|
56 |
+
<h4>Original (Low-light)</h4>
|
57 |
<img src="data:image/png;base64,{{ original_image }}" alt="Original">
|
58 |
</div>
|
59 |
<div class="image-container">
|
60 |
+
<h4>ZeroIG Enhanced</h4>
|
61 |
<img src="data:image/png;base64,{{ result_image }}" alt="Enhanced">
|
62 |
+
<br><br>
|
63 |
+
<a href="data:image/png;base64,{{ result_image }}" download="zeroig_enhanced.png"
|
64 |
+
style="background: #007bff; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px;">
|
65 |
+
π₯ Download Enhanced Image
|
66 |
+
</a>
|
67 |
</div>
|
68 |
</div>
|
69 |
</div>
|
70 |
{% endif %}
|
71 |
+
|
72 |
+
<div style="margin-top: 40px; padding: 20px; background: #f8f9fa; border-radius: 10px;">
|
73 |
+
<h3>About ZeroIG</h3>
|
74 |
+
<p>Zero-shot illumination-guided joint denoising and adaptive enhancement for low-light images.</p>
|
75 |
+
<p><strong>Features:</strong> No training data required β’ Joint denoising & enhancement β’ Prevents over-enhancement</p>
|
76 |
+
<p>
|
77 |
+
π <a href="https://openaccess.thecvf.com/content/CVPR2024/papers/Shi_ZERO-IG_Zero-Shot_Illumination-Guided_Joint_Denoising_and_Adaptive_Enhancement_for_Low-Light_CVPR_2024_paper.pdf">Research Paper</a> |
|
78 |
+
π» <a href="https://github.com/Doyle59217/ZeroIG">Source Code</a>
|
79 |
+
</p>
|
80 |
+
</div>
|
81 |
</div>
|
82 |
</body>
|
83 |
</html>
|
84 |
"""
|
85 |
|
86 |
+
class ZeroIGProcessor:
|
87 |
+
def __init__(self):
|
88 |
+
self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
89 |
+
self.model = self.load_model()
|
90 |
+
print(f"ZeroIG initialized on {self.device}")
|
91 |
|
92 |
+
def load_model(self):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
try:
|
94 |
+
# Import your uploaded ZeroIG files
|
95 |
+
from model import Finetunemodel, Network
|
96 |
|
97 |
+
# Try to load trained weights - check multiple possible paths
|
98 |
+
possible_weights = [
|
99 |
+
"./weights/LOL.pt", # Your trained model
|
100 |
+
"./weights/model.pt", # Generic name
|
101 |
+
"./LOL.pt", # If uploaded to root
|
102 |
+
"./model.pt" # If uploaded to root
|
103 |
+
]
|
104 |
|
105 |
+
loaded_model = None
|
106 |
+
for weight_path in possible_weights:
|
107 |
+
if os.path.exists(weight_path):
|
108 |
+
print(f"Found model weights at {weight_path}")
|
109 |
+
try:
|
110 |
+
loaded_model = Finetunemodel(weight_path)
|
111 |
+
print(f"β
Loaded ZeroIG Finetunemodel from {weight_path}")
|
112 |
+
break
|
113 |
+
except Exception as e:
|
114 |
+
print(f"Failed to load {weight_path}: {e}")
|
115 |
+
continue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
116 |
|
117 |
+
if loaded_model is None:
|
118 |
+
print("No trained weights found, using Network with random initialization")
|
119 |
+
loaded_model = Network()
|
120 |
+
print("β οΈ Using ZeroIG Network with random weights - results will be poor")
|
121 |
+
|
122 |
+
loaded_model.to(self.device)
|
123 |
+
loaded_model.eval()
|
124 |
+
print(f"Model moved to {self.device}")
|
125 |
+
return loaded_model
|
126 |
|
127 |
+
except ImportError as e:
|
128 |
+
print(f"β Could not import ZeroIG modules: {e}")
|
129 |
+
print("Make sure you have uploaded: model.py, loss.py, utils.py")
|
130 |
+
return None
|
131 |
+
except Exception as e:
|
132 |
+
print(f"β Could not load ZeroIG model: {e}")
|
133 |
+
return None
|
134 |
|
135 |
+
def enhance_image(self, image):
|
136 |
+
"""Enhance image using your ZeroIG model"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
137 |
try:
|
138 |
+
if self.model is None:
|
139 |
+
return self.simple_enhance(image), "β ZeroIG model not available - using simple enhancement"
|
|
|
|
|
|
|
|
|
|
|
140 |
|
141 |
+
# Resize if too large to prevent memory issues
|
142 |
+
original_size = image.size
|
143 |
+
max_size = 800 # Adjust based on your needs
|
144 |
+
if max(image.size) > max_size:
|
145 |
+
ratio = max_size / max(image.size)
|
146 |
+
new_size = tuple(int(dim * ratio) for dim in image.size)
|
147 |
+
image = image.resize(new_size, Image.Resampling.LANCZOS)
|
148 |
+
print(f"Resized image from {original_size} to {image.size}")
|
149 |
+
|
150 |
+
# Convert to tensor
|
151 |
+
transform = transforms.ToTensor()
|
152 |
+
input_tensor = transform(image).unsqueeze(0).to(self.device)
|
153 |
+
print(f"Input tensor shape: {input_tensor.shape}")
|
154 |
+
|
155 |
+
# Run your ZeroIG model
|
156 |
+
with torch.no_grad():
|
157 |
+
if hasattr(self.model, 'enhance') and hasattr(self.model, 'denoise_1'):
|
158 |
+
# Finetunemodel - returns (enhanced, denoised)
|
159 |
+
enhanced, denoised = self.model(input_tensor)
|
160 |
+
result_tensor = denoised # Use denoised output
|
161 |
+
status = "β
Enhanced with ZeroIG Finetunemodel"
|
162 |
+
print("Used Finetunemodel")
|
163 |
+
else:
|
164 |
+
# Network model - returns multiple outputs
|
165 |
+
outputs = self.model(input_tensor)
|
166 |
+
result_tensor = outputs[13] # H3 is the final denoised result
|
167 |
+
status = "β
Enhanced with ZeroIG Network model"
|
168 |
+
print("Used Network model")
|
169 |
+
|
170 |
+
# Convert back to PIL
|
171 |
+
result_tensor = result_tensor.squeeze(0).cpu().clamp(0, 1)
|
172 |
+
enhanced_image = transforms.ToPILImage()(result_tensor)
|
173 |
+
print(f"Output image size: {enhanced_image.size}")
|
174 |
+
|
175 |
+
# Resize back to original size if needed
|
176 |
+
if enhanced_image.size != original_size and original_size != image.size:
|
177 |
+
enhanced_image = enhanced_image.resize(original_size, Image.Resampling.LANCZOS)
|
178 |
+
print(f"Resized back to original size: {enhanced_image.size}")
|
179 |
+
|
180 |
+
return enhanced_image, status
|
181 |
+
|
182 |
+
except Exception as e:
|
183 |
+
print(f"ZeroIG enhancement error: {e}")
|
184 |
+
import traceback
|
185 |
+
traceback.print_exc()
|
186 |
+
return self.simple_enhance(image), f"β οΈ ZeroIG failed, using simple enhancement: {str(e)}"
|
187 |
+
|
188 |
+
def simple_enhance(self, image):
|
189 |
+
"""Fallback simple enhancement"""
|
190 |
+
arr = np.array(image).astype(np.float32)
|
191 |
+
enhanced = np.clip(arr * 1.8, 0, 255).astype(np.uint8)
|
192 |
+
return Image.fromarray(enhanced)
|
193 |
|
194 |
+
# Initialize ZeroIG processor
|
195 |
+
print("π Loading ZeroIG processor...")
|
196 |
+
zeroig = ZeroIGProcessor()
|
|
|
|
|
197 |
|
198 |
def image_to_base64(image):
|
199 |
+
"""Convert PIL image to base64 string"""
|
200 |
img_buffer = io.BytesIO()
|
201 |
image.save(img_buffer, format='PNG')
|
202 |
img_str = base64.b64encode(img_buffer.getvalue()).decode()
|
|
|
213 |
try:
|
214 |
file = request.files['image']
|
215 |
if file:
|
216 |
+
print(f"Processing uploaded image: {file.filename}")
|
217 |
|
218 |
+
# Open and process image
|
219 |
image = Image.open(file.stream).convert('RGB')
|
220 |
+
print(f"Image size: {image.size}")
|
221 |
+
|
222 |
+
# Store original for comparison
|
223 |
original_image = image_to_base64(image)
|
224 |
|
225 |
+
# Enhance with your ZeroIG model
|
226 |
+
enhanced_image, enhancement_status = zeroig.enhance_image(image)
|
227 |
+
|
228 |
+
# Convert result to base64
|
229 |
+
result_image = image_to_base64(enhanced_image)
|
230 |
+
status = enhancement_status
|
231 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
232 |
except Exception as e:
|
233 |
+
error = f"Error processing image: {str(e)}"
|
234 |
print(f"Error: {e}")
|
235 |
+
import traceback
|
236 |
+
traceback.print_exc()
|
237 |
|
238 |
+
return render_template_string(HTML_TEMPLATE,
|
|
|
|
|
239 |
original_image=original_image,
|
240 |
+
result_image=result_image,
|
241 |
+
status=status,
|
242 |
error=error)
|
243 |
|
244 |
if __name__ == '__main__':
|
245 |
+
print("π Starting ZeroIG Flask app...")
|
246 |
app.run(host='0.0.0.0', port=7860)
|