make variables public, vesion variable and minor changes
This commit is contained in:
@@ -5,12 +5,12 @@
|
||||
1. 根据平台和python 版本下载对应的`.whl`文件
|
||||
2. 运行pip命令安装:
|
||||
```bash
|
||||
pip install orisys-{version}-{python}-none-{os}_amd64.whl
|
||||
pip install orisys-{version}-{python-version}-none-{os}_amd64.whl
|
||||
```
|
||||
3. 并测试导入:
|
||||
```python
|
||||
import orisys
|
||||
orisys.__version__
|
||||
print(orisys.__version__)
|
||||
```
|
||||
---
|
||||
## `Sensor` 类别
|
||||
@@ -39,7 +39,7 @@ sensor = Sensor("/data/data.mp4")
|
||||
|
||||
---
|
||||
|
||||
## 2. `get_img` 方法
|
||||
## 2. `Sensor.get_img` 方法
|
||||
|
||||
### 描述
|
||||
|
||||
@@ -59,7 +59,7 @@ if img is None:
|
||||
```
|
||||
---
|
||||
|
||||
## 3. `compute_deformation` 方法
|
||||
## 3. `Sensor.compute_deformation` 方法
|
||||
|
||||
### 描述
|
||||
|
||||
@@ -81,7 +81,7 @@ if img is None:
|
||||
sensor.compute_deformation()
|
||||
```
|
||||
---
|
||||
## 4. `compute_contact` 方法
|
||||
## 4. `Sensor.compute_contact` 方法
|
||||
|
||||
### 描述
|
||||
基于已计算的法向形变场进行接触区域分析,输出接触质心与轮廓等信息,并生成接触强度图。
|
||||
@@ -120,7 +120,7 @@ centroid, contour, depth = sensor.read_info(
|
||||
)
|
||||
```
|
||||
---
|
||||
## 4. `read_info` 方法
|
||||
## 4. `Sensor.read_info` 方法
|
||||
|
||||
### 描述
|
||||
|
||||
@@ -176,7 +176,7 @@ flow = sensor.read_info(sensor.info.VRAW)
|
||||
```
|
||||
---
|
||||
|
||||
## 5. `disconnect` 方法
|
||||
## 5. `Sensor.disconnect` 方法
|
||||
|
||||
### 描述
|
||||
|
||||
@@ -188,21 +188,7 @@ flow = sensor.read_info(sensor.info.VRAW)
|
||||
---
|
||||
# 配置标定相关
|
||||
|
||||
## 1. `export_config` 方法
|
||||
|
||||
### 描述
|
||||
导出当前传感器实例使用的配置参数字典。
|
||||
|
||||
### 输入参数
|
||||
* **config_name**: 需要导出的配置参数字典名字`str`
|
||||
* **path**: 导出的路径,默认`"./config/exported_config.json"`
|
||||
|
||||
### 返回
|
||||
|
||||
* `dict`: 导出的配置参数字典。
|
||||
|
||||
---
|
||||
## 2. `list_config` 方法
|
||||
## 1. `list_config` 方法
|
||||
|
||||
### 描述
|
||||
列出内置传感器配置参数字典。
|
||||
@@ -210,6 +196,23 @@ flow = sensor.read_info(sensor.info.VRAW)
|
||||
### 返回
|
||||
|
||||
* `list`: 内置传感器配置参数字典的名字列表。
|
||||
---
|
||||
## 2. `export_config` 方法
|
||||
|
||||
### 描述
|
||||
导出当前传感器实例使用的配置参数字典。
|
||||
|
||||
### 输入参数
|
||||
* **config_name**: 需要导出的配置参数字典名字,`str`
|
||||
* **path**: 导出的路径,默认`"./config/exported_config.json"`
|
||||
|
||||
### 返回
|
||||
|
||||
* `dict`: 导出的配置参数字典。
|
||||
|
||||
### 保存
|
||||
|
||||
* 导出内置的`config_name`配置参数字典至`path`
|
||||
|
||||
---
|
||||
---
|
||||
|
||||
@@ -11,6 +11,9 @@ import platform
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
# Package version
|
||||
VERSION = "0.2.0"
|
||||
|
||||
# Python versions to build for
|
||||
PYTHON_VERSIONS = ['3.8', '3.9', '3.10']
|
||||
|
||||
@@ -157,7 +160,7 @@ def copy_additional_files():
|
||||
shutil.copytree('config', 'dist_obfuscated/config', dirs_exist_ok=True)
|
||||
|
||||
# Copy README and other metadata files
|
||||
files_to_copy = ['README.md', 'Readme(未完成).md', 'requirements.txt', 'LICENSE']
|
||||
files_to_copy = ['README.md', 'requirements.txt', 'LICENSE']
|
||||
for file in files_to_copy:
|
||||
if os.path.exists(file):
|
||||
print(f"Copying {file}...")
|
||||
@@ -217,7 +220,7 @@ for pkg in packages:
|
||||
|
||||
setup(
|
||||
name="orisys",
|
||||
version="0.2.0",
|
||||
version="{VERSION}",
|
||||
description="Orisys - Optical Tactile Sensor SDK (Protected)",
|
||||
author="Your Name",
|
||||
package_dir={{"": "src"}},
|
||||
@@ -335,7 +338,7 @@ def build_wheel_for_version(python_version):
|
||||
return False
|
||||
|
||||
print(f"✅ Wheel for Python {python_version} on {PLATFORM_NAME} built successfully!")
|
||||
print(f" Expected: orisys-0.2.0-{py_tag}-none-{plat_tag}.whl")
|
||||
print(f" Expected: orisys-{VERSION}-{py_tag}-none-{plat_tag}.whl")
|
||||
return True
|
||||
|
||||
|
||||
@@ -444,7 +447,7 @@ def main():
|
||||
for wheel in Path(f'dist/{PLATFORM_NAME}').glob('*.whl'):
|
||||
print(f" - {wheel.name}")
|
||||
print("\nInstall with:")
|
||||
print(f" pip install dist/{PLATFORM_NAME}/orisys-0.2.0-*.whl")
|
||||
print(f" pip install dist/{PLATFORM_NAME}/orisys-{VERSION}-*.whl")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
+12
-3
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Orisys - Optical Tactile Sensor SDK
|
||||
orisys - Optical Tactile Sensor SDK
|
||||
|
||||
A Python library for processing and analyzing optical tactile sensor data,
|
||||
including image stitching, optical flow tracking, and deformation decomposition.
|
||||
@@ -9,7 +9,6 @@ __version__ = "0.2.0"
|
||||
__author__ = "Orisys"
|
||||
|
||||
from .sensor import Sensor
|
||||
from .MultiSensor import MultiSensor
|
||||
from . import util
|
||||
from . import configs
|
||||
|
||||
@@ -17,4 +16,14 @@ from . import configs
|
||||
list_config = configs.list_config
|
||||
export_config = configs.export_config
|
||||
|
||||
__all__ = ['Sensor', 'MultiSensor', 'util', 'list_config', 'export_config']
|
||||
__all__ = [
|
||||
# class
|
||||
'Sensor',
|
||||
|
||||
# util functions
|
||||
'util',
|
||||
|
||||
# configs utilities functions
|
||||
'list_config',
|
||||
'export_config'
|
||||
]
|
||||
|
||||
@@ -67,6 +67,29 @@ def list_config():
|
||||
config_files = sorted(CONFIG_DIR.glob("*.json"))
|
||||
return [f.stem for f in config_files]
|
||||
|
||||
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.
|
||||
|
||||
Args:
|
||||
config: Configuration dictionary to export
|
||||
path: Output file path (default: "./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:
|
||||
formatted = _format_json_value(config)
|
||||
f.write(formatted)
|
||||
f.write('\n') # Add trailing newline
|
||||
|
||||
print(f"Config name: {config_name} exported to {path}")
|
||||
return config
|
||||
|
||||
def _format_json_value(value, indent_level=0, indent_str=" "):
|
||||
"""
|
||||
Recursively format JSON value with arrays/lists on single line.
|
||||
@@ -96,28 +119,4 @@ def _format_json_value(value, indent_level=0, indent_str=" "):
|
||||
return "[" + ", ".join(items) + "]"
|
||||
else:
|
||||
# Primitive types (string, number, bool, None)
|
||||
return json.dumps(value, ensure_ascii=False)
|
||||
|
||||
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.
|
||||
|
||||
Args:
|
||||
config: Configuration dictionary to export
|
||||
path: Output file path (default: "./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:
|
||||
formatted = _format_json_value(config)
|
||||
f.write(formatted)
|
||||
f.write('\n') # Add trailing newline
|
||||
|
||||
print(f"Config name: {config_name} exported to {path}")
|
||||
return config
|
||||
|
||||
return json.dumps(value, ensure_ascii=False)
|
||||
+58
-58
@@ -92,21 +92,21 @@ class Sensor:
|
||||
self.compute_deformation_valid = False
|
||||
self.compute_contact_valid = False
|
||||
|
||||
# Data Variables
|
||||
self.__img = None
|
||||
self.__img_size_raw = None
|
||||
self.__img_filtered = None
|
||||
self.__flow = None
|
||||
self.__vnormal = None
|
||||
self.__vshear = None
|
||||
self.__n_boxes = None
|
||||
self.__fnormal = self.__fshearx = self.__fsheary = self.__ftor = 0.0
|
||||
# Data Variables (public)
|
||||
self.img = None
|
||||
self.img_size_raw = None
|
||||
self.img_filtered = None
|
||||
self.flow = None
|
||||
self.vnormal = None
|
||||
self.vshear = None
|
||||
self.n_boxes = None
|
||||
self.fnormal = self.fshearx = self.fsheary = self.ftor = 0.0
|
||||
|
||||
# Contact Data Variables
|
||||
self.__centroid = None
|
||||
self.__centroid_found = False
|
||||
self.__contour = None
|
||||
self.__depth_map = None
|
||||
# Contact Data Variables (public)
|
||||
self.centroid = None
|
||||
self.centroid_found = False
|
||||
self.contour = None
|
||||
self.depth_map = None
|
||||
|
||||
# Timing
|
||||
self.last_log_time = time.perf_counter()
|
||||
@@ -180,26 +180,26 @@ class Sensor:
|
||||
img = np.ascontiguousarray(self.__stitcher.stitch_rt_optimized())
|
||||
if log_timing: timings['stitch'] = (time.perf_counter() - t0) * 1000
|
||||
|
||||
self.__img_size_raw = img.shape
|
||||
self.img_size_raw = img.shape
|
||||
# Resize
|
||||
img = cv2.resize(img, tuple(self._config_resolution_stitch), interpolation=cv2.INTER_CUBIC)
|
||||
|
||||
# Temporal filtering (noise reduction)
|
||||
alpha = 0.7
|
||||
if self.__img_filtered is None:
|
||||
self.__img_filtered = img.astype(np.float32)
|
||||
if self.img_filtered is None:
|
||||
self.img_filtered = img.astype(np.float32)
|
||||
else:
|
||||
self.__img_filtered = alpha * img + (1 - alpha) * self.__img_filtered
|
||||
self.img_filtered = alpha * img + (1 - alpha) * self.img_filtered
|
||||
|
||||
self.__img = self.__img_filtered.astype(np.uint8)
|
||||
self.img = self.img_filtered.astype(np.uint8)
|
||||
|
||||
if log_timing:
|
||||
timings['total'] = (time.perf_counter() - t_start) * 1000
|
||||
self._print_timings("get_img", timings)
|
||||
if self.mirror:
|
||||
self.__img = cv2.flip(self.__img, 1)
|
||||
self.img = cv2.flip(self.img, 1)
|
||||
|
||||
return self.__img.copy()
|
||||
return self.img.copy()
|
||||
|
||||
def compute_deformation(self):
|
||||
"""
|
||||
@@ -208,7 +208,7 @@ class Sensor:
|
||||
Returns:
|
||||
np.ndarray: Optical flow field
|
||||
"""
|
||||
if self.__img is None:
|
||||
if self.img is None:
|
||||
raise Exception("Warning: Get image first")
|
||||
|
||||
self.__compute_flow()
|
||||
@@ -220,17 +220,17 @@ class Sensor:
|
||||
return True
|
||||
|
||||
def __compute_flow(self):
|
||||
if self.__img is None:
|
||||
if self.img is None:
|
||||
raise Exception("Warning: Get image first")
|
||||
|
||||
flow = self.__tracker.track(self.__img)
|
||||
flow = self.__tracker.track(self.img)
|
||||
if hasattr(flow, 'download'):
|
||||
flow = flow.download()
|
||||
self.__flow = flow
|
||||
self.flow = flow
|
||||
|
||||
def __compute_hhd(self):
|
||||
|
||||
flow = self.__flow
|
||||
flow = self.flow
|
||||
if flow is None:
|
||||
raise Exception("Warning: Compute flow first")
|
||||
|
||||
@@ -243,11 +243,11 @@ class Sensor:
|
||||
_, forces = self.__hhd.get_force_gpu_v2(flow)
|
||||
else:
|
||||
_, forces = self.__hhd.get_force(flow)
|
||||
self.__fnormal, self.__fshearx, self.__fsheary, self.__ftor = forces
|
||||
self.fnormal, self.fshearx, self.fsheary, self.ftor = forces
|
||||
|
||||
# Get components
|
||||
self.__vnormal = self.__hhd.get_normal()
|
||||
self.__vshear = self.__hhd.get_shear()
|
||||
self.vnormal = self.__hhd.get_normal()
|
||||
self.vshear = self.__hhd.get_shear()
|
||||
|
||||
# Update state
|
||||
self.compute_deformation_valid = True
|
||||
@@ -259,7 +259,7 @@ class Sensor:
|
||||
Compute contact information from vnormal field.
|
||||
|
||||
Args:
|
||||
vnormal: Optional vnormal field. If None, uses self.__vnormal.
|
||||
vnormal: Optional vnormal field. If None, uses self.vnormal.
|
||||
|
||||
Returns:
|
||||
dict: Contact result with centroid, contour, and depth_map
|
||||
@@ -267,7 +267,7 @@ class Sensor:
|
||||
multiple_foe = False
|
||||
|
||||
# Use provided vnormal or fall back to internal state
|
||||
vnormal_to_use = vnormal if vnormal is not None else self.__vnormal
|
||||
vnormal_to_use = vnormal if vnormal is not None else self.vnormal
|
||||
|
||||
if vnormal_to_use is None:
|
||||
raise Exception("Warning: vnormal is required for contact computation")
|
||||
@@ -280,11 +280,11 @@ class Sensor:
|
||||
else:
|
||||
result = self.__contact3d.compute_foe(vnormal_to_use)
|
||||
|
||||
self.__centroid = result['centroid']
|
||||
self.__centroid_found = result['centroid_found']
|
||||
self.centroid = result['centroid']
|
||||
self.centroid_found = result['centroid_found']
|
||||
if 'contour' in result:
|
||||
self.__contour = result['contour']
|
||||
self.__depth_map = result['divergence_map']
|
||||
self.contour = result['contour']
|
||||
self.depth_map = result['divergence_map']
|
||||
|
||||
self.compute_contact_valid = True
|
||||
return result
|
||||
@@ -301,7 +301,7 @@ class Sensor:
|
||||
Tuple: Requested data in order
|
||||
"""
|
||||
if any(arg in [self.info.IMG] for arg in args):
|
||||
if self.__img is None:
|
||||
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):
|
||||
@@ -314,26 +314,26 @@ class Sensor:
|
||||
|
||||
mapping = {
|
||||
# Return copies of arrays to prevent external modification of internal state
|
||||
self.info.VRAW: lambda: self.__flow.copy() if self.__flow is not None else np.array([]),
|
||||
self.info.VNORMAL: lambda: self.__vnormal.copy() if self.__vnormal is not None else np.array([]),
|
||||
self.info.VSHEAR: lambda: self.__vshear.copy() if self.__vshear is not None else np.array([]),
|
||||
self.info.VRAW: lambda: self.flow.copy() if self.flow is not None else np.array([]),
|
||||
self.info.VNORMAL: lambda: self.vnormal.copy() if self.vnormal is not None else np.array([]),
|
||||
self.info.VSHEAR: lambda: self.vshear.copy() if self.vshear is not None else np.array([]),
|
||||
# Scalar values are safe to return directly
|
||||
self.info.FNORMAL: lambda: self.__fnormal,
|
||||
self.info.FSHEARX: lambda: self.__fshearx,
|
||||
self.info.FSHEARY: lambda: self.__fsheary,
|
||||
self.info.FNORMAL: lambda: self.fnormal,
|
||||
self.info.FSHEARX: lambda: self.fshearx,
|
||||
self.info.FSHEARY: lambda: self.fsheary,
|
||||
self.info.FPS: lambda: self._fps,
|
||||
self.info.TIMESTAMP: lambda: time.strftime('%H:%M:%S'),
|
||||
# Return copies of arrays to prevent external modification
|
||||
self.info.DEPTH: lambda: self.__depth_map.copy() if self.__depth_map is not None else np.array([]),
|
||||
self.info.DEPTH: lambda: self.depth_map.copy() if self.depth_map is not None else np.array([]),
|
||||
# Centroid: can be tuple, list, or array - return a copy to prevent modification
|
||||
# Return None if centroid is not found or empty
|
||||
self.info.CENTROID: lambda: (
|
||||
np.array(self.__centroid).copy()
|
||||
if self.__centroid is not None and len(self.__centroid) > 0
|
||||
np.array(self.centroid).copy()
|
||||
if self.centroid is not None and len(self.centroid) > 0
|
||||
else None
|
||||
),
|
||||
self.info.CONTOUR: lambda: self.__contour.copy() if self.__contour is not None else np.array([]),
|
||||
self.info.IMG: lambda: self.__img.copy() if self.__img is not None else np.array([]),
|
||||
self.info.CONTOUR: lambda: self.contour.copy() if self.contour is not None else np.array([]),
|
||||
self.info.IMG: lambda: self.img.copy() if self.img is not None else np.array([]),
|
||||
}
|
||||
|
||||
return tuple(mapping.get(arg, lambda: None)() for arg in args if isinstance(arg, self.info))
|
||||
@@ -475,7 +475,7 @@ class Sensor:
|
||||
break
|
||||
v_mean0 = v_mean
|
||||
|
||||
self.__n_boxes = n_boxes
|
||||
self.n_boxes = n_boxes
|
||||
self.stitch_valid = self.isstitch and n_boxes == self._config_units
|
||||
|
||||
status = "valid for stitching" if self.stitch_valid else "non-stitch mode"
|
||||
@@ -607,9 +607,9 @@ class Sensor:
|
||||
# continue
|
||||
|
||||
# # Put the processed image in the queue
|
||||
# # get_img() updates self.__img, so we use that
|
||||
# if self.__img is not None:
|
||||
# self._queue_put(self._capture_queue, self.__img.copy())
|
||||
# # get_img() updates self.img, so we use that
|
||||
# if self.img is not None:
|
||||
# self._queue_put(self._capture_queue, self.img.copy())
|
||||
# except Exception as e:
|
||||
# if self.verbose: print(f"Capture error: {e}")
|
||||
|
||||
@@ -619,9 +619,9 @@ class Sensor:
|
||||
# try:
|
||||
# img = self._capture_queue.get(timeout=0.1)
|
||||
# # Set __img to reuse __compute_flow()
|
||||
# self.__img = img
|
||||
# self.img = img
|
||||
# self.__compute_flow()
|
||||
# flow = self.__flow.copy() if self.__flow is not None else None
|
||||
# flow = self.flow.copy() if self.flow is not None else None
|
||||
# self._queue_put(self._flow_queue, (img, flow))
|
||||
# except Empty:
|
||||
# continue
|
||||
@@ -635,16 +635,16 @@ class Sensor:
|
||||
# img, flow = self._flow_queue.get(timeout=0.1)
|
||||
|
||||
# # Set __flow to reuse __compute_hhd()
|
||||
# self.__flow = flow
|
||||
# self.flow = flow
|
||||
# self.__compute_hhd()
|
||||
# # Get results from internal variables set by __compute_hhd()
|
||||
# result = {
|
||||
# 'img': img, 'flow': flow,
|
||||
# 'normal': self.__vnormal.copy() if self.__vnormal is not None else None,
|
||||
# 'shear': self.__vshear.copy() if self.__vshear is not None else None,
|
||||
# 'normal': self.vnormal.copy() if self.vnormal is not None else None,
|
||||
# 'shear': self.vshear.copy() if self.vshear is not None else None,
|
||||
# # 'depth': getFilteredDensity(flow, use_cuda=self.cuda),
|
||||
# 'fnormal': self.__fnormal, 'fshearx': self.__fshearx,
|
||||
# 'fsheary': self.__fsheary, 'ftor': self.__ftor,
|
||||
# 'fnormal': self.fnormal, 'fshearx': self.fshearx,
|
||||
# 'fsheary': self.fsheary, 'ftor': self.ftor,
|
||||
# }
|
||||
|
||||
# # Always include contact fields to ensure consistent result structure
|
||||
|
||||
@@ -103,3 +103,4 @@ def draw_contact(image, contour, centroid, point_color=(0, 255, 0), region_color
|
||||
cv2.circle(image, (cx, cy), 10, point_color, 2)
|
||||
|
||||
return image
|
||||
|
||||
|
||||
Reference in New Issue
Block a user