feat: add safe image opening function with retry mechanism

- Introduced a new function `safe_open_image_with_retry` to handle image file opening with retries for truncated or corrupted files.
- Enhanced error handling and logging for image processing in `check_palette_and_structure_sim`.
- Updated the logic to safely open both source and target images, ensuring robust evaluation without altering existing functionality.

These changes improve the reliability of image handling in the GIMP evaluator while maintaining the original code logic.
This commit is contained in:
yuanmengqi
2025-07-18 18:36:09 +00:00
parent 4fa59ebba2
commit d52f3b1fca

View File

@@ -567,27 +567,119 @@ def check_image_size(src_path, rule):
return 0.
def safe_open_image_with_retry(file_path, max_retries=3, retry_delay=0.5):
"""
Safely open an image file with retry mechanism for handling truncated files
"""
import os
import time
import logging
logger = logging.getLogger(__name__)
if not file_path or not os.path.exists(file_path):
logger.error(f"File does not exist: {file_path}")
return None
for attempt in range(max_retries):
try:
# Check file size first
file_size = os.path.getsize(file_path)
if file_size == 0:
logger.warning(f"File is empty: {file_path}")
if attempt < max_retries - 1:
time.sleep(retry_delay)
continue
return None
logger.info(f"Opening image: {file_path} (size: {file_size} bytes, attempt: {attempt + 1})")
# Try to open with PIL
image = Image.open(file_path)
# Verify image can be loaded (trigger actual parsing)
image.load()
logger.info(f"Successfully opened image: {image.format} {image.mode} {image.size}")
return image
except (OSError, IOError) as e:
if "truncated" in str(e).lower() or "cannot identify" in str(e).lower():
logger.warning(f"Attempt {attempt + 1}: Image file appears truncated or corrupted: {e}")
if attempt < max_retries - 1:
logger.info(f"Retrying in {retry_delay} seconds...")
time.sleep(retry_delay)
continue
else:
logger.error(f"IO error opening image: {e}")
break
except Exception as e:
logger.error(f"Unexpected error opening image: {e}")
break
logger.error(f"Failed to open image after {max_retries} attempts: {file_path}")
return None
def check_palette_and_structure_sim(src_path, tgt_path):
"""
Check if the src image is palette-based and the structure of the two images are similar
Enhanced with robust error handling for file format issues and truncated files
gimp:06ca5602-62ca-47f6-ad4f-da151cde54cc
"""
import logging
logger = logging.getLogger(__name__)
logger.info(f"Evaluating palette and structure similarity: src={src_path}, tgt={tgt_path}")
if src_path is None or tgt_path is None:
logger.warning("Source or target path is None")
return 0.
# Check if the source image is palette-based
source_image = Image.open(src_path)
palette_based = source_image.mode == 'P'
# Check structure
target_image = Image.open(tgt_path)
source_image = source_image.convert('RGB')
structure_same = structure_check_by_ssim(source_image, target_image)
if palette_based and structure_same:
return 1.
else:
# Safely open source image with retry mechanism
source_image = safe_open_image_with_retry(src_path)
if source_image is None:
logger.error("Failed to open source image")
return 0.
try:
# Check if the source image is palette-based
palette_based = source_image.mode == 'P'
logger.info(f"Source image mode: {source_image.mode}, palette-based: {palette_based}")
# Safely open target image
target_image = safe_open_image_with_retry(tgt_path)
if target_image is None:
logger.error("Failed to open target image")
source_image.close()
return 0.
try:
# Convert source image to RGB for comparison
source_rgb = source_image.convert('RGB')
logger.info(f"Source converted to RGB: {source_rgb.mode} {source_rgb.size}")
# Check structure
structure_same = structure_check_by_ssim(source_rgb, target_image)
logger.info(f"Structure similarity check: {structure_same}")
# Evaluation logic
if palette_based and structure_same:
result = 1.0
else:
result = 0.0
logger.info(f"Evaluation result: {result} (palette_based={palette_based}, structure_same={structure_same})")
return result
finally:
target_image.close()
except Exception as e:
logger.error(f"Error during evaluation: {e}")
return 0.
finally:
source_image.close()
def check_textbox_on_leftside(src_path):
"""