tune for dq6a to reach 30 fps for 2 sensors
This commit is contained in:
+79
-13
@@ -406,7 +406,7 @@ class Stitcher:
|
||||
wx[-m:] = ramp[::-1]
|
||||
return wy[:, None] * wx[None, :]
|
||||
|
||||
def _blend_tile_inplace(self, blend, wsum, cov, src, weight, y0, x0):
|
||||
def _blend_tile_inplace(self, blend, wsum, cov, src, weight, y0, x0, scratch=None):
|
||||
"""Copy non-overlap tiles directly; blend only when this tile touches existing content."""
|
||||
h, w = src.shape[:2]
|
||||
H, W = blend.shape[:2]
|
||||
@@ -426,6 +426,19 @@ class Stitcher:
|
||||
if not np.any(cov_patch):
|
||||
blend_patch[:] = src_patch
|
||||
wsum_patch[:] = w_patch
|
||||
elif scratch is not None:
|
||||
s_src, s_alpha = scratch
|
||||
sf = s_src[:sh, :sw]
|
||||
sa = s_alpha[:sh, :sw]
|
||||
sf[:] = src_patch
|
||||
np.add(wsum_patch, w_patch, out=wsum_patch)
|
||||
np.maximum(wsum_patch, 1e-6, out=sa)
|
||||
np.divide(w_patch, sa, out=sa)
|
||||
np.subtract(sf, blend_patch, out=sf)
|
||||
sf[:, :, 0] *= sa
|
||||
sf[:, :, 1] *= sa
|
||||
sf[:, :, 2] *= sa
|
||||
np.add(blend_patch, sf, out=blend_patch)
|
||||
else:
|
||||
total = wsum_patch + w_patch
|
||||
alpha = w_patch / np.maximum(total, 1e-6)
|
||||
@@ -436,13 +449,15 @@ class Stitcher:
|
||||
cov_patch[:] = 1
|
||||
|
||||
def _mean_gray_bgr(self, bgr):
|
||||
return float(cv2.cvtColor(bgr, cv2.COLOR_BGR2GRAY).mean())
|
||||
ch = cv2.mean(bgr)
|
||||
return (ch[0] + ch[1] + ch[2]) / 3.0
|
||||
|
||||
def _match_bgr_brightness(self, tile_bgr, ref_mean_gray):
|
||||
delta = int(round(ref_mean_gray - self._mean_gray_bgr(tile_bgr)))
|
||||
if delta == 0:
|
||||
return tile_bgr
|
||||
return np.clip(tile_bgr.astype(np.int16) + delta, 0, 255).astype(np.uint8)
|
||||
lut = np.clip(np.arange(256, dtype=np.int16) + delta, 0, 255).astype(np.uint8)
|
||||
return cv2.LUT(tile_bgr, lut)
|
||||
|
||||
def _largest_fully_covered_rect(self, cov):
|
||||
if cov.size == 0:
|
||||
@@ -519,13 +534,22 @@ class Stitcher:
|
||||
do_profile = verbose and frame_idx % print_every == 0
|
||||
t_profile0 = cv2.getTickCount() / cv2.getTickFrequency() if do_profile else 0.0
|
||||
|
||||
t_ref_bgr = self._tile_to_bgr(t_ref_bgr)
|
||||
if t_ref_bgr.ndim == 2:
|
||||
t_ref_bgr = cv2.cvtColor(t_ref_bgr, cv2.COLOR_GRAY2BGR)
|
||||
h0c, w0c = t_ref_bgr.shape[:2]
|
||||
if match_brightness:
|
||||
ref_mean_gray = self._mean_gray_bgr(t_ref_bgr)
|
||||
tiles_bgr_c = [self._match_bgr_brightness(self._tile_to_bgr(tb), ref_mean_gray) for tb in tiles_bgr]
|
||||
tiles_bgr_c = [
|
||||
self._match_bgr_brightness(
|
||||
tb if tb.ndim == 3 else cv2.cvtColor(tb, cv2.COLOR_GRAY2BGR),
|
||||
ref_mean_gray,
|
||||
)
|
||||
for tb in tiles_bgr
|
||||
]
|
||||
elif any(tb.ndim == 2 for tb in tiles_bgr):
|
||||
tiles_bgr_c = [tb if tb.ndim == 3 else cv2.cvtColor(tb, cv2.COLOR_GRAY2BGR) for tb in tiles_bgr]
|
||||
else:
|
||||
tiles_bgr_c = [self._tile_to_bgr(tb) for tb in tiles_bgr]
|
||||
tiles_bgr_c = tiles_bgr
|
||||
t_after_brightness = cv2.getTickCount() / cv2.getTickFrequency() if do_profile else 0.0
|
||||
|
||||
geom_key = self._geom_cache_key(t_ref_bgr, tiles_bgr_c, pad)
|
||||
@@ -563,6 +587,20 @@ class Stitcher:
|
||||
tx, ty = self._translation_from_H_to_ref(H_use)
|
||||
pastes.append((int(np.floor(ty - y_min_f)), int(np.floor(tx - x_min_f))))
|
||||
|
||||
_dr_y0 = max(0, ref_y0)
|
||||
_dr_x0 = max(0, ref_x0)
|
||||
_dr_y1 = min(out_h, ref_y0 + h0c)
|
||||
_dr_x1 = min(out_w, ref_x0 + w0c)
|
||||
for _tb, _pa in zip(tiles_bgr_c, pastes):
|
||||
if _pa is None:
|
||||
continue
|
||||
_py, _px = _pa
|
||||
_th, _tw = _tb.shape[:2]
|
||||
_dr_y0 = min(_dr_y0, max(0, _py))
|
||||
_dr_x0 = min(_dr_x0, max(0, _px))
|
||||
_dr_y1 = max(_dr_y1, min(out_h, _py + _th))
|
||||
_dr_x1 = max(_dr_x1, min(out_w, _px + _tw))
|
||||
|
||||
canvas_cache["geom_key"] = geom_key
|
||||
canvas_cache["out_w"] = out_w
|
||||
canvas_cache["out_h"] = out_h
|
||||
@@ -572,6 +610,7 @@ class Stitcher:
|
||||
canvas_cache["x_min_f"] = x_min_f
|
||||
canvas_cache["y_min_f"] = y_min_f
|
||||
canvas_cache["crop_rect"] = None
|
||||
canvas_cache["dirty_rect"] = (_dr_y0, _dr_y1, _dr_x0, _dr_x1)
|
||||
canvas_cache["blend_buf"] = np.zeros((out_h, out_w, 3), dtype=np.float32)
|
||||
canvas_cache["wsum_buf"] = np.zeros((out_h, out_w), dtype=np.float32)
|
||||
canvas_cache["cov_buf"] = np.zeros((out_h, out_w), dtype=np.uint8)
|
||||
@@ -588,12 +627,32 @@ class Stitcher:
|
||||
wsum = canvas_cache["wsum_buf"]
|
||||
cov = canvas_cache["cov_buf"]
|
||||
weight_maps = canvas_cache["weight_maps"]
|
||||
blend_buf.fill(0)
|
||||
wsum.fill(0)
|
||||
cov.fill(0)
|
||||
if geom_cache_hit:
|
||||
_dr = canvas_cache.get("dirty_rect")
|
||||
if _dr is not None:
|
||||
_dy0, _dy1, _dx0, _dx1 = _dr
|
||||
blend_buf[_dy0:_dy1, _dx0:_dx1] = 0
|
||||
wsum[_dy0:_dy1, _dx0:_dx1] = 0
|
||||
cov[_dy0:_dy1, _dx0:_dx1] = 0
|
||||
else:
|
||||
blend_buf.fill(0)
|
||||
wsum.fill(0)
|
||||
cov.fill(0)
|
||||
t_after_reset = cv2.getTickCount() / cv2.getTickFrequency() if do_profile else 0.0
|
||||
|
||||
blend_margin = 16 if blend else 0
|
||||
max_th = max(h0c, *(tb.shape[0] for tb in tiles_bgr_c)) if tiles_bgr_c else h0c
|
||||
max_tw = max(w0c, *(tb.shape[1] for tb in tiles_bgr_c)) if tiles_bgr_c else w0c
|
||||
_ss = canvas_cache.get("scratch_src")
|
||||
_sa = canvas_cache.get("scratch_alpha")
|
||||
if _ss is None or _ss.shape[0] < max_th or _ss.shape[1] < max_tw:
|
||||
_ss = np.empty((max_th, max_tw, 3), dtype=np.float32)
|
||||
canvas_cache["scratch_src"] = _ss
|
||||
if _sa is None or _sa.shape[0] < max_th or _sa.shape[1] < max_tw:
|
||||
_sa = np.empty((max_th, max_tw), dtype=np.float32)
|
||||
canvas_cache["scratch_alpha"] = _sa
|
||||
_scratch = (_ss, _sa)
|
||||
|
||||
blended_tiles = 0
|
||||
for tile_bgr, H_use, paste in zip(tiles_bgr_c, H_use_list, pastes):
|
||||
if H_use is None or paste is None:
|
||||
@@ -603,13 +662,13 @@ class Stitcher:
|
||||
wkey = (th, tw, blend_margin)
|
||||
if wkey not in weight_maps:
|
||||
weight_maps[wkey] = self._tile_weight_map(th, tw, blend_margin)
|
||||
self._blend_tile_inplace(blend_buf, wsum, cov, tile_bgr, weight_maps[wkey], y0, x0)
|
||||
self._blend_tile_inplace(blend_buf, wsum, cov, tile_bgr, weight_maps[wkey], y0, x0, _scratch)
|
||||
blended_tiles += 1
|
||||
|
||||
ref_wkey = (h0c, w0c, blend_margin)
|
||||
if ref_wkey not in weight_maps:
|
||||
weight_maps[ref_wkey] = self._tile_weight_map(h0c, w0c, blend_margin)
|
||||
self._blend_tile_inplace(blend_buf, wsum, cov, t_ref_bgr, weight_maps[ref_wkey], ref_y0, ref_x0)
|
||||
self._blend_tile_inplace(blend_buf, wsum, cov, t_ref_bgr, weight_maps[ref_wkey], ref_y0, ref_x0, _scratch)
|
||||
t_after_blend = cv2.getTickCount() / cv2.getTickFrequency() if do_profile else 0.0
|
||||
|
||||
crop_rect = canvas_cache.get("crop_rect")
|
||||
@@ -618,9 +677,16 @@ class Stitcher:
|
||||
canvas_cache["crop_rect"] = crop_rect
|
||||
if crop_rect is not None:
|
||||
y0, y1, x0, x1 = crop_rect
|
||||
stitched = np.clip(blend_buf[y0:y1, x0:x1], 0, 255).astype(np.uint8)
|
||||
roi = blend_buf[y0:y1, x0:x1]
|
||||
else:
|
||||
stitched = np.clip(blend_buf, 0, 255).astype(np.uint8)
|
||||
roi = blend_buf
|
||||
rh, rw = roi.shape[:2]
|
||||
stitch_buf = canvas_cache.get("stitch_buf")
|
||||
if stitch_buf is None or stitch_buf.shape[0] != rh or stitch_buf.shape[1] != rw:
|
||||
stitch_buf = np.empty((rh, rw, 3), dtype=np.uint8)
|
||||
canvas_cache["stitch_buf"] = stitch_buf
|
||||
cv2.convertScaleAbs(roi, dst=stitch_buf)
|
||||
stitched = stitch_buf
|
||||
t_after_finalize = cv2.getTickCount() / cv2.getTickFrequency() if do_profile else 0.0
|
||||
|
||||
if do_profile:
|
||||
|
||||
Reference in New Issue
Block a user