11"""Ultra-fast random image creation utilities for maximum throughput."""
22
33import asyncio
4- import io
54import random
65import time
76from collections .abc import AsyncIterator
87
9- from PIL import Image , ImageDraw
8+ import cv2 as cv
9+ import numpy as np
1010
1111from resolver_athena_client .client .models import ImageData
1212
1313# Global cache for reusable objects and constants
14- _image_cache : dict [
15- tuple [int , int ], tuple [Image .Image , ImageDraw .ImageDraw ]
16- ] = {}
14+ _image_cache : dict [tuple [int , int ], np .ndarray ] = {}
1715_rng = random .Random () # noqa: S311 - Not used for cryptographic purposes
1816
1917
20- def _get_cached_image (
21- width : int , height : int
22- ) -> tuple [Image .Image , ImageDraw .ImageDraw ]:
23- """Get cached image and draw objects, creating if needed."""
18+ def _get_cached_image (width : int , height : int ) -> np .ndarray :
19+ """Get cached image array, creating if needed."""
2420 key = (width , height )
2521 if key not in _image_cache :
26- img = Image .new ("RGB" , (width , height ), (0 , 0 , 0 ))
27- draw = ImageDraw .Draw (img )
28- _image_cache [key ] = (img , draw )
22+ img = np .zeros ((height , width , 3 ), dtype = np .uint8 )
23+ _image_cache [key ] = img
2924 return _image_cache [key ]
3025
3126
@@ -45,8 +40,9 @@ def create_random_image(
4540 PNG image bytes
4641
4742 """
48- # Get cached image and draw objects
49- image , draw = _get_cached_image (width , height )
43+ # Get cached image array
44+ image = _get_cached_image (width , height )
45+ img = image .copy ()
5046
5147 # Random background color
5248 bg_r , bg_g , bg_b = (
@@ -56,21 +52,24 @@ def create_random_image(
5652 )
5753
5854 # Fill with background color
59- draw . rectangle ([ 0 , 0 , width , height ], fill = ( bg_r , bg_g , bg_b ))
55+ img [:, :] = ( bg_b , bg_g , bg_r ) # OpenCV uses BGR
6056
6157 # Add single accent rectangle for visual variation
62- accent_color = (255 - bg_r , 255 - bg_g , 255 - bg_b )
58+ accent_color = (255 - bg_b , 255 - bg_g , 255 - bg_r ) # BGR
6359 x1 , y1 = width // 4 , height // 4
6460 x2 , y2 = (width * 3 ) // 4 , (height * 3 ) // 4
65- draw .rectangle ([ x1 , y1 , x2 , y2 ], fill = accent_color )
61+ img = cv .rectangle (img , ( x1 , y1 ), ( x2 , y2 ), accent_color , thickness = - 1 )
6662
6763 if img_format .upper () == "RAW_UINT8" :
68- return image .tobytes ()
64+ return img .tobytes ()
6965
70- # Convert to PNG bytes
71- buffer = io .BytesIO ()
72- image .save (buffer , format = img_format )
73- return buffer .getvalue ()
66+ # Convert to PNG/JPEG bytes
67+ ext = f".{ img_format .lower ()} "
68+ success , buf = cv .imencode (ext , img )
69+ if not success :
70+ err = f"Failed to encode image as { img_format } "
71+ raise RuntimeError (err )
72+ return buf .tobytes ()
7473
7574
7675def create_batch_images (
@@ -90,29 +89,32 @@ def create_batch_images(
9089
9190 """
9291 images : list [bytes ] = []
93- image , draw = _get_cached_image (width , height )
92+ image = _get_cached_image (width , height )
9493
9594 # Pre-calculate accent rectangle coordinates
9695 x1 , y1 = width // 4 , height // 4
9796 x2 , y2 = (width * 3 ) // 4 , (height * 3 ) // 4
9897
9998 for _ in range (count ):
99+ img = image .copy ()
100100 # Random background
101101 bg_r , bg_g , bg_b = (
102102 _rng .randint (0 , 255 ),
103103 _rng .randint (0 , 255 ),
104104 _rng .randint (0 , 255 ),
105105 )
106- draw . rectangle ([ 0 , 0 , width , height ], fill = ( bg_r , bg_g , bg_b ))
106+ img [:, :] = ( bg_b , bg_g , bg_r ) # OpenCV uses BGR
107107
108108 # Complement accent color
109- accent_color = (255 - bg_r , 255 - bg_g , 255 - bg_b )
110- draw .rectangle ([ x1 , y1 , x2 , y2 ], fill = accent_color )
109+ accent_color = (255 - bg_b , 255 - bg_g , 255 - bg_r ) # BGR
110+ img = cv .rectangle (img , ( x1 , y1 ), ( x2 , y2 ), accent_color , thickness = - 1 )
111111
112112 # Convert to PNG bytes
113- buffer = io .BytesIO ()
114- image .save (buffer , format = "PNG" )
115- images .append (buffer .getvalue ())
113+ success , buf = cv .imencode (".png" , img )
114+ if not success :
115+ msg = "Failed to encode image as PNG"
116+ raise RuntimeError (msg )
117+ images .append (buf .tobytes ())
116118
117119 return images
118120
0 commit comments