Advertisement
nrzmalik

Image Choice Maker Python Script

Jun 20th, 2025 (edited)
472
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.10 KB | Source Code | 0 0
  1. import os
  2. import random
  3. import json
  4. from PIL import Image, ImageDraw, ImageFont
  5. from typing import Dict, List, Tuple
  6. import argparse
  7.  
  8. class QuizImageGenerator:
  9.     """
  10.    Enhanced Quiz Image Generator for E-Learning Applications
  11.    
  12.    This class generates interactive quiz choice images by combining
  13.    placeholder images (like bugs, characters, etc.) with text options.
  14.    """
  15.    
  16.     def __init__(self, config_file: str = None):
  17.         """Initialize the generator with default or custom configuration."""
  18.         self.config = self.load_config(config_file)
  19.         self.setup_directories()
  20.    
  21.     def load_config(self, config_file: str = None) -> Dict:
  22.         """Load configuration from file or use defaults."""
  23.         default_config = {
  24.             "options_dir": "./Options",
  25.             "output_dir": "./GeneratedChoices",
  26.             "placeholder_images": ["bug 1.png", "bug 2.png", "bug 3.png"],
  27.             "text_overlay": {
  28.                 "position": "bottom_center",  # top_center, bottom_center, center
  29.                 "padding": 10,
  30.                 "background_color": (255, 255, 255, 200),  # Semi-transparent white
  31.                 "text_color": (0, 0, 0, 255),  # Black
  32.                 "font_size": 24
  33.             },
  34.             "image_settings": {
  35.                 "text_width_ratio": 0.8,  # Text width as ratio of placeholder width
  36.                 "text_height_ratio": 0.25,  # Text height as ratio of placeholder height
  37.                 "output_format": "PNG",
  38.                 "quality": 95
  39.             }
  40.         }
  41.        
  42.         if config_file and os.path.exists(config_file):
  43.             try:
  44.                 with open(config_file, 'r') as f:
  45.                     custom_config = json.load(f)
  46.                     default_config.update(custom_config)
  47.             except Exception as e:
  48.                 print(f"Warning: Could not load config file {config_file}: {e}")
  49.                 print("Using default configuration.")
  50.        
  51.         return default_config
  52.    
  53.     def setup_directories(self):
  54.         """Create necessary directories."""
  55.         os.makedirs(self.config["output_dir"], exist_ok=True)
  56.        
  57.         if not os.path.exists(self.config["options_dir"]):
  58.             raise FileNotFoundError(f"Options directory '{self.config['options_dir']}' not found!")
  59.    
  60.     def get_placeholder_images(self) -> List[str]:
  61.         """Get full paths to placeholder images."""
  62.         placeholder_paths = []
  63.         for img in self.config["placeholder_images"]:
  64.             full_path = os.path.join(self.config["options_dir"], img)
  65.             if os.path.exists(full_path):
  66.                 placeholder_paths.append(full_path)
  67.             else:
  68.                 print(f"Warning: Placeholder image '{img}' not found!")
  69.        
  70.         if not placeholder_paths:
  71.             raise FileNotFoundError("No placeholder images found!")
  72.        
  73.         return placeholder_paths
  74.    
  75.     def group_question_files(self) -> Dict[str, List[str]]:
  76.         """Group option files by question number."""
  77.         option_files = [f for f in os.listdir(self.config["options_dir"])
  78.                        if f.startswith('Q') and f.endswith('.png')]
  79.        
  80.         if not option_files:
  81.             raise FileNotFoundError("No question option files found!")
  82.        
  83.         questions = {}
  84.         for f in option_files:
  85.             try:
  86.                 q_num = f.split('-')[0]  # e.g., Q01
  87.                 questions.setdefault(q_num, []).append(f)
  88.             except IndexError:
  89.                 print(f"Warning: Skipping file with unexpected format: {f}")
  90.        
  91.         return questions
  92.    
  93.     def calculate_text_position(self, placeholder_size: Tuple[int, int],
  94.                                text_size: Tuple[int, int]) -> Tuple[int, int]:
  95.         """Calculate text position based on configuration."""
  96.         p_width, p_height = placeholder_size
  97.         t_width, t_height = text_size
  98.        
  99.         position = self.config["text_overlay"]["position"]
  100.         padding = self.config["text_overlay"]["padding"]
  101.        
  102.         if position == "top_center":
  103.             return ((p_width - t_width) // 2, padding)
  104.         elif position == "bottom_center":
  105.             return ((p_width - t_width) // 2, p_height - t_height - padding)
  106.         elif position == "center":
  107.             return ((p_width - t_width) // 2, (p_height - t_height) // 2)
  108.         else:
  109.             # Default to bottom center
  110.             return ((p_width - t_width) // 2, p_height - t_height - padding)
  111.    
  112.     def create_text_background(self, text_img: Image.Image) -> Image.Image:
  113.         """Add a semi-transparent background to text for better readability."""
  114.         bg_color = self.config["text_overlay"]["background_color"]
  115.         padding = self.config["text_overlay"]["padding"]
  116.        
  117.         # Create background with padding
  118.         bg_width = text_img.width + (padding * 2)
  119.         bg_height = text_img.height + (padding * 2)
  120.        
  121.         background = Image.new('RGBA', (bg_width, bg_height), bg_color)
  122.        
  123.         # Paste text on background
  124.         background.alpha_composite(text_img, (padding, padding))
  125.        
  126.         return background
  127.    
  128.     def generate_choice_image(self, placeholder_path: str, option_path: str) -> Image.Image:
  129.         """Generate a single choice image by combining placeholder and option text."""
  130.         try:
  131.             # Load images
  132.             placeholder_img = Image.open(placeholder_path).convert('RGBA')
  133.             option_img = Image.open(option_path).convert('RGBA')
  134.            
  135.             # Calculate new size for option text
  136.             text_width = int(placeholder_img.width * self.config["image_settings"]["text_width_ratio"])
  137.             text_height = int(placeholder_img.height * self.config["image_settings"]["text_height_ratio"])
  138.            
  139.             # Resize option image
  140.             option_img = option_img.resize((text_width, text_height), Image.Resampling.LANCZOS)
  141.            
  142.             # Add background to text for better readability
  143.             option_with_bg = self.create_text_background(option_img)
  144.            
  145.             # Calculate position
  146.             position = self.calculate_text_position(
  147.                 (placeholder_img.width, placeholder_img.height),
  148.                 (option_with_bg.width, option_with_bg.height)
  149.             )
  150.            
  151.             # Combine images
  152.             combined = placeholder_img.copy()
  153.             combined.alpha_composite(option_with_bg, position)
  154.            
  155.             return combined
  156.            
  157.         except Exception as e:
  158.             print(f"Error generating image for {option_path}: {e}")
  159.             return None
  160.    
  161.     def generate_all_images(self, randomize_placeholders: bool = True) -> Dict[str, int]:
  162.         """Generate all quiz choice images."""
  163.         placeholder_images = self.get_placeholder_images()
  164.         questions = self.group_question_files()
  165.        
  166.         stats = {"total_questions": 0, "total_images": 0, "errors": 0}
  167.        
  168.         print(f"Found {len(questions)} questions to process...")
  169.        
  170.         for q_num, opts in questions.items():
  171.             print(f"Processing {q_num} with {len(opts)} options...")
  172.            
  173.             # Sort options to ensure consistent ordering
  174.             opts.sort()
  175.            
  176.             # Create question folder
  177.             q_folder = os.path.join(self.config["output_dir"], q_num)
  178.             os.makedirs(q_folder, exist_ok=True)
  179.            
  180.             # Randomize or cycle through placeholders
  181.             if randomize_placeholders:
  182.                 # Shuffle placeholders for this question
  183.                 question_placeholders = random.sample(placeholder_images, len(placeholder_images))
  184.             else:
  185.                 question_placeholders = placeholder_images
  186.            
  187.             # Generate images for each option
  188.             for idx, opt_file in enumerate(opts):
  189.                 try:
  190.                     # Cycle through placeholders if more options than placeholders
  191.                     placeholder_path = question_placeholders[idx % len(question_placeholders)]
  192.                     option_path = os.path.join(self.config["options_dir"], opt_file)
  193.                    
  194.                     # Generate combined image
  195.                     combined_img = self.generate_choice_image(placeholder_path, option_path)
  196.                    
  197.                     if combined_img:
  198.                         # Create meaningful filename
  199.                         opt_letter = chr(ord('A') + idx)
  200.                         output_filename = f"{q_num}_Option_{opt_letter}.png"
  201.                         output_path = os.path.join(q_folder, output_filename)
  202.                        
  203.                         # Save image
  204.                         combined_img.save(output_path,
  205.                                         format=self.config["image_settings"]["output_format"],
  206.                                         quality=self.config["image_settings"]["quality"])
  207.                        
  208.                         stats["total_images"] += 1
  209.                         print(f"  ✓ Generated: {output_filename}")
  210.                     else:
  211.                         stats["errors"] += 1
  212.                        
  213.                 except Exception as e:
  214.                     print(f"  ✗ Error processing {opt_file}: {e}")
  215.                     stats["errors"] += 1
  216.            
  217.             stats["total_questions"] += 1
  218.        
  219.         return stats
  220.    
  221.     def create_sample_config(self, filename: str = "quiz_config.json"):
  222.         """Create a sample configuration file."""
  223.         with open(filename, 'w') as f:
  224.             json.dump(self.config, f, indent=4)
  225.         print(f"Sample configuration created: {filename}")
  226.  
  227.  
  228. def main():
  229.     """Main function with command-line interface."""
  230.     parser = argparse.ArgumentParser(description="Generate quiz choice images for e-learning")
  231.     parser.add_argument("--config", type=str, help="Path to configuration file")
  232.     parser.add_argument("--create-config", action="store_true",
  233.                        help="Create a sample configuration file")
  234.     parser.add_argument("--no-randomize", action="store_true",
  235.                        help="Don't randomize placeholder assignments")
  236.    
  237.     args = parser.parse_args()
  238.    
  239.     try:
  240.         generator = QuizImageGenerator(args.config)
  241.        
  242.         if args.create_config:
  243.             generator.create_sample_config()
  244.             return
  245.        
  246.         print("🎮 Quiz Image Generator for E-Learning")
  247.         print("=" * 40)
  248.        
  249.         # Generate images
  250.         stats = generator.generate_all_images(randomize_placeholders=not args.no_randomize)
  251.        
  252.         # Print summary
  253.         print("\n📊 Generation Complete!")
  254.         print(f"Questions processed: {stats['total_questions']}")
  255.         print(f"Images generated: {stats['total_images']}")
  256.         print(f"Errors: {stats['errors']}")
  257.         print(f"Output directory: {generator.config['output_dir']}")
  258.        
  259.     except Exception as e:
  260.         print(f"❌ Error: {e}")
  261.         return 1
  262.    
  263.     return 0
  264.  
  265.  
  266. if __name__ == "__main__":
  267.     exit(main())
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement