diff --git a/Readme.md b/Readme.md index 74812e6..d67c343 100644 --- a/Readme.md +++ b/Readme.md @@ -1,26 +1,36 @@ -# OrisysSDK API 文档 +# orisys API 文档 -本文件提供了 OrisysSDK 的各类方法 +本文件提供了 `orisys` 套件的中文文档 ## 使用方法 -pip install . +1. 根据平台和python 版本下载对应的`.whl`文件 +2. 运行pip命令安装: +```bash +pip install orisys-{version}-{python}-none-{os}_amd64.whl +``` +3. 并测试导入: +```python +import orisys +orisys.__version__ +``` --- ## `Sensor` 类别 ### 描述 -创建一个传感器实例 +`orisys`套件的核心类,用于从视频文件或实时摄像头中读取传感器图像,创建一个具体的传感器对象(实例)。并完成形变、光流及力信息的计算与管理。 ### 输入参数 -* **cam_id** (`int | str`, 可选): 传感器 ID、序列号或视频路径。默认为 `0`。 +* **vid_src** (`int | str`, 必填): 传感器 ID、相机编号或视频路径。默认为 `0`。 * **isstitch** (`bool`,可选): 是否启用图像拼接,默认为 `True`。 * **cuda** (`bool`,可选): 是否使用 GPU cuda 加速,默认为 `False`。 +* **verbose** (`bool`,可选): 是否输出初始化及运行过程中的详细日志信息,默认为 `False`。 +* **config_name** (`str`,可选): 内部算法或传感器配置名称,用于加载对应的参数配置,默认为 `prototype`。 ### 返回 -* `Sensor` 对象 +* `Sensor` 实例,用于后续图像采集、形变计算和数据读取操作。 ### 示例 ```python # Example 1: 用相机编号创建 from orisys import Sensor -from src.sensor import Sensor sensor = Sensor(0) # Example 2: 用视频文件创建 @@ -33,59 +43,196 @@ sensor = Sensor("/data/data.mp4") ### 描述 -从传感器获取图像,进行复眼图像提取和拼接。 +采集并预处理一帧图像。 ### 返回 +* `np.ndarray`:处理后的灰度图(`uint8`,尺寸为`config_resolution_stitch`对应分辨率)。 +* `None`:当输入源读取失败(例如视频结束、摄像头断开)时返回。 +### 状态更新 +* 成功时更新`sensor`类里面的灰度图。 +### 示例 -* 提取(`issitch = False`)或拼接后(`issitch = True`)的图像 - +```python +img = sensor.get_img() +if img is None: + break +``` --- ## 3. `compute_deformation` 方法 ### 描述 -计算传感器变形 +基于当前帧图像计算光流/形变向量场,并通过 HHD 分解将形变场分解为法向/切向分量,同时估计法向力、切向力与扭矩等标量输出。该方法会更新 +`sensor`的形变场与力相关状态,并返回当前帧的光流场。 +调用前置条件:需要先通过`get_img()`成功更新图像。 +### 返回 + +无 + +### 示例 + +```python +img = sensor.get_img() +if img is None: + break + +sensor.compute_deformation() +``` +--- +## 4. `compute_contact` 方法 + +### 描述 +基于已计算的法向形变场进行接触区域分析,输出接触质心与轮廓等信息,并生成接触强度图。 + +调用前置条件:需先执行`compute_deformation()`。 + +### 输入参数 +* **min_contour_area** (`int`,可选): 多接触点模式下的最小轮廓面积阈值,默认为 `3000`。 +* **min_magnitude** (`float`,可选): 最小光流幅度阈值,低于该阈值的区域不参与接触分析,默认为 `1.0`。 +* **expansion_threshold** (`float`,可选): 扩张/收缩判定阈值,用于接触区域分割,默认为 `0.01`。 +* **downsample_factor** (`int`,可选): 下采样因子,用于平滑与加速计算,默认为 `1`。 +* **kernel_size** (`tuple`,可选): 形态学操作核大小,默认为 `(5, 5)`。 +* **denoise_sigma** (`float`,可选): 高斯降噪强度;设为` 0 `可禁用降噪,默认为 `15`。 + +### 返回 +`dict`为接触分析结果字典,至少包含以下键: +* **expansion_mask**: 扩张区域掩码(`np.ndarray`)。 +* **contraction_mask**: 收缩区域掩码(`np.ndarray`)。 +* **divergence_map**: 散度图(`np.ndarray`)。 +* **centroid**: 质心坐标(单点模式为 `(x, y)`;多点模式为列表)。 +* **centroid_found**: 是否检测到接触区域(`bool`)。 +* **contour**: 最大轮廓或轮廓列表(可选字段,存在时返回)。 + +### 状态更新 + +* **sensor.centroid**: 接触质心(单点为 `(x, y)`,多点为列表)。 +* **sensor.centroid_found**: 是否找到质心(`bool`)。 + +### 示例 +```python +sensor.compute_contact() +centroid, contour, depth = sensor.read_info( + sensor.info.CENTROID, + sensor.info.CONTOUR, + sensor.info.DEPTH, +) +``` --- ## 4. `read_info` 方法 ### 描述 -获取指定类型的传感器数据,返回数量和顺序与输入参数一致。 +按指定的信息类型(`sensor.info`枚举值)批量读取传感器状态数据,并按传入顺序返回对应值的元组。 +该方法通常用于在完成形变计算后,读取光流场、分量场、力、帧率等信息。 ### 输入参数 * **args**: 任意数量的 `Sensor.info` 枚举,用于指定需要获取的数据类型。支持的枚举值及对应数据如下: - * VRAW: Optional[np.ndarray] # 原始光流场, shape=(400, 400, 2) - * VNORMAL: Optional[np.ndarray] # 法向光流场, shape=(400, 400, 2) - * VSHEAR: Optional[np.ndarray] # 切向光流场, shape=(400, 400,2) - * DEPTH: Optional[np.ndarray] # 深度场, shape=(400, 400,1) +### 支持的枚 info keys: +向量/图像类 +* VRAW: 原始光流场, `np.ndarray`,shape=(H, W, 2) +* VNORMAL: 法向光流场, `np.ndarray`,shape=(H, W, 2) +* VSHEAR: 切向光流场, `np.ndarray`,shape=(H, W,2) +* DEPTH: 深度场(可选), 未启用/未生成时返回空数组`np.array([])`。 - * FNORMAL: Optional[np.float64] # 法相力信息 - * FSHEARX: Optional[np.float64] # 横向切向力分量 - * FSHEARY: Optional[np.float64] # 纵向切向力分量 +标量类 +* FNORMAL: 法向力,`float`。 +* FSHEARX: X切向力,`float`。 +* FSHEARY: Y切向力,`float`。 +* FPS: 输出帧率,`float`。 - * FPS: Optional[float] # 输出帧率 - * TIMESTAMP: Optional[str] # 时间戳 +时间戳 +* TIMESTAMP: 时间戳字符串(HH:MM:SS),`str`。 - * TimeStamp: Optional[float] # 传感器时间戳,单位s +注:H/W 由当前配置分辨率决定。 ### 返回 -* 所请求的传感器数据(返回数量和顺序与输入参数一致) +`Tuple` + +按传入的 `sensor.info` 参数顺序返回一个元组, +返回值数量与有效参数数量一致。 + +- 向量/图像类数据:`np.ndarray` + - 若当前流程中尚未生成该数据,返回空数组 `np.array([])` +- 标量类数据:`float` +- 时间戳:`str`(`TIMESTAMP`,格式为 `HH:MM:SS`) + +### 示例 +```python +# Example 1:读取标量数据 +fps, fn, fx, fy = sensor.read_info( + sensor.info.FPS, + sensor.info.FNORMAL, + sensor.info.FSHEARX, + sensor.info.FSHEARY, +) + +# Example 2: 读取单个向量场(注意解包方式) +flow = sensor.read_info(sensor.info.VRAW) +``` --- ## 5. `disconnect` 方法 ### 描述 -断开及释放资源。 +释放传感器相关资源并停止后台异步线程。 调用后将关闭视频/摄像头输入源,并销毁所有 OpenCV 窗口。 + +建议在程序退出前调用以确保资源被正确释放。 --- +--- +# 配置标定相关 +## 1. `export_config` 方法 + +### 描述 +导出当前传感器实例使用的配置参数字典。 + +### 输入参数 +* **config_name**: 需要导出的配置参数字典名字`str` +* **path**: 导出的路径,默认`"./config/exported_config.json"` + +### 返回 + +* `dict`: 导出的配置参数字典。 + +--- +## 2. `list_config` 方法 + +### 描述 +列出内置传感器配置参数字典。 + +### 返回 + +* `list`: 内置传感器配置参数字典的名字列表。 + +--- --- ## 常见问题解答 (FAQ) -TBC \ No newline at end of file +### Q1:为什么必须先调用`get_img()`,再调用`compute_deformation()`? +A:`compute_deformation()`依赖最近一次获取并拼接的图像数据进行形变与力计算。 +在调用该方法前,必须先通过`get_img()`更新当前帧图像,否则无法进行有效计算。 + +### Q2:为什么调用了`read_info()`,但读到的数据是空的? +A:`read_info()`仅用于读取当前已计算的数据状态,不会触发任何计算。 +部分字段依赖特定计算步骤生成,例如: +* `VRAW / VNORMAL / VSHEAR`依赖`compute_deformation()`; +* `DEPTH / CENTROID / CONTOUR`依赖`compute_contact()`。 + +### Q3:`compute_contact()`为一定要调用吗?什么时候需要? +A:当需要接触相关信息(如接触质心、轮廓或深度/散度图)时,必须调用`compute_contact()`。 +若仅关注形变场或力信息(如光流、法向力、切向力),则无需调用该方法。 + +### Q4:`read_info()`的返回值顺序是如何确定的? +A:`read_info()`按传入的`sensor.info`参数顺序返回一个元组。 +返回值顺序与参数顺序一一对应,与内部存储顺序无关。 + +### Q5:如果没有标定文件,或拼接结果不正确,会发生什么? +A:若未指定或未找到标定文件,系统会在需要时创建新的拼接标定数据并保存到默认路径。 +拼接结果异常通常与标定数据、拼接配置或输入图像质量有关,建议重新生成拼接标定并检查配置参数。 \ No newline at end of file diff --git a/config/compound_eye.png b/config/compound_eye.png index e324d1a..ef3b17b 100644 Binary files a/config/compound_eye.png and b/config/compound_eye.png differ diff --git a/example1_basic_pipeline.py b/example1_basic_pipeline.py index a9d5378..c588dc3 100644 --- a/example1_basic_pipeline.py +++ b/example1_basic_pipeline.py @@ -23,7 +23,6 @@ def main(): # 方式2: 从摄像头读取(取消注释使用) sensor = orisys.Sensor(0, cuda=True, verbose=True, config_name="prototype") - sensor.export_config(path="./config/exported_config.json") # 导出配置文件供查看和修改 print("\n按 'q' 退出程序,按 'r' 重置光流追踪器") diff --git a/src/orisys/__init__.py b/src/orisys/__init__.py index edfbfca..202c791 100644 --- a/src/orisys/__init__.py +++ b/src/orisys/__init__.py @@ -11,5 +11,10 @@ __author__ = "Orisys" from .sensor import Sensor from .MultiSensor import MultiSensor from . import util +from . import configs -__all__ = ['Sensor', 'MultiSensor', 'util'] +# Re-export list_configs as list_config for convenience +list_config = configs.list_config +export_config = configs.export_config + +__all__ = ['Sensor', 'MultiSensor', 'util', 'list_config', 'export_config'] diff --git a/src/orisys/configs/__init__.py b/src/orisys/configs/__init__.py index b4f5381..b89735e 100644 --- a/src/orisys/configs/__init__.py +++ b/src/orisys/configs/__init__.py @@ -56,7 +56,7 @@ def load_config(config_name_or_path): with open(package_config_path, 'r', encoding='utf-8') as f: return json.load(f) -def list_configs(): +def list_config(): """ List all available JSON configuration files in the configs directory. @@ -98,7 +98,7 @@ def _format_json_value(value, indent_level=0, indent_str=" "): # Primitive types (string, number, bool, None) return json.dumps(value, ensure_ascii=False) -def export_config(config, path="./config/exported_config.json"): +def export_config(config_name = None, path="./config/exported_config.json"): """ Export configuration to JSON file with readable formatting. Arrays/lists are kept on a single line, while nested objects are indented. @@ -110,6 +110,7 @@ def export_config(config, path="./config/exported_config.json"): Returns: The exported config dictionary """ + config = load_config(config_name) Path(path).parent.mkdir(parents=True, exist_ok=True) with open(path, 'w', encoding='utf-8') as f: @@ -117,5 +118,6 @@ def export_config(config, path="./config/exported_config.json"): f.write(formatted) f.write('\n') # Add trailing newline + print(f"Config name: {config_name} exported to {path}") return config \ No newline at end of file diff --git a/src/orisys/sensor.py b/src/orisys/sensor.py index 828445c..5a22a0a 100644 --- a/src/orisys/sensor.py +++ b/src/orisys/sensor.py @@ -21,7 +21,6 @@ from .nHHD import nHHD from . import configs from .get_depth import Contact3D - class Sensor: """ Optical tactile sensor interface with deformation analysis. @@ -200,7 +199,7 @@ class Sensor: if self.mirror: self.__img = cv2.flip(self.__img, 1) - return True + return self.__img.copy() def compute_deformation(self): """ @@ -228,7 +227,6 @@ class Sensor: if hasattr(flow, 'download'): flow = flow.download() self.__flow = flow - return True def __compute_hhd(self): @@ -358,12 +356,14 @@ class Sensor: # Public API - Configuration & Calibration # ========================================================================= - def export_config(self, path="./config/exported_config.json"): - """Return current configuration dict.""" - configs.export_config(self._config, path) - if self.verbose: - print(f"Config exported to {path}") - return True + # def export_config(self, path="./config/exported_config.json"): + # """Return current configuration dict.""" + # configs.export_config(self._config, path) + # if self.verbose: + # print(f"Config exported to {path}") + # return True + + def stitch_cal(self): """