mohamed12ahmed commited on
Commit
32f4e36
·
verified ·
1 Parent(s): af92fdd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +197 -75
app.py CHANGED
@@ -3,8 +3,6 @@ import cv2
3
  import torch
4
  from flask import Flask, request, jsonify, send_file, render_template_string
5
  from basicsr.archs.srvgg_arch import SRVGGNetCompact
6
- # استيراد shutil للتنظيف الآمن للملفات المؤقتة
7
- import shutil
8
  from gfpgan.utils import GFPGANer
9
  from realesrgan.utils import RealESRGANer
10
  import tempfile
@@ -12,27 +10,11 @@ import uuid
12
 
13
  app = Flask(__name__)
14
 
15
- # -------------------------------------------------------------------------
16
- # 🛠️ التعديل 1: فرض استخدام CPU وإلغاء half=True
17
- # -------------------------------------------------------------------------
18
- device = torch.device('cpu')
19
- print(f"Using device: {device}")
20
-
21
  # Initialize models
22
  model = SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=32, upscale=4, act_type='prelu')
23
  model_path = 'realesr-general-x4v3.pth'
24
- half = False # تم التغيير من الإعداد الديناميكي إلى False لـ CPU
25
- upsampler = RealESRGANer(
26
- scale=4,
27
- model_path=model_path,
28
- model=model,
29
- tile=0,
30
- tile_pad=10,
31
- pre_pad=0,
32
- half=half,
33
- device=device # 💡 تمرير device='cpu' هنا
34
- )
35
- # -------------------------------------------------------------------------
36
 
37
  # Ensure output directory exists
38
  os.makedirs('output', exist_ok=True)
@@ -50,8 +32,7 @@ def download_weights():
50
 
51
  for weight_file, url in weights.items():
52
  if not os.path.exists(weight_file):
53
- # تم التعديل لاستخدام curl بدلاً من wget في حالة عدم توافرها في البيئة
54
- os.system(f"curl -L {url} -o {weight_file}")
55
 
56
  download_weights()
57
 
@@ -67,33 +48,29 @@ def process_image(img_path, version, scale, weight=0.5):
67
  img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
68
  else:
69
  img_mode = None
70
-
71
  h, w = img.shape[0:2]
72
  if h < 300:
73
  img = cv2.resize(img, (w * 2, h * 2), interpolation=cv2.INTER_LANCZOS4)
74
-
75
- # -------------------------------------------------------------------------
76
- # 🛠️ التعديل 2: تمرير device='cpu' إلى GFPGANer في جميع الحالات
77
- # -------------------------------------------------------------------------
78
  if version == 'v1.2':
79
  face_enhancer = GFPGANer(
80
- model_path='GFPGANv1.2.pth', upscale=2, arch='clean', channel_multiplier=2, bg_upsampler=upsampler, device=device)
81
  elif version == 'v1.3':
82
  face_enhancer = GFPGANer(
83
- model_path='GFPGANv1.3.pth', upscale=2, arch='clean', channel_multiplier=2, bg_upsampler=upsampler, device=device)
84
  elif version == 'v1.4':
85
  face_enhancer = GFPGANer(
86
- model_path='GFPGANv1.4.pth', upscale=2, arch='clean', channel_multiplier=2, bg_upsampler=upsampler, device=device)
87
  elif version == 'RestoreFormer':
88
  face_enhancer = GFPGANer(
89
- model_path='RestoreFormer.pth', upscale=2, arch='RestoreFormer', channel_multiplier=2, bg_upsampler=upsampler, device=device)
90
  elif version == 'CodeFormer':
91
  face_enhancer = GFPGANer(
92
- model_path='CodeFormer.pth', upscale=2, arch='CodeFormer', channel_multiplier=2, bg_upsampler=upsampler, device=device)
93
  elif version == 'RealESR-General-x4v3':
94
  face_enhancer = GFPGANer(
95
- model_path='realesr-general-x4v3.pth', upscale=2, arch='realesr-general', channel_multiplier=2, bg_upsampler=upsampler, device=device)
96
- # -------------------------------------------------------------------------
97
 
98
  try:
99
  if version == 'CodeFormer':
@@ -103,7 +80,7 @@ def process_image(img_path, version, scale, weight=0.5):
103
  except RuntimeError as error:
104
  print('Error', error)
105
  raise Exception(f"Enhancement error: {str(error)}")
106
-
107
  try:
108
  if scale != 2:
109
  interpolation = cv2.INTER_AREA if scale < 2 else cv2.INTER_LANCZOS4
@@ -111,31 +88,134 @@ def process_image(img_path, version, scale, weight=0.5):
111
  output = cv2.resize(output, (int(w * scale / 2), int(h * scale / 2)), interpolation=interpolation)
112
  except Exception as error:
113
  print('wrong scale input.', error)
114
-
115
  # Save to temporary file
116
  output_filename = f"output_{uuid.uuid4().hex}.jpg"
117
- output_path = os.path.join(tempfile.gettempdir(), output_filename) # حفظ الإخراج في مجلد مؤقت آمن
118
 
119
  if img_mode == 'RGBA':
120
  cv2.imwrite(output_path, output, [int(cv2.IMWRITE_PNG_COMPRESSION), 9])
121
  else:
122
  cv2.imwrite(output_path, output, [int(cv2.IMWRITE_JPEG_QUALITY), 95])
123
-
124
  return output_path
125
-
126
  except Exception as error:
127
  print('Global exception', error)
128
  raise Exception(f"Processing error: {str(error)}")
129
 
130
  @app.route('/')
131
  def index():
132
- # كود HTML لم يتغير
133
- return render_template_string('''<!DOCTYPE html><html><head> <title>Image Upscaling & Restoration API</title> <style> body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; } .container { border: 1px solid #ddd; padding: 20px; border-radius: 5px; } .form-group { margin-bottom: 15px; } label { display: block; margin-bottom: 5px; } input, select { width: 100%; padding: 8px; box-sizing: border-box; } button { background-color: #4CAF50; color: white; padding: 10px 15px; border: none; border-radius: 4px; cursor: pointer; } button:hover { background-color: #45a049; } #result { margin-top: 20px; } #preview { max-width: 100%; margin-top: 10px; } #apiUsage { background-color: #f5f5f5; padding: 15px; border-radius: 5px; margin-top: 20px; font-family: monospace; white-space: pre-wrap; } #apiUsage h3 { margin-top: 0; } #formDataPreview { max-height: 200px; overflow-y: auto; margin-bottom: 10px; } .code-block { background-color: #f8f8f8; padding: 10px; border-radius: 4px; border-left: 3px solid #4CAF50; } .comment { color: #666; font-style: italic; } .loader { width: 48px; height: 48px; border: 5px solid #4CAF50; border-bottom-color: transparent; border-radius: 50%; display: inline-block; box-sizing: border-box; animation: rotation 1s linear infinite; margin: 20px auto; display: none; /* 初期状態では非表示 */ } @keyframes rotation { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } </style></head><body> <h1>Image Upscaling & Restoration API</h1> <div class="container"> <form id="uploadForm" enctype="multipart/form-data"> <div class="form-group"> <label for="file">Upload Image:</label> <input type="file" id="file" name="file" required> </div> <div class="form-group"> <label for="version">Version:</label> <select id="version" name="version"> <option value="v1.2">GFPGANv1.2</option> <option value="v1.3">GFPGANv1.3</option> <option value="v1.4" selected>GFPGANv1.4</option> <option value="RestoreFormer">RestoreFormer</option> <option value="CodeFormer">CodeFormer</option> <option value="RealESR-General-x4v3">RealESR-General-x4v3</option> </select> </div> <div class="form-group"> <label for="scale">Rescaling factor:</label> <input type="number" id="scale" name="scale" value="2" step="0.1" min="1" max="4" required> </div> <div class="form-group" id="weightGroup" style="display: none;"> <label for="weight">CodeFormer Weight (0-1):</label> <input type="number" id="weight" name="weight" value="0.5" step="0.1" min="0" max="1"> </div> <button type="submit" id="submitButton">Process Image</button> </form> <div id="loading" class="loader"></div> <div id="result"> <h3>Result:</h3> <div id="outputContainer" style="display: none;"> <img id="preview" src="" alt="Processed Image"> <a id="downloadLink" href="#" download>Download Image</a> </div> </div> <div id="apiUsage"> <h3>API Usage:</h3> <div id="fetchCode" class="code-block"> // JavaScript fetch code will appear here </div> </div> </div> <script> // CodeFormerが選択された時にweightパラメータを表示 document.getElementById('version').addEventListener('change', function() { const weightGroup = document.getElementById('weightGroup'); if (this.value === 'CodeFormer') { weightGroup.style.display = 'block'; } else { weightGroup.style.display = 'none'; } updateApiUsage(); }); // フォームの変更を監視してAPI使用例を更新 function updateApiUsage() { const fileInput = document.getElementById('file');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  const version = document.getElementById('version').value;
135
  const scale = document.getElementById('scale').value;
136
- const weight = document.getElementById('weight').value; // 現在のURLからベースURLを取得(パス、パラメータ、ハッシュを含めない)
 
 
137
  const baseUrl = window.location.origin;
138
- const apiUrl = baseUrl + '/api/restore'; // ファイルのプレビュー用文字列を準備
 
 
139
  let filePreview = '"img-dataURL"';
140
  if (fileInput.files.length > 0) {
141
  const file = fileInput.files[0];
@@ -152,32 +232,77 @@ def index():
152
  reader.readAsDataURL(file);
153
  } else {
154
  updateFetchCode(apiUrl, version, scale, weight, filePreview);
155
- } } function updateFetchCode(apiUrl, version, scale, weight, filePreview) {
 
 
 
156
  const fetchCodeDiv = document.getElementById('fetchCode');
157
- let code = `// JavaScript fetch example:const formData = new FormData();formData.append('file', ${filePreview});formData.append('version', '${version}');formData.append('scale', ${scale});`;
 
 
 
 
158
  if (version === 'CodeFormer') {
159
- code += `formData.append('weight', ${weight});`;
 
160
  }
161
- code += `fetch('${apiUrl}', { method: 'POST', body: formData}).then(response => { if (!response.ok) { return response.json().then(err => { throw new Error(err.error || 'Unknown error'); }); } return response.blob();}).then(blob => { // Process the returned image blob const url = URL.createObjectURL(blob); console.log('Image processed successfully', url); // Example: document.getElementById('resultImage').src = url;}).catch(error => { console.error('Error:', error.message);});`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  fetchCodeDiv.innerHTML = code;
163
- } // フォーム要素の変更を監視
 
 
164
  document.getElementById('file').addEventListener('change', updateApiUsage);
165
  document.getElementById('version').addEventListener('change', updateApiUsage);
166
  document.getElementById('scale').addEventListener('input', updateApiUsage);
167
- document.getElementById('weight').addEventListener('input', updateApiUsage); // 初期表示
168
- updateApiUsage(); document.getElementById('uploadForm').addEventListener('submit', function(e) {
169
- e.preventDefault(); // ボタンを無効化し、ローディングを表示
 
 
 
 
 
 
170
  const submitButton = document.getElementById('submitButton');
171
- const loadingElement = document.getElementById('loading'); submitButton.disabled = true;
172
- loadingElement.style.display = 'block'; const formData = new FormData();
 
 
 
 
173
  formData.append('file', document.getElementById('file').files[0]);
174
  formData.append('version', document.getElementById('version').value);
175
- formData.append('scale', document.getElementById('scale').value); // CodeFormerが選択されている場合はweightも追���
 
 
176
  if (document.getElementById('version').value === 'CodeFormer') {
177
  formData.append('weight', document.getElementById('weight').value);
178
- } // 現在のURLからベースURLを取得(パス、パラメータ、ハッシュを含めない)
 
 
179
  const baseUrl = window.location.origin;
180
- const apiUrl = baseUrl + '/api/restore'; fetch(apiUrl, {
 
 
181
  method: 'POST',
182
  body: formData
183
  })
@@ -191,7 +316,9 @@ def index():
191
  const url = URL.createObjectURL(blob);
192
  const preview = document.getElementById('preview');
193
  const downloadLink = document.getElementById('downloadLink');
194
- const outputContainer = document.getElementById('outputContainer'); preview.src = url;
 
 
195
  downloadLink.href = url;
196
  downloadLink.download = 'restored_' + document.getElementById('file').files[0].name;
197
  outputContainer.style.display = 'block';
@@ -203,13 +330,18 @@ def index():
203
  // 処理が終わったらローディングを非表示にし、ボタンを再有効化
204
  loadingElement.style.display = 'none';
205
  submitButton.disabled = false;
206
- }); }); </script></body></html> ''')
 
 
 
 
 
207
 
208
  @app.route('/api/restore', methods=['POST'])
209
  def api_restore():
210
  if 'file' not in request.files:
211
  return jsonify({'error': 'No file uploaded'}), 400
212
-
213
  file = request.files['file']
214
  version = request.form.get('version', 'v1.4')
215
  scale = float(request.form.get('scale', 2))
@@ -217,10 +349,6 @@ def api_restore():
217
 
218
  if file.filename == '':
219
  return jsonify({'error': 'No selected file'}), 400
220
-
221
- temp_dir = None
222
- input_path = None
223
- output_path = None
224
 
225
  try:
226
  # Save uploaded file to temp location
@@ -232,23 +360,17 @@ def api_restore():
232
  output_path = process_image(input_path, version, scale, weight)
233
 
234
  # Return the processed image
235
- return send_file(output_path, mimetype='image/jpeg', as_attachment=False)
236
-
237
  except Exception as e:
238
  return jsonify({'error': str(e)}), 500
239
-
240
  finally:
241
  # Clean up temp files
242
- if input_path and os.path.exists(input_path):
243
  os.remove(input_path)
244
-
245
- # استخدام shutil.rmtree لحذف المجلد المؤقت بشكل آمن
246
- if temp_dir and os.path.exists(temp_dir):
247
- shutil.rmtree(temp_dir, ignore_errors=True)
248
-
249
- # حذف ملف الإخراج المؤقت بعد إرساله
250
- if output_path and os.path.exists(output_path):
251
- os.remove(output_path)
252
 
253
  if __name__ == '__main__':
254
  app.run(host='0.0.0.0', port=7860, debug=True)
 
3
  import torch
4
  from flask import Flask, request, jsonify, send_file, render_template_string
5
  from basicsr.archs.srvgg_arch import SRVGGNetCompact
 
 
6
  from gfpgan.utils import GFPGANer
7
  from realesrgan.utils import RealESRGANer
8
  import tempfile
 
10
 
11
  app = Flask(__name__)
12
 
 
 
 
 
 
 
13
  # Initialize models
14
  model = SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=32, upscale=4, act_type='prelu')
15
  model_path = 'realesr-general-x4v3.pth'
16
+ half = True if torch.cuda.is_available() else False
17
+ upsampler = RealESRGANer(scale=4, model_path=model_path, model=model, tile=0, tile_pad=10, pre_pad=0, half=half)
 
 
 
 
 
 
 
 
 
 
18
 
19
  # Ensure output directory exists
20
  os.makedirs('output', exist_ok=True)
 
32
 
33
  for weight_file, url in weights.items():
34
  if not os.path.exists(weight_file):
35
+ os.system(f"wget {url} -O {weight_file}")
 
36
 
37
  download_weights()
38
 
 
48
  img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
49
  else:
50
  img_mode = None
51
+
52
  h, w = img.shape[0:2]
53
  if h < 300:
54
  img = cv2.resize(img, (w * 2, h * 2), interpolation=cv2.INTER_LANCZOS4)
55
+
 
 
 
56
  if version == 'v1.2':
57
  face_enhancer = GFPGANer(
58
+ model_path='GFPGANv1.2.pth', upscale=2, arch='clean', channel_multiplier=2, bg_upsampler=upsampler)
59
  elif version == 'v1.3':
60
  face_enhancer = GFPGANer(
61
+ model_path='GFPGANv1.3.pth', upscale=2, arch='clean', channel_multiplier=2, bg_upsampler=upsampler)
62
  elif version == 'v1.4':
63
  face_enhancer = GFPGANer(
64
+ model_path='GFPGANv1.4.pth', upscale=2, arch='clean', channel_multiplier=2, bg_upsampler=upsampler)
65
  elif version == 'RestoreFormer':
66
  face_enhancer = GFPGANer(
67
+ model_path='RestoreFormer.pth', upscale=2, arch='RestoreFormer', channel_multiplier=2, bg_upsampler=upsampler)
68
  elif version == 'CodeFormer':
69
  face_enhancer = GFPGANer(
70
+ model_path='CodeFormer.pth', upscale=2, arch='CodeFormer', channel_multiplier=2, bg_upsampler=upsampler)
71
  elif version == 'RealESR-General-x4v3':
72
  face_enhancer = GFPGANer(
73
+ model_path='realesr-general-x4v3.pth', upscale=2, arch='realesr-general', channel_multiplier=2, bg_upsampler=upsampler)
 
74
 
75
  try:
76
  if version == 'CodeFormer':
 
80
  except RuntimeError as error:
81
  print('Error', error)
82
  raise Exception(f"Enhancement error: {str(error)}")
83
+
84
  try:
85
  if scale != 2:
86
  interpolation = cv2.INTER_AREA if scale < 2 else cv2.INTER_LANCZOS4
 
88
  output = cv2.resize(output, (int(w * scale / 2), int(h * scale / 2)), interpolation=interpolation)
89
  except Exception as error:
90
  print('wrong scale input.', error)
91
+
92
  # Save to temporary file
93
  output_filename = f"output_{uuid.uuid4().hex}.jpg"
94
+ output_path = os.path.join('output', output_filename)
95
 
96
  if img_mode == 'RGBA':
97
  cv2.imwrite(output_path, output, [int(cv2.IMWRITE_PNG_COMPRESSION), 9])
98
  else:
99
  cv2.imwrite(output_path, output, [int(cv2.IMWRITE_JPEG_QUALITY), 95])
100
+
101
  return output_path
 
102
  except Exception as error:
103
  print('Global exception', error)
104
  raise Exception(f"Processing error: {str(error)}")
105
 
106
  @app.route('/')
107
  def index():
108
+ return render_template_string('''
109
+ <!DOCTYPE html>
110
+ <html>
111
+ <head>
112
+ <title>Image Upscaling & Restoration API</title>
113
+ <style>
114
+ body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
115
+ .container { border: 1px solid #ddd; padding: 20px; border-radius: 5px; }
116
+ .form-group { margin-bottom: 15px; }
117
+ label { display: block; margin-bottom: 5px; }
118
+ input, select { width: 100%; padding: 8px; box-sizing: border-box; }
119
+ button { background-color: #4CAF50; color: white; padding: 10px 15px; border: none; border-radius: 4px; cursor: pointer; }
120
+ button:hover { background-color: #45a049; }
121
+ #result { margin-top: 20px; }
122
+ #preview { max-width: 100%; margin-top: 10px; }
123
+ #apiUsage { background-color: #f5f5f5; padding: 15px; border-radius: 5px; margin-top: 20px; font-family: monospace; white-space: pre-wrap; }
124
+ #apiUsage h3 { margin-top: 0; }
125
+ #formDataPreview { max-height: 200px; overflow-y: auto; margin-bottom: 10px; }
126
+ .code-block { background-color: #f8f8f8; padding: 10px; border-radius: 4px; border-left: 3px solid #4CAF50; }
127
+ .comment { color: #666; font-style: italic; }
128
+
129
+ .loader {
130
+ width: 48px;
131
+ height: 48px;
132
+ border: 5px solid #4CAF50;
133
+ border-bottom-color: transparent;
134
+ border-radius: 50%;
135
+ display: inline-block;
136
+ box-sizing: border-box;
137
+ animation: rotation 1s linear infinite;
138
+ margin: 20px auto;
139
+ display: none; /* 初期状態では非表示 */
140
+ }
141
+ @keyframes rotation {
142
+ 0% {
143
+ transform: rotate(0deg);
144
+ }
145
+ 100% {
146
+ transform: rotate(360deg);
147
+ }
148
+ }
149
+ </style>
150
+ </head>
151
+ <body>
152
+ <h1>Image Upscaling & Restoration API</h1>
153
+ <div class="container">
154
+ <form id="uploadForm" enctype="multipart/form-data">
155
+ <div class="form-group">
156
+ <label for="file">Upload Image:</label>
157
+ <input type="file" id="file" name="file" required>
158
+ </div>
159
+ <div class="form-group">
160
+ <label for="version">Version:</label>
161
+ <select id="version" name="version">
162
+ <option value="v1.2">GFPGANv1.2</option>
163
+ <option value="v1.3">GFPGANv1.3</option>
164
+ <option value="v1.4" selected>GFPGANv1.4</option>
165
+ <option value="RestoreFormer">RestoreFormer</option>
166
+ <option value="CodeFormer">CodeFormer</option>
167
+ <option value="RealESR-General-x4v3">RealESR-General-x4v3</option>
168
+ </select>
169
+ </div>
170
+ <div class="form-group">
171
+ <label for="scale">Rescaling factor:</label>
172
+ <input type="number" id="scale" name="scale" value="2" step="0.1" min="1" max="4" required>
173
+ </div>
174
+ <div class="form-group" id="weightGroup" style="display: none;">
175
+ <label for="weight">CodeFormer Weight (0-1):</label>
176
+ <input type="number" id="weight" name="weight" value="0.5" step="0.1" min="0" max="1">
177
+ </div>
178
+ <button type="submit" id="submitButton">Process Image</button>
179
+ </form>
180
+ <div id="loading" class="loader"></div>
181
+ <div id="result">
182
+ <h3>Result:</h3>
183
+ <div id="outputContainer" style="display: none;">
184
+ <img id="preview" src="" alt="Processed Image">
185
+ <a id="downloadLink" href="#" download>Download Image</a>
186
+ </div>
187
+ </div>
188
+ <div id="apiUsage">
189
+ <h3>API Usage:</h3>
190
+ <div id="fetchCode" class="code-block">
191
+ // JavaScript fetch code will appear here
192
+ </div>
193
+ </div>
194
+ </div>
195
+
196
+ <script>
197
+ // CodeFormerが選択された時にweightパラメータを表示
198
+ document.getElementById('version').addEventListener('change', function() {
199
+ const weightGroup = document.getElementById('weightGroup');
200
+ if (this.value === 'CodeFormer') {
201
+ weightGroup.style.display = 'block';
202
+ } else {
203
+ weightGroup.style.display = 'none';
204
+ }
205
+ updateApiUsage();
206
+ });
207
+ // フォームの変更を監視してAPI使用例を更新
208
+ function updateApiUsage() {
209
+ const fileInput = document.getElementById('file');
210
  const version = document.getElementById('version').value;
211
  const scale = document.getElementById('scale').value;
212
+ const weight = document.getElementById('weight').value;
213
+
214
+ // 現在のURLからベースURLを取得(パス、パラメータ、ハッシュを含めない)
215
  const baseUrl = window.location.origin;
216
+ const apiUrl = baseUrl + '/api/restore';
217
+
218
+ // ファイルのプレビュー用文字列を準備
219
  let filePreview = '"img-dataURL"';
220
  if (fileInput.files.length > 0) {
221
  const file = fileInput.files[0];
 
232
  reader.readAsDataURL(file);
233
  } else {
234
  updateFetchCode(apiUrl, version, scale, weight, filePreview);
235
+ }
236
+ }
237
+
238
+ function updateFetchCode(apiUrl, version, scale, weight, filePreview) {
239
  const fetchCodeDiv = document.getElementById('fetchCode');
240
+ let code = `// JavaScript fetch example:
241
+ const formData = new FormData();
242
+ formData.append('file', ${filePreview});
243
+ formData.append('version', '${version}');
244
+ formData.append('scale', ${scale});`;
245
  if (version === 'CodeFormer') {
246
+ code += `
247
+ formData.append('weight', ${weight});`;
248
  }
249
+ code += `
250
+ fetch('${apiUrl}', {
251
+ method: 'POST',
252
+ body: formData
253
+ })
254
+ .then(response => {
255
+ if (!response.ok) {
256
+ return response.json().then(err => { throw new Error(err.error || 'Unknown error'); });
257
+ }
258
+ return response.blob();
259
+ })
260
+ .then(blob => {
261
+ // Process the returned image blob
262
+ const url = URL.createObjectURL(blob);
263
+ console.log('Image processed successfully', url);
264
+ // Example: document.getElementById('resultImage').src = url;
265
+ })
266
+ .catch(error => {
267
+ console.error('Error:', error.message);
268
+ });`;
269
  fetchCodeDiv.innerHTML = code;
270
+ }
271
+
272
+ // フォーム要素の変更を監視
273
  document.getElementById('file').addEventListener('change', updateApiUsage);
274
  document.getElementById('version').addEventListener('change', updateApiUsage);
275
  document.getElementById('scale').addEventListener('input', updateApiUsage);
276
+ document.getElementById('weight').addEventListener('input', updateApiUsage);
277
+
278
+ // 初期表示
279
+ updateApiUsage();
280
+
281
+ document.getElementById('uploadForm').addEventListener('submit', function(e) {
282
+ e.preventDefault();
283
+
284
+ // ボタンを無効化し、ローディングを表示
285
  const submitButton = document.getElementById('submitButton');
286
+ const loadingElement = document.getElementById('loading');
287
+
288
+ submitButton.disabled = true;
289
+ loadingElement.style.display = 'block';
290
+
291
+ const formData = new FormData();
292
  formData.append('file', document.getElementById('file').files[0]);
293
  formData.append('version', document.getElementById('version').value);
294
+ formData.append('scale', document.getElementById('scale').value);
295
+
296
+ // CodeFormerが選択されている場合はweightも追加
297
  if (document.getElementById('version').value === 'CodeFormer') {
298
  formData.append('weight', document.getElementById('weight').value);
299
+ }
300
+
301
+ // 現在のURLからベースURLを取得(パス、パラメータ、ハッシュを含めない)
302
  const baseUrl = window.location.origin;
303
+ const apiUrl = baseUrl + '/api/restore';
304
+
305
+ fetch(apiUrl, {
306
  method: 'POST',
307
  body: formData
308
  })
 
316
  const url = URL.createObjectURL(blob);
317
  const preview = document.getElementById('preview');
318
  const downloadLink = document.getElementById('downloadLink');
319
+ const outputContainer = document.getElementById('outputContainer');
320
+
321
+ preview.src = url;
322
  downloadLink.href = url;
323
  downloadLink.download = 'restored_' + document.getElementById('file').files[0].name;
324
  outputContainer.style.display = 'block';
 
330
  // 処理が終わったらローディングを非表示にし、ボタンを再有効化
331
  loadingElement.style.display = 'none';
332
  submitButton.disabled = false;
333
+ });
334
+ });
335
+ </script>
336
+ </body>
337
+ </html>
338
+ ''')
339
 
340
  @app.route('/api/restore', methods=['POST'])
341
  def api_restore():
342
  if 'file' not in request.files:
343
  return jsonify({'error': 'No file uploaded'}), 400
344
+
345
  file = request.files['file']
346
  version = request.form.get('version', 'v1.4')
347
  scale = float(request.form.get('scale', 2))
 
349
 
350
  if file.filename == '':
351
  return jsonify({'error': 'No selected file'}), 400
 
 
 
 
352
 
353
  try:
354
  # Save uploaded file to temp location
 
360
  output_path = process_image(input_path, version, scale, weight)
361
 
362
  # Return the processed image
363
+ return send_file(output_path, mimetype='image/jpeg')
364
+
365
  except Exception as e:
366
  return jsonify({'error': str(e)}), 500
367
+
368
  finally:
369
  # Clean up temp files
370
+ if 'input_path' in locals() and os.path.exists(input_path):
371
  os.remove(input_path)
372
+ if 'temp_dir' in locals() and os.path.exists(temp_dir):
373
+ os.rmdir(temp_dir)
 
 
 
 
 
 
374
 
375
  if __name__ == '__main__':
376
  app.run(host='0.0.0.0', port=7860, debug=True)