import os import io import base64 import numpy as np from flask import Flask, request, jsonify from flask_cors import CORS from PIL import Image, ImageEnhance, ImageFilter, ImageDraw, ImageFont import qrcode from qrcode.constants import ERROR_CORRECT_H, ERROR_CORRECT_M, ERROR_CORRECT_L import cv2 from io import BytesIO import requests from werkzeug.utils import secure_filename import logging from sklearn.cluster import KMeans import colorsys import math # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) app = Flask(__name__) CORS(app) # Configuration MAX_FILE_SIZE = 10 * 1024 * 1024 # 10MB ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp'} def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS def create_background_logo_qr(image, qr_data, style='modern_overlay', size=400): """Create QR with modern styles and better image visibility""" try: # Create QR code with high error correction qr = qrcode.QRCode( version=4, # Higher version for better logo visibility error_correction=ERROR_CORRECT_H, # 30% error correction box_size=10, border=4, ) qr.add_data(qr_data) qr.make(fit=True) # Get QR modules modules = qr.modules module_count = len(modules) # Route to different modern styles if style == 'modern_overlay': return create_modern_overlay_style(image, modules, module_count, size) elif style == 'gradient_blend': return create_gradient_blend_style(image, modules, module_count, size) elif style == 'neon_glow': return create_neon_glow_style(image, modules, module_count, size) elif style == 'glassmorphism': return create_glassmorphism_style(image, modules, module_count, size) elif style == 'minimal_dots': return create_minimal_dots_style(image, modules, module_count, size) elif style == 'artistic_shadow': return create_artistic_shadow_style(image, modules, module_count, size) elif style == 'vibrant_overlay': return create_vibrant_overlay_style(image, modules, module_count, size) else: return create_modern_overlay_style(image, modules, module_count, size) except Exception as e: logger.error(f"Error creating background logo QR: {str(e)}") return create_basic_qr(qr_data, size) def create_modern_overlay_style(image, modules, module_count, size): """Modern style with better image visibility and sleek QR dots""" # Step 1: Create vibrant background with full logo background = Image.new('RGB', (size, size), (255, 255, 255)) # Resize logo to fill most of the background logo_size = int(size * 0.85) # Logo takes 85% of the area logo_resized = image.resize((logo_size, logo_size), Image.LANCZOS) # Enhance logo colors enhancer = ImageEnhance.Color(logo_resized) logo_enhanced = enhancer.enhance(1.3) # Boost colors enhancer = ImageEnhance.Contrast(logo_enhanced) logo_enhanced = enhancer.enhance(1.1) # Slight contrast boost # Center the logo on background logo_pos = ((size - logo_size) // 2, (size - logo_size) // 2) background.paste(logo_enhanced, logo_pos) # Step 2: Add minimal semi-transparent overlay only where QR dots will be overlay = Image.new('RGBA', (size, size), (0, 0, 0, 0)) # Transparent base overlay_draw = ImageDraw.Draw(overlay) # Calculate QR area qr_area_size = int(size * 0.9) qr_offset = (size - qr_area_size) // 2 module_size = qr_area_size // module_count # Pre-draw light overlay only where QR dots will be placed for i in range(module_count): for j in range(module_count): if modules[i][j]: x = qr_offset + j * module_size y = qr_offset + i * module_size # Add subtle white background only for QR dots margin = module_size // 6 overlay_draw.ellipse([x + margin, y + margin, x + module_size - margin, y + module_size - margin], fill=(255, 255, 255, 120)) background_rgba = background.convert('RGBA') background_with_overlay = Image.alpha_composite(background_rgba, overlay) # Step 3: Draw modern QR dots draw = ImageDraw.Draw(background_with_overlay) for i in range(module_count): for j in range(module_count): if modules[i][j]: x = qr_offset + j * module_size y = qr_offset + i * module_size if is_finder_pattern(i, j, module_count): # Modern rounded finder patterns draw.rounded_rectangle([x, y, x + module_size - 1, y + module_size - 1], radius=module_size // 3, fill=(0, 0, 0)) else: # Modern circular dots with slight margin dot_margin = module_size // 4 draw.ellipse([x + dot_margin, y + dot_margin, x + module_size - dot_margin, y + module_size - dot_margin], fill=(0, 0, 0)) return background_with_overlay.convert('RGB') def create_gradient_blend_style(image, modules, module_count, size): """Create QR with gradient blend effect""" # Create gradient background background = Image.new('RGB', (size, size), (255, 255, 255)) # Full logo background logo_resized = image.resize((size, size), Image.LANCZOS) # Create radial gradient mask gradient = Image.new('L', (size, size), 0) gradient_draw = ImageDraw.Draw(gradient) center_x, center_y = size // 2, size // 2 max_radius = size // 2 for radius in range(max_radius, 0, -5): intensity = int(255 * (radius / max_radius) * 0.8) gradient_draw.ellipse([center_x - radius, center_y - radius, center_x + radius, center_y + radius], fill=intensity) # Apply gradient mask to logo logo_rgba = logo_resized.convert('RGBA') gradient_rgba = gradient.convert('RGBA') # Blend logo with gradient blended = Image.alpha_composite( Image.new('RGBA', (size, size), (255, 255, 255, 255)), logo_rgba ) # Calculate QR area qr_area_size = int(size * 0.88) qr_offset = (size - qr_area_size) // 2 module_size = qr_area_size // module_count # Draw QR with gradient colors draw = ImageDraw.Draw(blended) for i in range(module_count): for j in range(module_count): if modules[i][j]: x = qr_offset + j * module_size y = qr_offset + i * module_size # Get color from original image at this position sample_x = min(x + module_size // 2, size - 1) sample_y = min(y + module_size // 2, size - 1) bg_color = logo_resized.getpixel((sample_x, sample_y)) # Create contrasting color r, g, b = bg_color brightness = (r + g + b) / 3 dot_color = (0, 0, 0) if brightness > 128 else (255, 255, 255) if is_finder_pattern(i, j, module_count): draw.rounded_rectangle([x, y, x + module_size, y + module_size], radius=module_size // 4, fill=dot_color) else: margin = module_size // 5 draw.ellipse([x + margin, y + margin, x + module_size - margin, y + module_size - margin], fill=dot_color) return blended.convert('RGB') def create_neon_glow_style(image, modules, module_count, size): """Create QR with neon glow effect""" # Dark background with logo background = Image.new('RGB', (size, size), (20, 20, 30)) # Resize and darken logo logo_size = int(size * 0.9) logo_resized = image.resize((logo_size, logo_size), Image.LANCZOS) # Apply dark filter to logo enhancer = ImageEnhance.Brightness(logo_resized) logo_darkened = enhancer.enhance(0.4) logo_pos = ((size - logo_size) // 2, (size - logo_size) // 2) background.paste(logo_darkened, logo_pos) # Create glow effect glow_layer = Image.new('RGBA', (size, size), (0, 0, 0, 0)) glow_draw = ImageDraw.Draw(glow_layer) # Calculate QR area qr_area_size = int(size * 0.85) qr_offset = (size - qr_area_size) // 2 module_size = qr_area_size // module_count # Draw glow effect first neon_color = (0, 255, 255) # Cyan neon for i in range(module_count): for j in range(module_count): if modules[i][j]: x = qr_offset + j * module_size y = qr_offset + i * module_size center_x, center_y = x + module_size // 2, y + module_size // 2 # Multiple glow layers for glow_size in [8, 6, 4, 2]: alpha = 30 if glow_size == 8 else 50 glow_draw.ellipse([center_x - glow_size, center_y - glow_size, center_x + glow_size, center_y + glow_size], fill=(*neon_color, alpha)) # Combine background with glow background_rgba = background.convert('RGBA') glowing_bg = Image.alpha_composite(background_rgba, glow_layer) # Draw main QR dots draw = ImageDraw.Draw(glowing_bg) for i in range(module_count): for j in range(module_count): if modules[i][j]: x = qr_offset + j * module_size y = qr_offset + i * module_size if is_finder_pattern(i, j, module_count): draw.rounded_rectangle([x, y, x + module_size, y + module_size], radius=module_size // 6, fill=neon_color) else: margin = module_size // 4 draw.ellipse([x + margin, y + margin, x + module_size - margin, y + module_size - margin], fill=neon_color) return glowing_bg.convert('RGB') def create_glassmorphism_style(image, modules, module_count, size): """Create QR with glassmorphism effect""" # Vibrant background background = Image.new('RGB', (size, size), (255, 255, 255)) # Full logo background with enhanced colors logo_resized = image.resize((size, size), Image.LANCZOS) enhancer = ImageEnhance.Color(logo_resized) logo_enhanced = enhancer.enhance(1.4) background.paste(logo_enhanced, (0, 0)) # Create glass effect overlay glass_overlay = Image.new('RGBA', (size, size), (0, 0, 0, 0)) glass_draw = ImageDraw.Draw(glass_overlay) # Calculate QR area qr_area_size = int(size * 0.88) qr_offset = (size - qr_area_size) // 2 module_size = qr_area_size // module_count # Draw glass panels for QR modules for i in range(module_count): for j in range(module_count): if modules[i][j]: x = qr_offset + j * module_size y = qr_offset + i * module_size # Glass panel background glass_draw.rounded_rectangle([x, y, x + module_size, y + module_size], radius=module_size // 6, fill=(255, 255, 255, 100)) # Glass border glass_draw.rounded_rectangle([x, y, x + module_size, y + module_size], radius=module_size // 6, outline=(255, 255, 255, 150), width=1) # Apply glass effect background_rgba = background.convert('RGBA') glass_bg = Image.alpha_composite(background_rgba, glass_overlay) # Draw QR dots on glass draw = ImageDraw.Draw(glass_bg) for i in range(module_count): for j in range(module_count): if modules[i][j]: x = qr_offset + j * module_size y = qr_offset + i * module_size if is_finder_pattern(i, j, module_count): draw.rounded_rectangle([x + 2, y + 2, x + module_size - 2, y + module_size - 2], radius=module_size // 4, fill=(0, 0, 0)) else: margin = module_size // 3 draw.ellipse([x + margin, y + margin, x + module_size - margin, y + module_size - margin], fill=(0, 0, 0)) return glass_bg.convert('RGB') def create_minimal_dots_style(image, modules, module_count, size): """Minimal style with maximum logo visibility""" # Full logo background background = image.resize((size, size), Image.LANCZOS) # Enhance image slightly enhancer = ImageEnhance.Color(background) background = enhancer.enhance(1.2) enhancer = ImageEnhance.Contrast(background) background = enhancer.enhance(1.05) # Calculate QR area qr_area_size = int(size * 0.92) qr_offset = (size - qr_area_size) // 2 module_size = qr_area_size // module_count # Create minimal overlay overlay = Image.new('RGBA', (size, size), (0, 0, 0, 0)) overlay_draw = ImageDraw.Draw(overlay) # Draw minimal QR dots with smart contrast for i in range(module_count): for j in range(module_count): if modules[i][j]: x = qr_offset + j * module_size y = qr_offset + i * module_size # Sample background color sample_x = min(x + module_size // 2, size - 1) sample_y = min(y + module_size // 2, size - 1) bg_color = background.getpixel((sample_x, sample_y)) r, g, b = bg_color brightness = (r + g + b) / 3 # Choose contrasting color with transparency if brightness > 140: dot_color = (0, 0, 0, 200) # Dark dots on light background outline_color = (255, 255, 255, 100) else: dot_color = (255, 255, 255, 220) # Light dots on dark background outline_color = (0, 0, 0, 100) if is_finder_pattern(i, j, module_count): # Minimal finder patterns overlay_draw.rounded_rectangle([x + 1, y + 1, x + module_size - 1, y + module_size - 1], radius=module_size // 5, fill=dot_color) else: # Small dots with outline for visibility margin = module_size // 3 overlay_draw.ellipse([x + margin - 1, y + margin - 1, x + module_size - margin + 1, y + module_size - margin + 1], fill=outline_color) overlay_draw.ellipse([x + margin, y + margin, x + module_size - margin, y + module_size - margin], fill=dot_color) # Combine with background background_rgba = background.convert('RGBA') result = Image.alpha_composite(background_rgba, overlay) return result.convert('RGB') def create_artistic_shadow_style(image, modules, module_count, size): """Artistic style with shadow effects""" # Create background background = Image.new('RGB', (size, size), (245, 245, 245)) # Logo with slight blur effect logo_size = int(size * 0.88) logo_resized = image.resize((logo_size, logo_size), Image.LANCZOS) logo_blurred = logo_resized.filter(ImageFilter.GaussianBlur(radius=0.5)) logo_pos = ((size - logo_size) // 2, (size - logo_size) // 2) background.paste(logo_blurred, logo_pos) # Calculate QR area qr_area_size = int(size * 0.86) qr_offset = (size - qr_area_size) // 2 module_size = qr_area_size // module_count # Create shadow layer shadow_layer = Image.new('RGBA', (size, size), (0, 0, 0, 0)) shadow_draw = ImageDraw.Draw(shadow_layer) # Draw shadows first for i in range(module_count): for j in range(module_count): if modules[i][j]: x = qr_offset + j * module_size y = qr_offset + i * module_size # Shadow offset shadow_offset = 2 margin = module_size // 4 # Draw shadow shadow_draw.ellipse([x + margin + shadow_offset, y + margin + shadow_offset, x + module_size - margin + shadow_offset, y + module_size - margin + shadow_offset], fill=(0, 0, 0, 60)) # Apply shadow background_rgba = background.convert('RGBA') shadowed_bg = Image.alpha_composite(background_rgba, shadow_layer) # Draw main QR dots draw = ImageDraw.Draw(shadowed_bg) for i in range(module_count): for j in range(module_count): if modules[i][j]: x = qr_offset + j * module_size y = qr_offset + i * module_size if is_finder_pattern(i, j, module_count): draw.rounded_rectangle([x, y, x + module_size, y + module_size], radius=module_size // 4, fill=(0, 0, 0)) else: margin = module_size // 4 # White outline for better visibility draw.ellipse([x + margin - 1, y + margin - 1, x + module_size - margin + 1, y + module_size - margin + 1], fill=(255, 255, 255)) draw.ellipse([x + margin, y + margin, x + module_size - margin, y + module_size - margin], fill=(0, 0, 0)) return shadowed_bg.convert('RGB') def create_vibrant_overlay_style(image, modules, module_count, size): """Vibrant style with colorful QR elements""" # Enhanced background background = Image.new('RGB', (size, size), (255, 255, 255)) # Full vibrant logo logo_resized = image.resize((size, size), Image.LANCZOS) enhancer = ImageEnhance.Color(logo_resized) logo_vibrant = enhancer.enhance(1.5) enhancer = ImageEnhance.Contrast(logo_vibrant) logo_vibrant = enhancer.enhance(1.1) background.paste(logo_vibrant, (0, 0)) # Calculate QR area qr_area_size = int(size * 0.90) qr_offset = (size - qr_area_size) // 2 module_size = qr_area_size // module_count # Extract dominant colors from logo small_logo = logo_resized.resize((50, 50)) colors = small_logo.getcolors(2500) if colors: dominant_color = max(colors, key=lambda x: x[0])[1] r, g, b = dominant_color # Create complementary color h, s, v = colorsys.rgb_to_hsv(r/255, g/255, b/255) comp_h = (h + 0.5) % 1.0 comp_r, comp_g, comp_b = colorsys.hsv_to_rgb(comp_h, s, v) accent_color = (int(comp_r * 255), int(comp_g * 255), int(comp_b * 255)) else: accent_color = (0, 100, 255) # Default blue # Draw QR with vibrant colors draw = ImageDraw.Draw(background) for i in range(module_count): for j in range(module_count): if modules[i][j]: x = qr_offset + j * module_size y = qr_offset + i * module_size # Alternate between black and accent color if is_finder_pattern(i, j, module_count): draw.rounded_rectangle([x, y, x + module_size, y + module_size], radius=module_size // 5, fill=(0, 0, 0)) else: # Use accent color for some dots use_accent = (i + j) % 4 == 0 dot_color = accent_color if use_accent else (0, 0, 0) margin = module_size // 4 # White outline for visibility draw.ellipse([x + margin - 1, y + margin - 1, x + module_size - margin + 1, y + module_size - margin + 1], fill=(255, 255, 255)) draw.ellipse([x + margin, y + margin, x + module_size - margin, y + module_size - margin], fill=dot_color) return background def is_finder_pattern(row, col, size): """Check if position is in finder pattern area""" return ((row < 9 and col < 9) or # Top-left (row < 9 and col >= size - 8) or # Top-right (row >= size - 8 and col < 9)) # Bottom-left def is_timing_pattern(row, col): """Check if position is in timing pattern""" return row == 6 or col == 6 def create_basic_qr(data, size): """Fallback basic QR code""" qr = qrcode.QRCode(version=1, error_correction=ERROR_CORRECT_H, box_size=10, border=4) qr.add_data(data) qr.make(fit=True) qr_img = qr.make_image(fill_color="black", back_color="white") return qr_img.resize((size, size), Image.LANCZOS) def calculate_compatibility_score(qr_image): """Calculate QR code compatibility score with enhanced metrics""" try: gray = cv2.cvtColor(np.array(qr_image), cv2.COLOR_RGB2GRAY) # Enhanced contrast analysis min_val, max_val = np.min(gray), np.max(gray) contrast_ratio = max_val / max(min_val, 1) # Edge detection for sharpness edges = cv2.Canny(gray, 50, 150) edge_ratio = np.sum(edges > 0) / edges.size # Pattern clarity (check for QR-like patterns) pattern_score = analyze_qr_patterns(gray) # Calculate enhanced scores contrast_score = min(contrast_ratio / 4, 1) * 35 edge_score = min(edge_ratio * 120, 1) * 35 pattern_score_final = pattern_score * 30 total_score = int(contrast_score + edge_score + pattern_score_final) return { 'overall': min(total_score, 100), 'contrast': int(contrast_score), 'sharpness': int(edge_score), 'pattern_clarity': int(pattern_score_final), 'recommendations': get_enhanced_recommendations(total_score) } except Exception as e: logger.error(f"Error calculating compatibility: {str(e)}") return {'overall': 85, 'contrast': 85, 'sharpness': 85, 'pattern_clarity': 85, 'recommendations': []} def analyze_qr_patterns(gray_image): """Analyze QR pattern clarity""" try: # Look for finder patterns (squares in corners) height, width = gray_image.shape corner_size = min(height, width) // 8 # Check corners for square patterns corners = [ gray_image[:corner_size, :corner_size], # Top-left gray_image[:corner_size, -corner_size:], # Top-right gray_image[-corner_size:, :corner_size] # Bottom-left ] pattern_scores = [] for corner in corners: # Simple pattern detection _, binary = cv2.threshold(corner, 128, 255, cv2.THRESH_BINARY) contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if len(contours) > 0: pattern_scores.append(1.0) else: pattern_scores.append(0.5) return sum(pattern_scores) / len(pattern_scores) except: return 0.8 def get_enhanced_recommendations(score): """Get enhanced improvement recommendations""" recommendations = [] if score < 80: recommendations.append("Try 'minimal_dots' or 'modern_overlay' for better scanning") if score < 70: recommendations.append("Consider using higher contrast logos") if score < 60: recommendations.append("Your logo might be too complex - try 'glassmorphism' style") if score > 85: recommendations.append("Excellent! This QR code should scan perfectly") return recommendations def image_to_base64(image): """Convert PIL image to base64 string""" buffer = BytesIO() image.save(buffer, format='PNG', optimize=True, quality=95) img_str = base64.b64encode(buffer.getvalue()).decode() return f"data:image/png;base64,{img_str}" @app.route('/', methods=['GET']) def home(): return jsonify({ 'message': 'Modern QR Generator is running', 'status': 'healthy', 'styles': ['modern_overlay', 'gradient_blend', 'neon_glow', 'glassmorphism', 'minimal_dots', 'artistic_shadow', 'vibrant_overlay'] }) @app.route('/health', methods=['GET']) def health_check(): return jsonify({'status': 'healthy', 'message': 'Modern QR Service is running'}) @app.route('/api/generate-artistic-qr', methods=['POST']) def generate_artistic_qr(): try: if 'image' not in request.files or 'url' not in request.form: return jsonify({'error': 'Missing image or URL'}), 400 file = request.files['image'] url = request.form['url'] style = request.form.get('style', 'modern_overlay') size = int(request.form.get('size', 500)) if file.filename == '' or not allowed_file(file.filename): return jsonify({'error': 'Invalid file'}), 400 if not url: return jsonify({'error': 'Invalid URL'}), 400 try: image = Image.open(file.stream).convert('RGB') except Exception: return jsonify({'error': 'Invalid image file'}), 400 # Generate modern QR code artistic_qr = create_background_logo_qr(image, url, style, size) compatibility = calculate_compatibility_score(artistic_qr) qr_base64 = image_to_base64(artistic_qr) # Standard QR for comparison standard_qr = create_basic_qr(url, size) standard_base64 = image_to_base64(standard_qr) return jsonify({ 'success': True, 'artistic_qr': qr_base64, 'standard_qr': standard_base64, 'compatibility': compatibility, 'style_used': style, 'size': size }) except Exception as e: logger.error(f"Error in generate_artistic_qr: {str(e)}") return jsonify({'error': f'Internal server error: {str(e)}'}), 500 @app.route('/api/styles', methods=['GET']) def get_available_styles(): styles = { 'modern_overlay': { 'name': 'Modern Overlay', 'description': 'Sleek modern style with enhanced logo visibility and minimal overlay', 'compatibility': 95, 'best_for': 'Corporate logos, clean designs' }, 'gradient_blend': { 'name': 'Gradient Blend', 'description': 'Sophisticated gradient effect with smart color adaptation', 'compatibility': 92, 'best_for': 'Colorful logos, artistic designs' }, 'neon_glow': { 'name': 'Neon Glow', 'description': 'Futuristic neon glow effect with dark background', 'compatibility': 90, 'best_for': 'Tech brands, gaming, nightlife' }, 'glassmorphism': { 'name': 'Glassmorphism', 'description': 'Trendy glass-like effect with frosted overlay', 'compatibility': 88, 'best_for': 'Modern apps, luxury brands' }, 'minimal_dots': { 'name': 'Minimal Dots', 'description': 'Ultra-minimal design with maximum logo visibility', 'compatibility': 97, 'best_for': 'Photography, art, detailed logos' }, 'artistic_shadow': { 'name': 'Artistic Shadow', 'description': 'Elegant shadow effects with artistic flair', 'compatibility': 91, 'best_for': 'Premium brands, portfolios' }, 'vibrant_overlay': { 'name': 'Vibrant Overlay', 'description': 'Bold colorful style with accent colors from your logo', 'compatibility': 89, 'best_for': 'Creative brands, events, social media' } } return jsonify({'success': True, 'styles': styles}) @app.route('/api/preview-styles', methods=['POST']) def preview_styles(): """Generate previews of all styles for a given image""" try: if 'image' not in request.files: return jsonify({'error': 'Missing image'}), 400 file = request.files['image'] url = request.form.get('url', 'https://example.com') size = int(request.form.get('size', 300)) # Smaller for previews if file.filename == '' or not allowed_file(file.filename): return jsonify({'error': 'Invalid file'}), 400 try: image = Image.open(file.stream).convert('RGB') except Exception: return jsonify({'error': 'Invalid image file'}), 400 # Generate previews for all styles previews = {} styles = ['modern_overlay', 'gradient_blend', 'neon_glow', 'glassmorphism', 'minimal_dots', 'artistic_shadow', 'vibrant_overlay'] for style in styles: try: qr_image = create_background_logo_qr(image, url, style, size) compatibility = calculate_compatibility_score(qr_image) previews[style] = { 'image': image_to_base64(qr_image), 'compatibility': compatibility['overall'] } except Exception as e: logger.error(f"Error generating preview for {style}: {str(e)}") previews[style] = { 'image': None, 'compatibility': 0, 'error': str(e) } return jsonify({ 'success': True, 'previews': previews }) except Exception as e: logger.error(f"Error in preview_styles: {str(e)}") return jsonify({'error': f'Internal server error: {str(e)}'}), 500 @app.route('/api/analyze-image', methods=['POST']) def analyze_image(): """Analyze uploaded image and suggest best styles""" try: if 'image' not in request.files: return jsonify({'error': 'Missing image'}), 400 file = request.files['image'] if file.filename == '' or not allowed_file(file.filename): return jsonify({'error': 'Invalid file'}), 400 try: image = Image.open(file.stream).convert('RGB') except Exception: return jsonify({'error': 'Invalid image file'}), 400 # Analyze image characteristics analysis = analyze_image_characteristics(image) # Suggest best styles based on analysis suggestions = suggest_styles_for_image(analysis) return jsonify({ 'success': True, 'analysis': analysis, 'suggestions': suggestions }) except Exception as e: logger.error(f"Error in analyze_image: {str(e)}") return jsonify({'error': f'Internal server error: {str(e)}'}), 500 def analyze_image_characteristics(image): """Analyze image characteristics to suggest best QR style""" try: # Resize for analysis small_image = image.resize((100, 100)) # Color analysis colors = small_image.getcolors(10000) if colors: total_pixels = sum(count for count, color in colors) # Calculate color diversity color_diversity = len(colors) / total_pixels # Find dominant color dominant_color = max(colors, key=lambda x: x[0])[1] # Calculate average brightness avg_brightness = sum(sum(color) for count, color in colors) / (len(colors) * 3) # Calculate color saturation hsv_colors = [colorsys.rgb_to_hsv(r/255, g/255, b/255) for count, (r, g, b) in colors] avg_saturation = sum(s for h, s, v in hsv_colors) / len(hsv_colors) else: color_diversity = 0.5 dominant_color = (128, 128, 128) avg_brightness = 128 avg_saturation = 0.5 # Complexity analysis using edge detection gray = cv2.cvtColor(np.array(small_image), cv2.COLOR_RGB2GRAY) edges = cv2.Canny(gray, 50, 150) complexity = np.sum(edges > 0) / edges.size return { 'color_diversity': color_diversity, 'dominant_color': dominant_color, 'avg_brightness': avg_brightness, 'avg_saturation': avg_saturation, 'complexity': complexity, 'is_dark': avg_brightness < 100, 'is_colorful': avg_saturation > 0.3, 'is_complex': complexity > 0.1 } except Exception as e: logger.error(f"Error analyzing image: {str(e)}") return { 'color_diversity': 0.5, 'dominant_color': (128, 128, 128), 'avg_brightness': 128, 'avg_saturation': 0.5, 'complexity': 0.1, 'is_dark': False, 'is_colorful': True, 'is_complex': False } def suggest_styles_for_image(analysis): """Suggest best QR styles based on image analysis""" suggestions = [] # For dark images if analysis['is_dark']: suggestions.append({ 'style': 'neon_glow', 'reason': 'Perfect for dark images with striking neon effect', 'compatibility': 90 }) suggestions.append({ 'style': 'minimal_dots', 'reason': 'Adapts well to dark backgrounds', 'compatibility': 95 }) # For colorful images if analysis['is_colorful']: suggestions.append({ 'style': 'vibrant_overlay', 'reason': 'Enhances colorful logos with accent colors', 'compatibility': 89 }) suggestions.append({ 'style': 'gradient_blend', 'reason': 'Beautiful gradient effects with colorful images', 'compatibility': 92 }) # For complex images if analysis['is_complex']: suggestions.append({ 'style': 'glassmorphism', 'reason': 'Glass effect reduces visual noise in complex images', 'compatibility': 88 }) suggestions.append({ 'style': 'artistic_shadow', 'reason': 'Shadow effects help separate QR from complex backgrounds', 'compatibility': 91 }) # For simple/clean images if not analysis['is_complex']: suggestions.append({ 'style': 'modern_overlay', 'reason': 'Clean modern style perfect for simple logos', 'compatibility': 95 }) suggestions.append({ 'style': 'minimal_dots', 'reason': 'Maximum logo visibility for clean designs', 'compatibility': 97 }) # Always include modern_overlay as a safe choice if not any(s['style'] == 'modern_overlay' for s in suggestions): suggestions.append({ 'style': 'modern_overlay', 'reason': 'Reliable choice for any image type', 'compatibility': 95 }) # Sort by compatibility score suggestions.sort(key=lambda x: x['compatibility'], reverse=True) return suggestions[:4] # Return top 4 suggestions if __name__ == '__main__': port = int(os.environ.get('PORT', 7860)) app.run(host='0.0.0.0', port=port, debug=False)