binaychandra commited on
Commit
2e990e1
·
1 Parent(s): dfeab9e

Add project files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.jpg filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
2
+ # you will also find guides on how best to write your Dockerfile
3
+
4
+ FROM python:3.10
5
+
6
+ RUN useradd -m -u 1000 user
7
+ USER user
8
+ ENV PATH="/home/user/.local/bin:$PATH"
9
+
10
+ WORKDIR /app
11
+
12
+ COPY --chown=user ./requirements.txt requirements.txt
13
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
14
+
15
+ COPY --chown=user . /app
16
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
app.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, request, jsonify, send_from_directory
2
+ import numpy as np
3
+ from PIL import Image
4
+ import io
5
+ import base64
6
+ from scipy.linalg import svd
7
+ import os
8
+
9
+ app = Flask(__name__, static_url_path='/static')
10
+
11
+ def calculate_storage(shape, r_value):
12
+ if len(shape) == 3: # RGB image
13
+ height, width, channels = shape
14
+ original_size = height * width * channels
15
+ compressed_size = channels * (height * r_value + r_value + width * r_value)
16
+ else: # Grayscale image
17
+ height, width = shape
18
+ original_size = height * width
19
+ compressed_size = height * r_value + r_value + width * r_value
20
+
21
+ return {
22
+ 'original': original_size,
23
+ 'compressed': compressed_size,
24
+ 'compression_ratio': original_size / compressed_size if compressed_size > 0 else 0
25
+ }
26
+
27
+ def process_image(image_data, r_value):
28
+ # Open image as RGB
29
+ img = Image.open(io.BytesIO(image_data))
30
+ img_array = np.array(img)
31
+
32
+ # Check if image is RGB (3 channels)
33
+ if len(img_array.shape) == 3 and img_array.shape[2] == 3:
34
+ # Process each channel separately
35
+ reconstructed = np.zeros_like(img_array)
36
+
37
+ for channel in range(3):
38
+ # Extract the channel
39
+ channel_data = img_array[:, :, channel]
40
+
41
+ # Perform SVD on this channel
42
+ U, s, Vt = svd(channel_data, full_matrices=False)
43
+
44
+ # Reconstruct image with r singular values
45
+ r = min(r_value, len(s))
46
+ reconstructed[:, :, channel] = np.dot(U[:, :r] * s[:r], Vt[:r, :])
47
+
48
+ # Clip values to valid range and convert to uint8
49
+ reconstructed = np.clip(reconstructed, 0, 255).astype(np.uint8)
50
+ else:
51
+ # Fallback to grayscale processing if not RGB
52
+ img_array = np.array(img.convert('L'))
53
+ U, s, Vt = svd(img_array, full_matrices=False)
54
+ r = min(r_value, len(s))
55
+ reconstructed = np.dot(U[:, :r] * s[:r], Vt[:r, :])
56
+ reconstructed = np.clip(reconstructed, 0, 255).astype(np.uint8)
57
+
58
+ # Convert back to image
59
+ reconstructed_img = Image.fromarray(reconstructed)
60
+
61
+ # Save to base64 string
62
+ buffered = io.BytesIO()
63
+ reconstructed_img.save(buffered, format="PNG")
64
+ img_str = base64.b64encode(buffered.getvalue()).decode()
65
+
66
+ # Calculate storage requirements
67
+ storage = calculate_storage(img_array.shape, r_value)
68
+
69
+ return {
70
+ 'processed_image': img_str,
71
+ 'dimensions': img_array.shape,
72
+ 'max_r': min(img_array.shape[0], img_array.shape[1]),
73
+ 'storage': storage
74
+ }
75
+
76
+ def get_predefined_images():
77
+ images_dir = os.path.join('static', 'images')
78
+ if not os.path.exists(images_dir):
79
+ return []
80
+ allowed_extensions = {'.jpg', '.jpeg', '.png', '.gif'}
81
+ images = []
82
+ for filename in os.listdir(images_dir):
83
+ if os.path.splitext(filename)[1].lower() in allowed_extensions:
84
+ images.append({
85
+ 'name': os.path.splitext(filename)[0].replace('_', ' ').title(),
86
+ 'path': f'/static/images/{filename}'
87
+ })
88
+ return images
89
+
90
+ @app.route('/')
91
+ def index():
92
+ predefined_images = get_predefined_images()
93
+ return render_template('index.html', predefined_images=predefined_images)
94
+
95
+ @app.route('/process', methods=['POST'])
96
+ def process():
97
+ image_data = None
98
+ if 'image' in request.files:
99
+ image = request.files['image'].read()
100
+ image_data = image
101
+ elif 'image_url' in request.form:
102
+ image_url = request.form['image_url']
103
+ if image_url.startswith('/static/'):
104
+ image_path = os.path.join(os.path.dirname(__file__), image_url[1:])
105
+ with open(image_path, 'rb') as f:
106
+ image_data = f.read()
107
+
108
+ if not image_data:
109
+ return jsonify({'error': 'No image provided'}), 400
110
+
111
+ r_value = int(request.form.get('r_value', 1))
112
+
113
+ try:
114
+ result = process_image(image_data, r_value)
115
+ return jsonify(result)
116
+ except Exception as e:
117
+ return jsonify({'error': str(e)}), 400
118
+
119
+ if __name__ == '__main__':
120
+ app.run(debug=True)
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ Flask==2.0.1
2
+ Werkzeug==2.0.3
3
+ numpy>=1.24.0
4
+ Pillow>=9.0.0
5
+ scipy>=1.10.0
static/images/Biker Girl.jpg ADDED

Git LFS Details

  • SHA256: 71640341871f4cadfd2bec526025b6209965300cbb1dba296c7ea89c5fde9769
  • Pointer size: 131 Bytes
  • Size of remote file: 101 kB
static/images/kittens_cute.jpg ADDED

Git LFS Details

  • SHA256: 00a3e21497842814f07b20228acbac69eb880cf4e6cbdb223728d664c664eb23
  • Pointer size: 131 Bytes
  • Size of remote file: 101 kB
static/images/landscape.jpg ADDED

Git LFS Details

  • SHA256: 88431cd9653ccd539741b555fb0a46b61558b301d4110412b5bc28b5e3ea6cb5
  • Pointer size: 131 Bytes
  • Size of remote file: 792 kB
templates/index.html ADDED
@@ -0,0 +1,397 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1.0">
6
+ <title>Image SVD Processor</title>
7
+ <style>
8
+ body {
9
+ font-family: Arial, sans-serif;
10
+ max-width: 1000px;
11
+ margin: 0 auto;
12
+ padding: 15px;
13
+ background-color: #f5f5f5;
14
+ }
15
+ .container {
16
+ background-color: white;
17
+ padding: 15px;
18
+ border-radius: 8px;
19
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
20
+ }
21
+ h1 {
22
+ text-align: center;
23
+ background-color: #3498db;
24
+ color: #fff;
25
+ padding: 20px;
26
+ margin: 0 0 20px 0;
27
+ border-radius: 8px;
28
+ }
29
+ .controls {
30
+ display: flex;
31
+ justify-content: space-between;
32
+ align-items: flex-start;
33
+ margin: 10px 0;
34
+ padding: 10px;
35
+ background-color: #f8f9fa;
36
+ border-radius: 8px;
37
+ gap: 10px;
38
+ }
39
+ .image-selection {
40
+ display: flex;
41
+ gap: 8px;
42
+ flex-wrap: wrap;
43
+ align-items: center;
44
+ }
45
+ .predefined-images, .upload-section {
46
+ display: flex;
47
+ flex-direction: column;
48
+ gap: 5px;
49
+ }
50
+ .slider-container {
51
+ flex: 0 0 40%;
52
+ margin-left: auto;
53
+ text-align: right;
54
+ }
55
+ #r-value {
56
+ width: 80%;
57
+ }
58
+ .image-container {
59
+ display: flex;
60
+ justify-content: space-between;
61
+ margin-top: 15px;
62
+ flex-wrap: wrap;
63
+ gap: 15px;
64
+ }
65
+ .image-box {
66
+ flex: 1;
67
+ min-width: 250px;
68
+ text-align: center;
69
+ margin-bottom: 15px;
70
+ padding: 10px;
71
+ background: #fff;
72
+ border-radius: 8px;
73
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
74
+ position: relative;
75
+ }
76
+ .image-box img {
77
+ max-width: 100%;
78
+ max-height: 300px;
79
+ border: 1px solid #ddd;
80
+ border-radius: 4px;
81
+ padding: 8px;
82
+ background: #fff;
83
+ }
84
+ .loading-overlay {
85
+ display: none;
86
+ position: absolute;
87
+ top: 0; left: 0; right: 0; bottom: 0;
88
+ background: rgba(255,255,255,0.8);
89
+ justify-content: center;
90
+ align-items: center;
91
+ }
92
+ .spinner {
93
+ width: 50px; height: 50px;
94
+ border: 5px solid #f3f3f3;
95
+ border-top: 5px solid #3498db;
96
+ border-radius: 50%;
97
+ animation: spin 1s linear infinite;
98
+ }
99
+ @keyframes spin {
100
+ 0% {transform: rotate(0deg);}
101
+ 100% {transform: rotate(360deg);}
102
+ }
103
+ .info {
104
+ margin-top: 10px;
105
+ color: #666;
106
+ padding: 5px;
107
+ background: #f8f9fa;
108
+ border-radius: 4px;
109
+ }
110
+ input[type="file"] {
111
+ padding: 5px;
112
+ border: none;
113
+ background: transparent;
114
+ }
115
+ input[type="file"]::-webkit-file-upload-button {
116
+ background: #3498db;
117
+ color: white;
118
+ padding: 8px 16px;
119
+ border: none;
120
+ border-radius: 4px;
121
+ cursor: pointer;
122
+ }
123
+ input[type="range"] {
124
+ height: 8px;
125
+ -webkit-appearance: none;
126
+ margin: 10px 0;
127
+ background: #ddd;
128
+ border-radius: 4px;
129
+ }
130
+ input[type="range"]::-webkit-slider-thumb {
131
+ -webkit-appearance: none;
132
+ width: 20px;
133
+ height: 20px;
134
+ background: #3498db;
135
+ border-radius: 50%;
136
+ cursor: pointer;
137
+ }
138
+ .storage-info {
139
+ font-size: 0.9em;
140
+ color: #666;
141
+ margin-top: 5px;
142
+ text-align: left;
143
+ padding: 8px;
144
+ background: #f8f9fa;
145
+ border-radius: 4px;
146
+ }
147
+ .storage-details {
148
+ display: flex;
149
+ flex-direction: column;
150
+ gap: 5px;
151
+ }
152
+ .storage-row {
153
+ display: flex;
154
+ justify-content: space-between;
155
+ }
156
+ .highlight {
157
+ color: #3498db;
158
+ font-weight: bold;
159
+ }
160
+ .or-divider {
161
+ display: flex;
162
+ align-items: center;
163
+ color: #666;
164
+ font-size: 0.9em;
165
+ padding: 5px;
166
+ }
167
+ </style>
168
+ </head>
169
+ <body>
170
+ <div class="container">
171
+ <h1>Image SVD Processor</h1>
172
+ <div class="controls">
173
+ <div class="image-selection">
174
+ <div class="predefined-images">
175
+ <label for="predefinedImage" style="margin-top: -5px;padding-bottom: 5px;">Select a predefined image:</label>
176
+ <select id="predefinedImage" style="height: 30px;">
177
+ <option value="">-- Select an image --</option>
178
+ {% for image in predefined_images %}
179
+ <option value="{{ image.path }}">{{ image.name }}</option>
180
+ {% endfor %}
181
+ </select>
182
+ </div>
183
+ <div class="or-divider">
184
+ <span>OR</span>
185
+ </div>
186
+ <div class="upload-section">
187
+ <label for="imageInput">Upload your own image:</label>
188
+ <input type="file" id="imageInput" accept="image/*">
189
+ </div>
190
+ </div>
191
+ <div class="slider-container" style="margin-top: 10px;">
192
+ <label for="r-value">R Value: <span id="r-value-display">5</span></label>
193
+ <input type="range" id="r-value" min="1" max="100" value="5">
194
+ </div>
195
+ </div>
196
+
197
+ <div class="image-container">
198
+ <div class="image-box">
199
+ <h3>Original Image</h3>
200
+ <img id="originalImage" src="" alt="Original image will appear here">
201
+ <div class="info" id="originalInfo"></div>
202
+ <div class="storage-info">
203
+ <div class="storage-details" id="originalStorage">
204
+ <div class="storage-row">
205
+ <span>Storage (bytes):</span>
206
+ <span class="highlight">-</span>
207
+ </div>
208
+ <div class="storage-row">
209
+ <span>Dimensions:</span>
210
+ <span>height × width × channels</span>
211
+ </div>
212
+ </div>
213
+ </div>
214
+ </div>
215
+ <div class="image-box">
216
+ <h3>Processed Image</h3>
217
+ <img id="processedImage" src="" alt="Processed image will appear here">
218
+ <div class="info" id="processedInfo"></div>
219
+ <div class="storage-info">
220
+ <div class="storage-details" id="processedStorage">
221
+ <div class="storage-row">
222
+ <span>Storage (bytes):</span>
223
+ <span class="highlight">-</span>
224
+ </div>
225
+ <div class="storage-row">
226
+ <span>SVD Components:</span>
227
+ <span>U×r + r + V×r</span>
228
+ </div>
229
+ <div class="storage-row">
230
+ <span>Compression Ratio:</span>
231
+ <span class="highlight">-</span>
232
+ </div>
233
+ </div>
234
+ </div>
235
+ <div class="loading-overlay" id="loading">
236
+ <div class="spinner"></div>
237
+ </div>
238
+ </div>
239
+ </div>
240
+ </div>
241
+
242
+ <script>
243
+ let originalImage = document.getElementById('originalImage');
244
+ let processedImage = document.getElementById('processedImage');
245
+ let rValue = document.getElementById('r-value');
246
+ let rValueDisplay = document.getElementById('r-value-display');
247
+ let loading = document.getElementById('loading');
248
+ let imageInput = document.getElementById('imageInput');
249
+ let predefinedSelect = document.getElementById('predefinedImage');
250
+ let maxR = 100;
251
+ let currentImage = null;
252
+
253
+ // Function to load first predefined image
254
+ function loadFirstPredefinedImage() {
255
+ if (predefinedSelect.options.length > 1) { // if we have any predefined images
256
+ predefinedSelect.selectedIndex = 1; // select first image (index 0 is the placeholder)
257
+ const selectedImageUrl = predefinedSelect.value;
258
+ if (selectedImageUrl) {
259
+ originalImage.src = selectedImageUrl;
260
+ processImageFromUrl(selectedImageUrl);
261
+ }
262
+ }
263
+ }
264
+
265
+ // Load first image when page loads
266
+ window.addEventListener('load', loadFirstPredefinedImage);
267
+
268
+ document.getElementById('predefinedImage').addEventListener('change', function(e) {
269
+ if (e.target.value) {
270
+ imageInput.value = '';
271
+ originalImage.src = e.target.value;
272
+ processImageFromUrl(e.target.value);
273
+ }
274
+ });
275
+
276
+ imageInput.addEventListener('change', function(e) {
277
+ if (e.target.files && e.target.files[0]) {
278
+ document.getElementById('predefinedImage').value = '';
279
+ let reader = new FileReader();
280
+ reader.onload = function(e) {
281
+ originalImage.src = e.target.result;
282
+ currentImage = imageInput.files[0];
283
+ processImage();
284
+ }
285
+ reader.readAsDataURL(e.target.files[0]);
286
+ }
287
+ });
288
+
289
+ async function processImageFromUrl(imageUrl) {
290
+ loading.style.display = 'flex';
291
+ const formData = new FormData();
292
+ formData.append('image_url', imageUrl);
293
+ formData.append('r_value', rValue.value);
294
+ try {
295
+ const response = await fetch('/process', {method: 'POST', body: formData});
296
+ const data = await response.json();
297
+ if (data.error) {
298
+ alert('Error: ' + data.error);
299
+ return;
300
+ }
301
+ processedImage.src = 'data:image/png;base64,' + data.processed_image;
302
+ maxR = data.max_r;
303
+ rValue.max = maxR;
304
+ document.getElementById('originalStorage').innerHTML = `
305
+ <div class="storage-row">
306
+ <span>Storage (bytes):</span>
307
+ <span class="highlight">${data.storage.original.toLocaleString()}</span>
308
+ </div>
309
+ <div class="storage-row">
310
+ <span>Dimensions:</span>
311
+ <span>${data.dimensions.join(' × ')}</span>
312
+ </div>`;
313
+ document.getElementById('processedStorage').innerHTML = `
314
+ <div class="storage-row">
315
+ <span>Storage (bytes):</span>
316
+ <span class="highlight">${data.storage.compressed.toLocaleString()}</span>
317
+ </div>
318
+ <div class="storage-row">
319
+ <span>SVD Components:</span>
320
+ <span>${data.dimensions[0]}×${rValue.value} + ${rValue.value} + ${data.dimensions[1]}×${rValue.value}</span>
321
+ </div>
322
+ <div class="storage-row">
323
+ <span>Compression Ratio:</span>
324
+ <span class="highlight">${data.storage.compression_ratio.toFixed(2)}:1</span>
325
+ </div>`;
326
+ document.getElementById('originalInfo').textContent =
327
+ `Dimensions: ${data.dimensions.join(' × ')}`;
328
+ document.getElementById('processedInfo').textContent =
329
+ `Using ${rValue.value} of ${maxR} singular values`;
330
+ } catch (error) {
331
+ alert('Error processing image: ' + error.message);
332
+ } finally {
333
+ loading.style.display = 'none';
334
+ }
335
+ }
336
+
337
+ rValue.addEventListener('input', function() {
338
+ rValueDisplay.textContent = this.value;
339
+ const predefinedImage = document.getElementById('predefinedImage');
340
+ if (predefinedImage.value) {
341
+ processImageFromUrl(predefinedImage.value);
342
+ } else if (currentImage) {
343
+ processImage();
344
+ }
345
+ });
346
+
347
+ async function processImage() {
348
+ if (!currentImage) {return;}
349
+ loading.style.display = 'flex';
350
+ const formData = new FormData();
351
+ formData.append('image', currentImage);
352
+ formData.append('r_value', rValue.value);
353
+ try {
354
+ const response = await fetch('/process', {method: 'POST', body: formData});
355
+ const data = await response.json();
356
+ if (data.error) {
357
+ alert('Error: ' + data.error);
358
+ return;
359
+ }
360
+ processedImage.src = 'data:image/png;base64,' + data.processed_image;
361
+ maxR = data.max_r;
362
+ rValue.max = maxR;
363
+ document.getElementById('originalStorage').innerHTML = `
364
+ <div class="storage-row">
365
+ <span>Storage (bytes):</span>
366
+ <span class="highlight">${data.storage.original.toLocaleString()}</span>
367
+ </div>
368
+ <div class="storage-row">
369
+ <span>Dimensions:</span>
370
+ <span>${data.dimensions.join(' × ')}</span>
371
+ </div>`;
372
+ document.getElementById('processedStorage').innerHTML = `
373
+ <div class="storage-row">
374
+ <span>Storage (bytes):</span>
375
+ <span class="highlight">${data.storage.compressed.toLocaleString()}</span>
376
+ </div>
377
+ <div class="storage-row">
378
+ <span>SVD Components:</span>
379
+ <span>${data.dimensions[0]}×${rValue.value} + ${rValue.value} + ${data.dimensions[1]}×${rValue.value}</span>
380
+ </div>
381
+ <div class="storage-row">
382
+ <span>Compression Ratio:</span>
383
+ <span class="highlight">${data.storage.compression_ratio.toFixed(2)}:1</span>
384
+ </div>`;
385
+ document.getElementById('originalInfo').textContent =
386
+ `Dimensions: ${data.dimensions.join(' × ')}`;
387
+ document.getElementById('processedInfo').textContent =
388
+ `Using ${rValue.value} of ${maxR} singular values`;
389
+ } catch (error) {
390
+ alert('Error processing image: ' + error.message);
391
+ } finally {
392
+ loading.style.display = 'none';
393
+ }
394
+ }
395
+ </script>
396
+ </body>
397
+ </html>