update doc
This commit is contained in:
+19
-6
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user