mike23415 commited on
Commit
111a3fc
·
verified ·
1 Parent(s): 9da5680

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +228 -293
app.py CHANGED
@@ -6,7 +6,7 @@ from flask import Flask, request, jsonify
6
  from flask_cors import CORS
7
  from PIL import Image, ImageEnhance, ImageFilter, ImageDraw, ImageFont
8
  import qrcode
9
- from qrcode.constants import ERROR_CORRECT_H, ERROR_CORRECT_M
10
  import cv2
11
  from io import BytesIO
12
  import requests
@@ -14,6 +14,7 @@ from werkzeug.utils import secure_filename
14
  import logging
15
  from sklearn.cluster import KMeans
16
  import colorsys
 
17
 
18
  # Configure logging
19
  logging.basicConfig(level=logging.INFO)
@@ -29,314 +30,248 @@ ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp'}
29
  def allowed_file(filename):
30
  return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
31
 
32
- def extract_dominant_colors(image, n_colors=5):
33
- """Extract dominant colors from image using K-means clustering"""
34
- # Resize image for faster processing
35
- image_small = image.resize((100, 100))
36
-
37
- # Convert to RGB array
38
- data = np.array(image_small)
39
- data = data.reshape((-1, 3))
40
-
41
- # Remove pure white and very light colors
42
- data = data[np.sum(data, axis=1) < 720] # Remove colors where R+G+B > 720
43
-
44
- if len(data) == 0:
45
- return [(0, 0, 0), (255, 255, 255)]
46
-
47
- # Perform K-means clustering
48
- kmeans = KMeans(n_clusters=min(n_colors, len(data)), random_state=42, n_init=10)
49
- kmeans.fit(data)
50
-
51
- colors = kmeans.cluster_centers_.astype(int)
52
-
53
- # Sort colors by darkness (darker first for better QR contrast)
54
- colors = sorted(colors, key=lambda x: sum(x))
55
-
56
- return [tuple(color) for color in colors]
57
-
58
- def create_brand_style_qr(image, qr_data, style='brand_integration', size=400):
59
- """Create brand-style QR codes like in the reference image"""
60
  try:
61
- # Extract dominant colors
62
- colors = extract_dominant_colors(image)
63
- primary_color = colors[0] if colors else (0, 0, 0)
64
- background_color = colors[-1] if len(colors) > 1 else (255, 255, 255)
 
 
 
 
 
65
 
66
- # Ensure good contrast
67
- if sum(primary_color) > 300: # If primary is too light
68
- primary_color = tuple(max(0, c - 100) for c in primary_color)
69
 
70
- if sum(background_color) < 400: # If background is too dark
71
- background_color = tuple(min(255, c + 100) for c in background_color)
72
-
73
- if style == 'brand_integration':
74
- return create_brand_integration_qr(image, qr_data, primary_color, background_color, size)
75
- elif style == 'logo_overlay':
76
- return create_logo_overlay_qr(image, qr_data, primary_color, background_color, size)
77
- elif style == 'pattern_fusion':
78
- return create_pattern_fusion_qr(image, qr_data, primary_color, background_color, size)
79
- elif style == 'artistic_dots':
80
- return create_artistic_dots_qr(image, qr_data, primary_color, background_color, size)
81
  else:
82
- return create_brand_integration_qr(image, qr_data, primary_color, background_color, size)
83
 
84
  except Exception as e:
85
- logger.error(f"Error creating brand QR: {str(e)}")
86
- # Fallback to basic QR
87
  return create_basic_qr(qr_data, size)
88
 
89
- def create_brand_integration_qr(image, qr_data, primary_color, bg_color, size):
90
- """Create QR with brand logo integrated in center like Pizza Hut, Starbucks style"""
91
-
92
- # Create QR code with medium error correction for logo integration
93
- qr = qrcode.QRCode(
94
- version=3, # Higher version for more space
95
- error_correction=ERROR_CORRECT_M, # 15% error correction
96
- box_size=10,
97
- border=2,
98
- )
99
- qr.add_data(qr_data)
100
- qr.make(fit=True)
101
-
102
- # Create base QR with brand colors
103
- qr_img = qr.make_image(fill_color=primary_color, back_color=bg_color)
104
- qr_img = qr_img.resize((size, size), Image.LANCZOS)
105
-
106
- # Prepare logo
107
- logo_size = size // 4 # Logo takes 1/4 of QR size
108
- logo = image.resize((logo_size, logo_size), Image.LANCZOS)
109
-
110
- # Create circular mask for logo (like in the examples)
111
- mask = Image.new('L', (logo_size, logo_size), 0)
112
- draw = ImageDraw.Draw(mask)
113
- draw.ellipse((0, 0, logo_size, logo_size), fill=255)
114
-
115
- # Apply circular mask to logo
116
- logo_circular = Image.new('RGBA', (logo_size, logo_size), (0, 0, 0, 0))
117
- logo_circular.paste(logo, (0, 0))
118
- logo_circular.putalpha(mask)
119
 
120
- # Create white background circle for logo
121
- white_circle = Image.new('RGBA', (logo_size + 20, logo_size + 20), (255, 255, 255, 255))
122
- mask_white = Image.new('L', (logo_size + 20, logo_size + 20), 0)
123
- draw_white = ImageDraw.Draw(mask_white)
124
- draw_white.ellipse((0, 0, logo_size + 20, logo_size + 20), fill=255)
125
- white_circle.putalpha(mask_white)
126
 
127
- # Convert QR to RGBA for compositing
128
- qr_rgba = qr_img.convert('RGBA')
 
129
 
130
- # Paste white circle first (background for logo)
131
- white_pos = ((size - logo_size - 20) // 2, (size - logo_size - 20) // 2)
132
- qr_rgba.paste(white_circle, white_pos, white_circle)
133
-
134
- # Paste logo on top
135
  logo_pos = ((size - logo_size) // 2, (size - logo_size) // 2)
136
- qr_rgba.paste(logo_circular, logo_pos, logo_circular)
137
-
138
- return qr_rgba.convert('RGB')
139
-
140
- def create_logo_overlay_qr(image, qr_data, primary_color, bg_color, size):
141
- """Create QR with logo overlaid and stylized dots"""
142
 
143
- # Create QR code
144
- qr = qrcode.QRCode(
145
- version=2,
146
- error_correction=ERROR_CORRECT_H, # High error correction for heavy modification
147
- box_size=8,
148
- border=3,
149
- )
150
- qr.add_data(qr_data)
151
- qr.make(fit=True)
152
 
153
- # Get QR matrix
154
- modules = qr.modules
155
- module_count = len(modules)
156
- module_size = size // (module_count + 6) # +6 for border
157
 
158
- # Create result image
159
- result = Image.new('RGB', (size, size), bg_color)
160
- draw = ImageDraw.Draw(result)
161
 
162
- # Draw QR modules as circles/rounded rectangles
163
  for i in range(module_count):
164
  for j in range(module_count):
165
- if modules[i][j]: # Black module
166
- x = (j + 3) * module_size
167
- y = (i + 3) * module_size
168
 
169
- # Skip center area for logo
170
- center_start = module_count // 2 - 3
171
- center_end = module_count // 2 + 3
172
- if center_start <= i <= center_end and center_start <= j <= center_end:
173
- continue
174
-
175
- # Draw rounded square or circle
176
  if is_finder_pattern(i, j, module_count):
177
- # Finder patterns as solid rectangles
178
  draw.rectangle([x, y, x + module_size - 1, y + module_size - 1],
179
- fill=primary_color)
 
 
 
 
180
  else:
181
- # Regular modules as circles
182
- margin = module_size // 4
183
- draw.ellipse([x + margin, y + margin,
184
- x + module_size - margin, y + module_size - margin],
185
- fill=primary_color)
186
 
187
- # Add logo in center
188
- logo_size = size // 3
189
- logo = image.resize((logo_size, logo_size), Image.LANCZOS)
190
- logo_pos = ((size - logo_size) // 2, (size - logo_size) // 2)
191
 
192
- # Create white background for logo
193
- draw.rectangle([logo_pos[0] - 10, logo_pos[1] - 10,
194
- logo_pos[0] + logo_size + 10, logo_pos[1] + logo_size + 10],
195
- fill=(255, 255, 255))
196
-
197
- result.paste(logo, logo_pos)
198
-
199
- return result
200
 
201
- def create_pattern_fusion_qr(image, qr_data, primary_color, bg_color, size):
202
- """Create QR with image patterns fused into the code"""
203
 
204
- # Create QR code
205
- qr = qrcode.QRCode(
206
- version=2,
207
- error_correction=ERROR_CORRECT_H,
208
- box_size=10,
209
- border=2,
210
- )
211
- qr.add_data(qr_data)
212
- qr.make(fit=True)
213
 
214
- # Get modules and resize image to match
215
- modules = qr.modules
216
- module_count = len(modules)
 
 
217
 
218
- # Resize image to QR dimensions
219
- qr_size = module_count * 10
220
- image_resized = image.resize((qr_size, qr_size), Image.LANCZOS)
 
221
 
222
- # Convert to arrays
223
- img_array = np.array(image_resized)
 
 
224
 
225
- # Create result
226
- result = np.full((qr_size, qr_size, 3), bg_color, dtype=np.uint8)
227
- module_size = 10
228
 
229
  for i in range(module_count):
230
  for j in range(module_count):
231
- y1, x1 = i * module_size, j * module_size
232
- y2, x2 = (i + 1) * module_size, (j + 1) * module_size
233
-
234
- if modules[i][j]: # Black module
235
- if is_finder_pattern(i, j, module_count) or is_timing_pattern(i, j):
236
- # Keep critical patterns solid
237
- result[y1:y2, x1:x2] = primary_color
 
238
  else:
239
- # Use image texture but darkened
240
- img_patch = img_array[y1:y2, x1:x2]
241
- darkened = (img_patch * 0.3).astype(np.uint8)
242
- result[y1:y2, x1:x2] = darkened
243
- else: # White module
244
- # Use image texture but lightened
245
- img_patch = img_array[y1:y2, x1:x2]
246
- lightened = (img_patch * 0.7 + np.array(bg_color) * 0.3).astype(np.uint8)
247
- result[y1:y2, x1:x2] = lightened
 
 
 
 
 
 
 
 
 
 
 
 
 
248
 
249
- # Resize to target size
250
- result_img = Image.fromarray(result)
251
- return result_img.resize((size, size), Image.LANCZOS)
252
 
253
- def create_artistic_dots_qr(image, qr_data, primary_color, bg_color, size):
254
- """Create QR with artistic dots and patterns like in the reference"""
255
 
256
- # Create QR code
257
- qr = qrcode.QRCode(
258
- version=2,
259
- error_correction=ERROR_CORRECT_H,
260
- box_size=12,
261
- border=2,
262
- )
263
- qr.add_data(qr_data)
264
- qr.make(fit=True)
265
 
266
- modules = qr.modules
267
- module_count = len(modules)
268
- module_size = size // (module_count + 4)
269
 
270
- # Create gradient background
271
- result = Image.new('RGB', (size, size), bg_color)
272
- draw = ImageDraw.Draw(result)
 
273
 
274
- # Add subtle gradient
275
- for y in range(size):
276
- color_factor = y / size
277
- grad_color = tuple(int(bg_color[i] * (1 - color_factor * 0.1)) for i in range(3))
278
- draw.line([(0, y), (size, y)], fill=grad_color)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
 
280
- # Draw QR modules with various artistic shapes
281
  for i in range(module_count):
282
  for j in range(module_count):
283
- if modules[i][j]: # Black module
284
- x = (j + 2) * module_size
285
- y = (i + 2) * module_size
286
 
287
  if is_finder_pattern(i, j, module_count):
288
- # Finder patterns with decorative elements
289
- draw.rectangle([x, y, x + module_size, y + module_size],
290
- fill=primary_color)
291
- # Add corner decorations
292
- corner_size = module_size // 4
293
- draw.ellipse([x - corner_size, y - corner_size,
294
- x + corner_size, y + corner_size],
295
- fill=primary_color)
296
  else:
297
- # Regular modules as artistic dots
298
- dot_size = module_size - 2
299
  center_x, center_y = x + module_size // 2, y + module_size // 2
 
300
 
301
- # Vary dot shapes
302
- if (i + j) % 3 == 0:
303
- # Circle
304
- draw.ellipse([center_x - dot_size//2, center_y - dot_size//2,
305
- center_x + dot_size//2, center_y + dot_size//2],
306
- fill=primary_color)
307
- elif (i + j) % 3 == 1:
308
- # Square
309
- draw.rectangle([center_x - dot_size//2, center_y - dot_size//2,
310
- center_x + dot_size//2, center_y + dot_size//2],
311
- fill=primary_color)
312
- else:
313
- # Diamond
314
- points = [
315
- (center_x, center_y - dot_size//2),
316
- (center_x + dot_size//2, center_y),
317
- (center_x, center_y + dot_size//2),
318
- (center_x - dot_size//2, center_y)
319
- ]
320
- draw.polygon(points, fill=primary_color)
321
-
322
- # Add logo in center
323
- logo_size = size // 4
324
- logo = image.resize((logo_size, logo_size), Image.LANCZOS)
325
-
326
- # Create decorative frame for logo
327
- frame_size = logo_size + 20
328
- frame_pos = ((size - frame_size) // 2, (size - frame_size) // 2)
329
-
330
- # Draw decorative frame
331
- draw.rectangle([frame_pos[0], frame_pos[1],
332
- frame_pos[0] + frame_size, frame_pos[1] + frame_size],
333
- fill=(255, 255, 255), outline=primary_color, width=3)
334
-
335
- # Paste logo
336
- logo_pos = ((size - logo_size) // 2, (size - logo_size) // 2)
337
- result.paste(logo, logo_pos)
338
-
339
- return result
 
340
 
341
  def is_finder_pattern(row, col, size):
342
  """Check if position is in finder pattern area"""
@@ -360,18 +295,23 @@ def calculate_compatibility_score(qr_image):
360
  """Calculate QR code compatibility score"""
361
  try:
362
  gray = cv2.cvtColor(np.array(qr_image), cv2.COLOR_RGB2GRAY)
 
 
363
  min_val, max_val = np.min(gray), np.max(gray)
364
  contrast_ratio = max_val / max(min_val, 1)
365
 
 
366
  edges = cv2.Canny(gray, 50, 150)
367
  edge_ratio = np.sum(edges > 0) / edges.size
368
 
 
369
  blur = cv2.GaussianBlur(gray, (5, 5), 0)
370
  noise = np.std(gray - blur)
371
 
372
- contrast_score = min(contrast_ratio / 8, 1) * 40
373
- edge_score = min(edge_ratio * 80, 1) * 35
374
- noise_score = max(0, 1 - noise / 40) * 25
 
375
 
376
  total_score = int(contrast_score + edge_score + noise_score)
377
 
@@ -384,17 +324,17 @@ def calculate_compatibility_score(qr_image):
384
  }
385
  except Exception as e:
386
  logger.error(f"Error calculating compatibility: {str(e)}")
387
- return {'overall': 75, 'contrast': 75, 'sharpness': 75, 'noise_level': 75, 'recommendations': []}
388
 
389
  def get_recommendations(score):
390
  """Get improvement recommendations"""
391
  recommendations = []
392
  if score < 70:
393
- recommendations.append("Try using a simpler logo with higher contrast")
394
  if score < 60:
395
- recommendations.append("Consider the 'Brand Integration' style for better scanning")
396
  if score < 50:
397
- recommendations.append("Your logo might be too complex - try a simpler version")
398
  return recommendations
399
 
400
  def image_to_base64(image):
@@ -407,14 +347,14 @@ def image_to_base64(image):
407
  @app.route('/', methods=['GET'])
408
  def home():
409
  return jsonify({
410
- 'message': 'Enhanced Brand QR Generator is running',
411
  'status': 'healthy',
412
- 'styles': ['brand_integration', 'logo_overlay', 'pattern_fusion', 'artistic_dots']
413
  })
414
 
415
  @app.route('/health', methods=['GET'])
416
  def health_check():
417
- return jsonify({'status': 'healthy', 'message': 'Enhanced QR Service is running'})
418
 
419
  @app.route('/api/generate-artistic-qr', methods=['POST'])
420
  def generate_artistic_qr():
@@ -424,8 +364,8 @@ def generate_artistic_qr():
424
 
425
  file = request.files['image']
426
  url = request.form['url']
427
- style = request.form.get('style', 'brand_integration')
428
- size = int(request.form.get('size', 400))
429
 
430
  if file.filename == '' or not allowed_file(file.filename):
431
  return jsonify({'error': 'Invalid file'}), 400
@@ -438,8 +378,8 @@ def generate_artistic_qr():
438
  except Exception:
439
  return jsonify({'error': 'Invalid image file'}), 400
440
 
441
- # Generate brand-style QR code
442
- artistic_qr = create_brand_style_qr(image, url, style, size)
443
  compatibility = calculate_compatibility_score(artistic_qr)
444
  qr_base64 = image_to_base64(artistic_qr)
445
 
@@ -458,30 +398,25 @@ def generate_artistic_qr():
458
 
459
  except Exception as e:
460
  logger.error(f"Error in generate_artistic_qr: {str(e)}")
461
- return jsonify({'error': 'Internal server error'}), 500
462
 
463
  @app.route('/api/styles', methods=['GET'])
464
  def get_available_styles():
465
  styles = {
466
- 'brand_integration': {
467
- 'name': 'Brand Integration',
468
- 'description': 'Logo in center with brand colors (like Pizza Hut, Starbucks)',
469
- 'compatibility': 85
470
- },
471
- 'logo_overlay': {
472
- 'name': 'Logo Overlay',
473
- 'description': 'Logo overlaid with stylized QR dots',
474
- 'compatibility': 75
475
  },
476
- 'pattern_fusion': {
477
- 'name': 'Pattern Fusion',
478
- 'description': 'Brand patterns fused with QR code',
479
- 'compatibility': 70
480
  },
481
- 'artistic_dots': {
482
- 'name': 'Artistic Dots',
483
- 'description': 'Decorative dots and shapes with logo',
484
- 'compatibility': 80
485
  }
486
  }
487
 
 
6
  from flask_cors import CORS
7
  from PIL import Image, ImageEnhance, ImageFilter, ImageDraw, ImageFont
8
  import qrcode
9
+ from qrcode.constants import ERROR_CORRECT_H, ERROR_CORRECT_M, ERROR_CORRECT_L
10
  import cv2
11
  from io import BytesIO
12
  import requests
 
14
  import logging
15
  from sklearn.cluster import KMeans
16
  import colorsys
17
+ import math
18
 
19
  # Configure logging
20
  logging.basicConfig(level=logging.INFO)
 
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(
38
+ version=4, # Higher version for better logo visibility
39
+ error_correction=ERROR_CORRECT_H, # 30% error correction
40
+ box_size=10,
41
+ border=4,
42
+ )
43
+ qr.add_data(qr_data)
44
+ qr.make(fit=True)
45
 
46
+ # Get QR modules
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):
147
+ if modules[i][j]:
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):
222
+ if modules[i][j]:
223
+ x = qr_offset + j * module_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"""
 
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
 
303
+ # Edge detection for sharpness
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
 
 
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):
 
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
 
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):
371
  return jsonify({'error': 'Invalid file'}), 400
 
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)
385
 
 
398
 
399
  except Exception as e:
400
  logger.error(f"Error in generate_artistic_qr: {str(e)}")
401
+ return jsonify({'error': f'Internal server error: {str(e)}'}), 500
402
 
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