mike23415 commited on
Commit
17c8d79
·
verified ·
1 Parent(s): 111a3fc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +703 -183
app.py CHANGED
@@ -30,8 +30,8 @@ ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp'}
30
  def allowed_file(filename):
31
  return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
32
 
33
- def create_background_logo_qr(image, qr_data, style='overlay_dots', size=400):
34
- """Create QR with logo as background and dots on top (like reference image)"""
35
  try:
36
  # Create QR code with high error correction
37
  qr = qrcode.QRCode(
@@ -47,100 +47,134 @@ def create_background_logo_qr(image, qr_data, style='overlay_dots', size=400):
47
  modules = qr.modules
48
  module_count = len(modules)
49
 
50
- if style == 'overlay_dots':
51
- return create_overlay_dots_style(image, modules, module_count, size)
52
- elif style == 'pattern_overlay':
53
- return create_pattern_overlay_style(image, modules, module_count, size)
54
- elif style == 'artistic_overlay':
55
- return create_artistic_overlay_style(image, modules, module_count, size)
 
 
 
 
 
 
 
 
 
56
  else:
57
- return create_overlay_dots_style(image, modules, module_count, size)
58
 
59
  except Exception as e:
60
  logger.error(f"Error creating background logo QR: {str(e)}")
61
  return create_basic_qr(qr_data, size)
62
 
63
- def create_overlay_dots_style(image, modules, module_count, size):
64
- """Create QR with logo background and black dots overlay (main style)"""
65
 
66
- # Step 1: Create background with logo
67
  background = Image.new('RGB', (size, size), (255, 255, 255))
68
 
69
  # Resize logo to fill most of the background
70
- logo_size = int(size * 0.6) # Logo takes 60% of the area
71
  logo_resized = image.resize((logo_size, logo_size), Image.LANCZOS)
72
 
73
- # Center the logo on white background
 
 
 
 
 
 
74
  logo_pos = ((size - logo_size) // 2, (size - logo_size) // 2)
75
- background.paste(logo_resized, logo_pos)
76
 
77
- # Step 2: Add semi-transparent white overlay for better contrast
78
- overlay = Image.new('RGBA', (size, size), (255, 255, 255, 180)) # Semi-transparent white
79
- background_rgba = background.convert('RGBA')
80
- background_with_overlay = Image.alpha_composite(background_rgba, overlay)
81
 
82
- # Step 3: Calculate QR area (centered, leaving border space)
83
- qr_area_size = int(size * 0.85) # QR takes 85% of canvas
84
  qr_offset = (size - qr_area_size) // 2
85
  module_size = qr_area_size // module_count
86
 
87
- # Step 4: Draw QR dots ON TOP of the background
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  draw = ImageDraw.Draw(background_with_overlay)
89
 
90
  for i in range(module_count):
91
  for j in range(module_count):
92
- if modules[i][j]: # This should be a black dot
93
  x = qr_offset + j * module_size
94
  y = qr_offset + i * module_size
95
 
96
- # Draw black dots/squares for QR data
97
  if is_finder_pattern(i, j, module_count):
98
- # Finder patterns as solid black squares
99
- draw.rectangle([x, y, x + module_size - 1, y + module_size - 1],
100
- fill=(0, 0, 0), outline=(0, 0, 0))
101
- elif is_timing_pattern(i, j):
102
- # Timing patterns as solid black
103
- draw.rectangle([x, y, x + module_size - 1, y + module_size - 1],
104
- fill=(0, 0, 0))
105
  else:
106
- # Regular data modules as black dots
107
  dot_margin = module_size // 4
108
  draw.ellipse([x + dot_margin, y + dot_margin,
109
  x + module_size - dot_margin, y + module_size - dot_margin],
110
  fill=(0, 0, 0))
111
 
112
- # Step 5: Add decorative corner elements (like in reference)
113
- add_corner_decorations(draw, size, (0, 0, 0))
114
-
115
  return background_with_overlay.convert('RGB')
116
 
117
- def create_pattern_overlay_style(image, modules, module_count, size):
118
- """Create QR with logo background and various pattern overlays"""
119
 
120
- # Create background with logo pattern
121
- background = Image.new('RGB', (size, size), (240, 240, 240))
122
 
123
- # Create tiled logo background
124
- logo_tile_size = size // 3
125
- logo_tile = image.resize((logo_tile_size, logo_tile_size), Image.LANCZOS)
126
 
127
- # Tile the logo across background
128
- for i in range(3):
129
- for j in range(3):
130
- pos = (i * logo_tile_size, j * logo_tile_size)
131
- background.paste(logo_tile, pos)
132
 
133
- # Add white overlay for contrast
134
- overlay = Image.new('RGBA', (size, size), (255, 255, 255, 200))
135
- background_rgba = background.convert('RGBA')
136
- background_with_overlay = Image.alpha_composite(background_rgba, overlay)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
- # Draw QR pattern on top
139
- qr_area_size = int(size * 0.8)
140
  qr_offset = (size - qr_area_size) // 2
141
  module_size = qr_area_size // module_count
142
 
143
- draw = ImageDraw.Draw(background_with_overlay)
 
144
 
145
  for i in range(module_count):
146
  for j in range(module_count):
@@ -148,74 +182,266 @@ def create_pattern_overlay_style(image, modules, module_count, size):
148
  x = qr_offset + j * module_size
149
  y = qr_offset + i * module_size
150
 
 
 
 
 
 
 
 
 
 
 
151
  if is_finder_pattern(i, j, module_count):
152
- # Finder patterns as solid rectangles
153
- draw.rectangle([x, y, x + module_size, y + module_size],
154
- fill=(0, 0, 0))
155
  else:
156
- # Data modules as various shapes
157
- shape_type = (i + j) % 3
158
  margin = module_size // 5
159
-
160
- if shape_type == 0: # Circle
161
- draw.ellipse([x + margin, y + margin,
162
- x + module_size - margin, y + module_size - margin],
163
- fill=(0, 0, 0))
164
- elif shape_type == 1: # Square
165
- draw.rectangle([x + margin, y + margin,
166
- x + module_size - margin, y + module_size - margin],
167
- fill=(0, 0, 0))
168
- else: # Diamond
169
- center_x, center_y = x + module_size // 2, y + module_size // 2
170
- radius = module_size // 2 - margin
171
- points = [
172
- (center_x, center_y - radius),
173
- (center_x + radius, center_y),
174
- (center_x, center_y + radius),
175
- (center_x - radius, center_y)
176
- ]
177
- draw.polygon(points, fill=(0, 0, 0))
178
 
179
- return background_with_overlay.convert('RGB')
180
 
181
- def create_artistic_overlay_style(image, modules, module_count, size):
182
- """Create artistic QR with logo background and creative dot patterns"""
183
 
184
- # Create gradient background
185
- background = Image.new('RGB', (size, size), (255, 255, 255))
186
 
187
- # Add logo with artistic effects
188
- logo_size = int(size * 0.7)
189
  logo_resized = image.resize((logo_size, logo_size), Image.LANCZOS)
190
 
191
- # Apply artistic effects to logo
192
- logo_enhanced = logo_resized.filter(ImageFilter.GaussianBlur(radius=1))
193
- logo_enhanced = ImageEnhance.Brightness(logo_enhanced).enhance(1.2)
194
- logo_enhanced = ImageEnhance.Contrast(logo_enhanced).enhance(0.8)
195
 
196
- # Center logo
197
  logo_pos = ((size - logo_size) // 2, (size - logo_size) // 2)
198
- background.paste(logo_enhanced, logo_pos)
 
 
 
 
199
 
200
- # Add artistic white overlay with patterns
201
- overlay = Image.new('RGBA', (size, size), (255, 255, 255, 160))
202
- draw_overlay = ImageDraw.Draw(overlay)
 
203
 
204
- # Add subtle pattern to overlay
205
- for i in range(0, size, 20):
206
- for j in range(0, size, 20):
207
- if (i + j) % 40 == 0:
208
- draw_overlay.ellipse([i, j, i + 4, j + 4], fill=(255, 255, 255, 100))
 
 
 
 
 
 
 
 
 
 
 
209
 
 
210
  background_rgba = background.convert('RGBA')
211
- background_with_overlay = Image.alpha_composite(background_rgba, overlay)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
 
213
- # Draw artistic QR dots
214
- qr_area_size = int(size * 0.82)
 
 
 
 
215
  qr_offset = (size - qr_area_size) // 2
216
  module_size = qr_area_size // module_count
217
 
218
- draw = ImageDraw.Draw(background_with_overlay)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
 
220
  for i in range(module_count):
221
  for j in range(module_count):
@@ -224,54 +450,82 @@ def create_artistic_overlay_style(image, modules, module_count, size):
224
  y = qr_offset + i * module_size
225
 
226
  if is_finder_pattern(i, j, module_count):
227
- # Artistic finder patterns
228
  draw.rounded_rectangle([x, y, x + module_size, y + module_size],
229
  radius=module_size // 4, fill=(0, 0, 0))
230
  else:
231
- # Artistic data dots
232
- center_x, center_y = x + module_size // 2, y + module_size // 2
233
- dot_size = module_size - 2
234
-
235
- # Create artistic dot with small decorative elements
236
- draw.ellipse([center_x - dot_size//2, center_y - dot_size//2,
237
- center_x + dot_size//2, center_y + dot_size//2],
238
  fill=(0, 0, 0))
239
-
240
- # Add tiny decorative dots around main dot
241
- if (i + j) % 5 == 0:
242
- for angle in [0, 90, 180, 270]:
243
- offset_x = int(2 * math.cos(math.radians(angle)))
244
- offset_y = int(2 * math.sin(math.radians(angle)))
245
- draw.ellipse([center_x + offset_x - 1, center_y + offset_y - 1,
246
- center_x + offset_x + 1, center_y + offset_y + 1],
247
- fill=(0, 0, 0))
248
-
249
- # Add corner decorative elements
250
- add_corner_decorations(draw, size, (0, 0, 0))
251
 
252
- return background_with_overlay.convert('RGB')
253
 
254
- def add_corner_decorations(draw, size, color):
255
- """Add decorative corner elements like in the reference image"""
256
- corner_size = 40
257
- positions = [
258
- (15, 15), # Top-left
259
- (size - 55, 15), # Top-right
260
- (15, size - 55), # Bottom-left
261
- (size - 55, size - 55) # Bottom-right
262
- ]
263
-
264
- for x, y in positions:
265
- # Draw concentric circles
266
- for radius in [8, 16, 24]:
267
- draw.ellipse([x + corner_size//2 - radius, y + corner_size//2 - radius,
268
- x + corner_size//2 + radius, y + corner_size//2 + radius],
269
- outline=color, width=2)
270
-
271
- # Add center dot
272
- center_x, center_y = x + corner_size//2, y + corner_size//2
273
- draw.ellipse([center_x - 3, center_y - 3, center_x + 3, center_y + 3],
274
- fill=color)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
 
276
  def is_finder_pattern(row, col, size):
277
  """Check if position is in finder pattern area"""
@@ -292,11 +546,11 @@ def create_basic_qr(data, size):
292
  return qr_img.resize((size, size), Image.LANCZOS)
293
 
294
  def calculate_compatibility_score(qr_image):
295
- """Calculate QR code compatibility score"""
296
  try:
297
  gray = cv2.cvtColor(np.array(qr_image), cv2.COLOR_RGB2GRAY)
298
 
299
- # Contrast analysis
300
  min_val, max_val = np.min(gray), np.max(gray)
301
  contrast_ratio = max_val / max(min_val, 1)
302
 
@@ -304,57 +558,86 @@ def calculate_compatibility_score(qr_image):
304
  edges = cv2.Canny(gray, 50, 150)
305
  edge_ratio = np.sum(edges > 0) / edges.size
306
 
307
- # Noise analysis
308
- blur = cv2.GaussianBlur(gray, (5, 5), 0)
309
- noise = np.std(gray - blur)
310
 
311
- # Calculate scores
312
- contrast_score = min(contrast_ratio / 6, 1) * 40 # Adjusted for overlay style
313
- edge_score = min(edge_ratio * 100, 1) * 35
314
- noise_score = max(0, 1 - noise / 50) * 25
315
 
316
- total_score = int(contrast_score + edge_score + noise_score)
317
 
318
  return {
319
- 'overall': total_score,
320
  'contrast': int(contrast_score),
321
  'sharpness': int(edge_score),
322
- 'noise_level': int(noise_score),
323
- 'recommendations': get_recommendations(total_score)
324
  }
325
  except Exception as e:
326
  logger.error(f"Error calculating compatibility: {str(e)}")
327
- return {'overall': 80, 'contrast': 80, 'sharpness': 80, 'noise_level': 80, 'recommendations': []}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
 
329
- def get_recommendations(score):
330
- """Get improvement recommendations"""
331
  recommendations = []
 
 
332
  if score < 70:
333
- recommendations.append("Try the 'overlay_dots' style for better scanning")
334
  if score < 60:
335
- recommendations.append("Your logo might need more contrast with the overlay")
336
- if score < 50:
337
- recommendations.append("Consider using a simpler logo or higher contrast version")
338
  return recommendations
339
 
340
  def image_to_base64(image):
341
  """Convert PIL image to base64 string"""
342
  buffer = BytesIO()
343
- image.save(buffer, format='PNG', optimize=True)
344
  img_str = base64.b64encode(buffer.getvalue()).decode()
345
  return f"data:image/png;base64,{img_str}"
346
 
347
  @app.route('/', methods=['GET'])
348
  def home():
349
  return jsonify({
350
- 'message': 'Background Logo QR Generator is running',
351
  'status': 'healthy',
352
- 'styles': ['overlay_dots', 'pattern_overlay', 'artistic_overlay']
353
  })
354
 
355
  @app.route('/health', methods=['GET'])
356
  def health_check():
357
- return jsonify({'status': 'healthy', 'message': 'Background Logo QR Service is running'})
358
 
359
  @app.route('/api/generate-artistic-qr', methods=['POST'])
360
  def generate_artistic_qr():
@@ -364,7 +647,7 @@ def generate_artistic_qr():
364
 
365
  file = request.files['image']
366
  url = request.form['url']
367
- style = request.form.get('style', 'overlay_dots')
368
  size = int(request.form.get('size', 500))
369
 
370
  if file.filename == '' or not allowed_file(file.filename):
@@ -378,7 +661,7 @@ def generate_artistic_qr():
378
  except Exception:
379
  return jsonify({'error': 'Invalid image file'}), 400
380
 
381
- # Generate background logo QR code (correct style)
382
  artistic_qr = create_background_logo_qr(image, url, style, size)
383
  compatibility = calculate_compatibility_score(artistic_qr)
384
  qr_base64 = image_to_base64(artistic_qr)
@@ -403,25 +686,262 @@ def generate_artistic_qr():
403
  @app.route('/api/styles', methods=['GET'])
404
  def get_available_styles():
405
  styles = {
406
- 'overlay_dots': {
407
- 'name': 'Overlay Dots',
408
- 'description': 'Logo background with black dots overlay (like Pizza Hut, Starbucks)',
409
- 'compatibility': 95
 
410
  },
411
- 'pattern_overlay': {
412
- 'name': 'Pattern Overlay',
413
- 'description': 'Tiled logo background with varied QR patterns',
414
- 'compatibility': 90
 
415
  },
416
- 'artistic_overlay': {
417
- 'name': 'Artistic Overlay',
418
- 'description': 'Enhanced logo background with artistic QR elements',
419
- 'compatibility': 88
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
420
  }
421
  }
422
 
423
  return jsonify({'success': True, 'styles': styles})
424
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
425
  if __name__ == '__main__':
426
  port = int(os.environ.get('PORT', 7860))
427
  app.run(host='0.0.0.0', port=port, debug=False)
 
30
  def allowed_file(filename):
31
  return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
32
 
33
+ def create_background_logo_qr(image, qr_data, style='modern_overlay', size=400):
34
+ """Create QR with modern styles and better image visibility"""
35
  try:
36
  # Create QR code with high error correction
37
  qr = qrcode.QRCode(
 
47
  modules = qr.modules
48
  module_count = len(modules)
49
 
50
+ # Route to different modern styles
51
+ if style == 'modern_overlay':
52
+ return create_modern_overlay_style(image, modules, module_count, size)
53
+ elif style == 'gradient_blend':
54
+ return create_gradient_blend_style(image, modules, module_count, size)
55
+ elif style == 'neon_glow':
56
+ return create_neon_glow_style(image, modules, module_count, size)
57
+ elif style == 'glassmorphism':
58
+ return create_glassmorphism_style(image, modules, module_count, size)
59
+ elif style == 'minimal_dots':
60
+ return create_minimal_dots_style(image, modules, module_count, size)
61
+ elif style == 'artistic_shadow':
62
+ return create_artistic_shadow_style(image, modules, module_count, size)
63
+ elif style == 'vibrant_overlay':
64
+ return create_vibrant_overlay_style(image, modules, module_count, size)
65
  else:
66
+ return create_modern_overlay_style(image, modules, module_count, size)
67
 
68
  except Exception as e:
69
  logger.error(f"Error creating background logo QR: {str(e)}")
70
  return create_basic_qr(qr_data, size)
71
 
72
+ def create_modern_overlay_style(image, modules, module_count, size):
73
+ """Modern style with better image visibility and sleek QR dots"""
74
 
75
+ # Step 1: Create vibrant background with full logo
76
  background = Image.new('RGB', (size, size), (255, 255, 255))
77
 
78
  # Resize logo to fill most of the background
79
+ logo_size = int(size * 0.85) # Logo takes 85% of the area
80
  logo_resized = image.resize((logo_size, logo_size), Image.LANCZOS)
81
 
82
+ # Enhance logo colors
83
+ enhancer = ImageEnhance.Color(logo_resized)
84
+ logo_enhanced = enhancer.enhance(1.3) # Boost colors
85
+ enhancer = ImageEnhance.Contrast(logo_enhanced)
86
+ logo_enhanced = enhancer.enhance(1.1) # Slight contrast boost
87
+
88
+ # Center the logo on background
89
  logo_pos = ((size - logo_size) // 2, (size - logo_size) // 2)
90
+ background.paste(logo_enhanced, logo_pos)
91
 
92
+ # Step 2: Add minimal semi-transparent overlay only where QR dots will be
93
+ overlay = Image.new('RGBA', (size, size), (0, 0, 0, 0)) # Transparent base
94
+ overlay_draw = ImageDraw.Draw(overlay)
 
95
 
96
+ # Calculate QR area
97
+ qr_area_size = int(size * 0.9)
98
  qr_offset = (size - qr_area_size) // 2
99
  module_size = qr_area_size // module_count
100
 
101
+ # Pre-draw light overlay only where QR dots will be placed
102
+ for i in range(module_count):
103
+ for j in range(module_count):
104
+ if modules[i][j]:
105
+ x = qr_offset + j * module_size
106
+ y = qr_offset + i * module_size
107
+
108
+ # Add subtle white background only for QR dots
109
+ margin = module_size // 6
110
+ overlay_draw.ellipse([x + margin, y + margin,
111
+ x + module_size - margin, y + module_size - margin],
112
+ fill=(255, 255, 255, 120))
113
+
114
+ background_rgba = background.convert('RGBA')
115
+ background_with_overlay = Image.alpha_composite(background_rgba, overlay)
116
+
117
+ # Step 3: Draw modern QR dots
118
  draw = ImageDraw.Draw(background_with_overlay)
119
 
120
  for i in range(module_count):
121
  for j in range(module_count):
122
+ if modules[i][j]:
123
  x = qr_offset + j * module_size
124
  y = qr_offset + i * module_size
125
 
 
126
  if is_finder_pattern(i, j, module_count):
127
+ # Modern rounded finder patterns
128
+ draw.rounded_rectangle([x, y, x + module_size - 1, y + module_size - 1],
129
+ radius=module_size // 3, fill=(0, 0, 0))
 
 
 
 
130
  else:
131
+ # Modern circular dots with slight margin
132
  dot_margin = module_size // 4
133
  draw.ellipse([x + dot_margin, y + dot_margin,
134
  x + module_size - dot_margin, y + module_size - dot_margin],
135
  fill=(0, 0, 0))
136
 
 
 
 
137
  return background_with_overlay.convert('RGB')
138
 
139
+ def create_gradient_blend_style(image, modules, module_count, size):
140
+ """Create QR with gradient blend effect"""
141
 
142
+ # Create gradient background
143
+ background = Image.new('RGB', (size, size), (255, 255, 255))
144
 
145
+ # Full logo background
146
+ logo_resized = image.resize((size, size), Image.LANCZOS)
 
147
 
148
+ # Create radial gradient mask
149
+ gradient = Image.new('L', (size, size), 0)
150
+ gradient_draw = ImageDraw.Draw(gradient)
 
 
151
 
152
+ center_x, center_y = size // 2, size // 2
153
+ max_radius = size // 2
154
+
155
+ for radius in range(max_radius, 0, -5):
156
+ intensity = int(255 * (radius / max_radius) * 0.8)
157
+ gradient_draw.ellipse([center_x - radius, center_y - radius,
158
+ center_x + radius, center_y + radius],
159
+ fill=intensity)
160
+
161
+ # Apply gradient mask to logo
162
+ logo_rgba = logo_resized.convert('RGBA')
163
+ gradient_rgba = gradient.convert('RGBA')
164
+
165
+ # Blend logo with gradient
166
+ blended = Image.alpha_composite(
167
+ Image.new('RGBA', (size, size), (255, 255, 255, 255)),
168
+ logo_rgba
169
+ )
170
 
171
+ # Calculate QR area
172
+ qr_area_size = int(size * 0.88)
173
  qr_offset = (size - qr_area_size) // 2
174
  module_size = qr_area_size // module_count
175
 
176
+ # Draw QR with gradient colors
177
+ draw = ImageDraw.Draw(blended)
178
 
179
  for i in range(module_count):
180
  for j in range(module_count):
 
182
  x = qr_offset + j * module_size
183
  y = qr_offset + i * module_size
184
 
185
+ # Get color from original image at this position
186
+ sample_x = min(x + module_size // 2, size - 1)
187
+ sample_y = min(y + module_size // 2, size - 1)
188
+ bg_color = logo_resized.getpixel((sample_x, sample_y))
189
+
190
+ # Create contrasting color
191
+ r, g, b = bg_color
192
+ brightness = (r + g + b) / 3
193
+ dot_color = (0, 0, 0) if brightness > 128 else (255, 255, 255)
194
+
195
  if is_finder_pattern(i, j, module_count):
196
+ draw.rounded_rectangle([x, y, x + module_size, y + module_size],
197
+ radius=module_size // 4, fill=dot_color)
 
198
  else:
 
 
199
  margin = module_size // 5
200
+ draw.ellipse([x + margin, y + margin,
201
+ x + module_size - margin, y + module_size - margin],
202
+ fill=dot_color)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
 
204
+ return blended.convert('RGB')
205
 
206
+ def create_neon_glow_style(image, modules, module_count, size):
207
+ """Create QR with neon glow effect"""
208
 
209
+ # Dark background with logo
210
+ background = Image.new('RGB', (size, size), (20, 20, 30))
211
 
212
+ # Resize and darken logo
213
+ logo_size = int(size * 0.9)
214
  logo_resized = image.resize((logo_size, logo_size), Image.LANCZOS)
215
 
216
+ # Apply dark filter to logo
217
+ enhancer = ImageEnhance.Brightness(logo_resized)
218
+ logo_darkened = enhancer.enhance(0.4)
 
219
 
 
220
  logo_pos = ((size - logo_size) // 2, (size - logo_size) // 2)
221
+ background.paste(logo_darkened, logo_pos)
222
+
223
+ # Create glow effect
224
+ glow_layer = Image.new('RGBA', (size, size), (0, 0, 0, 0))
225
+ glow_draw = ImageDraw.Draw(glow_layer)
226
 
227
+ # Calculate QR area
228
+ qr_area_size = int(size * 0.85)
229
+ qr_offset = (size - qr_area_size) // 2
230
+ module_size = qr_area_size // module_count
231
 
232
+ # Draw glow effect first
233
+ neon_color = (0, 255, 255) # Cyan neon
234
+
235
+ for i in range(module_count):
236
+ for j in range(module_count):
237
+ if modules[i][j]:
238
+ x = qr_offset + j * module_size
239
+ y = qr_offset + i * module_size
240
+ center_x, center_y = x + module_size // 2, y + module_size // 2
241
+
242
+ # Multiple glow layers
243
+ for glow_size in [8, 6, 4, 2]:
244
+ alpha = 30 if glow_size == 8 else 50
245
+ glow_draw.ellipse([center_x - glow_size, center_y - glow_size,
246
+ center_x + glow_size, center_y + glow_size],
247
+ fill=(*neon_color, alpha))
248
 
249
+ # Combine background with glow
250
  background_rgba = background.convert('RGBA')
251
+ glowing_bg = Image.alpha_composite(background_rgba, glow_layer)
252
+
253
+ # Draw main QR dots
254
+ draw = ImageDraw.Draw(glowing_bg)
255
+
256
+ for i in range(module_count):
257
+ for j in range(module_count):
258
+ if modules[i][j]:
259
+ x = qr_offset + j * module_size
260
+ y = qr_offset + i * module_size
261
+
262
+ if is_finder_pattern(i, j, module_count):
263
+ draw.rounded_rectangle([x, y, x + module_size, y + module_size],
264
+ radius=module_size // 6, fill=neon_color)
265
+ else:
266
+ margin = module_size // 4
267
+ draw.ellipse([x + margin, y + margin,
268
+ x + module_size - margin, y + module_size - margin],
269
+ fill=neon_color)
270
+
271
+ return glowing_bg.convert('RGB')
272
+
273
+ def create_glassmorphism_style(image, modules, module_count, size):
274
+ """Create QR with glassmorphism effect"""
275
+
276
+ # Vibrant background
277
+ background = Image.new('RGB', (size, size), (255, 255, 255))
278
+
279
+ # Full logo background with enhanced colors
280
+ logo_resized = image.resize((size, size), Image.LANCZOS)
281
+ enhancer = ImageEnhance.Color(logo_resized)
282
+ logo_enhanced = enhancer.enhance(1.4)
283
+
284
+ background.paste(logo_enhanced, (0, 0))
285
 
286
+ # Create glass effect overlay
287
+ glass_overlay = Image.new('RGBA', (size, size), (0, 0, 0, 0))
288
+ glass_draw = ImageDraw.Draw(glass_overlay)
289
+
290
+ # Calculate QR area
291
+ qr_area_size = int(size * 0.88)
292
  qr_offset = (size - qr_area_size) // 2
293
  module_size = qr_area_size // module_count
294
 
295
+ # Draw glass panels for QR modules
296
+ for i in range(module_count):
297
+ for j in range(module_count):
298
+ if modules[i][j]:
299
+ x = qr_offset + j * module_size
300
+ y = qr_offset + i * module_size
301
+
302
+ # Glass panel background
303
+ glass_draw.rounded_rectangle([x, y, x + module_size, y + module_size],
304
+ radius=module_size // 6,
305
+ fill=(255, 255, 255, 100))
306
+
307
+ # Glass border
308
+ glass_draw.rounded_rectangle([x, y, x + module_size, y + module_size],
309
+ radius=module_size // 6,
310
+ outline=(255, 255, 255, 150), width=1)
311
+
312
+ # Apply glass effect
313
+ background_rgba = background.convert('RGBA')
314
+ glass_bg = Image.alpha_composite(background_rgba, glass_overlay)
315
+
316
+ # Draw QR dots on glass
317
+ draw = ImageDraw.Draw(glass_bg)
318
+
319
+ for i in range(module_count):
320
+ for j in range(module_count):
321
+ if modules[i][j]:
322
+ x = qr_offset + j * module_size
323
+ y = qr_offset + i * module_size
324
+
325
+ if is_finder_pattern(i, j, module_count):
326
+ draw.rounded_rectangle([x + 2, y + 2, x + module_size - 2, y + module_size - 2],
327
+ radius=module_size // 4, fill=(0, 0, 0))
328
+ else:
329
+ margin = module_size // 3
330
+ draw.ellipse([x + margin, y + margin,
331
+ x + module_size - margin, y + module_size - margin],
332
+ fill=(0, 0, 0))
333
+
334
+ return glass_bg.convert('RGB')
335
+
336
+ def create_minimal_dots_style(image, modules, module_count, size):
337
+ """Minimal style with maximum logo visibility"""
338
+
339
+ # Full logo background
340
+ background = image.resize((size, size), Image.LANCZOS)
341
+
342
+ # Enhance image slightly
343
+ enhancer = ImageEnhance.Color(background)
344
+ background = enhancer.enhance(1.2)
345
+ enhancer = ImageEnhance.Contrast(background)
346
+ background = enhancer.enhance(1.05)
347
+
348
+ # Calculate QR area
349
+ qr_area_size = int(size * 0.92)
350
+ qr_offset = (size - qr_area_size) // 2
351
+ module_size = qr_area_size // module_count
352
+
353
+ # Create minimal overlay
354
+ overlay = Image.new('RGBA', (size, size), (0, 0, 0, 0))
355
+ overlay_draw = ImageDraw.Draw(overlay)
356
+
357
+ # Draw minimal QR dots with smart contrast
358
+ for i in range(module_count):
359
+ for j in range(module_count):
360
+ if modules[i][j]:
361
+ x = qr_offset + j * module_size
362
+ y = qr_offset + i * module_size
363
+
364
+ # Sample background color
365
+ sample_x = min(x + module_size // 2, size - 1)
366
+ sample_y = min(y + module_size // 2, size - 1)
367
+ bg_color = background.getpixel((sample_x, sample_y))
368
+ r, g, b = bg_color
369
+ brightness = (r + g + b) / 3
370
+
371
+ # Choose contrasting color with transparency
372
+ if brightness > 140:
373
+ dot_color = (0, 0, 0, 200) # Dark dots on light background
374
+ outline_color = (255, 255, 255, 100)
375
+ else:
376
+ dot_color = (255, 255, 255, 220) # Light dots on dark background
377
+ outline_color = (0, 0, 0, 100)
378
+
379
+ if is_finder_pattern(i, j, module_count):
380
+ # Minimal finder patterns
381
+ overlay_draw.rounded_rectangle([x + 1, y + 1, x + module_size - 1, y + module_size - 1],
382
+ radius=module_size // 5, fill=dot_color)
383
+ else:
384
+ # Small dots with outline for visibility
385
+ margin = module_size // 3
386
+ overlay_draw.ellipse([x + margin - 1, y + margin - 1,
387
+ x + module_size - margin + 1, y + module_size - margin + 1],
388
+ fill=outline_color)
389
+ overlay_draw.ellipse([x + margin, y + margin,
390
+ x + module_size - margin, y + module_size - margin],
391
+ fill=dot_color)
392
+
393
+ # Combine with background
394
+ background_rgba = background.convert('RGBA')
395
+ result = Image.alpha_composite(background_rgba, overlay)
396
+
397
+ return result.convert('RGB')
398
+
399
+ def create_artistic_shadow_style(image, modules, module_count, size):
400
+ """Artistic style with shadow effects"""
401
+
402
+ # Create background
403
+ background = Image.new('RGB', (size, size), (245, 245, 245))
404
+
405
+ # Logo with slight blur effect
406
+ logo_size = int(size * 0.88)
407
+ logo_resized = image.resize((logo_size, logo_size), Image.LANCZOS)
408
+ logo_blurred = logo_resized.filter(ImageFilter.GaussianBlur(radius=0.5))
409
+
410
+ logo_pos = ((size - logo_size) // 2, (size - logo_size) // 2)
411
+ background.paste(logo_blurred, logo_pos)
412
+
413
+ # Calculate QR area
414
+ qr_area_size = int(size * 0.86)
415
+ qr_offset = (size - qr_area_size) // 2
416
+ module_size = qr_area_size // module_count
417
+
418
+ # Create shadow layer
419
+ shadow_layer = Image.new('RGBA', (size, size), (0, 0, 0, 0))
420
+ shadow_draw = ImageDraw.Draw(shadow_layer)
421
+
422
+ # Draw shadows first
423
+ for i in range(module_count):
424
+ for j in range(module_count):
425
+ if modules[i][j]:
426
+ x = qr_offset + j * module_size
427
+ y = qr_offset + i * module_size
428
+
429
+ # Shadow offset
430
+ shadow_offset = 2
431
+ margin = module_size // 4
432
+
433
+ # Draw shadow
434
+ shadow_draw.ellipse([x + margin + shadow_offset, y + margin + shadow_offset,
435
+ x + module_size - margin + shadow_offset,
436
+ y + module_size - margin + shadow_offset],
437
+ fill=(0, 0, 0, 60))
438
+
439
+ # Apply shadow
440
+ background_rgba = background.convert('RGBA')
441
+ shadowed_bg = Image.alpha_composite(background_rgba, shadow_layer)
442
+
443
+ # Draw main QR dots
444
+ draw = ImageDraw.Draw(shadowed_bg)
445
 
446
  for i in range(module_count):
447
  for j in range(module_count):
 
450
  y = qr_offset + i * module_size
451
 
452
  if is_finder_pattern(i, j, module_count):
 
453
  draw.rounded_rectangle([x, y, x + module_size, y + module_size],
454
  radius=module_size // 4, fill=(0, 0, 0))
455
  else:
456
+ margin = module_size // 4
457
+ # White outline for better visibility
458
+ draw.ellipse([x + margin - 1, y + margin - 1,
459
+ x + module_size - margin + 1, y + module_size - margin + 1],
460
+ fill=(255, 255, 255))
461
+ draw.ellipse([x + margin, y + margin,
462
+ x + module_size - margin, y + module_size - margin],
463
  fill=(0, 0, 0))
 
 
 
 
 
 
 
 
 
 
 
 
464
 
465
+ return shadowed_bg.convert('RGB')
466
 
467
+ def create_vibrant_overlay_style(image, modules, module_count, size):
468
+ """Vibrant style with colorful QR elements"""
469
+
470
+ # Enhanced background
471
+ background = Image.new('RGB', (size, size), (255, 255, 255))
472
+
473
+ # Full vibrant logo
474
+ logo_resized = image.resize((size, size), Image.LANCZOS)
475
+ enhancer = ImageEnhance.Color(logo_resized)
476
+ logo_vibrant = enhancer.enhance(1.5)
477
+ enhancer = ImageEnhance.Contrast(logo_vibrant)
478
+ logo_vibrant = enhancer.enhance(1.1)
479
+
480
+ background.paste(logo_vibrant, (0, 0))
481
+
482
+ # Calculate QR area
483
+ qr_area_size = int(size * 0.90)
484
+ qr_offset = (size - qr_area_size) // 2
485
+ module_size = qr_area_size // module_count
486
+
487
+ # Extract dominant colors from logo
488
+ small_logo = logo_resized.resize((50, 50))
489
+ colors = small_logo.getcolors(2500)
490
+ if colors:
491
+ dominant_color = max(colors, key=lambda x: x[0])[1]
492
+ r, g, b = dominant_color
493
+ # Create complementary color
494
+ h, s, v = colorsys.rgb_to_hsv(r/255, g/255, b/255)
495
+ comp_h = (h + 0.5) % 1.0
496
+ comp_r, comp_g, comp_b = colorsys.hsv_to_rgb(comp_h, s, v)
497
+ accent_color = (int(comp_r * 255), int(comp_g * 255), int(comp_b * 255))
498
+ else:
499
+ accent_color = (0, 100, 255) # Default blue
500
+
501
+ # Draw QR with vibrant colors
502
+ draw = ImageDraw.Draw(background)
503
+
504
+ for i in range(module_count):
505
+ for j in range(module_count):
506
+ if modules[i][j]:
507
+ x = qr_offset + j * module_size
508
+ y = qr_offset + i * module_size
509
+
510
+ # Alternate between black and accent color
511
+ if is_finder_pattern(i, j, module_count):
512
+ draw.rounded_rectangle([x, y, x + module_size, y + module_size],
513
+ radius=module_size // 5, fill=(0, 0, 0))
514
+ else:
515
+ # Use accent color for some dots
516
+ use_accent = (i + j) % 4 == 0
517
+ dot_color = accent_color if use_accent else (0, 0, 0)
518
+
519
+ margin = module_size // 4
520
+ # White outline for visibility
521
+ draw.ellipse([x + margin - 1, y + margin - 1,
522
+ x + module_size - margin + 1, y + module_size - margin + 1],
523
+ fill=(255, 255, 255))
524
+ draw.ellipse([x + margin, y + margin,
525
+ x + module_size - margin, y + module_size - margin],
526
+ fill=dot_color)
527
+
528
+ return background
529
 
530
  def is_finder_pattern(row, col, size):
531
  """Check if position is in finder pattern area"""
 
546
  return qr_img.resize((size, size), Image.LANCZOS)
547
 
548
  def calculate_compatibility_score(qr_image):
549
+ """Calculate QR code compatibility score with enhanced metrics"""
550
  try:
551
  gray = cv2.cvtColor(np.array(qr_image), cv2.COLOR_RGB2GRAY)
552
 
553
+ # Enhanced contrast analysis
554
  min_val, max_val = np.min(gray), np.max(gray)
555
  contrast_ratio = max_val / max(min_val, 1)
556
 
 
558
  edges = cv2.Canny(gray, 50, 150)
559
  edge_ratio = np.sum(edges > 0) / edges.size
560
 
561
+ # Pattern clarity (check for QR-like patterns)
562
+ pattern_score = analyze_qr_patterns(gray)
 
563
 
564
+ # Calculate enhanced scores
565
+ contrast_score = min(contrast_ratio / 4, 1) * 35
566
+ edge_score = min(edge_ratio * 120, 1) * 35
567
+ pattern_score_final = pattern_score * 30
568
 
569
+ total_score = int(contrast_score + edge_score + pattern_score_final)
570
 
571
  return {
572
+ 'overall': min(total_score, 100),
573
  'contrast': int(contrast_score),
574
  'sharpness': int(edge_score),
575
+ 'pattern_clarity': int(pattern_score_final),
576
+ 'recommendations': get_enhanced_recommendations(total_score)
577
  }
578
  except Exception as e:
579
  logger.error(f"Error calculating compatibility: {str(e)}")
580
+ return {'overall': 85, 'contrast': 85, 'sharpness': 85, 'pattern_clarity': 85, 'recommendations': []}
581
+
582
+ def analyze_qr_patterns(gray_image):
583
+ """Analyze QR pattern clarity"""
584
+ try:
585
+ # Look for finder patterns (squares in corners)
586
+ height, width = gray_image.shape
587
+ corner_size = min(height, width) // 8
588
+
589
+ # Check corners for square patterns
590
+ corners = [
591
+ gray_image[:corner_size, :corner_size], # Top-left
592
+ gray_image[:corner_size, -corner_size:], # Top-right
593
+ gray_image[-corner_size:, :corner_size] # Bottom-left
594
+ ]
595
+
596
+ pattern_scores = []
597
+ for corner in corners:
598
+ # Simple pattern detection
599
+ _, binary = cv2.threshold(corner, 128, 255, cv2.THRESH_BINARY)
600
+ contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
601
+ if len(contours) > 0:
602
+ pattern_scores.append(1.0)
603
+ else:
604
+ pattern_scores.append(0.5)
605
+
606
+ return sum(pattern_scores) / len(pattern_scores)
607
+ except:
608
+ return 0.8
609
 
610
+ def get_enhanced_recommendations(score):
611
+ """Get enhanced improvement recommendations"""
612
  recommendations = []
613
+ if score < 80:
614
+ recommendations.append("Try 'minimal_dots' or 'modern_overlay' for better scanning")
615
  if score < 70:
616
+ recommendations.append("Consider using higher contrast logos")
617
  if score < 60:
618
+ recommendations.append("Your logo might be too complex - try 'glassmorphism' style")
619
+ if score > 85:
620
+ recommendations.append("Excellent! This QR code should scan perfectly")
621
  return recommendations
622
 
623
  def image_to_base64(image):
624
  """Convert PIL image to base64 string"""
625
  buffer = BytesIO()
626
+ image.save(buffer, format='PNG', optimize=True, quality=95)
627
  img_str = base64.b64encode(buffer.getvalue()).decode()
628
  return f"data:image/png;base64,{img_str}"
629
 
630
  @app.route('/', methods=['GET'])
631
  def home():
632
  return jsonify({
633
+ 'message': 'Modern QR Generator is running',
634
  'status': 'healthy',
635
+ 'styles': ['modern_overlay', 'gradient_blend', 'neon_glow', 'glassmorphism', 'minimal_dots', 'artistic_shadow', 'vibrant_overlay']
636
  })
637
 
638
  @app.route('/health', methods=['GET'])
639
  def health_check():
640
+ return jsonify({'status': 'healthy', 'message': 'Modern QR Service is running'})
641
 
642
  @app.route('/api/generate-artistic-qr', methods=['POST'])
643
  def generate_artistic_qr():
 
647
 
648
  file = request.files['image']
649
  url = request.form['url']
650
+ style = request.form.get('style', 'modern_overlay')
651
  size = int(request.form.get('size', 500))
652
 
653
  if file.filename == '' or not allowed_file(file.filename):
 
661
  except Exception:
662
  return jsonify({'error': 'Invalid image file'}), 400
663
 
664
+ # Generate modern QR code
665
  artistic_qr = create_background_logo_qr(image, url, style, size)
666
  compatibility = calculate_compatibility_score(artistic_qr)
667
  qr_base64 = image_to_base64(artistic_qr)
 
686
  @app.route('/api/styles', methods=['GET'])
687
  def get_available_styles():
688
  styles = {
689
+ 'modern_overlay': {
690
+ 'name': 'Modern Overlay',
691
+ 'description': 'Sleek modern style with enhanced logo visibility and minimal overlay',
692
+ 'compatibility': 95,
693
+ 'best_for': 'Corporate logos, clean designs'
694
  },
695
+ 'gradient_blend': {
696
+ 'name': 'Gradient Blend',
697
+ 'description': 'Sophisticated gradient effect with smart color adaptation',
698
+ 'compatibility': 92,
699
+ 'best_for': 'Colorful logos, artistic designs'
700
  },
701
+ 'neon_glow': {
702
+ 'name': 'Neon Glow',
703
+ 'description': 'Futuristic neon glow effect with dark background',
704
+ 'compatibility': 90,
705
+ 'best_for': 'Tech brands, gaming, nightlife'
706
+ },
707
+ 'glassmorphism': {
708
+ 'name': 'Glassmorphism',
709
+ 'description': 'Trendy glass-like effect with frosted overlay',
710
+ 'compatibility': 88,
711
+ 'best_for': 'Modern apps, luxury brands'
712
+ },
713
+ 'minimal_dots': {
714
+ 'name': 'Minimal Dots',
715
+ 'description': 'Ultra-minimal design with maximum logo visibility',
716
+ 'compatibility': 97,
717
+ 'best_for': 'Photography, art, detailed logos'
718
+ },
719
+ 'artistic_shadow': {
720
+ 'name': 'Artistic Shadow',
721
+ 'description': 'Elegant shadow effects with artistic flair',
722
+ 'compatibility': 91,
723
+ 'best_for': 'Premium brands, portfolios'
724
+ },
725
+ 'vibrant_overlay': {
726
+ 'name': 'Vibrant Overlay',
727
+ 'description': 'Bold colorful style with accent colors from your logo',
728
+ 'compatibility': 89,
729
+ 'best_for': 'Creative brands, events, social media'
730
  }
731
  }
732
 
733
  return jsonify({'success': True, 'styles': styles})
734
 
735
+ @app.route('/api/preview-styles', methods=['POST'])
736
+ def preview_styles():
737
+ """Generate previews of all styles for a given image"""
738
+ try:
739
+ if 'image' not in request.files:
740
+ return jsonify({'error': 'Missing image'}), 400
741
+
742
+ file = request.files['image']
743
+ url = request.form.get('url', 'https://example.com')
744
+ size = int(request.form.get('size', 300)) # Smaller for previews
745
+
746
+ if file.filename == '' or not allowed_file(file.filename):
747
+ return jsonify({'error': 'Invalid file'}), 400
748
+
749
+ try:
750
+ image = Image.open(file.stream).convert('RGB')
751
+ except Exception:
752
+ return jsonify({'error': 'Invalid image file'}), 400
753
+
754
+ # Generate previews for all styles
755
+ previews = {}
756
+ styles = ['modern_overlay', 'gradient_blend', 'neon_glow', 'glassmorphism',
757
+ 'minimal_dots', 'artistic_shadow', 'vibrant_overlay']
758
+
759
+ for style in styles:
760
+ try:
761
+ qr_image = create_background_logo_qr(image, url, style, size)
762
+ compatibility = calculate_compatibility_score(qr_image)
763
+ previews[style] = {
764
+ 'image': image_to_base64(qr_image),
765
+ 'compatibility': compatibility['overall']
766
+ }
767
+ except Exception as e:
768
+ logger.error(f"Error generating preview for {style}: {str(e)}")
769
+ previews[style] = {
770
+ 'image': None,
771
+ 'compatibility': 0,
772
+ 'error': str(e)
773
+ }
774
+
775
+ return jsonify({
776
+ 'success': True,
777
+ 'previews': previews
778
+ })
779
+
780
+ except Exception as e:
781
+ logger.error(f"Error in preview_styles: {str(e)}")
782
+ return jsonify({'error': f'Internal server error: {str(e)}'}), 500
783
+
784
+ @app.route('/api/analyze-image', methods=['POST'])
785
+ def analyze_image():
786
+ """Analyze uploaded image and suggest best styles"""
787
+ try:
788
+ if 'image' not in request.files:
789
+ return jsonify({'error': 'Missing image'}), 400
790
+
791
+ file = request.files['image']
792
+
793
+ if file.filename == '' or not allowed_file(file.filename):
794
+ return jsonify({'error': 'Invalid file'}), 400
795
+
796
+ try:
797
+ image = Image.open(file.stream).convert('RGB')
798
+ except Exception:
799
+ return jsonify({'error': 'Invalid image file'}), 400
800
+
801
+ # Analyze image characteristics
802
+ analysis = analyze_image_characteristics(image)
803
+
804
+ # Suggest best styles based on analysis
805
+ suggestions = suggest_styles_for_image(analysis)
806
+
807
+ return jsonify({
808
+ 'success': True,
809
+ 'analysis': analysis,
810
+ 'suggestions': suggestions
811
+ })
812
+
813
+ except Exception as e:
814
+ logger.error(f"Error in analyze_image: {str(e)}")
815
+ return jsonify({'error': f'Internal server error: {str(e)}'}), 500
816
+
817
+ def analyze_image_characteristics(image):
818
+ """Analyze image characteristics to suggest best QR style"""
819
+ try:
820
+ # Resize for analysis
821
+ small_image = image.resize((100, 100))
822
+
823
+ # Color analysis
824
+ colors = small_image.getcolors(10000)
825
+ if colors:
826
+ total_pixels = sum(count for count, color in colors)
827
+
828
+ # Calculate color diversity
829
+ color_diversity = len(colors) / total_pixels
830
+
831
+ # Find dominant color
832
+ dominant_color = max(colors, key=lambda x: x[0])[1]
833
+
834
+ # Calculate average brightness
835
+ avg_brightness = sum(sum(color) for count, color in colors) / (len(colors) * 3)
836
+
837
+ # Calculate color saturation
838
+ hsv_colors = [colorsys.rgb_to_hsv(r/255, g/255, b/255) for count, (r, g, b) in colors]
839
+ avg_saturation = sum(s for h, s, v in hsv_colors) / len(hsv_colors)
840
+
841
+ else:
842
+ color_diversity = 0.5
843
+ dominant_color = (128, 128, 128)
844
+ avg_brightness = 128
845
+ avg_saturation = 0.5
846
+
847
+ # Complexity analysis using edge detection
848
+ gray = cv2.cvtColor(np.array(small_image), cv2.COLOR_RGB2GRAY)
849
+ edges = cv2.Canny(gray, 50, 150)
850
+ complexity = np.sum(edges > 0) / edges.size
851
+
852
+ return {
853
+ 'color_diversity': color_diversity,
854
+ 'dominant_color': dominant_color,
855
+ 'avg_brightness': avg_brightness,
856
+ 'avg_saturation': avg_saturation,
857
+ 'complexity': complexity,
858
+ 'is_dark': avg_brightness < 100,
859
+ 'is_colorful': avg_saturation > 0.3,
860
+ 'is_complex': complexity > 0.1
861
+ }
862
+
863
+ except Exception as e:
864
+ logger.error(f"Error analyzing image: {str(e)}")
865
+ return {
866
+ 'color_diversity': 0.5,
867
+ 'dominant_color': (128, 128, 128),
868
+ 'avg_brightness': 128,
869
+ 'avg_saturation': 0.5,
870
+ 'complexity': 0.1,
871
+ 'is_dark': False,
872
+ 'is_colorful': True,
873
+ 'is_complex': False
874
+ }
875
+
876
+ def suggest_styles_for_image(analysis):
877
+ """Suggest best QR styles based on image analysis"""
878
+ suggestions = []
879
+
880
+ # For dark images
881
+ if analysis['is_dark']:
882
+ suggestions.append({
883
+ 'style': 'neon_glow',
884
+ 'reason': 'Perfect for dark images with striking neon effect',
885
+ 'compatibility': 90
886
+ })
887
+ suggestions.append({
888
+ 'style': 'minimal_dots',
889
+ 'reason': 'Adapts well to dark backgrounds',
890
+ 'compatibility': 95
891
+ })
892
+
893
+ # For colorful images
894
+ if analysis['is_colorful']:
895
+ suggestions.append({
896
+ 'style': 'vibrant_overlay',
897
+ 'reason': 'Enhances colorful logos with accent colors',
898
+ 'compatibility': 89
899
+ })
900
+ suggestions.append({
901
+ 'style': 'gradient_blend',
902
+ 'reason': 'Beautiful gradient effects with colorful images',
903
+ 'compatibility': 92
904
+ })
905
+
906
+ # For complex images
907
+ if analysis['is_complex']:
908
+ suggestions.append({
909
+ 'style': 'glassmorphism',
910
+ 'reason': 'Glass effect reduces visual noise in complex images',
911
+ 'compatibility': 88
912
+ })
913
+ suggestions.append({
914
+ 'style': 'artistic_shadow',
915
+ 'reason': 'Shadow effects help separate QR from complex backgrounds',
916
+ 'compatibility': 91
917
+ })
918
+
919
+ # For simple/clean images
920
+ if not analysis['is_complex']:
921
+ suggestions.append({
922
+ 'style': 'modern_overlay',
923
+ 'reason': 'Clean modern style perfect for simple logos',
924
+ 'compatibility': 95
925
+ })
926
+ suggestions.append({
927
+ 'style': 'minimal_dots',
928
+ 'reason': 'Maximum logo visibility for clean designs',
929
+ 'compatibility': 97
930
+ })
931
+
932
+ # Always include modern_overlay as a safe choice
933
+ if not any(s['style'] == 'modern_overlay' for s in suggestions):
934
+ suggestions.append({
935
+ 'style': 'modern_overlay',
936
+ 'reason': 'Reliable choice for any image type',
937
+ 'compatibility': 95
938
+ })
939
+
940
+ # Sort by compatibility score
941
+ suggestions.sort(key=lambda x: x['compatibility'], reverse=True)
942
+
943
+ return suggestions[:4] # Return top 4 suggestions
944
+
945
  if __name__ == '__main__':
946
  port = int(os.environ.get('PORT', 7860))
947
  app.run(host='0.0.0.0', port=port, debug=False)