update hex test video
This commit is contained in:
+235
-216
@@ -29,6 +29,8 @@ class Stitcher:
|
||||
self.bound=self.nominal_ov_pos - self.margin
|
||||
self.row_tol = config_stitcher["row_tol"]
|
||||
self.box_size = config_stitcher["box_size"]
|
||||
|
||||
# stich related (BF-matching)
|
||||
self._blend_coef_cache = {}
|
||||
self._feature_stitch_cache = {
|
||||
"ref_idx": None,
|
||||
@@ -77,58 +79,58 @@ class Stitcher:
|
||||
if feature_stitched is not None:
|
||||
return feature_stitched
|
||||
|
||||
tile_list = self.tile_list
|
||||
stitch_calibration = self.stitch_calibration
|
||||
ncol = self.ncol
|
||||
nrow = self.nrow
|
||||
# tile_list = self.tile_list
|
||||
# stitch_calibration = self.stitch_calibration
|
||||
# ncol = self.ncol
|
||||
# nrow = self.nrow
|
||||
|
||||
img_stitched_height = None
|
||||
# img_stitched_height = None
|
||||
|
||||
for i in range(nrow):
|
||||
# Start with first tile of the row
|
||||
img_stitched_width = tile_list[i * ncol]
|
||||
# for i in range(nrow):
|
||||
# # Start with first tile of the row
|
||||
# img_stitched_width = tile_list[i * ncol]
|
||||
|
||||
# Stitch horizontally within the row
|
||||
for j in range(1, ncol):
|
||||
img_1 = img_stitched_width
|
||||
img_2 = tile_list[i * ncol + j]
|
||||
# # Stitch horizontally within the row
|
||||
# for j in range(1, ncol):
|
||||
# img_1 = img_stitched_width
|
||||
# img_2 = tile_list[i * ncol + j]
|
||||
|
||||
ov_pos = int(stitch_calibration[i * ncol + j - 1][1])
|
||||
ov_pos, ov1, ov2 = self.__find_ov(img_1, img_2, ov_pos, dim=1)
|
||||
# ov_pos = int(stitch_calibration[i * ncol + j - 1][1])
|
||||
# ov_pos, ov1, ov2 = self.__find_ov(img_1, img_2, ov_pos, dim=1)
|
||||
|
||||
img_stitched_width = self.__stitch_optimized(
|
||||
img_1, img_2, ov_pos, ov1, ov2, dim=1, blend=blend
|
||||
)
|
||||
# img_stitched_width = self.__stitch_optimized(
|
||||
# img_1, img_2, ov_pos, ov1, ov2, dim=1, blend=blend
|
||||
# )
|
||||
|
||||
# Stitch rows vertically
|
||||
if i == 0:
|
||||
img_stitched_height = img_stitched_width
|
||||
else:
|
||||
width_h = img_stitched_height.shape[1]
|
||||
width_w = img_stitched_width.shape[1]
|
||||
wdiff = width_h - width_w
|
||||
# # Stitch rows vertically
|
||||
# if i == 0:
|
||||
# img_stitched_height = img_stitched_width
|
||||
# else:
|
||||
# width_h = img_stitched_height.shape[1]
|
||||
# width_w = img_stitched_width.shape[1]
|
||||
# wdiff = width_h - width_w
|
||||
|
||||
if wdiff != 0:
|
||||
if wdiff > 0:
|
||||
padding = (
|
||||
((0, 0), (0, wdiff), (0, 0))
|
||||
if len(img_stitched_width.shape) == 3
|
||||
else ((0, 0), (0, wdiff))
|
||||
)
|
||||
img_stitched_width = np.pad(img_stitched_width, padding, mode='edge')
|
||||
else:
|
||||
img_stitched_width = img_stitched_width[:, :width_h]
|
||||
# if wdiff != 0:
|
||||
# if wdiff > 0:
|
||||
# padding = (
|
||||
# ((0, 0), (0, wdiff), (0, 0))
|
||||
# if len(img_stitched_width.shape) == 3
|
||||
# else ((0, 0), (0, wdiff))
|
||||
# )
|
||||
# img_stitched_width = np.pad(img_stitched_width, padding, mode='edge')
|
||||
# else:
|
||||
# img_stitched_width = img_stitched_width[:, :width_h]
|
||||
|
||||
ov_pos_h = int(stitch_calibration[(i + 1) * nrow - 1][1])
|
||||
ov_pos_h, ov_h1, ov_h2 = self.__find_ov(
|
||||
img_stitched_height, img_stitched_width, ov_pos_h, dim=0
|
||||
)
|
||||
# ov_pos_h = int(stitch_calibration[(i + 1) * nrow - 1][1])
|
||||
# ov_pos_h, ov_h1, ov_h2 = self.__find_ov(
|
||||
# img_stitched_height, img_stitched_width, ov_pos_h, dim=0
|
||||
# )
|
||||
|
||||
img_stitched_height = self.__stitch_optimized(
|
||||
img_stitched_height, img_stitched_width, ov_pos_h, ov_h1, ov_h2, dim=0, blend=blend
|
||||
)
|
||||
# img_stitched_height = self.__stitch_optimized(
|
||||
# img_stitched_height, img_stitched_width, ov_pos_h, ov_h1, ov_h2, dim=0, blend=blend
|
||||
# )
|
||||
|
||||
return img_stitched_height
|
||||
# return img_stitched_height
|
||||
|
||||
def stitch_feature_optimized(self, blend=True, match_brightness=False, pad=5, print_every=30):
|
||||
"""
|
||||
@@ -140,10 +142,10 @@ class Stitcher:
|
||||
if plan is None:
|
||||
return None
|
||||
|
||||
ref_idx, t_ref, moving_indices, moving_tiles = plan
|
||||
ref_idx, t_ref, moving_indices, moving_tiles, selected_positions = plan
|
||||
self._feature_stitch_frame_idx += 1
|
||||
frame_idx = self._feature_stitch_frame_idx
|
||||
tile_shapes = tuple(tile.shape[:2] for tile in self.tile_list[: self.nrow * self.ncol])
|
||||
tile_shapes = tuple(self.tile_list[pos].shape[:2] for pos in selected_positions)
|
||||
cache = self._feature_stitch_cache
|
||||
cache_changed = (
|
||||
cache["ref_idx"] != ref_idx
|
||||
@@ -157,6 +159,11 @@ class Stitcher:
|
||||
cache["tile_shapes"] = tile_shapes
|
||||
cache["transforms"] = None
|
||||
cache["canvas"] = {}
|
||||
if self.verbose:
|
||||
print(
|
||||
"Feature stitch tile selection: "
|
||||
f"ref_idx={ref_idx}, moving_indices={list(moving_indices)}"
|
||||
)
|
||||
|
||||
H_use_list = cache["transforms"]
|
||||
if H_use_list is None:
|
||||
@@ -187,46 +194,58 @@ class Stitcher:
|
||||
blend=blend,
|
||||
)
|
||||
|
||||
def __find_ov(self, img1, img2, ov_pos, dim=1):
|
||||
# def __find_ov(self, img1, img2, ov_pos, dim=1):
|
||||
|
||||
_dim = len(img1.shape)
|
||||
# _dim = len(img1.shape)
|
||||
|
||||
if dim == 1:
|
||||
if _dim == 3:
|
||||
# RGB case
|
||||
ov_1 = img1[:, -ov_pos:, :]
|
||||
ov_2 = img2[:, :ov_pos, :]
|
||||
else:
|
||||
# gray scale
|
||||
ov_1 = img1[:, -ov_pos:]
|
||||
ov_2 = img2[:, :ov_pos]
|
||||
elif dim == 0:
|
||||
if _dim == 3:
|
||||
ov_1 = img1[-ov_pos:, :, :]
|
||||
ov_2 = img2[:ov_pos, :, :]
|
||||
else:
|
||||
# gray scale
|
||||
ov_1 = img1[-ov_pos:, :]
|
||||
ov_2 = img2[:ov_pos, :]
|
||||
# if dim == 1:
|
||||
# if _dim == 3:
|
||||
# # RGB case
|
||||
# ov_1 = img1[:, -ov_pos:, :]
|
||||
# ov_2 = img2[:, :ov_pos, :]
|
||||
# else:
|
||||
# # gray scale
|
||||
# ov_1 = img1[:, -ov_pos:]
|
||||
# ov_2 = img2[:, :ov_pos]
|
||||
# elif dim == 0:
|
||||
# if _dim == 3:
|
||||
# ov_1 = img1[-ov_pos:, :, :]
|
||||
# ov_2 = img2[:ov_pos, :, :]
|
||||
# else:
|
||||
# # gray scale
|
||||
# ov_1 = img1[-ov_pos:, :]
|
||||
# ov_2 = img2[:ov_pos, :]
|
||||
|
||||
return ov_pos, ov_1, ov_2
|
||||
# return ov_pos, ov_1, ov_2
|
||||
|
||||
def _get_feature_stitch_plan(self):
|
||||
if not self.isstitch:
|
||||
return None
|
||||
|
||||
total_tiles = self.nrow * self.ncol
|
||||
if total_tiles <= 1 or len(self.tile_list) < total_tiles:
|
||||
if isinstance(self.index, int):
|
||||
configured_indices = (self.index,)
|
||||
elif isinstance(self.index, np.ndarray):
|
||||
configured_indices = tuple(int(i) for i in self.index.tolist())
|
||||
else:
|
||||
configured_indices = tuple(int(i) for i in self.index)
|
||||
|
||||
total_tiles = len(configured_indices)
|
||||
expected_tiles = self.nrow * self.ncol
|
||||
if total_tiles <= 1 or total_tiles != expected_tiles or len(self.tile_list) < total_tiles:
|
||||
return None
|
||||
|
||||
# Use the center tile as the reference frame for a stable translation-only canvas.
|
||||
ref_idx = (self.nrow // 2) * self.ncol + (self.ncol // 2)
|
||||
if ref_idx >= total_tiles:
|
||||
# Use the configured grid order from self.index and keep the same reference tile
|
||||
# location as the original stitch setup.
|
||||
ref_pos = (self.nrow // 2) * self.ncol + (self.ncol // 2)
|
||||
if ref_pos >= total_tiles:
|
||||
return None
|
||||
|
||||
moving_indices = tuple(i for i in range(total_tiles) if i != ref_idx)
|
||||
moving_tiles = [self.tile_list[i] for i in moving_indices]
|
||||
return ref_idx, self.tile_list[ref_idx], moving_indices, moving_tiles
|
||||
selected_positions = tuple(range(total_tiles))
|
||||
ref_idx = configured_indices[ref_pos]
|
||||
moving_positions = tuple(pos for pos in selected_positions if pos != ref_pos)
|
||||
moving_indices = tuple(configured_indices[pos] for pos in moving_positions)
|
||||
moving_tiles = [self.tile_list[pos] for pos in moving_positions]
|
||||
return ref_idx, self.tile_list[ref_pos], moving_indices, moving_tiles, selected_positions
|
||||
|
||||
def _tile_to_bgr(self, img):
|
||||
if img.ndim == 2:
|
||||
@@ -701,173 +720,173 @@ class Stitcher:
|
||||
|
||||
return tile_list
|
||||
|
||||
def stitch_cal(self):
|
||||
# def stitch_cal(self):
|
||||
|
||||
# init
|
||||
tile_list = self.tile_list
|
||||
img_stitched_height = None
|
||||
stitch_calibration = []
|
||||
# Flip col and row for porting
|
||||
nrow, ncol = self.ncol, self.nrow
|
||||
if not self.isstitch:
|
||||
return self.tile_list[0], None
|
||||
# # init
|
||||
# tile_list = self.tile_list
|
||||
# img_stitched_height = None
|
||||
# stitch_calibration = []
|
||||
# # Flip col and row for porting
|
||||
# nrow, ncol = self.ncol, self.nrow
|
||||
# if not self.isstitch:
|
||||
# return self.tile_list[0], None
|
||||
|
||||
for i in range(ncol):
|
||||
img_stitched_width = None
|
||||
for j in range(nrow):
|
||||
if j == 0:
|
||||
img_j0 = tile_list[i*ncol]
|
||||
img_stitched_width = img_j0
|
||||
else:
|
||||
img_1 = tile_list[i*ncol + j-1]
|
||||
img_2 = tile_list[i*ncol + j]
|
||||
# for i in range(ncol):
|
||||
# img_stitched_width = None
|
||||
# for j in range(nrow):
|
||||
# if j == 0:
|
||||
# img_j0 = tile_list[i*ncol]
|
||||
# img_stitched_width = img_j0
|
||||
# else:
|
||||
# img_1 = tile_list[i*ncol + j-1]
|
||||
# img_2 = tile_list[i*ncol + j]
|
||||
|
||||
ov_pos, ov1, ov2, c_corr = self.__get_ov(img_1.astype(np.float32), img_2.astype(np.float32), dim=1)
|
||||
# ov_pos, ov1, ov2, c_corr = self.__get_ov(img_1.astype(np.float32), img_2.astype(np.float32), dim=1)
|
||||
|
||||
stitch_calibration.append(["hor {}".format(j-1),ov_pos])
|
||||
# stitch_calibration.append(["hor {}".format(j-1),ov_pos])
|
||||
|
||||
# stitch images with stacking
|
||||
img_stitched_width = self.__stitch_optimized(img_stitched_width, img_2, ov_pos, ov1, ov2, blend=True, dim=1)
|
||||
# # stitch images with stacking
|
||||
# img_stitched_width = self.__stitch_optimized(img_stitched_width, img_2, ov_pos, ov1, ov2, blend=True, dim=1)
|
||||
|
||||
if i == 0:
|
||||
img_stitched_height = img_stitched_width
|
||||
stitch_calibration.append(["ver {}".format(i),0])
|
||||
pass
|
||||
else:
|
||||
width = img_stitched_height.shape[1]
|
||||
width_new = img_stitched_width.shape[1]
|
||||
wdiff = width - width_new
|
||||
if wdiff > 0:
|
||||
img_stitched_width = np.hstack((img_stitched_width, img_stitched_width[:, -wdiff:]))
|
||||
elif wdiff < 0:
|
||||
img_stitched_width = img_stitched_width[:, :wdiff]
|
||||
# if i == 0:
|
||||
# img_stitched_height = img_stitched_width
|
||||
# stitch_calibration.append(["ver {}".format(i),0])
|
||||
# pass
|
||||
# else:
|
||||
# width = img_stitched_height.shape[1]
|
||||
# width_new = img_stitched_width.shape[1]
|
||||
# wdiff = width - width_new
|
||||
# if wdiff > 0:
|
||||
# img_stitched_width = np.hstack((img_stitched_width, img_stitched_width[:, -wdiff:]))
|
||||
# elif wdiff < 0:
|
||||
# img_stitched_width = img_stitched_width[:, :wdiff]
|
||||
|
||||
ov_pos_h, ov_h1, ov_h2, c_corr = self.__get_ov(img_stitched_height, img_stitched_width, dim=0)
|
||||
# ov_pos_h, ov_h1, ov_h2, c_corr = self.__get_ov(img_stitched_height, img_stitched_width, dim=0)
|
||||
|
||||
stitch_calibration.append(["ver {}".format(i),ov_pos_h])
|
||||
# stitch_calibration.append(["ver {}".format(i),ov_pos_h])
|
||||
|
||||
img_stitched_height = self.__stitch_optimized(img_stitched_height, img_stitched_width, ov_pos_h, ov_h1, ov_h2, blend=True, dim=0)
|
||||
# img_stitched_height = self.__stitch_optimized(img_stitched_height, img_stitched_width, ov_pos_h, ov_h1, ov_h2, blend=True, dim=0)
|
||||
|
||||
self.stitch_calibration = stitch_calibration
|
||||
# self.stitch_calibration = stitch_calibration
|
||||
|
||||
return img_stitched_height, stitch_calibration
|
||||
# return img_stitched_height, stitch_calibration
|
||||
|
||||
def __stitch_optimized(self, img1, img2, ov_pos, ov1, ov2, blend=True, dim=1):
|
||||
_dim = len(img1.shape)
|
||||
# def __stitch_optimized(self, img1, img2, ov_pos, ov1, ov2, blend=True, dim=1):
|
||||
# _dim = len(img1.shape)
|
||||
|
||||
# Clamp overlap for safety
|
||||
if dim == 1:
|
||||
ov_pos = int(max(0, min(ov_pos, img1.shape[1], img2.shape[1])))
|
||||
else:
|
||||
ov_pos = int(max(0, min(ov_pos, img1.shape[0], img2.shape[0])))
|
||||
# # Clamp overlap for safety
|
||||
# if dim == 1:
|
||||
# ov_pos = int(max(0, min(ov_pos, img1.shape[1], img2.shape[1])))
|
||||
# else:
|
||||
# ov_pos = int(max(0, min(ov_pos, img1.shape[0], img2.shape[0])))
|
||||
|
||||
# Fast blend path with cached coefficient tensors
|
||||
if not blend or ov_pos <= 1:
|
||||
ov = ov1
|
||||
else:
|
||||
blend_coef = self._get_blend_coef(ov_pos, dim, _dim)
|
||||
ov = (
|
||||
ov1.astype(np.float32, copy=False) * blend_coef
|
||||
+ ov2.astype(np.float32, copy=False) * (1.0 - blend_coef)
|
||||
).astype(np.uint8)
|
||||
# # Fast blend path with cached coefficient tensors
|
||||
# if not blend or ov_pos <= 1:
|
||||
# ov = ov1
|
||||
# else:
|
||||
# blend_coef = self._get_blend_coef(ov_pos, dim, _dim)
|
||||
# ov = (
|
||||
# ov1.astype(np.float32, copy=False) * blend_coef
|
||||
# + ov2.astype(np.float32, copy=False) * (1.0 - blend_coef)
|
||||
# ).astype(np.uint8)
|
||||
|
||||
# Build stitched output with one target allocation and slice writes
|
||||
if dim == 1:
|
||||
left_w = img1.shape[1] - ov_pos
|
||||
right_w = img2.shape[1] - ov_pos
|
||||
out_w = left_w + ov_pos + right_w
|
||||
if _dim == 3:
|
||||
stitched_img = np.empty((img1.shape[0], out_w, img1.shape[2]), dtype=img1.dtype)
|
||||
if left_w > 0:
|
||||
stitched_img[:, :left_w, :] = img1[:, :left_w, :]
|
||||
if ov_pos > 0:
|
||||
stitched_img[:, left_w:left_w + ov_pos, :] = ov
|
||||
if right_w > 0:
|
||||
stitched_img[:, left_w + ov_pos:, :] = img2[:, ov_pos:, :]
|
||||
else:
|
||||
stitched_img = np.empty((img1.shape[0], out_w), dtype=img1.dtype)
|
||||
if left_w > 0:
|
||||
stitched_img[:, :left_w] = img1[:, :left_w]
|
||||
if ov_pos > 0:
|
||||
stitched_img[:, left_w:left_w + ov_pos] = ov
|
||||
if right_w > 0:
|
||||
stitched_img[:, left_w + ov_pos:] = img2[:, ov_pos:]
|
||||
else:
|
||||
top_h = img1.shape[0] - ov_pos
|
||||
bottom_h = img2.shape[0] - ov_pos
|
||||
out_h = top_h + ov_pos + bottom_h
|
||||
if _dim == 3:
|
||||
stitched_img = np.empty((out_h, img1.shape[1], img1.shape[2]), dtype=img1.dtype)
|
||||
if top_h > 0:
|
||||
stitched_img[:top_h, :, :] = img1[:top_h, :, :]
|
||||
if ov_pos > 0:
|
||||
stitched_img[top_h:top_h + ov_pos, :, :] = ov
|
||||
if bottom_h > 0:
|
||||
stitched_img[top_h + ov_pos:, :, :] = img2[ov_pos:, :, :]
|
||||
else:
|
||||
stitched_img = np.empty((out_h, img1.shape[1]), dtype=img1.dtype)
|
||||
if top_h > 0:
|
||||
stitched_img[:top_h, :] = img1[:top_h, :]
|
||||
if ov_pos > 0:
|
||||
stitched_img[top_h:top_h + ov_pos, :] = ov
|
||||
if bottom_h > 0:
|
||||
stitched_img[top_h + ov_pos:, :] = img2[ov_pos:, :]
|
||||
# # Build stitched output with one target allocation and slice writes
|
||||
# if dim == 1:
|
||||
# left_w = img1.shape[1] - ov_pos
|
||||
# right_w = img2.shape[1] - ov_pos
|
||||
# out_w = left_w + ov_pos + right_w
|
||||
# if _dim == 3:
|
||||
# stitched_img = np.empty((img1.shape[0], out_w, img1.shape[2]), dtype=img1.dtype)
|
||||
# if left_w > 0:
|
||||
# stitched_img[:, :left_w, :] = img1[:, :left_w, :]
|
||||
# if ov_pos > 0:
|
||||
# stitched_img[:, left_w:left_w + ov_pos, :] = ov
|
||||
# if right_w > 0:
|
||||
# stitched_img[:, left_w + ov_pos:, :] = img2[:, ov_pos:, :]
|
||||
# else:
|
||||
# stitched_img = np.empty((img1.shape[0], out_w), dtype=img1.dtype)
|
||||
# if left_w > 0:
|
||||
# stitched_img[:, :left_w] = img1[:, :left_w]
|
||||
# if ov_pos > 0:
|
||||
# stitched_img[:, left_w:left_w + ov_pos] = ov
|
||||
# if right_w > 0:
|
||||
# stitched_img[:, left_w + ov_pos:] = img2[:, ov_pos:]
|
||||
# else:
|
||||
# top_h = img1.shape[0] - ov_pos
|
||||
# bottom_h = img2.shape[0] - ov_pos
|
||||
# out_h = top_h + ov_pos + bottom_h
|
||||
# if _dim == 3:
|
||||
# stitched_img = np.empty((out_h, img1.shape[1], img1.shape[2]), dtype=img1.dtype)
|
||||
# if top_h > 0:
|
||||
# stitched_img[:top_h, :, :] = img1[:top_h, :, :]
|
||||
# if ov_pos > 0:
|
||||
# stitched_img[top_h:top_h + ov_pos, :, :] = ov
|
||||
# if bottom_h > 0:
|
||||
# stitched_img[top_h + ov_pos:, :, :] = img2[ov_pos:, :, :]
|
||||
# else:
|
||||
# stitched_img = np.empty((out_h, img1.shape[1]), dtype=img1.dtype)
|
||||
# if top_h > 0:
|
||||
# stitched_img[:top_h, :] = img1[:top_h, :]
|
||||
# if ov_pos > 0:
|
||||
# stitched_img[top_h:top_h + ov_pos, :] = ov
|
||||
# if bottom_h > 0:
|
||||
# stitched_img[top_h + ov_pos:, :] = img2[ov_pos:, :]
|
||||
|
||||
return stitched_img
|
||||
# return stitched_img
|
||||
|
||||
def __get_ov(self, img1, img2, dim=1):
|
||||
'''
|
||||
default dimension is along axis 1 (width of the images)
|
||||
'''
|
||||
# pass parameters
|
||||
nominal_ov = self.nominal_ov_pos
|
||||
bound_ov = self.bound
|
||||
# def __get_ov(self, img1, img2, dim=1):
|
||||
# '''
|
||||
# default dimension is along axis 1 (width of the images)
|
||||
# '''
|
||||
# # pass parameters
|
||||
# nominal_ov = self.nominal_ov_pos
|
||||
# bound_ov = self.bound
|
||||
|
||||
cross_corr = []
|
||||
h1, w1 = img1.shape[:2]
|
||||
h2, w2 = img2.shape[:2]
|
||||
# clamp tested overlap to valid range (1..min dimension)
|
||||
max_possible = w1 if dim == 1 else h1
|
||||
max_possible = min(max_possible, w2 if dim == 1 else h2)
|
||||
lo = max(1, nominal_ov - bound_ov)
|
||||
hi = min(max_possible, nominal_ov + bound_ov)
|
||||
ov_space = range(lo, hi + 1)
|
||||
# cross_corr = []
|
||||
# h1, w1 = img1.shape[:2]
|
||||
# h2, w2 = img2.shape[:2]
|
||||
# # clamp tested overlap to valid range (1..min dimension)
|
||||
# max_possible = w1 if dim == 1 else h1
|
||||
# max_possible = min(max_possible, w2 if dim == 1 else h2)
|
||||
# lo = max(1, nominal_ov - bound_ov)
|
||||
# hi = min(max_possible, nominal_ov + bound_ov)
|
||||
# ov_space = range(lo, hi + 1)
|
||||
|
||||
for ov in ov_space:
|
||||
if dim == 1:
|
||||
ov_area1 = img1[:, max(0, w1 - ov):, :]
|
||||
ov_area2 = img2[:, :min(ov, w2), :]
|
||||
elif dim == 0:
|
||||
ov_area1 = img1[max(0, h1 - ov):, :, :]
|
||||
ov_area2 = img2[:min(ov, h2), :, :]
|
||||
# for ov in ov_space:
|
||||
# if dim == 1:
|
||||
# ov_area1 = img1[:, max(0, w1 - ov):, :]
|
||||
# ov_area2 = img2[:, :min(ov, w2), :]
|
||||
# elif dim == 0:
|
||||
# ov_area1 = img1[max(0, h1 - ov):, :, :]
|
||||
# ov_area2 = img2[:min(ov, h2), :, :]
|
||||
|
||||
# require exact shape match (detect_nonblack_regions guarantees uniform tile size)
|
||||
if ov_area1.size == 0 or ov_area2.size == 0 or ov_area1.shape != ov_area2.shape:
|
||||
cross_corr.append(-1.0)
|
||||
continue
|
||||
# # require exact shape match (detect_nonblack_regions guarantees uniform tile size)
|
||||
# if ov_area1.size == 0 or ov_area2.size == 0 or ov_area1.shape != ov_area2.shape:
|
||||
# cross_corr.append(-1.0)
|
||||
# continue
|
||||
|
||||
# multi-channel cross correlation (average over channels)
|
||||
corr = 0.0
|
||||
for i in range(min(3, ov_area1.shape[2])):
|
||||
a = ov_area1[:, :, i].astype(np.float32)
|
||||
b = ov_area2[:, :, i].astype(np.float32)
|
||||
denom = np.sqrt(np.sum(a * a) * np.sum(b * b)) + 1e-12
|
||||
corr += float(np.sum(a * b) / denom)
|
||||
corr = corr / float(min(3, ov_area1.shape[2]))
|
||||
cross_corr.append(corr)
|
||||
# # multi-channel cross correlation (average over channels)
|
||||
# corr = 0.0
|
||||
# for i in range(min(3, ov_area1.shape[2])):
|
||||
# a = ov_area1[:, :, i].astype(np.float32)
|
||||
# b = ov_area2[:, :, i].astype(np.float32)
|
||||
# denom = np.sqrt(np.sum(a * a) * np.sum(b * b)) + 1e-12
|
||||
# corr += float(np.sum(a * b) / denom)
|
||||
# corr = corr / float(min(3, ov_area1.shape[2]))
|
||||
# cross_corr.append(corr)
|
||||
|
||||
# pick best overlap (ignore invalid entries)
|
||||
best_idx = int(np.argmax(cross_corr))
|
||||
ov_pos = list(ov_space)[best_idx]
|
||||
# # pick best overlap (ignore invalid entries)
|
||||
# best_idx = int(np.argmax(cross_corr))
|
||||
# ov_pos = list(ov_space)[best_idx]
|
||||
|
||||
# extract the overlap regions (assume shapes match)
|
||||
if dim == 1:
|
||||
ov_1 = img1[:, max(0, w1 - ov_pos):, :].copy()
|
||||
ov_2 = img2[:, :min(ov_pos, w2), :].copy()
|
||||
else:
|
||||
ov_1 = img1[max(0, h1 - ov_pos):, :, :].copy()
|
||||
ov_2 = img2[:min(ov_pos, h2), :, :].copy()
|
||||
return ov_pos, ov_1, ov_2, cross_corr
|
||||
# # extract the overlap regions (assume shapes match)
|
||||
# if dim == 1:
|
||||
# ov_1 = img1[:, max(0, w1 - ov_pos):, :].copy()
|
||||
# ov_2 = img2[:, :min(ov_pos, w2), :].copy()
|
||||
# else:
|
||||
# ov_1 = img1[max(0, h1 - ov_pos):, :, :].copy()
|
||||
# ov_2 = img2[:min(ov_pos, h2), :, :].copy()
|
||||
# return ov_pos, ov_1, ov_2, cross_corr
|
||||
|
||||
|
||||
# Helper functions for stitching
|
||||
|
||||
+32
-26
@@ -108,7 +108,7 @@ class Sensor:
|
||||
self.compute_contact_valid = False
|
||||
|
||||
# Image Data Variables (public)
|
||||
self.stitch_calibration = []
|
||||
# self.stitch_calibration = []
|
||||
self.frame = self.img = None
|
||||
self.initial_img = None
|
||||
shape = (self.__config_cam["resolution_stitch"][1], self.__config_cam["resolution_stitch"][0])
|
||||
@@ -402,7 +402,9 @@ class Sensor:
|
||||
def reset(self):
|
||||
"""Reset optical flow tracker."""
|
||||
self.__tracker.reset()
|
||||
self.initial_img = self.img.copy()
|
||||
if self.img is not None:
|
||||
self.initial_img = self.img.copy()
|
||||
|
||||
|
||||
def disconnect(self):
|
||||
"""Release resources and stop async threads."""
|
||||
@@ -438,31 +440,31 @@ class Sensor:
|
||||
|
||||
|
||||
|
||||
def stitch_cal(self):
|
||||
"""
|
||||
Load or create stitch calibration.
|
||||
# def stitch_cal(self):
|
||||
# """
|
||||
# Load or create stitch calibration.
|
||||
|
||||
Returns:
|
||||
np.ndarray: Stitched calibration image
|
||||
"""
|
||||
if self.cal_path is None:
|
||||
self.cal_path = "./config/cal.npy"
|
||||
# Returns:
|
||||
# np.ndarray: Stitched calibration image
|
||||
# """
|
||||
# if self.cal_path is None:
|
||||
# self.cal_path = "./config/cal.npy"
|
||||
|
||||
if os.path.exists(self.cal_path):
|
||||
if self.verbose:
|
||||
print("Loading calibration from", self.cal_path)
|
||||
self.stitch_calibration = np.load(self.cal_path, allow_pickle=True)
|
||||
self.stitcher.stitch_calibration = self.stitch_calibration
|
||||
return self.stitcher.stitch_rt_optimized()
|
||||
else:
|
||||
if self.verbose:
|
||||
print("Creating new calibration...")
|
||||
img, self.stitch_calibration = self.stitcher.stitch_cal()
|
||||
os.makedirs("./config", exist_ok=True)
|
||||
np.save(self.cal_path, self.stitch_calibration)
|
||||
if self.verbose:
|
||||
print("Calibration saved to", self.cal_path)
|
||||
return img
|
||||
# if os.path.exists(self.cal_path):
|
||||
# if self.verbose:
|
||||
# print("Loading calibration from", self.cal_path)
|
||||
# self.stitch_calibration = np.load(self.cal_path, allow_pickle=True)
|
||||
# self.stitcher.stitch_calibration = self.stitch_calibration
|
||||
# return self.stitcher.stitch_rt_optimized()
|
||||
# else:
|
||||
# if self.verbose:
|
||||
# print("Creating new calibration...")
|
||||
# img, self.stitch_calibration = self.stitcher.stitch_cal()
|
||||
# os.makedirs("./config", exist_ok=True)
|
||||
# np.save(self.cal_path, self.stitch_calibration)
|
||||
# if self.verbose:
|
||||
# print("Calibration saved to", self.cal_path)
|
||||
# return img
|
||||
|
||||
# =========================================================================
|
||||
# Private - Initialization Helpers
|
||||
@@ -575,7 +577,11 @@ class Sensor:
|
||||
else:
|
||||
self._find_stitch_boxes(frame)
|
||||
self._extract_tiles(frame)
|
||||
img = self.stitch_cal()
|
||||
# Feature-based stitching no longer needs eager overlap calibration here.
|
||||
# Keep stitch_cal() as a fallback so older overlap-based stitching still works.
|
||||
img = self.stitcher.stitch_rt_optimized()
|
||||
# if img is None:
|
||||
# img = self.stitch_cal()
|
||||
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
||||
self.resolution_stitch = img.shape
|
||||
self.initial_img = cv2.resize(img, tuple(self._config_resolution_stitch), interpolation=cv2.INTER_CUBIC)
|
||||
|
||||
@@ -60,6 +60,10 @@ class Tracker:
|
||||
|
||||
# Aggressive optimization for CPU
|
||||
self.__tracking_inst.setFinestScale(config_tracker["dis_mode"]) # Skip finest pyramid level (2x faster)
|
||||
# self.__tracking_inst.setUseSpatialPropagation(False)
|
||||
# self.__tracking_inst.setUseMeanNormalization(False)
|
||||
# self.__tracking_inst.setPatchSize(4) # 8
|
||||
# self.__tracking_inst.setPatchStride(2) # 3
|
||||
|
||||
# else:
|
||||
# # CUDA flow method with stream support
|
||||
|
||||
Reference in New Issue
Block a user