fix(stereo_depth): negative disparity window + percentile normalize — movie stereo works
Movie SBS converges on the screen plane, so real content sits at NEGATIVE disparity; minDisparity=0 clipped everything behind convergence to black. Window is now ±num_disp/2 with 2-98 percentile normalization and a validity mask. Verified on Big Buck Bunny (half-OU) + One Night In Hell (half-SBS): structured depth + colored clouds that render in Blender. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
95708ad220
commit
cf402e322c
@ -50,8 +50,9 @@ subprocess.run([FFMPEG, "-y", "-loglevel", "error", "-i", src,
|
||||
frames = sorted(glob.glob(os.path.join(frames_dir, "*.png")))
|
||||
print(f"[stereo] {len(frames)} frames, num_disp={num_disp} block={block}", flush=True)
|
||||
|
||||
sgbm = cv2.StereoSGBM_create(
|
||||
minDisparity=0, numDisparities=num_disp, blockSize=block,
|
||||
min_disp = -(num_disp // 2) # movie stereo converges on the screen plane — content
|
||||
sgbm = cv2.StereoSGBM_create( # lives BOTH sides of it, so the window must go negative
|
||||
minDisparity=min_disp, numDisparities=num_disp, blockSize=block,
|
||||
P1=8 * 3 * block * block, P2=32 * 3 * block * block,
|
||||
disp12MaxDiff=1, uniquenessRatio=10, speckleWindowSize=100, speckleRange=2,
|
||||
mode=cv2.STEREO_SGBM_MODE_SGBM_3WAY)
|
||||
@ -66,11 +67,16 @@ for i, f in enumerate(frames):
|
||||
L, R = img[:, : w // 2], img[:, w // 2:]
|
||||
gl, gr = cv2.cvtColor(L, cv2.COLOR_BGR2GRAY), cv2.cvtColor(R, cv2.COLOR_BGR2GRAY)
|
||||
disp = sgbm.compute(gl, gr).astype(np.float32) / 16.0
|
||||
disp[disp < 0] = 0
|
||||
norm = (disp / max(1e-6, disp.max()) * 255).astype(np.uint8)
|
||||
valid = disp > (min_disp - 0.5) # cv2 marks unmatched pixels min_disp-1
|
||||
if valid.sum() > 100:
|
||||
lo, hi = np.percentile(disp[valid], [2, 98])
|
||||
else:
|
||||
lo, hi = 0.0, 1.0
|
||||
norm = np.zeros(disp.shape, np.uint8)
|
||||
norm[valid] = (np.clip((disp[valid] - lo) / max(1e-6, hi - lo), 0, 1) * 255).astype(np.uint8)
|
||||
cv2.imwrite(os.path.join(depth_dir, os.path.basename(f)), norm)
|
||||
if f == mid: # colored point cloud of the middle frame
|
||||
ys, xs = np.where(disp > 1)
|
||||
ys, xs = np.where(valid & (norm > 2))
|
||||
step = max(1, len(ys) // 400_000) # cap cloud size
|
||||
ys, xs = ys[::step], xs[::step]
|
||||
z = disp[ys, xs]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user