yum-archive/2ner

A toon shader for Unity's BIRP.

git clone https://git.yummers.dev/yum-archive/2ner

yumssao now applies to basecolor instead of lighting91f89f5

master
2.6 KiB74 linesraw
1#!/usr/bin/env python3
2
3import numpy as np
4import cv2
5import argparse
6import os
7
8def compute_sdf(img, n_px, bit_depth=8):
9    # Convert to binary image if not already
10    _, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
11    
12    # Compute distance transform for both foreground and background
13    dist_transform_fg = cv2.distanceTransform(binary, cv2.DIST_L2, 5)
14    dist_transform_bg = cv2.distanceTransform(255 - binary, cv2.DIST_L2, 5)
15    
16    # Combine to get signed distance field (positive outside, negative inside)
17    sdf = dist_transform_fg - dist_transform_bg
18    
19    # Clip to ±n_px range
20    sdf = np.clip(sdf, -n_px, n_px)
21    
22    # Map from [-n_px, +n_px] to [0, 1]
23    sdf_normalized = (sdf + n_px) / (2 * n_px)
24    
25    # Quantize to requested bit depth
26    if bit_depth == 8:
27        max_value = 255
28        dtype = np.uint8
29    elif bit_depth == 16:
30        max_value = 65535
31        dtype = np.uint16
32    else:
33        raise ValueError(f"Unsupported bit depth: {bit_depth}")
34    
35    sdf_quantized = np.round(sdf_normalized * max_value).astype(dtype)
36    
37    return sdf_quantized
38
39def main():
40    parser = argparse.ArgumentParser(description='Generate SDF from black and white image with fixed range encoding')
41    parser.add_argument('input_images', nargs='+', help='Path to input image(s)')
42    parser.add_argument('--n_px', type=float, default=64.0, 
43                        help='Maximum distance to encode in pixels (default: 64)')
44    parser.add_argument('--bit_depth', type=int, default=8, choices=[8, 16],
45                        help='Output bit depth (default: 8)')
46    args = parser.parse_args()
47    
48    # Process each input image
49    for input_path in args.input_images:
50        # Get input and output paths
51        filename, ext = os.path.splitext(input_path)
52        output_path = f"{filename}-sdf{ext}"
53        
54        # Read input image
55        img = cv2.imread(input_path, cv2.IMREAD_GRAYSCALE)
56        if img is None:
57            print(f"Error: Could not read image {input_path}")
58            continue
59        
60        # Compute SDF with fixed range
61        sdf = compute_sdf(img, args.n_px, args.bit_depth)
62        
63        # Save result
64        if args.bit_depth == 16:
65            # For 16-bit images, ensure proper saving
66            cv2.imwrite(output_path, sdf)
67        else:
68            cv2.imwrite(output_path, sdf)
69        
70        print(f"SDF generated and saved to {output_path} (±{args.n_px}px range, {args.bit_depth}-bit)")
71        print(f"  Decoding: 0.5 = contour, 0.0 = -{args.n_px}px (inside), 1.0 = +{args.n_px}px (outside)")
72
73if __name__ == "__main__":
74    main()