from PIL import Image, ImageDraw import io import logging from datetime import datetime logger = logging.getLogger(__name__) def halftone_effect(img, dot_size=10): """ Apply a halftone effect to the input image. """ grayscale = img.convert('L') width, height = grayscale.size halftone_image = Image.new('L', (width, height), color=255) draw = ImageDraw.Draw(halftone_image) for x in range(0, width, dot_size): for y in range(0, height, dot_size): block_width = min(dot_size, width - x) block_height = min(dot_size, height - y) region = grayscale.crop((x, y, x + block_width, y + block_height)) avg_brightness = sum(region.getdata()) / (block_width * block_height) max_radius = dot_size // 2 radius = int((255 - avg_brightness) / 255 * max_radius) radius = max(0, min(radius, max_radius)) center_x = x + dot_size // 2 center_y = y + dot_size // 2 draw.ellipse( ( center_x - radius, center_y - radius, center_x + radius, center_y + radius ), fill=0 ) return halftone_image def apply_halftone(image_data, dot_size=10): start_time = datetime.now() logger.info(f"Applying halftone effect - {start_time}") try: input_image = Image.open(io.BytesIO(image_data)) logger.info(f"Image opened. Mode: {input_image.mode}, Size: {input_image.size}") dot_size = max(5, min(20, dot_size)) logger.info(f"Applying halftone effect with dot size: {dot_size}") output_image = halftone_effect(input_image, dot_size) img_io = io.BytesIO() output_image.save(img_io, 'PNG') img_io.seek(0) duration = (datetime.now() - start_time).total_seconds() logger.info(f"Successfully processed. Duration: {duration} seconds") return img_io except Exception as e: logger.error(f"Halftone processing error: {str(e)}", exc_info=True) raise