fix: properly handle transparency in trim_whitespace

Simplify transparency handling by flattening image with white
background for bounds detection while preserving original
transparency in the final crop. This fixes the issue where
transparent areas were being treated as black during trimming.
This commit is contained in:
Esa Kataja
2024-12-03 22:06:51 +02:00
parent 84e1a6a82e
commit 75dc1c3e2c
+42 -17
View File
@@ -96,12 +96,18 @@ def trim_whitespace(image_path: str) -> bool:
# Open image with PIL # Open image with PIL
image = Image.open(image_path) image = Image.open(image_path)
# Convert to grayscale if not already # Create flattened version with white background for finding bounds
if image.mode != 'L': flattened = Image.new('RGB', image.size, 'white')
image = image.convert('L') if image.mode == 'RGBA':
flattened.paste(image, mask=image.split()[3])
else:
flattened.paste(image)
# Convert to grayscale for finding bounds
flattened = flattened.convert('L')
# Get the bounding box of non-white pixels # Get the bounding box of non-white pixels
bbox = image.getbbox() bbox = flattened.getbbox()
if not bbox: if not bbox:
print(f"Warning: No content found in {image_path}") print(f"Warning: No content found in {image_path}")
return False return False
@@ -115,11 +121,11 @@ def trim_whitespace(image_path: str) -> bool:
x2 = min(width, x2 + padding) x2 = min(width, x2 + padding)
y2 = min(height, y2 + padding) y2 = min(height, y2 + padding)
# Crop the image # Crop the original image (preserving transparency)
image = image.crop((x1, y1, x2, y2)) cropped = image.crop((x1, y1, x2, y2))
# Save the image # Save the cropped image
image.save(image_path, "PNG", optimize=False) cropped.save(image_path, "PNG", optimize=False)
return True return True
except Exception as e: except Exception as e:
@@ -203,25 +209,44 @@ def deskew():
def convert_to_monochrome(image_path: str) -> bool: def convert_to_monochrome(image_path: str) -> bool:
"""Convert image to 1-bit monochrome. """Convert image to 1-bit monochrome.
Handles RGBA images by converting transparent pixels to white before thresholding.
Returns: Returns:
bool: True if successful, False otherwise bool: True if successful, False otherwise
""" """
try: try:
# Read the image # Open image with PIL
img = cv2.imread(image_path) image = Image.open(image_path)
if img is None:
print(f"Warning: Could not read image {image_path}") # Convert to RGBA if not already
return False if image.mode != 'RGBA':
image = image.convert('RGBA')
# Get the image data as a list of pixels
data = image.getdata()
# Create new image data, replacing transparent pixels with white
new_data = []
for item in data:
# If pixel is transparent (alpha < 128), make it white
if item[3] < 128:
new_data.append((255, 255, 255, 255))
else:
new_data.append(item)
# Create new image with modified data
image.putdata(new_data)
# Convert to grayscale # Convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) image = image.convert('L')
# Apply threshold # Convert to 1-bit using threshold
_, mono = cv2.threshold(gray, settings.MONOCHROME_THRESHOLD, 255, cv2.THRESH_BINARY) image = image.point(lambda x: 255 if x > settings.MONOCHROME_THRESHOLD else 0, '1')
# Save the monochrome image # Save the monochrome image
cv2.imwrite(image_path, mono) image.save(image_path, "PNG", optimize=False)
return True return True
except Exception as e: except Exception as e:
print(f"Error converting to monochrome {image_path}: {str(e)}") print(f"Error converting to monochrome {image_path}: {str(e)}")
return False return False