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:
+42
-17
@@ -96,12 +96,18 @@ def trim_whitespace(image_path: str) -> bool:
|
||||
# Open image with PIL
|
||||
image = Image.open(image_path)
|
||||
|
||||
# Convert to grayscale if not already
|
||||
if image.mode != 'L':
|
||||
image = image.convert('L')
|
||||
# Create flattened version with white background for finding bounds
|
||||
flattened = Image.new('RGB', image.size, 'white')
|
||||
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
|
||||
bbox = image.getbbox()
|
||||
bbox = flattened.getbbox()
|
||||
if not bbox:
|
||||
print(f"Warning: No content found in {image_path}")
|
||||
return False
|
||||
@@ -115,11 +121,11 @@ def trim_whitespace(image_path: str) -> bool:
|
||||
x2 = min(width, x2 + padding)
|
||||
y2 = min(height, y2 + padding)
|
||||
|
||||
# Crop the image
|
||||
image = image.crop((x1, y1, x2, y2))
|
||||
# Crop the original image (preserving transparency)
|
||||
cropped = image.crop((x1, y1, x2, y2))
|
||||
|
||||
# Save the image
|
||||
image.save(image_path, "PNG", optimize=False)
|
||||
# Save the cropped image
|
||||
cropped.save(image_path, "PNG", optimize=False)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
@@ -203,25 +209,44 @@ def deskew():
|
||||
def convert_to_monochrome(image_path: str) -> bool:
|
||||
"""Convert image to 1-bit monochrome.
|
||||
|
||||
Handles RGBA images by converting transparent pixels to white before thresholding.
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
# Read the image
|
||||
img = cv2.imread(image_path)
|
||||
if img is None:
|
||||
print(f"Warning: Could not read image {image_path}")
|
||||
return False
|
||||
# Open image with PIL
|
||||
image = Image.open(image_path)
|
||||
|
||||
# Convert to RGBA if not already
|
||||
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
|
||||
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
||||
image = image.convert('L')
|
||||
|
||||
# Apply threshold
|
||||
_, mono = cv2.threshold(gray, settings.MONOCHROME_THRESHOLD, 255, cv2.THRESH_BINARY)
|
||||
# Convert to 1-bit using threshold
|
||||
image = image.point(lambda x: 255 if x > settings.MONOCHROME_THRESHOLD else 0, '1')
|
||||
|
||||
# Save the monochrome image
|
||||
cv2.imwrite(image_path, mono)
|
||||
image.save(image_path, "PNG", optimize=False)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error converting to monochrome {image_path}: {str(e)}")
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user