update doc

This commit is contained in:
ykcheungab
2026-03-06 15:05:24 +08:00
parent c95e810e87
commit 44b14cded1
20 changed files with 359 additions and 178 deletions
+19 -6
View File
@@ -91,12 +91,18 @@ class nHHD(object):
if self.poisson_backend == 'cpu' or self.poisson_backend == 'opencl':
return self.h
elif self.poisson_backend == 'cupy':
# Use CPU copy from get_force_cuda when set (stable snapshot; avoids jumps)
if hasattr(self, 'h') and self.h is not None:
return self.h
return cp.asnumpy(self._h_gpu)
def get_normal(self):
if self.poisson_backend == 'cpu' or self.poisson_backend == 'opencl':
return self.d
elif self.poisson_backend == 'cupy':
# Use CPU copy from get_force_cuda when set (stable snapshot; avoids jumps)
if hasattr(self, 'd') and self.d is not None:
return self.d
return cp.asnumpy(self._d_gpu)
def _solve_poisson_backend(self, field, verbose=False):
@@ -249,12 +255,11 @@ class nHHD(object):
print('div =', div_gpu.shape, float(div_gpu.min()), float(div_gpu.max()))
print('curlw =', curlw_gpu.shape, float(curlw_gpu.min()), float(curlw_gpu.max()))
with self.stream1:
nD_gpu = self.psolver._solve_gpu_internal(div_gpu)
with self.stream2:
nRu_gpu = self.psolver._solve_gpu_internal(curlw_gpu)
self.stream1.synchronize()
self.stream2.synchronize()
# Use a single stream for both solves. Running concurrent FFT-based
# solves on separate streams can cause occasional long-run jitter.
nD_gpu = self.psolver._solve_gpu_internal(div_gpu)
nRu_gpu = self.psolver._solve_gpu_internal(curlw_gpu)
cp.cuda.get_current_stream().synchronize()
if verbose > 0:
print('nD =', nD_gpu.shape, float(nD_gpu.min()), float(nD_gpu.max()))
@@ -282,6 +287,9 @@ class nHHD(object):
if verbose > 0:
print('h =', h_gpu.shape, float(h_gpu.min()), float(h_gpu.max()))
# Ensure all default-stream work (gradient, etc.) is complete before use
cp.cuda.get_current_stream().synchronize()
# Store GPU arrays
self._d_gpu = d_gpu
self._h_gpu = h_gpu
@@ -467,6 +475,11 @@ class nHHD(object):
self.Sn, self.Stx, self.Sty, self.Stor = Sn, Stx, Sty, Stor
output = numpy.array([Sn, Stx, Sty, Stor], dtype=numpy.float32)
# Keep stable CPU copies for get_normal()/get_shear() so callers get this frame's
# result instead of reading GPU memory that can change (avoids CuPy-only jumps)
self.d = cp.asnumpy(self._d_gpu)
self.h = cp.asnumpy(self._h_gpu)
flag = (Sn >= 300 and Stx >= 300 and Sty >= 300 and abs(Stor) >= 1000)
+1 -1
View File
@@ -5,7 +5,7 @@ A Python library for processing and analyzing optical tactile sensor data,
including image stitching, optical flow tracking, and deformation decomposition.
"""
__version__ = "0.2.3"
__version__ = "0.3.0"
__author__ = "Orisys"
from .sensor import Sensor
+37 -21
View File
@@ -60,7 +60,7 @@ class Sensor:
# =========================================================================
def __init__(self, vid_src, cuda=True, isstitch=True, verbose=True, debug=False,
config_name="prototype", cal_path=None, override=False, box=None, mirror=True, poisson_backend='auto'):
config_name="prototype", cal_path=None, override=False, box=None, mirror=True, backend='auto'):
"""
Initialize Sensor with video source and configuration.
@@ -73,6 +73,11 @@ class Sensor:
cal_path: Path to calibration file (default: None)
override: Skip auto-detection, use provided box (default: False)
box: Manual bounding boxes when override=True (default: None)
backend: Poisson backend (default: "auto")
"auto": Automatically select the best backend based on availability
"cupy": Use CuPy for GPU acceleration
"opencl": Use OpenCL for GPU acceleration
"cpu": Use CPU for computation
"""
self.CUPY_AVAILABLE = check_cupy_available()
@@ -112,8 +117,9 @@ class Sensor:
# Optical Flow Data Variables (public)
self.flow = np.zeros(shape + (2,), dtype=np.float32)
self.vnormal = np.zeros_like(self.flow)
self.vshear = np.zeros_like(self.flow)
self.vtemplate = np.zeros_like(self.flow)
self.vnormal = self.vtemplate
self.vshear = self.vtemplate
self.n_boxes = None
self.fnormal = self.fshearx = self.fsheary = self.ftor = 0.0
@@ -134,7 +140,7 @@ class Sensor:
self.__tracker = Tracker(adaptive=True, cuda=cuda, verbose=self.verbose, config_tracker=self.__config_tracker)
self.__contact3d = Contact3D(config_contact=self.__config_contact, verbose=self.verbose)
self.__hhd = None
self.poisson_backend = poisson_backend
self.poisson_backend = backend
self._use_pyav = False
self._av_container = None
self._av_stream = None
@@ -211,15 +217,16 @@ class Sensor:
self._fps = 1.0 / (time.perf_counter() - self.last_log_time)
self.last_log_time = time.perf_counter()
return None
return self.img.copy()
def detect_motion(self, threshold=4):
if self.initial_img is None:
return False
diff = cv2.absdiff(self.img, self.initial_img)
return diff.mean() > threshold
def detect_motion(self, threshold=6):
def compute_deformation(self, decompose=True):
# detect motion by optical flow magnitude
mag = np.linalg.norm(self.flow, axis=1)
# print(f"mag: {mag.max()}, {mag.min()}, {mag.mean()}")
return mag.mean() > threshold
def compute_deformation(self, decompose=True, check_motion=True, threshold=6):
"""
Compute optical flow and decompose into force components.
@@ -230,10 +237,19 @@ class Sensor:
raise Exception("Warning: Get image first")
self.compute_flow()
if decompose:
self.compute_hhd()
# self.compute_hhd()
if check_motion:
if self.detect_motion(threshold=threshold):
self.compute_hhd()
return True
else:
return False
else:
if decompose:
self.compute_hhd()
return True
return None
# return True
def compute_flow(self):
if self.img is None:
@@ -326,14 +342,14 @@ class Sensor:
if self.img is None:
raise Exception("Warning: Get image first")
if any(arg in [self.info.VRAW, self.info.VNORMAL, self.info.VSHEAR, self.info.FNORMAL, self.info.FSHEARX, self.info.FSHEARY] for arg in args):
if not self.compute_deformation_valid:
print("Warning: Compute deformation first")
# raise Exception("Warning: Compute deformation first")
# if any(arg in [self.info.VRAW, self.info.VNORMAL, self.info.VSHEAR, self.info.FNORMAL, self.info.FSHEARX, self.info.FSHEARY] for arg in args):
# if not self.compute_deformation_valid:
# print("Warning: Compute deformation first")
# # raise Exception("Warning: Compute deformation first")
if any(arg in [self.info.CENTROID, self.info.CONTOUR, self.info.DEPTH] for arg in args):
if not self.compute_contact_valid:
print("Warning: Compute contact first")
# if any(arg in [self.info.CENTROID, self.info.CONTOUR, self.info.DEPTH] for arg in args):
# if not self.compute_contact_valid:
# print("Warning: Compute contact first")
# raise Exception("Warning: Compute contact first")
mapping = {