Fix convergence sign preservation in CPU stereo fill modes

- Fix sign loss bug in all 6 CPU fill functions where even exponents
  (e.g., stereo_offset_exponent=2.0) caused depth values on opposite
  sides of the convergence point to produce identical offsets
- Changed from: offset = (normalized_depth ** exponent) * divergence
- Changed to: offset = sign(depth) * abs(depth)**exponent * divergence
- This matches the GPU warp implementation which was already correct
- Affected functions: enhanced_inverse_mapping_with_mask,
  naive_mapping_with_mask, inverse_mapping_with_mask,
  apply_stereo_divergence_inverse, apply_stereo_divergence_naive,
  apply_stereo_divergence_polylines

Additional fixes:
- Replace Unicode checkmarks (✓/✗) with ASCII ([OK]/[--]) in __init__.py
  for Windows cp1252 compatibility
- Add tests/ to .gitignore (tests kept in dev branch only)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Dobidop
2026-02-06 11:57:04 +01:00
co-authored by Claude Sonnet 4.5
parent 9bb9266845
commit a33c104559
3 changed files with 25 additions and 12 deletions
+2 -1
View File
@@ -7,4 +7,5 @@ output_*.png
test_image.jpg
test_depth.png
/cloneComfyStereo.bat
/.claude
/.claude
tests/
+5 -5
View File
@@ -58,15 +58,15 @@ NODE_DISPLAY_NAME_MAPPINGS = {
print("\n" + "="*60)
print("ComfyStereo - Loaded Modules:")
print("="*60)
print(f"✓ Stereo Image Generation ({len(STEREO_NODES)} nodes)")
print(f" [OK] Stereo Image Generation ({len(STEREO_NODES)} nodes)")
if NATIVE_AVAILABLE:
print(f"✓ Native VR Viewer ({len(NATIVE_NODES)} nodes)")
print(f" [OK] Native VR Viewer ({len(NATIVE_NODES)} nodes)")
else:
print(f"✗ Native VR Viewer (not available)")
print(f" [--] Native VR Viewer (not available)")
if DIFFUSION_AVAILABLE:
print(f"✓ StereoDiffusion ({len(DIFFUSION_NODES)} nodes)")
print(f" [OK] StereoDiffusion ({len(DIFFUSION_NODES)} nodes)")
else:
print(f"✗ StereoDiffusion (not available)")
print(f" [--] StereoDiffusion (not available)")
print(f"\nTotal: {len(NODE_CLASS_MAPPINGS)} nodes loaded")
print("="*60 + "\n")
+18 -6
View File
@@ -784,7 +784,9 @@ def enhanced_inverse_mapping_with_mask(original_image, normalized_depth, diverge
sigma = 1.0 # standard deviation for the subpixel kernel
for row in prange(h):
for x in range(w):
offset = (normalized_depth[row, x] ** stereo_offset_exponent) * divergence_px
d = normalized_depth[row, x]
sign_d = 1.0 if d >= 0.0 else -1.0
offset = sign_d * (abs(d) ** stereo_offset_exponent) * divergence_px
dest_x = x + 0.5 + offset + separation_px
j_center = int(math.floor(dest_x))
for d in (-1, 0, 1):
@@ -822,7 +824,9 @@ def naive_mapping_with_mask(original_image, normalized_depth, divergence_px: flo
else:
rng = range(w - 1, -1, -1)
for col in rng:
offset = (normalized_depth[row, col] ** stereo_offset_exponent) * divergence_px + separation_px
d = normalized_depth[row, col]
sign_d = 1.0 if d >= 0.0 else -1.0
offset = sign_d * (abs(d) ** stereo_offset_exponent) * divergence_px + separation_px
col_d = col + int(offset)
if 0 <= col_d < w:
derived_image[row, col_d] = original_image[row, col]
@@ -841,7 +845,9 @@ def inverse_mapping_with_mask(original_image, normalized_depth, divergence_px: f
for row in prange(h):
depth_buffer = np.full(w, -1.0, dtype=np.float32)
for x in range(w):
offset = (normalized_depth[row, x] ** stereo_offset_exponent) * divergence_px
d = normalized_depth[row, x]
sign_d = 1.0 if d >= 0.0 else -1.0
offset = sign_d * (abs(d) ** stereo_offset_exponent) * divergence_px
dest_x = x + 0.5 + offset + separation_px
closeness = normalized_depth[row, x]
j = int(np.floor(dest_x))
@@ -865,7 +871,9 @@ def apply_stereo_divergence_inverse(original_image, normalized_depth, divergence
for row in prange(h):
depth_buffer = np.full(w, -1.0, dtype=np.float32)
for x in range(w):
offset = (normalized_depth[row, x] ** stereo_offset_exponent) * divergence_px
d = normalized_depth[row, x]
sign_d = 1.0 if d >= 0.0 else -1.0
offset = sign_d * (abs(d) ** stereo_offset_exponent) * divergence_px
dest_x = x + 0.5 + offset + separation_px
closeness = normalized_depth[row, x]
j = int(np.floor(dest_x))
@@ -1004,7 +1012,9 @@ def apply_stereo_divergence_naive(
# Swipe order should ensure that pixels that are closer overwrite
# (at their destination) pixels that are less close
for col in range(w) if divergence_px < 0 else range(w - 1, -1, -1):
col_d = col + int((normalized_depth[row][col] ** stereo_offset_exponent) * divergence_px + separation_px)
d = normalized_depth[row][col]
sign_d = 1.0 if d >= 0.0 else -1.0
col_d = col + int(sign_d * (abs(d) ** stereo_offset_exponent) * divergence_px + separation_px)
if 0 <= col_d < w:
derived_image[row][col_d] = original_image[row][col]
filled[row * w + col_d] = 1
@@ -1063,7 +1073,9 @@ def apply_stereo_divergence_polylines(original_image, normalized_depth, divergen
pt[pt_end] = [-1.0 * w, 0.0, 0.0]
pt_end += 1
for col in range(0, w):
coord_d = (normalized_depth[row][col] ** stereo_offset_exponent) * divergence_px
d = normalized_depth[row][col]
sign_d = 1.0 if d >= 0.0 else -1.0
coord_d = sign_d * (abs(d) ** stereo_offset_exponent) * divergence_px
coord_x = col + 0.5 + coord_d + separation_px
if PIXEL_HALF_WIDTH < EPSILON:
pt[pt_end] = [coord_x, abs(coord_d), col]