diff --git a/Readme.md b/Readme.md index dbda6df..f22f13f 100644 --- a/Readme.md +++ b/Readme.md @@ -18,14 +18,14 @@ https://anaconda.org/channels/nvidia/packages/cuda/overview conda install nvidia/label/cuda-12.9.0::cuda-toolkit nvidia::cudnn ``` ## SDK安装方法 -1. 根据平台(win / linux)和python (如p38、p39、p310)版本下载对应的`.whl`文件 +1. 根据系统(win / linux),平台(amd64, aarch64), python(如p38、p39、p310) 版本下载对应的`.whl`文件 2. 运行pip命令安装: ```bash -pip install orisys-{version}-{python-version}-{python-version}-{os}_amd64.whl +pip install orisys-{version}-{python-version}-{python-version}-{os}_{machine}.whl ``` 如 ```bash -pip install orisys-0.2.0-cp310-cp310-win_amd64.whl +pip install orisys-0.2.3-cp310-cp310-win_amd64.whl ``` 3. **安装 CuPy(根据您的 CUDA 版本选择):** @@ -184,6 +184,8 @@ centroid, contour, depth = sensor.read_info( * FSHEARX: X切向力,`float`。 * FSHEARY: Y切向力,`float`。 * FPS: 输出帧率,`float`。 +* CENTROID: 触碰点的坐标,(x, y)。 +* CONTOUR: 触碰区域轮廓,`np.ndarray`, shape=(H, W), 未生成时返回`np.empty` 时间戳 * TIMESTAMP: 时间戳字符串(HH:MM:SS),`str`。 @@ -198,7 +200,7 @@ centroid, contour, depth = sensor.read_info( 返回值数量与有效参数数量一致。 - 向量/图像类数据:`np.ndarray` - - 若当前流程中尚未生成该数据,返回空数组 `np.array([])` + - 若当前流程中尚未生成该数据,返回空数组 - 标量类数据:`float` - 时间戳:`str`(`TIMESTAMP`,格式为 `HH:MM:SS`) @@ -279,4 +281,7 @@ A:`read_info()`按传入的`sensor.info`参数顺序返回一个元组。 ### Q5:如果没有标定文件,或拼接结果不正确,会发生什么? A:若未指定或未找到标定文件,系统会在需要时创建新的拼接标定数据并保存到默认路径。 -拼接结果异常通常与标定数据、拼接配置或输入图像质量有关,建议重新生成拼接标定并检查配置参数。 \ No newline at end of file +拼接结果异常通常与标定数据、拼接配置或输入图像质量有关,建议重新生成拼接标定并检查配置参数。 + +### Q6: 运行报错:“AttributeError: 'Stitcher' object has no attribute 'pos_default'” +A: 请检查视频源的编号是否正确,以及是否能正常打开视频源。在linux 使用`v4l2-ctl --list-devices` 列出设备 \ No newline at end of file diff --git a/build_nuitka.py b/build_nuitka.py index 6455576..bbb0a6e 100644 --- a/build_nuitka.py +++ b/build_nuitka.py @@ -16,7 +16,7 @@ def install_nuitka(): def build(): install_nuitka() - version = "0.2.0" + version = "0.2.3" src_dir = Path("src/orisys") output_dir = Path("build_nuitka") dist_dir = Path("dist_nuitka") diff --git a/build_nuitka_linux.py b/build_nuitka_linux.py index b5b86e1..e9d7043 100644 --- a/build_nuitka_linux.py +++ b/build_nuitka_linux.py @@ -16,7 +16,7 @@ def install_nuitka(): def build(): install_nuitka() - version = "0.2.0" + version = "0.2.3" src_dir = Path("src/orisys") output_dir = Path("build_nuitka") dist_dir = Path("dist_nuitka") diff --git a/calibration_data_collection.py b/calibration_data_collection.py index 845b140..bc26b96 100644 --- a/calibration_data_collection.py +++ b/calibration_data_collection.py @@ -18,7 +18,7 @@ def main(): print("\n") print(f"Collecting data for device ID: {device_id} on test type: {test_type}, video source: {vid_source}") print("\n") - sensor = orisys.Sensor(vid_source, config_name="./config/prototype_of3.json", cal_path= f"./config/{sample_name}.npy", isstitch=True, cuda=True, verbose=True) + sensor = orisys.Sensor(vid_source, config_name="./config/prototype_of3.json", cal_path= f"./config/{sample_name}.npy", cuda=False, verbose=True) print(f"\n按 'q' 退出程序,按 'r' 重置光流追踪器,按 's' 开始保存,按 'e' 停止保存,按 'j' 减小力,按 'l' 增大力") force = 0 # kgf @@ -60,7 +60,7 @@ def main(): sensor.compute_deformation() sensor.compute_contact() - fps, flow, vnormal, img, contour, centroid, depth_map,fnormal,fshearx,fsheary = sensor.read_info( + fps, flow, vnormal, img, contour, centroid, depth_map = sensor.read_info( sensor.info.FPS, sensor.info.VRAW, sensor.info.VNORMAL, @@ -145,8 +145,6 @@ def main(): print(f"Stop writing, total frames: {frame_count}") frame_count = 0 - if key == ord("p"): - print(f"normal force:{fnormal}, X {fshearx}, Y{fsheary}") if key == ord("i"): save_mode_index += 1 if save_mode_index >= len(save_mode): diff --git a/config/compound_eye.png b/config/compound_eye.png index e067c53..3cfe523 100644 Binary files a/config/compound_eye.png and b/config/compound_eye.png differ diff --git a/config/prototype_of1.json b/config/prototype_of1.json deleted file mode 100644 index 0c895a0..0000000 --- a/config/prototype_of1.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "prototype", - "version": "1.0", - "camera": { - "resolution_raw": [1920, 1080], - "fps": 60, - "units": 15, - "units_used": 9, - "resolution_stitch": [400, 400] - }, - "stitcher": { - "min_area_frac": 0.018, - "max_area_frac": 0.035, - "max_aspect_ratio": 100, - "index": [1,2,3,6,7,8,11,12,13], - "margin": 10, - "nominal_ov_pos": 100, - "n": [3,3] - }, - "tracker": { - "adaptive": true, - "lower_threshold": 12.0, - "upper_threshold": 18.0, - "loop_closure_interval": 1, - "dis_preset": "medium", - "finest_scale": 1 - }, - "contact": { - "min_contour_area": 3000, - "flow_threshold":1, - "div_threshold":0.02, - "expansion_threshold": 0.01, - "kernel_size": [5,5], - "denoise_sigma": 15 - } -} - - diff --git a/config/prototype_of2.json b/config/prototype_of2.json deleted file mode 100644 index 9b6b3d5..0000000 --- a/config/prototype_of2.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "prototype", - "version": "1.0", - "camera": { - "resolution_raw": [1920, 1080], - "fps": 60, - "units": 15, - "units_used": 9, - "resolution_stitch": [400, 400] - }, - "stitcher": { - "min_area_frac": 0.018, - "max_area_frac": 0.035, - "max_aspect_ratio": 100, - "index": [1,2,3,6,7,8,11,12,13], - "margin": 10, - "nominal_ov_pos": 100, - "n": [3,3] - }, - "tracker": { - "adaptive": true, - "lower_threshold": 12.0, - "upper_threshold": 18.0, - "loop_closure_interval": 1, - "dis_preset": "medium", - "finest_scale": 2 - }, - "contact": { - "min_contour_area": 3000, - "flow_threshold":1, - "div_threshold":0.02, - "expansion_threshold": 0.01, - "kernel_size": [5,5], - "denoise_sigma": 15 - } -} - - diff --git a/config/prototype_of3.json b/config/prototype_of3.json index 12d5082..ac2758c 100644 --- a/config/prototype_of3.json +++ b/config/prototype_of3.json @@ -3,7 +3,7 @@ "version": "1.0", "camera": { "resolution_raw": [1920, 1080], - "fps": 60, + "fps": 30, "units": 15, "units_used": 9, "resolution_stitch": [400, 400] @@ -15,7 +15,8 @@ "index": [1,2,3,6,7,8,11,12,13], "margin": 10, "nominal_ov_pos": 100, - "n": [3,3] + "n": [3,3], + "row_tol": 0.6 }, "tracker": { "adaptive": true, @@ -23,7 +24,7 @@ "upper_threshold": 18.0, "loop_closure_interval": 1, "dis_preset": "medium", - "finest_scale": 3 + "of_mode": 3 }, "contact": { "min_contour_area": 3000, diff --git a/example1_basic_pipeline.py b/example1_basic_pipeline.py index 88898d4..e15440d 100644 --- a/example1_basic_pipeline.py +++ b/example1_basic_pipeline.py @@ -14,16 +14,17 @@ os.environ["OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS"] = "0" import cv2 import orisys import numpy as np -import time -def main(): - - # 步骤1:创建传感器对象 (必要步骤),config_name为配置文件名(内置配置文件名)或路径(自定义配置文件路径) - # 方式1: 从视频文件读取 - # sensor = orisys.Sensor("s1.mp4", config_name="./config/prototype_of3.json", cal_path= "./config/cal.npy", isstitch=True, cuda=True, verbose=True) - +def main(): + # 步骤1:创建传感器对象 (必要步骤), + # config_name为配置文件名(内置配置文件名)或路径(自定义配置文件路径) + # cal_path为校准文件路径, 每一个设备需要创建/使用一个校准文件 + + # 方式1: 从视频文件读取 + sensor = orisys.Sensor("s1.mp4", config_name="./config/prototype_of3.json", cal_path= "./config/dd01a_0.npy", isstitch=True, cuda=False, verbose=True) + # 方式2: 从摄像头读取(取消注释使用) - sensor = orisys.Sensor(0, config_name="./config/prototype_of3.json", cal_path= "./config/cal.npy", isstitch=True, cuda=True, verbose=True) + # sensor = orisys.Sensor(0, config_name="./config/prototype_of3.json", cal_path= "./config/cal.npy", isstitch=True, cuda=True, verbose=True) print("\n按 'q' 退出程序,按 'r' 重置光流追踪器") diff --git a/nuitka-crash-report.xml b/nuitka-crash-report.xml new file mode 100644 index 0000000..2864272 --- /dev/null +++ b/nuitka-crash-report.xml @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/patch_generation/grayscale_from_cmyk.png b/patch_generation/grayscale_from_cmyk.png deleted file mode 100644 index 41c794d..0000000 Binary files a/patch_generation/grayscale_from_cmyk.png and /dev/null differ diff --git a/patch_generation/grayscale_from_color.png b/patch_generation/grayscale_from_color.png deleted file mode 100644 index e10481a..0000000 Binary files a/patch_generation/grayscale_from_color.png and /dev/null differ diff --git a/patch_generation/optimized_grayscale.png b/patch_generation/optimized_grayscale.png deleted file mode 100644 index 69642d5..0000000 Binary files a/patch_generation/optimized_grayscale.png and /dev/null differ diff --git a/patch_generation/raw_1.jpg b/patch_generation/raw_1.jpg new file mode 100644 index 0000000..24bfbdd Binary files /dev/null and b/patch_generation/raw_1.jpg differ diff --git a/patch_generation/red_pattern.png b/patch_generation/red_pattern.png new file mode 100644 index 0000000..eaeab93 Binary files /dev/null and b/patch_generation/red_pattern.png differ diff --git a/patch_generation/s1.jpg b/patch_generation/s1.jpg new file mode 100644 index 0000000..5b6cce0 Binary files /dev/null and b/patch_generation/s1.jpg differ diff --git a/patch_generation/yellow_pattern.png b/patch_generation/yellow_pattern.png new file mode 100644 index 0000000..6dc0e91 Binary files /dev/null and b/patch_generation/yellow_pattern.png differ diff --git a/setup.py b/setup.py index c52c9f2..2d3402d 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ if configs_dir.exists(): setup( name="orisys", - version="0.2.0", + version="0.2.3", description="Orisys - Optical Tactile Sensor SDK", author="Your Name", package_dir={"": "src"}, @@ -21,7 +21,6 @@ setup( install_requires=[ "numpy>=1.24.4", "opencv-python>=4.10.0.84", - "opencv-contrib-python", "scipy>=1.10.1", ], extras_require={ diff --git a/src/orisys/HHD.py b/src/orisys/HHD.py index 91b2b70..ca326ea 100644 --- a/src/orisys/HHD.py +++ b/src/orisys/HHD.py @@ -36,12 +36,13 @@ try: CUPY_AVAILABLE = True # Try to import CuPy streams for parallel execution - try: - from cupy.cuda import Stream - STREAMS_AVAILABLE = True - except ImportError: - Stream = None - STREAMS_AVAILABLE = False + if CUPY_AVAILABLE: + try: + from cupy.cuda import Stream + STREAMS_AVAILABLE = True + except ImportError: + Stream = None + STREAMS_AVAILABLE = False # Create fused elementwise kernel for torque computation # Computes cross products and combines them in a single kernel pass @@ -103,9 +104,9 @@ class nHHD(object): raise ValueError("nHHD Solver works only for 2 and 3 dimensions") self.psolver.prepare(False) - - self.stream1 = cp.cuda.Stream() - self.stream2 = cp.cuda.Stream() + if CUPY_AVAILABLE: + self.stream1 = cp.cuda.Stream() + self.stream2 = cp.cuda.Stream() # create the 3-component decomposition def decompose(self, vfield, verbose=0): diff --git a/src/orisys/Stitcher.py b/src/orisys/Stitcher.py index 6867466..e1a9403 100644 --- a/src/orisys/Stitcher.py +++ b/src/orisys/Stitcher.py @@ -27,6 +27,7 @@ class Stitcher: self.nominal_ov_pos=config_stitcher["nominal_ov_pos"] self.margin=config_stitcher["margin"] self.bound=self.nominal_ov_pos - self.margin + self.row_tol = config_stitcher["row_tol"] if self.verbose: print(f"Stitcher initialized with config: {self.config_stitcher}") @@ -110,7 +111,7 @@ class Stitcher: def find_stitch_boxes(self,img_src,show = False): img_corr = self.__correct_edge_bloom(img_src) - self.__detect_nonblack_regions(img_corr,show = show) + self.__detect_nonblack_regions(img_corr,show = show, row_tol = self.row_tol) return len(self.boxes), self.boxes # return self.boxes @@ -431,7 +432,7 @@ class Stitcher: return pos_list # Computation Functions - def __detect_nonblack_regions(self, img, morph_k=3, n_boxes_target=15, bk_thresh_step=10, bk_thresh_max=100,show = False): + def __detect_nonblack_regions(self, img, morph_k=3, n_boxes_target=15, bk_thresh_step=10, bk_thresh_max=100,show = False, row_tol = 0.4): """ Return binary mask of non-black regions and list of bounding boxes (x,y,w,h). Uses adaptive thresholding on grayscale + small morphology to be robust to illumination. @@ -509,7 +510,7 @@ class Stitcher: heights = [b[3] - b[1] for b in cropped_boxes] median_height = np.median(heights) if heights else target_h # Use a fraction of the box height as tolerance (e.g., 20% of box height) - row_tolerance = max(1, int(median_height * 0.2)) + row_tolerance = max(1, int(median_height * row_tol)) # Quantize y-coordinates to group boxes into rows def get_row_key(box): diff --git a/src/orisys/__init__.py b/src/orisys/__init__.py index a3a6ffb..12003ea 100644 --- a/src/orisys/__init__.py +++ b/src/orisys/__init__.py @@ -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.0" +__version__ = "0.2.3" __author__ = "Orisys" from .sensor import Sensor diff --git a/src/orisys/configs/prototype.json b/src/orisys/configs/prototype.json index f7b841b..b4aecb7 100644 --- a/src/orisys/configs/prototype.json +++ b/src/orisys/configs/prototype.json @@ -3,7 +3,7 @@ "version": "1.0", "camera": { "resolution_raw": [1920, 1080], - "fps": 60, + "fps": 30, "units": 15, "units_used": 9, "resolution_stitch": [400, 400] @@ -15,7 +15,8 @@ "index": [1,2,3,6,7,8,11,12,13], "margin": 10, "nominal_ov_pos": 100, - "n": [3,3] + "n": [3,3], + "row_tol": 0.6 }, "tracker": { "adaptive": true, @@ -23,7 +24,7 @@ "upper_threshold": 18.0, "loop_closure_interval": 1, "dis_preset": "medium", - "finest_scale": 1 + "finest_scale": 3 }, "contact": { "min_contour_area": 3000, diff --git a/src/orisys/tracker.py b/src/orisys/tracker.py index e8face2..0e67140 100644 --- a/src/orisys/tracker.py +++ b/src/orisys/tracker.py @@ -35,7 +35,7 @@ class Tracker: # Aggressive optimization for CPU - self.__tracking_inst.setFinestScale(config_tracker["finest_scale"]) # Skip finest pyramid level (2x faster) + self.__tracking_inst.setFinestScale(config_tracker["of_mode"]) # Skip finest pyramid level (2x faster) # self.__tracking_inst.setPatchSize(8) # Smaller patches (default 8) # self.__tracking_inst.setPatchStride(4) # Larger stride = fewer patches # self.__tracking_inst.setGradientDescentIterations(12) # Reduce iterations (default 25) @@ -213,7 +213,6 @@ class Tracker: error = self.__getForwardBackwardError( self.__fine_base_frame_queue[i], frame, f) if error < self.__lower_threshold: - # print("loop closed at frame ", str(i)) self.__fine_base_frame_queue = self.__fine_base_frame_queue[: i + 1]