File size: 3,429 Bytes
ba80d43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8e987de
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import cv2
import numpy as np
from PIL import Image

def preprocess_image(image, target_width=420, target_height=130):
    """
    Preprocess input image to match training data format:
    - Convert to black background with white foreground
    - Resize to target dimensions
    - Enhance contrast for better detection
    
    Args:
        image: Input image (numpy array or PIL Image)
        target_width: Width to resize to (default: 420)
        target_height: Height to resize to (default: 130)
        
    Returns:
        Preprocessed image as numpy array
    """
    # Convert PIL Image to numpy array if needed
    if isinstance(image, Image.Image):
        image_np = np.array(image)
    else:
        image_np = image.copy()
    
    # Convert to grayscale if colored
    if len(image_np.shape) == 3 and image_np.shape[2] == 3:
        gray_img = cv2.cvtColor(image_np, cv2.COLOR_RGB2GRAY)
    else:
        gray_img = image_np
    
    # Make sure image is uint8 for cv2 operations
    if gray_img.dtype != np.uint8:
        gray_img = (gray_img * 255).astype(np.uint8)
    
    # Determine foreground/background colors
    mean_value = np.mean(gray_img)
    is_dark_background = mean_value < 128
    
    # Invert if needed to get dark background/light foreground
    if not is_dark_background:
        gray_img = 255 - gray_img
    
    # Apply adaptive thresholding to enhance contrast
    # This helps with handwritten content with varying pressure/intensity
    binary_img = cv2.adaptiveThreshold(
        gray_img,
        255,
        cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
        cv2.THRESH_BINARY_INV,
        11,  # Block size
        2    # Constant subtracted from mean
    )
    
    # Resize to target dimensions
    # Use INTER_AREA for shrinking, INTER_CUBIC for enlarging
    current_height, current_width = binary_img.shape
    if current_width > target_width or current_height > target_height:
        interpolation = cv2.INTER_AREA
    else:
        interpolation = cv2.INTER_CUBIC
    
    # Calculate scaling ratio to maintain aspect ratio
    width_ratio = target_width / current_width
    height_ratio = target_height / current_height
    scaling_ratio = min(width_ratio, height_ratio)
    
    # Calculate new dimensions
    new_width = int(current_width * scaling_ratio)
    new_height = int(current_height * scaling_ratio)
    
    # Resize while maintaining aspect ratio
    resized_img = cv2.resize(binary_img, (new_width, new_height), interpolation=interpolation)
    
    # Create black canvas of target size
    final_img = np.zeros((target_height, target_width), dtype=np.uint8)
    
    # Calculate position to center the resized image
    x_offset = (target_width - new_width) // 2
    y_offset = (target_height - new_height) // 2
    
    # Place the resized image on the canvas
    final_img[y_offset:y_offset+new_height, x_offset:x_offset+new_width] = resized_img
    
    # Invert to get white foreground on black background
    final_img = 255 - final_img
    
    # Apply additional contrast enhancement if needed
    # This can help with faint or inconsistent handwriting
    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
    enhanced_img = clahe.apply(final_img)
    
    # Optional: Apply slight Gaussian blur to reduce noise
    # Only apply if the image seems noisy
    enhanced_img = cv2.GaussianBlur(enhanced_img, (3, 3), 0)
    
    return enhanced_img