summaryrefslogtreecommitdiffstats
path: root/Scripts/gen_sdf
blob: 88469dcac21e0399dad427b473770e6bbc84030f (plain)
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
#!/usr/bin/env python3

import numpy as np
import cv2
import argparse
import os

def compute_sdf(img, n_px, bit_depth=8):
    # Convert to binary image if not already
    _, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
    
    # Compute distance transform for both foreground and background
    dist_transform_fg = cv2.distanceTransform(binary, cv2.DIST_L2, 5)
    dist_transform_bg = cv2.distanceTransform(255 - binary, cv2.DIST_L2, 5)
    
    # Combine to get signed distance field (positive outside, negative inside)
    sdf = dist_transform_fg - dist_transform_bg
    
    # Clip to ±n_px range
    sdf = np.clip(sdf, -n_px, n_px)
    
    # Map from [-n_px, +n_px] to [0, 1]
    sdf_normalized = (sdf + n_px) / (2 * n_px)
    
    # Quantize to requested bit depth
    if bit_depth == 8:
        max_value = 255
        dtype = np.uint8
    elif bit_depth == 16:
        max_value = 65535
        dtype = np.uint16
    else:
        raise ValueError(f"Unsupported bit depth: {bit_depth}")
    
    sdf_quantized = np.round(sdf_normalized * max_value).astype(dtype)
    
    return sdf_quantized

def main():
    parser = argparse.ArgumentParser(description='Generate SDF from black and white image with fixed range encoding')
    parser.add_argument('input_images', nargs='+', help='Path to input image(s)')
    parser.add_argument('--n_px', type=float, default=64.0, 
                        help='Maximum distance to encode in pixels (default: 64)')
    parser.add_argument('--bit_depth', type=int, default=8, choices=[8, 16],
                        help='Output bit depth (default: 8)')
    args = parser.parse_args()
    
    # Process each input image
    for input_path in args.input_images:
        # Get input and output paths
        filename, ext = os.path.splitext(input_path)
        output_path = f"{filename}-sdf{ext}"
        
        # Read input image
        img = cv2.imread(input_path, cv2.IMREAD_GRAYSCALE)
        if img is None:
            print(f"Error: Could not read image {input_path}")
            continue
        
        # Compute SDF with fixed range
        sdf = compute_sdf(img, args.n_px, args.bit_depth)
        
        # Save result
        if args.bit_depth == 16:
            # For 16-bit images, ensure proper saving
            cv2.imwrite(output_path, sdf)
        else:
            cv2.imwrite(output_path, sdf)
        
        print(f"SDF generated and saved to {output_path}{args.n_px}px range, {args.bit_depth}-bit)")
        print(f"  Decoding: 0.5 = contour, 0.0 = -{args.n_px}px (inside), 1.0 = +{args.n_px}px (outside)")

if __name__ == "__main__":
    main()