Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import os
- import random
- import json
- from PIL import Image, ImageDraw, ImageFont
- from typing import Dict, List, Tuple
- import argparse
- class QuizImageGenerator:
- """
- Enhanced Quiz Image Generator for E-Learning Applications
- This class generates interactive quiz choice images by combining
- placeholder images (like bugs, characters, etc.) with text options.
- """
- def __init__(self, config_file: str = None):
- """Initialize the generator with default or custom configuration."""
- self.config = self.load_config(config_file)
- self.setup_directories()
- def load_config(self, config_file: str = None) -> Dict:
- """Load configuration from file or use defaults."""
- default_config = {
- "options_dir": "./Options",
- "output_dir": "./GeneratedChoices",
- "placeholder_images": ["bug 1.png", "bug 2.png", "bug 3.png"],
- "text_overlay": {
- "position": "bottom_center", # top_center, bottom_center, center
- "padding": 10,
- "background_color": (255, 255, 255, 200), # Semi-transparent white
- "text_color": (0, 0, 0, 255), # Black
- "font_size": 24
- },
- "image_settings": {
- "text_width_ratio": 0.8, # Text width as ratio of placeholder width
- "text_height_ratio": 0.25, # Text height as ratio of placeholder height
- "output_format": "PNG",
- "quality": 95
- }
- }
- if config_file and os.path.exists(config_file):
- try:
- with open(config_file, 'r') as f:
- custom_config = json.load(f)
- default_config.update(custom_config)
- except Exception as e:
- print(f"Warning: Could not load config file {config_file}: {e}")
- print("Using default configuration.")
- return default_config
- def setup_directories(self):
- """Create necessary directories."""
- os.makedirs(self.config["output_dir"], exist_ok=True)
- if not os.path.exists(self.config["options_dir"]):
- raise FileNotFoundError(f"Options directory '{self.config['options_dir']}' not found!")
- def get_placeholder_images(self) -> List[str]:
- """Get full paths to placeholder images."""
- placeholder_paths = []
- for img in self.config["placeholder_images"]:
- full_path = os.path.join(self.config["options_dir"], img)
- if os.path.exists(full_path):
- placeholder_paths.append(full_path)
- else:
- print(f"Warning: Placeholder image '{img}' not found!")
- if not placeholder_paths:
- raise FileNotFoundError("No placeholder images found!")
- return placeholder_paths
- def group_question_files(self) -> Dict[str, List[str]]:
- """Group option files by question number."""
- option_files = [f for f in os.listdir(self.config["options_dir"])
- if f.startswith('Q') and f.endswith('.png')]
- if not option_files:
- raise FileNotFoundError("No question option files found!")
- questions = {}
- for f in option_files:
- try:
- q_num = f.split('-')[0] # e.g., Q01
- questions.setdefault(q_num, []).append(f)
- except IndexError:
- print(f"Warning: Skipping file with unexpected format: {f}")
- return questions
- def calculate_text_position(self, placeholder_size: Tuple[int, int],
- text_size: Tuple[int, int]) -> Tuple[int, int]:
- """Calculate text position based on configuration."""
- p_width, p_height = placeholder_size
- t_width, t_height = text_size
- position = self.config["text_overlay"]["position"]
- padding = self.config["text_overlay"]["padding"]
- if position == "top_center":
- return ((p_width - t_width) // 2, padding)
- elif position == "bottom_center":
- return ((p_width - t_width) // 2, p_height - t_height - padding)
- elif position == "center":
- return ((p_width - t_width) // 2, (p_height - t_height) // 2)
- else:
- # Default to bottom center
- return ((p_width - t_width) // 2, p_height - t_height - padding)
- def create_text_background(self, text_img: Image.Image) -> Image.Image:
- """Add a semi-transparent background to text for better readability."""
- bg_color = self.config["text_overlay"]["background_color"]
- padding = self.config["text_overlay"]["padding"]
- # Create background with padding
- bg_width = text_img.width + (padding * 2)
- bg_height = text_img.height + (padding * 2)
- background = Image.new('RGBA', (bg_width, bg_height), bg_color)
- # Paste text on background
- background.alpha_composite(text_img, (padding, padding))
- return background
- def generate_choice_image(self, placeholder_path: str, option_path: str) -> Image.Image:
- """Generate a single choice image by combining placeholder and option text."""
- try:
- # Load images
- placeholder_img = Image.open(placeholder_path).convert('RGBA')
- option_img = Image.open(option_path).convert('RGBA')
- # Calculate new size for option text
- text_width = int(placeholder_img.width * self.config["image_settings"]["text_width_ratio"])
- text_height = int(placeholder_img.height * self.config["image_settings"]["text_height_ratio"])
- # Resize option image
- option_img = option_img.resize((text_width, text_height), Image.Resampling.LANCZOS)
- # Add background to text for better readability
- option_with_bg = self.create_text_background(option_img)
- # Calculate position
- position = self.calculate_text_position(
- (placeholder_img.width, placeholder_img.height),
- (option_with_bg.width, option_with_bg.height)
- )
- # Combine images
- combined = placeholder_img.copy()
- combined.alpha_composite(option_with_bg, position)
- return combined
- except Exception as e:
- print(f"Error generating image for {option_path}: {e}")
- return None
- def generate_all_images(self, randomize_placeholders: bool = True) -> Dict[str, int]:
- """Generate all quiz choice images."""
- placeholder_images = self.get_placeholder_images()
- questions = self.group_question_files()
- stats = {"total_questions": 0, "total_images": 0, "errors": 0}
- print(f"Found {len(questions)} questions to process...")
- for q_num, opts in questions.items():
- print(f"Processing {q_num} with {len(opts)} options...")
- # Sort options to ensure consistent ordering
- opts.sort()
- # Create question folder
- q_folder = os.path.join(self.config["output_dir"], q_num)
- os.makedirs(q_folder, exist_ok=True)
- # Randomize or cycle through placeholders
- if randomize_placeholders:
- # Shuffle placeholders for this question
- question_placeholders = random.sample(placeholder_images, len(placeholder_images))
- else:
- question_placeholders = placeholder_images
- # Generate images for each option
- for idx, opt_file in enumerate(opts):
- try:
- # Cycle through placeholders if more options than placeholders
- placeholder_path = question_placeholders[idx % len(question_placeholders)]
- option_path = os.path.join(self.config["options_dir"], opt_file)
- # Generate combined image
- combined_img = self.generate_choice_image(placeholder_path, option_path)
- if combined_img:
- # Create meaningful filename
- opt_letter = chr(ord('A') + idx)
- output_filename = f"{q_num}_Option_{opt_letter}.png"
- output_path = os.path.join(q_folder, output_filename)
- # Save image
- combined_img.save(output_path,
- format=self.config["image_settings"]["output_format"],
- quality=self.config["image_settings"]["quality"])
- stats["total_images"] += 1
- print(f" ✓ Generated: {output_filename}")
- else:
- stats["errors"] += 1
- except Exception as e:
- print(f" ✗ Error processing {opt_file}: {e}")
- stats["errors"] += 1
- stats["total_questions"] += 1
- return stats
- def create_sample_config(self, filename: str = "quiz_config.json"):
- """Create a sample configuration file."""
- with open(filename, 'w') as f:
- json.dump(self.config, f, indent=4)
- print(f"Sample configuration created: {filename}")
- def main():
- """Main function with command-line interface."""
- parser = argparse.ArgumentParser(description="Generate quiz choice images for e-learning")
- parser.add_argument("--config", type=str, help="Path to configuration file")
- parser.add_argument("--create-config", action="store_true",
- help="Create a sample configuration file")
- parser.add_argument("--no-randomize", action="store_true",
- help="Don't randomize placeholder assignments")
- args = parser.parse_args()
- try:
- generator = QuizImageGenerator(args.config)
- if args.create_config:
- generator.create_sample_config()
- return
- print("🎮 Quiz Image Generator for E-Learning")
- print("=" * 40)
- # Generate images
- stats = generator.generate_all_images(randomize_placeholders=not args.no_randomize)
- # Print summary
- print("\n📊 Generation Complete!")
- print(f"Questions processed: {stats['total_questions']}")
- print(f"Images generated: {stats['total_images']}")
- print(f"Errors: {stats['errors']}")
- print(f"Output directory: {generator.config['output_dir']}")
- except Exception as e:
- print(f"❌ Error: {e}")
- return 1
- return 0
- if __name__ == "__main__":
- exit(main())
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement