darkfire514's picture
Upload 160 files
399b80c verified
import platform
import os
import logging
from typing import Optional, Tuple
from PIL import Image
import pyautogui
logger = logging.getLogger(__name__)
platform_name = platform.system()
class ScreenshotHelper:
def __init__(self):
self.platform = platform_name
self.adapter = None
try:
if platform_name == "Darwin":
from ..platform_adapters.macos_adapter import MacOSAdapter
self.adapter = MacOSAdapter()
elif platform_name == "Linux":
from ..platform_adapters.linux_adapter import LinuxAdapter
self.adapter = LinuxAdapter()
elif platform_name == "Windows":
from ..platform_adapters.windows_adapter import WindowsAdapter
self.adapter = WindowsAdapter()
except ImportError as e:
logger.warning(f"Failed to import platform adapter: {e}")
def capture(self, output_path: str, with_cursor: bool = True) -> bool:
try:
# Ensure directory exists
os.makedirs(os.path.dirname(output_path), exist_ok=True)
if with_cursor and self.adapter:
# Use platform-specific method to capture screenshot (with cursor)
return self.adapter.capture_screenshot_with_cursor(output_path)
else:
# Use pyautogui to capture screenshot (without cursor)
screenshot = pyautogui.screenshot()
screenshot.save(output_path)
logger.info(f"Screenshot successfully (without cursor): {output_path}")
return True
except Exception as e:
logger.error(f"Screenshot failed: {e}")
return False
def capture_region(
self,
output_path: str,
x: int,
y: int,
width: int,
height: int
) -> bool:
"""
Capture specified screen region
Args:
output_path: Output path
x: Starting x coordinate
y: Starting y coordinate
width: Width
height: Height
Returns:
Whether successful
"""
try:
os.makedirs(os.path.dirname(output_path), exist_ok=True)
screenshot = pyautogui.screenshot(region=(x, y, width, height))
screenshot.save(output_path)
logger.info(f"Region screenshot successfully: {output_path}")
return True
except Exception as e:
logger.error(f"Region screenshot failed: {e}")
return False
def get_screen_size(self) -> Tuple[int, int]:
"""
Get screen size
Returns:
(width, height)
"""
try:
size = pyautogui.size()
return (size.width, size.height)
except Exception as e:
logger.error(f"Failed to get screen size: {e}")
return (1920, 1080) # Default value
def get_cursor_position(self) -> Tuple[int, int]:
"""
Get cursor position
Returns:
(x, y)
"""
try:
pos = pyautogui.position()
return (pos.x, pos.y)
except Exception as e:
logger.error(f"Failed to get cursor position: {e}")
return (0, 0)
def capture_to_base64(self, with_cursor: bool = True) -> Optional[str]:
"""
Capture screenshot and convert to base64
Args:
with_cursor: Whether to include cursor
Returns:
Base64 encoded image string
"""
import tempfile
import base64
try:
# Create temporary file
with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp:
tmp_path = tmp.name
# Capture screenshot
if self.capture(tmp_path, with_cursor):
# Read and encode
with open(tmp_path, 'rb') as f:
img_data = f.read()
img_base64 = base64.b64encode(img_data).decode('utf-8')
# Delete temporary file
os.remove(tmp_path)
return img_base64
else:
if os.path.exists(tmp_path):
os.remove(tmp_path)
return None
except Exception as e:
logger.error(f"Failed to convert screenshot to base64: {e}")
return None
def compare_screenshots(self, path1: str, path2: str) -> float:
"""
Compare similarity between two screenshots
Args:
path1: First image path
path2: Second image path
Returns:
Similarity (0-1), 1 means identical
"""
try:
from PIL import ImageChops
import math
import operator
from functools import reduce
img1 = Image.open(path1)
img2 = Image.open(path2)
# Ensure same size
if img1.size != img2.size:
# Resize to same size
img2 = img2.resize(img1.size)
# Calculate difference
diff = ImageChops.difference(img1, img2)
# Calculate statistics
stat = diff.histogram()
sum_of_squares = reduce(
operator.add,
map(lambda h, i: h * (i ** 2), stat, range(len(stat)))
)
# Calculate RMS
rms = math.sqrt(sum_of_squares / float(img1.size[0] * img1.size[1]))
# Normalize to 0-1, RMS max value is approximately 441 (for RGB)
similarity = 1 - (rms / 441.0)
return max(0, min(1, similarity))
except Exception as e:
logger.error(f"Failed to compare screenshots: {e}")
return 0.0
def annotate_screenshot(
self,
input_path: str,
output_path: str,
annotations: list
) -> bool:
"""
Add annotations to screenshot
Args:
input_path: Input image path
output_path: Output image path
annotations: List of annotations, each annotation is a dict:
{'type': 'rectangle'/'text', 'x': int, 'y': int,
'width': int, 'height': int, 'text': str, 'color': tuple}
Returns:
Whether successful
"""
try:
from PIL import ImageDraw, ImageFont
img = Image.open(input_path)
draw = ImageDraw.Draw(img)
for annotation in annotations:
ann_type = annotation.get('type', 'rectangle')
color = annotation.get('color', (255, 0, 0))
if ann_type == 'rectangle':
x = annotation.get('x', 0)
y = annotation.get('y', 0)
width = annotation.get('width', 100)
height = annotation.get('height', 100)
draw.rectangle(
[(x, y), (x + width, y + height)],
outline=color,
width=2
)
elif ann_type == 'text':
x = annotation.get('x', 0)
y = annotation.get('y', 0)
text = annotation.get('text', '')
try:
font = ImageFont.truetype("Arial.ttf", 20)
except:
font = ImageFont.load_default()
draw.text((x, y), text, fill=color, font=font)
img.save(output_path)
logger.info(f"Annotated screenshot successfully: {output_path}")
return True
except Exception as e:
logger.error(f"Failed to annotate screenshot: {e}")
return False