diff --git a/.gitignore b/.gitignore index 4ba26778e1c9c1981ccb5631dada0d05965f533b..8b04bde97a79723a1fd61ac23b33e0219de4c07f 100644 --- a/.gitignore +++ b/.gitignore @@ -28,9 +28,9 @@ LZ-*.py # Picture -*.png -*.jpg + # Zip *.tar *.zip +third_party/ \ No newline at end of file diff --git a/Cpp_example/A01_capture/CMakeLists.txt b/Cpp_example/A01_capture/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..6f010a112ed4192393315438be4f700dc5b0d58d --- /dev/null +++ b/Cpp_example/A01_capture/CMakeLists.txt @@ -0,0 +1,33 @@ +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test_capture) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 配置摄像头数据 +add_executable(Test-Capture test_capture.cc) +target_include_directories(Test-Capture PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-Capture PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-Capture + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/A01_capture/README.md b/Cpp_example/A01_capture/README.md new file mode 100755 index 0000000000000000000000000000000000000000..6a5098eebe612fa9ea3f1d79abf12e05e1377007 --- /dev/null +++ b/Cpp_example/A01_capture/README.md @@ -0,0 +1,344 @@ +# 摄像头模块使用指南 + +## 章节说明 +本章节主要演示如何使用LockAI进行视频流的读取,同时使用Edit模块进行图像传输。 + +--- + +## 1. 基础知识讲解 + +### 1.1 OpenCV简介 +OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉库,提供丰富的图像处理和视频捕获功能。通过其`VideoCapture`类,开发者可以轻松调用摄像头设备并获取视频流。 + +### 1.2 VideoCapture模块 +`cv::VideoCapture`是OpenCV中用于管理视频输入的核心类,支持从摄像头、视频文件或网络流读取帧。常用功能包括: +- 设备初始化与参数设置(分辨率、帧率) +- 逐帧捕获图像 +- 资源释放管理 + +--- + +## 2. API文档 + +### 2.1 cv::VideoCapture类 +#### 2.1.1 cv::VideoCapture类依赖头文件 +```cpp +#include +``` +#### 2.1.2 初始化摄像头 +```cpp +cv::VideoCapture cap; +``` +- **功能**:创建摄像头管理对象 +- **说明**:该对象用于后续所有摄像头操作,未调用`open()`前不占用硬件资源 + +#### 2.1.3 设置摄像头分辨率 +```cpp +cap.set(cv::CAP_PROP_FRAME_WIDTH, width); +cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); +``` +- **参数**: + - `cv::CAP_PROP_FRAME_WIDTH`: 帧宽度(像素) + - `cv::CAP_PROP_FRAME_HEIGHT`: 帧高度(像素) + +**分辨率对照表**:根据摄像头的分辨率和帧率,选择合适的分辨率和帧率。以下为常见分辨率与帧率对照表 +| 摄像头分辨率(4:3) | FPS | +|---------------------|-----| +| 480x360 | 25 | +| 640x480 | 25 | +| 960x720 | 14 | +| 1280x960 | 13 | +| 1920x1440 | 13 | + +| 摄像头分辨率(16:9) | FPS | +|----------------------|-----| +| 480x270 | 25 | +| 640x360 | 25 | +| 960x540 | 25 | +| 1280x720 | 15 | +| 1920x1080 | 12 | + +#### 2.1.4 打开摄像头设备 +```cpp +cap.open(0); +``` +- **参数**:0表示默认摄像头设备,也可以指定其他设备编号 +- **返回值**:成功打开返回`true`,否则返回`false` + +#### 2.1.5 读取视频帧 +```cpp +cap >> frame; +``` +- **说明**:读取下一帧图像,如果当前帧为空,则返回`false` + +--- + +### 2.2 lockzhiner_vision_module::edit::Edit类 + +#### 2.2.1 依赖头文件 +```cpp +#include +``` + +#### 2.2.2 初始化模块 +```cpp +Edit edit; +``` +- **说明**:创建Edit对象,用于后续图像传输操作 + +#### 2.2.3 建立连接 +```cpp +edit.StartAndAcceptConnection(); +``` +- **参数**:无 +- **返回值**:成功建立连接返回`true`,否则返回`false` + +#### 2.2.4 图像传输 +```cpp +edit.Print(frame); +``` +- **参数**:`cv::Mat`对象,表示图像帧 +- **返回值**:无 + +--- + +## 3. 综合代码解析 + +### 3.1 基础摄像头读取 + +#### 3.1.1 流程图 + + + +#### 3.1.2 代码解析 +- 初始化摄像头 +```cpp +cv::VideoCapture cap; +const int width = 640; +const int height = 480; +cap.set(cv::CAP_PROP_FRAME_WIDTH, width); +cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); +``` +- 逐帧捕获图像 +```cpp +while (true) { + cv::Mat frame; + cap >> frame; + if (frame.empty()) { + std::cerr << "Warning: Couldn't read a frame from the camera." + << std::endl; + continue; + } +} +``` + +#### 3.1.3 完整代码实现 + +```cpp +#include +#include + +int main() { + cv::VideoCapture cap; + cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); + + cap.open(0); // 参数0表示默认摄像头设备 + if (!cap.isOpened()) { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + while (true) { + cv::Mat frame; + cap >> frame; + if (frame.empty()) { + std::cerr << "Warning: Couldn't read a frame from the camera." + << std::endl; + continue; + } + } + + cap.release(); + return 0; +} +``` + +--- + +### 3.2 摄像头图像传输 + +#### 3.2.1 流程图 + + + +#### 3.2.2 代码解析 +- 初始化摄像头和Edit模块 +```cpp +cv::VideoCapture cap; +const int width = 640; +const int height = 480; +cap.set(cv::CAP_PROP_FRAME_WIDTH, width); +cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); + +lockzhiner_vision_module::edit::Edit edit; +``` +- 建立连接 +```cpp +if (!edit.StartAndAcceptConnection()) { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; +} +``` +- 逐帧捕获图像并传输 +```cpp +while (true) { + cv::Mat frame; + cap >> frame; + if (frame.empty()) { + std::cerr << "Warning: Couldn't read a frame from the camera." + << std::endl; + continue; + } + edit.Print(frame); +} +``` +#### 3.2.3 完整代码实现 +```cpp +#include +#include +#include + +int main() +{ + // 初始化 edit 模块 + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + + // 初始化摄像头 + cv::VideoCapture cap; + int width = 640; // 设置摄像头分辨率宽度 + int height = 480; // 设置摄像头分辨率高度 + cap.set(cv::CAP_PROP_FRAME_WIDTH, width); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); + + // 打开摄像头设备 + cap.open(0); // 参数 0 表示默认摄像头设备 + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + // 主循环:读取摄像头帧并传递给 edit 模块 + while (true) + { + cv::Mat frame; // 存储每一帧图像 + cap >> frame; // 获取新的一帧 + + // 检查是否成功读取帧 + if (frame.empty()) + { + std::cerr << "Warning: Couldn't read a frame from the camera." + << std::endl; + continue; + } + + // 使用 edit 模块处理帧 + edit.Print(frame); + } + + // 释放摄像头资源 + cap.release(); + return 0; +} +``` + +--- + +## 4. 编译过程 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时以正确连接开发板。 +### 4.2 Cmake介绍 +```cmake +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test_capture) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 配置摄像头数据 +add_executable(Test-Capture test_capture.cc) +target_include_directories(Test-Capture PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-Capture PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-Capture + RUNTIME DESTINATION . +) +``` +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/A01_capture +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` + +在执行完上述命令后,会在build目录下生成可执行文件。 + +--- +## 5. 例程运行示例 +### 5.1 准备工作 +- 下载凌智视觉模块图片传输助手:[点击下载](https://gitee.com/LockzhinerAI/LockzhinerVisionModule/releases/download/v0.0.0/LockzhinerVisionModuleImageFetcher.exe) +### 5.2 运行过程 +在凌智视觉模块中输入以下命令: +```shell +chmod 777 Test_Capture +./Test_Capture +``` +### 5.3 运行效果 +![示例图片](./images/image_1.png) + +--- + +## 6. 总结 +本文档介绍了如何使用 LockAI 和 OpenCV 实现摄像头模块的视频流读取与图像传输。核心步骤包括: +- 初始化摄像头并设置分辨率; +- 打开摄像头并逐帧捕获图像; +- 使用 Edit 模块进行图像传输。 + +**注意事项**: +1. 推荐使用 `640x480` 分辨率以平衡性能和画质; +2. 确保 Edit 模块连接成功后再进行图像传输; \ No newline at end of file diff --git a/Cpp_example/A01_capture/images/1.png b/Cpp_example/A01_capture/images/1.png new file mode 100755 index 0000000000000000000000000000000000000000..34b0ea3f16c89a760d76057c2e14c35b876a2727 Binary files /dev/null and b/Cpp_example/A01_capture/images/1.png differ diff --git a/Cpp_example/A01_capture/images/2.png b/Cpp_example/A01_capture/images/2.png new file mode 100755 index 0000000000000000000000000000000000000000..b198b46d200b138154217405cdb3da4731df5e35 Binary files /dev/null and b/Cpp_example/A01_capture/images/2.png differ diff --git a/Cpp_example/A01_capture/images/image_1.png b/Cpp_example/A01_capture/images/image_1.png new file mode 100755 index 0000000000000000000000000000000000000000..fa14e50723cb19d0d4f4788fb86314b808f8d87b Binary files /dev/null and b/Cpp_example/A01_capture/images/image_1.png differ diff --git a/Cpp_example/A01_capture/test_capture.cc b/Cpp_example/A01_capture/test_capture.cc new file mode 100755 index 0000000000000000000000000000000000000000..42aad825c1d8a6b16ea20fa881548fb77b93e2de --- /dev/null +++ b/Cpp_example/A01_capture/test_capture.cc @@ -0,0 +1,52 @@ +#include +#include +#include + +int main() +{ + // 初始化 edit 模块 + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + + // 初始化摄像头 + cv::VideoCapture cap; + int width = 640; // 设置摄像头分辨率宽度 + int height = 480; // 设置摄像头分辨率高度 + cap.set(cv::CAP_PROP_FRAME_WIDTH, width); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); + + // 打开摄像头设备 + cap.open(0); // 参数 0 表示默认摄像头设备 + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + // 主循环:读取摄像头帧并传递给 edit 模块 + while (true) + { + cv::Mat frame; // 存储每一帧图像 + cap >> frame; // 获取新的一帧 + + // 检查是否成功读取帧 + if (frame.empty()) + { + std::cerr << "Warning: Couldn't read a frame from the camera." + << std::endl; + continue; + } + + // 使用 edit 模块处理帧 + edit.Print(frame); + } + + // 释放摄像头资源 + cap.release(); + return 0; +} \ No newline at end of file diff --git a/Cpp_example/A02_GPIO/CMakeLists.txt b/Cpp_example/A02_GPIO/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..9099af51fca67ef05cbcdd1042fadd7c82fd86f2 --- /dev/null +++ b/Cpp_example/A02_GPIO/CMakeLists.txt @@ -0,0 +1,34 @@ +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test_gpio) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 配置 GPIO 输出 Demo +add_executable(Test-GPIO-Write GPIO_Write.cc) +target_include_directories(Test-GPIO-Write PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-GPIO-Write PRIVATE ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +# 配置 GPIO 读取 Demo +add_executable(Test-GPIO-Read GPIO_Read.cc) +target_include_directories(Test-GPIO-Read PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-GPIO-Read PRIVATE ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-GPIO-Read + TARGETS Test-GPIO-Write + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/A02_GPIO/GPIO_Read.cc b/Cpp_example/A02_GPIO/GPIO_Read.cc new file mode 100755 index 0000000000000000000000000000000000000000..7716c93854ee137aafeae43be04b4a2903f8de43 --- /dev/null +++ b/Cpp_example/A02_GPIO/GPIO_Read.cc @@ -0,0 +1,25 @@ +#include + +#include +#include + +int main() +{ + lockzhiner_vision_module::periphery::GPIO0A0 gpio; + + if (!gpio.Config(lockzhiner_vision_module::periphery::GPIOMode::IN)) + { + std::cout << "Failed to config gpio mode" << std::endl; + return 1; + } + + lockzhiner_vision_module::periphery::GPIOState state; + if (!gpio.Read(state)) + { + std::cout << "Failed to read gpio mode" << std::endl; + return 1; + } + + std::cout << "state is " << static_cast(state) << std::endl; + return 0; +} \ No newline at end of file diff --git a/Cpp_example/A02_GPIO/GPIO_Write.cc b/Cpp_example/A02_GPIO/GPIO_Write.cc new file mode 100755 index 0000000000000000000000000000000000000000..dea2b4b82dd8ca043408b6900008b3a9eebca70a --- /dev/null +++ b/Cpp_example/A02_GPIO/GPIO_Write.cc @@ -0,0 +1,30 @@ +#include + +#include +#include + +int main() { + lockzhiner_vision_module::periphery::GPIO0A0 gpio; + + if (!gpio.Config(lockzhiner_vision_module::periphery::GPIOMode::OUT)) { + std::cout << "Failed to config gpio mode" << std::endl; + return 1; + } + + if (!gpio.Write(lockzhiner_vision_module::periphery::GPIOState::HIGH)) { + std::cout << "Failed to config gpio mode" << std::endl; + return 1; + } + + for (int i = 0; i < 10; i++) { + std::cout << "Wait: " << i << "/" << 10 << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + if (!gpio.Write(lockzhiner_vision_module::periphery::GPIOState::LOW)) { + std::cout << "Failed to config gpio mode" << std::endl; + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/Cpp_example/A02_GPIO/README.md b/Cpp_example/A02_GPIO/README.md new file mode 100755 index 0000000000000000000000000000000000000000..dc7ec88f05e8d1d704e954333de2dc1fe0273c3c --- /dev/null +++ b/Cpp_example/A02_GPIO/README.md @@ -0,0 +1,316 @@ +# GPIO使用指南 +在外设的使用过程中,最基本的就是GPIO的使用。在本章节,我们将介绍GPIO的使用。 + +--- + +## 1. GPIO基本知识 +### 1.1 什么是GPIO +GPIO(General Purpose Input/Output,通用输入输出)是微控制器或处理器上的一种引脚,可以由用户配置为输入模式或输出模式,用于与外部设备进行交互。 +- 输入模式:GPIO引脚可以读取外部信号的状态(高电平或低电平)。 +- 输出模式:GPIO引脚可以输出高电平或低电平信号,驱动外部设备。 +### 1.2 GPIO的工作模式 +GPIO通常支持以下两种工作模式: +- 输入模式:用于检测外部信号的状态,例如按键输入。 +- 输出模式:用于控制外部设备,例如点亮LED灯。 +### 1.3 GPIO的状态 +GPIO的状态可以是以下三种之一: +- 低电平(LOW):表示逻辑0,通常对应于0V。 +- 高电平(HIGH):表示逻辑1,通常对应于供电电压(例如3.3V或5V)。 +- 错误状态(ERROR):表示GPIO操作失败。 + +--- + +## 2.API文档 + +### 2.1 头文件 +```c++ +#include +``` +### 2.2 GPIOMode枚举类型 +```c++ +enum class GPIOMode { + IN, ///< 输入模式(信号检测) + OUT ///< 输出模式(设备驱动) +}; +``` +- 作用:定义GPIO引脚的工作模式 +- 成员说明: + - IN:表示输入模式。 + - OUT:表示输出模式。 +### 2.3 GPIOState枚举类型 +```c++ +enum class GPIOState { + LOW = 0, ///< 低电平(0V) + HIGH = 1, ///< 高电平(3.3V/5V) + ERROR = 2 ///< 操作异常状态 +}; +``` +- 作用:定义GPIO引脚的状态。 +- 成员说明: + - LOW:表示低电平(0V)。 + - HIGH:表示高电平(3.3V/5V)。 + - ERROR:操作异常状态 +### 2.4 GPIOBase类 +#### 2.4.1 头文件 +```cpp +#include +``` +#### 2.4.1 配置GPIO工作模式 +```c++ +bool config(GPIOMode mode); +``` +- 参数: + - mode:GPIO的工作模式,可以是GPIOMode::IN或GPIOMode::OUT。 +- 返回值: + - true:表示配置成功。 + - false:表示配置失败。 +#### 2.4.2 设置GPIO输出状态 +```c++ +bool Write(GPIOState state); +``` +- 参数: + - state:GPIO的状态,可以是GPIOState::LOW或GPIOState::HIGH。 +- 返回值: + - true:表示设置成功。 + - false':表示设置失败。 +#### 2.4.3 读取GPIO状态 +```c++ +bool Read(GPIOState& state); +``` +- 参数: + - state:GPIO的状态,用于存储读取到的状态。 +- 返回值: + - true:表示读取成功。 + - false:表示读取失败。 +### 2.5 GPIO硬件映射 +```c++ +/*---- GPIO0系列 ----*/ +using GPIO0A0 = GPIOBase<0, 'A', 0>; ///< 端口0A的第0脚 + +/*---- GPIO1系列 ----*/ +using GPIO1C7 = GPIOBase<1, 'C', 7>; ///< 端口1C的第7脚 + +/*---- GPIO2系列 ----*/ +using GPIO2A0 = GPIOBase<2, 'A', 0>; ///< 端口2A的第0脚 +using GPIO2A1 = GPIOBase<2, 'A', 1>; ///< 端口2A的第1脚 +``` +- 说明: + - GPIO0A0: 表示端口组0,端口A的第0脚 + - GPIO1C7: 表示端口组1,端口C的第7脚 + - GPIO2A0: 表示端口组2,端口A的第0脚 + +--- + +## 3. 综合代码解析 +### 3.1 GPIO输入模式使用示例 +#### 3.1.1 流程图 + + + +#### 3.1.2 代码解析 +- 创建GPIO0A0对象gpio +```c++ + lockzhiner_vision_module::periphery::GPIO0A0 gpio; +``` +- 配置GPIOState变量state +```c++ +lockzhiner_vision_module::periphery::GPIOState state; +``` +- 配置GPIO为输入模式 +```c++ +if (!gpio.Config(lockzhiner_vision_module::periphery::GPIOMode::IN)) { + std::cout << "Failed to config gpio mode" << std::endl; + return 1; + } +``` +- 读取GPIO状态,如果无法读取的话打印 Failed to read gpio mode。 +```c++ +if (!gpio.Read(state)) { + std::cout << "Failed to read gpio mode" << std::endl; + return 1; +} +``` +- 输出结果 +```c++ +td::cout << "state is " << static_cast(state) << std::endl; +``` +#### 3.1.2 代码实现 +```c++ +#include + +#include +#include + +int main() +{ + lockzhiner_vision_module::periphery::GPIO0A0 gpio; + + if (!gpio.Config(lockzhiner_vision_module::periphery::GPIOMode::IN)) + { + std::cout << "Failed to config gpio mode" << std::endl; + return 1; + } + + lockzhiner_vision_module::periphery::GPIOState state; + if (!gpio.Read(state)) + { + std::cout << "Failed to read gpio mode" << std::endl; + return 1; + } + + std::cout << "state is " << static_cast(state) << std::endl; + return 0; +} +``` +### 3.2 GPIO输出模式使用示例 +#### 3.2.1 流程图 + + + +#### 3.2.2 代码解析 +- 创建GPIO0A0对象gpio +```c++ +lockzhiner_vision_module::periphery::GPIO0A0 gpio; +``` +- 配置GPIOState变量state +```c++ +lockzhiner_vision_module::periphery::GPIOState state; +``` +- 配置GPIO为输出模式 +```c++ +if (!gpio.Config(lockzhiner_vision_module::periphery::GPIOMode::OUT)) { + std::cout << "Failed to config gpio mode" << std::endl; + return 1; + } +``` +- 输出高电平信号 +```c++ +if (!gpio.Write(lockzhiner_vision_module::periphery::GPIOState::HIGH)) { + std::cout << "Failed to write gpio mode" << std::endl; + return 1; + } +``` +#### 3.2.2 代码实现 +```c++ +#include + +#include +#include + +int main() { + lockzhiner_vision_module::periphery::GPIO0A0 gpio; + + if (!gpio.Config(lockzhiner_vision_module::periphery::GPIOMode::OUT)) { + std::cout << "Failed to config gpio mode" << std::endl; + return 1; + } + + if (!gpio.Write(lockzhiner_vision_module::periphery::GPIOState::HIGH)) { + std::cout << "Failed to config gpio mode" << std::endl; + return 1; + } + + for (int i = 0; i < 10; i++) { + std::cout << "Wait: " << i << "/" << 10 << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + if (!gpio.Write(lockzhiner_vision_module::periphery::GPIOState::LOW)) { + std::cout << "Failed to config gpio mode" << std::endl; + return 1; + } + + return 0; +} +``` +--- +## 4.编译过程 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时以正确连接开发板。 +### 4.2 Cmake介绍 +```cmake +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test_gpio) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 配置 GPIO 输出 Demo +add_executable(Test-GPIO-Write GPIO_Write.cc) +target_include_directories(Test-GPIO-Write PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-GPIO-Write PRIVATE ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +# 配置 GPIO 读取 Demo +add_executable(Test-GPIO-Read GPIO_Read.cc) +target_include_directories(Test-GPIO-Read PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-GPIO-Read PRIVATE ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-GPIO-Read + TARGETS Test-GPIO-Write + RUNTIME DESTINATION . +) +``` +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/A02_GPIO +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` +--- +## 5. 运行效果展示 +### 5.1 准备工作 +1. 下载凌智视觉模块图片传输助手:[点击下载](https://gitee.com/LockzhinerAI/LockzhinerVisionModule/releases/download/v0.0.0/LockzhinerVisionModuleImageFetcher.exe) + +### 5.2 运行过程 +在凌智视觉模块中输入以下命令: +```shell + chmod 777 Test-GPIO-Read + ./Test-GPIO-Read + chmod 777 Test-GPIO-Write + ./Test-GPIO-Write +``` +### 5.3 运行结果 +- 运行GPIO输出例程 +我们可以查看示波器看到,GPIO_0A0输出了3.4V左右的电压 +![](./images/image_1.png) +电压持续10s后回复了正常 +![](./images/image_2.png) +- 运行GPIO输入例程 +可以看到,在接高电平引脚的情况下,引脚的状态信息为 GPIOState.LOW +![](./images/image_3.png) +在接低电平引脚的情况下,引脚的状态信息为 GPIOState.HIGH +![](./images/image_4.png) + +--- + +## 6. 总结 +通过上述内容,我们介绍了GPIO的基本概念、API定义以及具体的使用示例。按照以下步骤,您可以轻松地使用GPIO: +- 配置GPIO的工作模式(输入或输出)。 +- 根据需求读取或写入GPIO状态。 +- 检查操作是否成功,并根据返回值处理异常。 + + + diff --git a/Cpp_example/A02_GPIO/images/image_1.png b/Cpp_example/A02_GPIO/images/image_1.png new file mode 100755 index 0000000000000000000000000000000000000000..99a3c2c694c21c1bd665ff51a26e5a03bd763cdd Binary files /dev/null and b/Cpp_example/A02_GPIO/images/image_1.png differ diff --git a/Cpp_example/A02_GPIO/images/image_2.png b/Cpp_example/A02_GPIO/images/image_2.png new file mode 100755 index 0000000000000000000000000000000000000000..5bf6b0f35aded3cc266b41565b05ff6c30272f97 Binary files /dev/null and b/Cpp_example/A02_GPIO/images/image_2.png differ diff --git a/Cpp_example/A02_GPIO/images/image_3.png b/Cpp_example/A02_GPIO/images/image_3.png new file mode 100755 index 0000000000000000000000000000000000000000..81839e85b134e5c64b0b2ab97f81ff14c1073a0e Binary files /dev/null and b/Cpp_example/A02_GPIO/images/image_3.png differ diff --git a/Cpp_example/A02_GPIO/images/image_4.png b/Cpp_example/A02_GPIO/images/image_4.png new file mode 100755 index 0000000000000000000000000000000000000000..293d2049cea5bfa876c1ae0d448af8e15140d017 Binary files /dev/null and b/Cpp_example/A02_GPIO/images/image_4.png differ diff --git a/Cpp_example/A02_GPIO/images/read.png b/Cpp_example/A02_GPIO/images/read.png new file mode 100755 index 0000000000000000000000000000000000000000..1209dbac1a1b5a672604188485bdf8807f2d09ac Binary files /dev/null and b/Cpp_example/A02_GPIO/images/read.png differ diff --git a/Cpp_example/A02_GPIO/images/write.png b/Cpp_example/A02_GPIO/images/write.png new file mode 100755 index 0000000000000000000000000000000000000000..ad98c19cc8c52817b720a54ee247d633e864b302 Binary files /dev/null and b/Cpp_example/A02_GPIO/images/write.png differ diff --git a/Cpp_example/A03_PWM/CMakeLists.txt b/Cpp_example/A03_PWM/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..421e19f19be144274f109c34196c18e655b80c25 --- /dev/null +++ b/Cpp_example/A03_PWM/CMakeLists.txt @@ -0,0 +1,28 @@ +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test_pwm) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 配置 PWM 输出 Demo +add_executable(Test-PWM PWM.cc) +target_include_directories(Test-PWM PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-PWM PRIVATE ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-PWM + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/A03_PWM/PWM.cc b/Cpp_example/A03_PWM/PWM.cc new file mode 100755 index 0000000000000000000000000000000000000000..4acc2bd587f2291137e94f61a6bd272a0c5917df --- /dev/null +++ b/Cpp_example/A03_PWM/PWM.cc @@ -0,0 +1,41 @@ +#include + +#include // 为了使用atoi() +#include // 为了使用strtof() +#include +#include + +int main(int argc, char *argv[]) +{ + uint32_t frequency = 1000000; + float duty_cycle = 0.5; + if (argc == 3) + { + frequency = std::atoi(argv[1]); + duty_cycle = std::strtof(argv[2], nullptr); + } + std::cout << "frequency is " << frequency << std::endl; + std::cout << "duty_cycle is " << duty_cycle << std::endl; + + lockzhiner_vision_module::periphery::PWM9 pwm; + + if (!pwm.Open(frequency, duty_cycle)) + { + std::cout << "Failed to open adc." << std::endl; + return 1; + } + + for (int i = 0; i < 10; i++) + { + std::cout << "Wait: " << i << "/" << 10 << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + // 关闭 PWM + if (!pwm.Close()) + { + std::cout << "Failed to open adc." << std::endl; + return 1; + } + return 0; +} \ No newline at end of file diff --git a/Cpp_example/A03_PWM/README.md b/Cpp_example/A03_PWM/README.md new file mode 100755 index 0000000000000000000000000000000000000000..8650d3b93ff936a359568b57230ed9db16767c68 --- /dev/null +++ b/Cpp_example/A03_PWM/README.md @@ -0,0 +1,190 @@ +# PWM使用指南 +- 在电子工程和嵌入式系统开发中,脉冲宽度调制(PWM, Pulse Width Modulation)是一项关键技术,它允许我们通过调整脉冲信号的占空比来控制模拟信号的平均电平。 +## 1. PWM基本知识 +### 1.1 什么是PWM +PWM(Pulse Width Modulation,脉冲宽度调制)是一种通过改变脉冲信号的占空比来控制输出信号的技术。 +- 频率:表示每秒产生脉冲信号的次数,单位为Hz。 +- 占空比:表示高电平时间与整个周期的比例,范围为0.0到1.0(或0%到100%)。例如: + - 占空比为0.5表示高电平占一半时间,低电平占一半时间。 + - 占空比为1.0表示始终为高电平。 + - 占空比为0.0表示始终为低电平。 +### 1.2 PWM的应用场景 +PWM广泛应用于以下领域: +- 电机控制:通过调节占空比控制电机转速。 +- LED亮度调节:通过改变占空比控制LED的亮度。 +- 信号生成:用于生成特定波形的信号。 + +--- + +## 2. API文档 +### 2.1 头文件 +```c++ +#include +``` +### 2.2 配置PWM输出参数 +```c++ +bool Config(uint32_t frequency, float duty_cycle); +``` +- 参数: + - frequency:表示每秒产生脉冲信号的次数,单位为Hz。 + - duty_cycle:表示高电平时间与整个周期的比例,范围为0.0到1.0(或0%到100%)。 +- 返回值: + - true:表示配置成功。 + - false:表示配置失败。 +### 2.3 打开PWM输出 +```c++ +bool Open(); +``` +- 参数:无 +- 返回值: + - true:表示打开成功。 + - false:表示打开失败。 +### 2.4 关闭PWM输出 +```c++ +bool Close(); +``` +- 参数:无 +- 返回值: + - true:表示关闭成功。 + - false:表示关闭失败。 +--- + +## 3. 综合代码解析 + +### 3.1 流程图 + + + + +### 3.2 代码解析 +- 解析命令行参数 +```c++ +if (argc == 3) { + frequency = std::atoi(argv[1]); + duty_cycle = std::strtof(argv[2], nullptr); + } +``` +- 实例化PWM9对象 +```c++ +lockzhiner_vision_module::periphery::PWM9 pwm; +``` +- 打开PWM通道 +```c++ +pwm.Open() +``` +### 3.3 全部代码 +```c++ +#include + +#include // 为了使用atoi() +#include // 为了使用strtof() +#include +#include + +int main(int argc, char *argv[]) +{ + uint32_t frequency = 1000000; + float duty_cycle = 0.5; + if (argc == 3) + { + frequency = std::atoi(argv[1]); + duty_cycle = std::strtof(argv[2], nullptr); + } + std::cout << "frequency is " << frequency << std::endl; + std::cout << "duty_cycle is " << duty_cycle << std::endl; + + lockzhiner_vision_module::periphery::PWM9 pwm; + + if (!pwm.Open(frequency, duty_cycle)) + { + std::cout << "Failed to open adc." << std::endl; + return 1; + } + + for (int i = 0; i < 10; i++) + { + std::cout << "Wait: " << i << "/" << 10 << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + // 关闭 PWM + if (!pwm.Close()) + { + std::cout << "Failed to open adc." << std::endl; + return 1; + } + return 0; +} +``` +## 4. 编译过程 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时以正确连接开发板。 +### 4.2 Cmake介绍 +```cmake +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test_pwm) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 配置 PWM 输出 Demo +add_executable(Test-PWM PWM.cc) +target_include_directories(Test-PWM PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-PWM PRIVATE ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-PWM + RUNTIME DESTINATION . +) +``` +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/A03_PWM +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` + +在执行完上述命令后,会在build目录下生成可执行文件。 + +--- + +## 5. 例程运行示例 +### 5.2 运行过程 +在凌智视觉模块中输入以下命令: +```shell +chmod 777 Test-PWM +./Test-PWM 1000 0.5 +``` +### 5.3 运行效果 +- 运行代码后,我们在示波器上可以看到如下波形 +![](./images/images_1.png) + +--- + +## 6. 总结 +通过上述内容,我们介绍了PWM的基本概念、API定义以及具体的使用示例。按照以下步骤,您可以轻松地使用PWM功能: +- 初始化PWM通道:通过Open()函数设置PWM的频率和占空比。 +- 运行PWM输出:根据需求保持PWM输出一段时间。 +- 关闭PWM通道:操作完成后,务必调用Close()函数释放资源,避免PWM持续输出。 diff --git a/Cpp_example/A03_PWM/images/images_1.png b/Cpp_example/A03_PWM/images/images_1.png new file mode 100755 index 0000000000000000000000000000000000000000..d6cda11eb8348fa79c7aec35c8f9384bfc035b5b Binary files /dev/null and b/Cpp_example/A03_PWM/images/images_1.png differ diff --git a/Cpp_example/A03_PWM/images/pwm.png b/Cpp_example/A03_PWM/images/pwm.png new file mode 100755 index 0000000000000000000000000000000000000000..b0f5568b774ecd16fbb69eddf6a46ec7dcb6a31a Binary files /dev/null and b/Cpp_example/A03_PWM/images/pwm.png differ diff --git a/Cpp_example/A04_ADC/ADC.cc b/Cpp_example/A04_ADC/ADC.cc new file mode 100755 index 0000000000000000000000000000000000000000..74f9408f0a93f246a75557e091dc2c4a4330cfe9 --- /dev/null +++ b/Cpp_example/A04_ADC/ADC.cc @@ -0,0 +1,16 @@ +#include + +#include + +int main() +{ + lockzhiner_vision_module::periphery::ADCIN1 adc; + float adc_data; + if (!adc.Read(adc_data)) + { + std::cout << "Failed to read adc data." << std::endl; + return 1; + } + std::cout << "adc_data is " << adc_data << "mV" << std::endl; + return 0; +} \ No newline at end of file diff --git a/Cpp_example/A04_ADC/CMakeLists.txt b/Cpp_example/A04_ADC/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..de5156eecb17dc526e1b60611e9100c460f88f4c --- /dev/null +++ b/Cpp_example/A04_ADC/CMakeLists.txt @@ -0,0 +1,28 @@ +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test_adc) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 配置 ADC 读取 Demo +add_executable(Test-ADC ADC.cc) +target_include_directories(Test-ADC PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-ADC PRIVATE ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-ADC + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/A04_ADC/README.md b/Cpp_example/A04_ADC/README.md new file mode 100755 index 0000000000000000000000000000000000000000..33215b8ee4e8e8454908004ed039fb99d51455fe --- /dev/null +++ b/Cpp_example/A04_ADC/README.md @@ -0,0 +1,153 @@ +# ADC使用指南 +- 在嵌入式系统开发中,读取模拟信号是常见的需求。例如,测量电池电量、温度传感器数据等场景都需要用到ADC(Analog-to-Digital Converter,模数转换器)。LockAI提供了ADC引脚,方便用户获取模拟信号。 +## 1. ADC基本知识 +### 1.1 什么是ADC +ADC(Analog-to-Digital Converter,模数转换器)是一种将模拟信号转换为数字信号的硬件模块。 +- 模拟信号:连续变化的信号,例如电压或电流。 +- 数字信号:离散的数值,通常以二进制形式表示。 +- 分辨率:ADC的精度,通常以位数表示(如12位ADC可以表示0到4095之间的值)。 +### 1.2 ADC应用场景 +ADC广泛应用于以下领域: +- 电池电量检测:通过测量电池电压估算电量。 +- 环境监测:读取温度、湿度等传感器的模拟输出。 +- 音频处理:将模拟音频信号转换为数字信号进行处理。 + +--- + +## 2. API 文档 +### 2.1 使用ADC类需要引用头文件 +```c++ +#include +``` +### 2.2 读取模拟信号(转换后的) +```c++ +bool Read(float& adc_data); +``` +- 参数: + - [out] adc_data: 存储转换后的电压值(单位:伏特) +- 返回值 + - true:表示读取成功。 + - false:表示读取失败。 +### 2.3 读取模拟信号(原始的) +```c++ +float Read(); +``` +- 参数:无 +- 返回值: + - 返回模拟信号值(单位:mV) + - 失败时返回0.0f +--- + +## 3. 综合代码解析 + +### 3.1 流程图 + + + + +### 3.2 代码解析 +- 初始化ADC引脚 +```c++ +lockzhiner_vision_module::periphery::ADCIN1 adc; +``` +- 读取模拟信号 +```c++ +adc.Read(adc_data) +``` + +### 3.3 完整代码实现 +```c++ +#include + +#include + +int main() +{ + lockzhiner_vision_module::periphery::ADCIN1 adc; + float adc_data; + if (!adc.Read(adc_data)) + { + std::cout << "Failed to read adc data." << std::endl; + return 1; + } + std::cout << "adc_data is " << adc_data << "mV" << std::endl; + return 0; +} +``` +--- + +## 4. 编译过程 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时以正确连接开发板。 +### 4.2 Cmake介绍 +```cmake +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test_adc) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 配置 ADC 读取 Demo +add_executable(Test-ADC ADC.cc) +target_include_directories(Test-ADC PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-ADC PRIVATE ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-ADC + RUNTIME DESTINATION . +) +``` +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/A01_adc +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` + +在执行完上述命令后,会在build目录下生成可执行文件。 + +--- + +## 5. 例程运行示例 +### 5.1 运行过程 +在凌智视觉模块中输入以下命令: +```shell +chmod 777 Test-ADC +./Test-ADC +``` +### 5.2 运行效果 +- 外部输入电压如下图所示 +![](./images/image_1.png) +- 程序运行结果如下图所示 +![](./images/image_2.png) +可以看到有一定的误差,误差一般在10mv以内 + +--- + +## 6. 总结 +通过上述内容,我们介绍了ADC的基本概念、API定义以及具体的使用示例。按照以下步骤,您可以轻松地使用ADC功能: +- 初始化ADC引脚:创建对应的ADC对象(如ADCIN1)。 +- 读取模拟信号:调用Read()函数获取模拟信号值。 +- 处理读取结果:检查返回值是否成功,并根据需要处理读取到的数据。 diff --git a/Cpp_example/A04_ADC/images/ADC.png b/Cpp_example/A04_ADC/images/ADC.png new file mode 100755 index 0000000000000000000000000000000000000000..a6fb456647e33ef54cc6a0ace4779cf8eead0354 Binary files /dev/null and b/Cpp_example/A04_ADC/images/ADC.png differ diff --git a/Cpp_example/A04_ADC/images/image_1.png b/Cpp_example/A04_ADC/images/image_1.png new file mode 100755 index 0000000000000000000000000000000000000000..f4a9a646d9d249fc126cd67f8dfc6249e38458fc Binary files /dev/null and b/Cpp_example/A04_ADC/images/image_1.png differ diff --git a/Cpp_example/A04_ADC/images/image_2.png b/Cpp_example/A04_ADC/images/image_2.png new file mode 100755 index 0000000000000000000000000000000000000000..a804622dff7ce8f72c1beed375ef1146eab2a835 Binary files /dev/null and b/Cpp_example/A04_ADC/images/image_2.png differ diff --git a/Cpp_example/A05_USART/CMakeLists.txt b/Cpp_example/A05_USART/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..50df496c04e85946f87230de5f16e20949a1a4ee --- /dev/null +++ b/Cpp_example/A05_USART/CMakeLists.txt @@ -0,0 +1,34 @@ +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test_usart) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 配置 USART 输出 Demo +add_executable(Test-USART-Write USART_Write.cc) +target_include_directories(Test-USART-Write PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-USART-Write PRIVATE ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +# 配置 USART 读取 Demo +add_executable(Test-USART-Read USART_Read.cc) +target_include_directories(Test-USART-Read PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-USART-Read PRIVATE ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-USART-Read + TARGETS Test-USART-Write + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/A05_USART/README.md b/Cpp_example/A05_USART/README.md new file mode 100755 index 0000000000000000000000000000000000000000..8900757b688d8e0ea54c08f3fb7598259bcce46c --- /dev/null +++ b/Cpp_example/A05_USART/README.md @@ -0,0 +1,251 @@ +# 串口通讯 +串口(Serial Port),也称为串行接口或串行通信接口,是一种用于连接计算机与外部设备并进行数据传输的接口技术。它使用较少的导线(通常只需要几根线),并且可以在较长的距离上可靠地传输数据,尽管速度相对较慢。 + +在本章节中,我们将介绍如何使用 Lockzhiner Vision Module 上的串口进行数据传输。为了方便调试,我们使用 CH340 USB 转串口模块(以下简称 CH340) 进行调试,请正确将模块的引脚按照以下方式连接: + +- LockzhinerVisionModule RX1 <-> CH340 TXD +- LockzhinerVisionModule TX1 <-> CH340 RXD +- LockzhinerVisionModule GND <-> CH340 GND + +--- + +## 1. 串口通讯基本知识 +### 1.1 什么是串口通讯 + +串口通讯是一种通过串行方式传输数据的技术,数据以位为单位逐位发送和接收。相比并行通讯,串口通讯具有以下特点: +- 优点: + - 使用较少的导线,适合远距离传输。 + - 实现简单,成本较低。 +- 缺点: + - 数据传输速度较慢。 +### 1.2常见参数 +串口通讯的关键参数包括: +- 波特率(Baud Rate):表示每秒传输的位数,通常为 9600、38400、57600、115200 等。 +- 数据位(Data Bits):表示每位传输的数据位数,通常为 8 位。 +- 停止位(Stop Bits):表示发送和接收的停止位,通常为 1 位。 +- 校验位(Parity Bit):表示校验位,通常为无校验位、奇校验位和偶校验位。 +* 在LockAI中我们主要关心波特率即可。 + +--- + +## 2. API文档 +### 2.1 头文件 +```c++ +#include +``` +### 2.2 打开串口 +```c++ +bool Open(uint32_t baud_rate = 115200); +``` +- 参数: + - baud_rate:波特率,默认为115200。(支持标准波特率值) +- 返回值: + - true:打开串口成功。 + - false:打开串口失败。 +### 2.3 关闭串口 +```c++ +bool Close(); +``` +- 返回值: + - true:关闭串口成功。 + - false:关闭串口失败。 +### 2.4 发送数据 +```c++ +bool Write(const std::string& data); +``` +- 参数: + - data:要发送的数据。 +- 返回值: + - true:发送数据成功。 + - false:发送数据失败。 +### 2.5 读取数据 +```c++ +bool Read(std::string& data, size_t size); +``` +- 参数: + - data:用于存储读取数据的字符串。 + - size:要读取的数据大小。 +- 返回值: + - true:读取数据成功。 + - false:读取数据失败。 + +--- + +## 3. 综合代码解析 +### 3.1 串口读数据示例 +#### 3.1.1 流程图 + + + +#### 3.1.2 代码解释 +- 初始化USART模块,创建USART1对象。 +```c++ +lockzhiner_vision_module::periphery::USART1 usart; +``` +- 打开串口 +```c++ +usart.Open(115200) +``` +- 串口读取 +```c++ + usart.Read(buffer, 1024); +``` +#### 3.1.3 代码实现 +```c++ +#include + +#include + +int main() +{ + lockzhiner_vision_module::periphery::USART1 usart; + if (!usart.Open(115200)) + { + std::cout << "Failed to open usart." << std::endl; + return 1; + } + + std::cout << "Start receiving serial port data." << std::endl; + while (1) + { + std::string buffer; + usart.Read(buffer, 1024); + if (!buffer.empty()) + { + std::cout << buffer << std::endl; + } + } + return 0; +} +``` + +### 3.2 串口写数据示例 +#### 3.2.1 流程图 + + + +#### 3.2.2 代码解释 +- 初始化USART模块, +```c++ + lockzhiner_vision_module::periphery::USART1 usart; +``` +- 打开串口 +```c++ +usart.Open(115200) +``` +- 发送数据 +```c++ +usart.Write("Hello World\n"); +``` +#### 3.2.3 代码实现 +```c++ +#include + +#include + +int main() +{ + lockzhiner_vision_module::periphery::USART1 usart; + if (!usart.Open(115200)) + { + std::cout << "Failed to open usart." << std::endl; + return 1; + } + + if (!usart.Write("Hello World\n")) + { + std::cout << "Failed to send data." << std::endl; + return 1; + } + return 0; +} +``` + +--- + +## 4. 编译过程 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时以正确连接开发板。 +### 4.2 Cmake介绍 +```cmake +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test_usart) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 配置 USART 输出 Demo +add_executable(Test-USART-Write USART_Write.cc) +target_include_directories(Test-USART-Write PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-USART-Write PRIVATE ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +# 配置 USART 读取 Demo +add_executable(Test-USART-Read USART_Read.cc) +target_include_directories(Test-USART-Read PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-USART-Read PRIVATE ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-USART-Read + TARGETS Test-USART-Write + RUNTIME DESTINATION . +) +``` +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/A05_USART +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` + +在执行完上述命令后,会在build目录下生成可执行文件。 + +--- + +## 5. 例程运行示例 +### 5.1 运行过程 +在凌智视觉模块中输入以下命令: +```shell +chmod 777 Test-USART-Read +./Test-USART-Read +chmod 777 Test-USART-Write +./Test-USART-Write +``` +### 5.2 运行结果 +- 在电脑端打开串口调试工具,选择对应串口,波特率为115200,数据位为8,停止位为1,校验位为无校验位。 +- 电脑端串口配置如下所示: +![](./images/image_1.png) +- 运行串口写数据示例后,我们在电脑端可以收到如下数据。 +![](./images/image_2.png) +- 运行串口读数据示例后,我们在电脑端发送Hello World,然后我们收到如下数据。 +![](./images/image_3.png) + +--- + +## 6. 总结 +通过上述内容,我们介绍了串口通讯的基本概念、API定义以及具体的使用示例。按照以下步骤,您可以轻松地使用串口功能: +- 初始化串口:调用Open()函数设置波特率并打开串口。 +- 读取数据:调用Read()函数从串口接收数据。 +- 写入数据:调用Write()函数向串口发送数据。 +- 关闭串口:操作完成后,调用Close()函数释放资源。 diff --git a/Cpp_example/A05_USART/USART_Read.cc b/Cpp_example/A05_USART/USART_Read.cc new file mode 100755 index 0000000000000000000000000000000000000000..4d4230e8b04e1d39fb02824820ad126121761580 --- /dev/null +++ b/Cpp_example/A05_USART/USART_Read.cc @@ -0,0 +1,25 @@ +#include + +#include + +int main() +{ + lockzhiner_vision_module::periphery::USART1 usart; + if (!usart.Open(115200)) + { + std::cout << "Failed to open usart." << std::endl; + return 1; + } + + std::cout << "Start receiving serial port data." << std::endl; + while (1) + { + std::string buffer; + usart.Read(buffer, 1024); + if (!buffer.empty()) + { + std::cout << buffer << std::endl; + } + } + return 0; +} \ No newline at end of file diff --git a/Cpp_example/A05_USART/USART_Write.cc b/Cpp_example/A05_USART/USART_Write.cc new file mode 100755 index 0000000000000000000000000000000000000000..0aba4e034ca10fe22365fdfcae2a77626c719ff0 --- /dev/null +++ b/Cpp_example/A05_USART/USART_Write.cc @@ -0,0 +1,20 @@ +#include + +#include + +int main() +{ + lockzhiner_vision_module::periphery::USART1 usart; + if (!usart.Open(115200)) + { + std::cout << "Failed to open usart." << std::endl; + return 1; + } + + if (!usart.Write("Hello World\n")) + { + std::cout << "Failed to send data." << std::endl; + return 1; + } + return 0; +} \ No newline at end of file diff --git a/Cpp_example/A05_USART/images/Read.png b/Cpp_example/A05_USART/images/Read.png new file mode 100755 index 0000000000000000000000000000000000000000..c61cd94f0e805845d395134ba7d0c98968b8f47f Binary files /dev/null and b/Cpp_example/A05_USART/images/Read.png differ diff --git a/Cpp_example/A05_USART/images/Write.png b/Cpp_example/A05_USART/images/Write.png new file mode 100755 index 0000000000000000000000000000000000000000..f6d9642b2e8e66a54e9d9e3b5ec0851cf1ed4280 Binary files /dev/null and b/Cpp_example/A05_USART/images/Write.png differ diff --git a/Cpp_example/A05_USART/images/image_1.png b/Cpp_example/A05_USART/images/image_1.png new file mode 100755 index 0000000000000000000000000000000000000000..1f9d5c851f8eb96979230b512a45ec50a4804805 Binary files /dev/null and b/Cpp_example/A05_USART/images/image_1.png differ diff --git a/Cpp_example/A05_USART/images/image_2.png b/Cpp_example/A05_USART/images/image_2.png new file mode 100755 index 0000000000000000000000000000000000000000..46cbd9a3649f526b410aed890a92f34445da7096 Binary files /dev/null and b/Cpp_example/A05_USART/images/image_2.png differ diff --git a/Cpp_example/A05_USART/images/image_3.png b/Cpp_example/A05_USART/images/image_3.png new file mode 100755 index 0000000000000000000000000000000000000000..b039b467eb99e295027745d5728eda0eb271a70c Binary files /dev/null and b/Cpp_example/A05_USART/images/image_3.png differ diff --git a/Cpp_example/B01_basic_method/CMakeLists.txt b/Cpp_example/B01_basic_method/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..fad677313b6a4c2a002b3a251fafa7c616fba8a5 --- /dev/null +++ b/Cpp_example/B01_basic_method/CMakeLists.txt @@ -0,0 +1,33 @@ +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test-basic-method) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 基本图像处理示例 +add_executable(Test-basic-method basic_method.cc) +target_include_directories(Test-basic-method PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-basic-method PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-basic-method + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/B01_basic_method/README.md b/Cpp_example/B01_basic_method/README.md new file mode 100755 index 0000000000000000000000000000000000000000..f4b1fbd910fdea7a6bc12aee7eea953fb7a9a5d7 --- /dev/null +++ b/Cpp_example/B01_basic_method/README.md @@ -0,0 +1,337 @@ +# 图像的基本运算 +在图像处理中,理解图像的基本操作是掌握计算机视觉技术的关键。本章节将介绍 OpenCV 中图像的基本运算方法,包括像素操作、逻辑运算和差值运算,并通过一个综合示例展示其实际应用。 + +--- + +## 1. 基本知识讲解 +### 1.1 图像坐标系 + +在图像处理中,坐标是一个非常重要的概念: + +- 原点:图像的左上角为原点 (0, 0)。 +- x 轴:从左到右递增。 +- y 轴:从上到下递增。 +### 1.2 图像的基本属性 +每张图像都有以下基本属性: + +- 宽度(Width):图像的列数。 +- 高度(Height):图像的行数。 +- 通道数(Channels): + - 灰度图:1 个通道。 + - 彩色图:通常为 3 个通道(BGR)。 +### 1.3 图像的基本操作 +图像的基本操作包括: + +- 获取和设置像素值:访问和修改图像中的像素值。 +- 逻辑运算:如按位与、或、异或等。 +- 差值运算:计算两张图像之间的差异。 + +--- + +## 2.API文档 +### 2.1 头文件 +```cpp +#include +``` +### 2.2 获取设置像素点 +```c++ +uchar cv::Mat::at(int row, int col) const; // 灰度图 +cv::Vec3b cv::Mat::at(int row, int col) const; // 彩色图 +``` +- 参数: + - row:行索引(y坐标) + - col:列索引(x坐标) +- 返回值: + - 对于灰度图,返回单通道值(unchar) + - 对于彩色图,返回三通道值(cv::Vec3b) +### 2.3 设置像素点: +```c++ +void cv::Mat::at(int row, int col) = value; // 灰度图 +void cv::Mat::at(int row, int col) = value; // 彩色图 +``` +- 参数: + - row:行索引(y坐标) + - col:列索引(x坐标) + - value:要设置的像素值(uchar或cv::Vec3b) +### 2.4 获取图像的宽度和高度 +#### 2.4.1 获取宽度 +```c++ +int cv::Mat::cols; +``` +- 返回值 + - 返回图像的宽度(列数) +#### 2.4.2 获取高度 +```c++ +int cv::Mat::rows; +``` +- 返回值 + - 返回图像的高度(行数) +### 2.5 获取图像的格式和大小 +#### 2.5.1 判断是否为灰度图 +```c++ +int cv::Mat::channels(); +``` +- 返回值 + - 1表示灰度图 + - 3表示彩色图 +#### 2.5.2 获取图像大小(字节数) +```c++ +size_t cv::Mat::total(); // 总像素数 +size_t cv::Mat::elemSize(); // 每个像素的字节数 +size_t totalBytes = img.total() * img.elemSize();//计算公式 +``` +- 返回值 + - 返回图像的总字节数 +### 2.6 图像取反 +```c++ +void cv::bitwise_not(InputArray src, OutputArray dst); +``` +- 参数: + - src:输入图像(cv::Mat) + - dst:输出图像(cv::Mat) +- 返回值: + - 结果储存在dst中 +### 2.7 图像逻辑运算 +#### 2.7.1 按位与(AND) +```c++ +void cv::bitwise_and(InputArray src1, InputArray src2, OutputArray dst); +``` +-参数: + - src1:输入图像1(cv::Mat) + - src2:输入图像2(cv::Mat) + - dst:输出图像(cv::Mat) +- 返回值: + - 结果储存在dst中 +#### 2.7.2 按位或(OR) +```c++ +void cv::bitwise_or(InputArray src1, InputArray src2, OutputArray dst); +``` +- 参数: + - src1:输入图像1(cv::Mat) + - src2:输入图像2(cv::Mat) + - dst:输出图像(cv::Mat) +- 返回值: + - 结果储存在dst中 +#### 2.7.3 按位异或(XOR) +```c++ +void cv::bitwise_xor(InputArray src1, InputArray src2, OutputArray dst); +``` +- 参数: + - src1:输入图像1(cv::Mat) + - src2:输入图像2(cv::Mat) + - dst:输出图像(cv::Mat) +- 返回值: + - 结果储存在dst中 +#### 2.7.4 按位取反(NOT) +```c++ +void cv::bitwise_not(InputArray src, OutputArray dst); +``` +- 参数: + - src:输入图像(cv::Mat) + - dst:输出图像(cv::Mat) +- 返回值: + - 结果储存在dst中 +#### 2.7.5 复杂的图像逻辑运算 +- 如果需要实现复杂的逻辑运算(如NAND、NOR、NXOR等),可以通过组合上述基本函数来完成。例如: +**NAND 与非运算 +```c++ +cv::Mat nandResult; +cv::bitwise_and(img1, img2, nandResult); // 计算 AND +cv::bitwise_not(nandResult, nandResult); // 取反得到 NAND +``` +- 通过这样的组合我们就可以实现更为复杂的逻辑运算了。 +### 2.8 绝对差值 +```c++ +void cv::absdiff(InputArray src1, InputArray src2, OutputArray dst); +``` +- 参数: + - src1:输入图像1(cv::Mat) + - src2:输入图像2(cv::Mat) + - dst:输出图像(cv::Mat) +- 返回值: + - 结果储存在dst中 + +--- + +## 3.综合代码解析 + +### 3.1 流程图 + + + +### 3.2 代码解析 +- 像素操作 +```c++ +// 从(10,10)开始,宽40,高40,设置为蓝色 +cv::Rect roi(10, 10, 40, 40); +image1(roi).setTo(cv::Scalar(255, 0, 0)); +``` +- 图像变换 +```c++ +// 图像取反 +cv::Mat invertedImage; +cv::bitwise_not(image1, invertedImage); +// 图像差分 +cv::Mat diffImage; +cv::absdiff(image1, image2, diffImage); +``` +- 显示结果 +```c++ + // 显示结果 +cv::imshow("Original Image", image1); +cv::imshow("Inverted Image", invertedImage); +cv::imshow("Difference Image", diffImage); +``` +### 3.3 代码实现 +```c++ +#include +#include +#include + +int main(int argc, char *argv[]) +{ + // 检查命令行参数数量是否正确 + if (argc != 3) + { + std::cerr << "Usage: " << argv[0] << " " << std::endl; + return -1; + } + + // 从命令行参数中读取图像路径 + std::string image1Path = argv[1]; + std::string image2Path = argv[2]; + + // 加载图像 + cv::Mat image1 = cv::imread(image1Path); + cv::Mat image2 = cv::imread(image2Path); + + // 检查图像是否加载成功 + if (image1.empty() || image2.empty()) + { + std::cerr << "Error: Could not load images!" << std::endl; + return -1; + } + + // 获取图像尺寸 + int width = image1.cols; + int height = image1.rows; + std::cout << "Image size: " << width << "x" << height << std::endl; + + // 从(10,10)开始,宽40,高40,设置为蓝色 + cv::Rect roi(10, 10, 40, 40); + image1(roi).setTo(cv::Scalar(255, 0, 0)); + + // 图像取反 + cv::Mat invertedImage; + cv::bitwise_not(image1, invertedImage); + + // 图像差分 + cv::Mat diffImage; + cv::absdiff(image1, image2, diffImage); + + // 显示结果 + cv::imshow("Original Image", image1); + cv::imshow("Inverted Image", invertedImage); + cv::imshow("Difference Image", diffImage); + + cv::waitKey(0); + return 0; +} +``` + +--- + +## 4. 编译过程 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时以正确连接开发板。 +### 4.2 Cmake介绍 +```cmake +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test-basic-method) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 基本图像处理示例 +add_executable(Test-basic-method basic_method.cc) +target_include_directories(Test-basic-method PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-basic-method PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-basic-method + RUNTIME DESTINATION . +) +``` +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/B01_basic_method +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` + +在执行完上述命令后,会在build目录下生成可执行文件。 + +--- + +## 5. 例程运行示例 +### 5.1 运行过程 + +在凌智视觉模块中输入以下命令: +```shell +chmod 777 Test-basic-method +# 需要输入两张大小一样的图片 +./Test-basic-method image1_path image2_path +``` +### 5.2 运行结果 +- 原始图像: + +![](./images/2.png) + +- 原始图像左上角加上一个40*40的矩形蓝像素点 + +![](./images/Original.png) + +- 图像取反结果: + +![](./images/Inverted.png) + +- 图像差分结果: + +![](./images/Difference.png) + +--- + +## 6.总结 +通过上述内容,我们介绍了图像的基本操作及其对应的 OpenCV API。按照以下步骤,您可以轻松地进行图像的基本运算: +- 获取和设置像素值:使用 cv::Mat::at 方法访问和修改像素。 +- 逻辑运算:使用 cv::bitwise_and、cv::bitwise_or 等函数实现逻辑运算。 +- 差值运算:使用 cv::absdiff 计算图像之间的差异。 +- 复杂运算:通过组合基本函数实现更复杂的逻辑运算。 + diff --git a/Cpp_example/B01_basic_method/basic_method.cc b/Cpp_example/B01_basic_method/basic_method.cc new file mode 100755 index 0000000000000000000000000000000000000000..5debb718dd2a135b4b7bd6a4b796f4af60b88937 --- /dev/null +++ b/Cpp_example/B01_basic_method/basic_method.cc @@ -0,0 +1,53 @@ +#include +#include +#include + +int main(int argc, char *argv[]) +{ + // 检查命令行参数数量是否正确 + if (argc != 3) + { + std::cerr << "Usage: " << argv[0] << " " << std::endl; + return -1; + } + + // 从命令行参数中读取图像路径 + std::string image1Path = argv[1]; + std::string image2Path = argv[2]; + + // 加载图像 + cv::Mat image1 = cv::imread(image1Path); + cv::Mat image2 = cv::imread(image2Path); + + // 检查图像是否加载成功 + if (image1.empty() || image2.empty()) + { + std::cerr << "Error: Could not load images!" << std::endl; + return -1; + } + + // 获取图像尺寸 + int width = image1.cols; + int height = image1.rows; + std::cout << "Image size: " << width << "x" << height << std::endl; + + // 从(10,10)开始,宽40,高40,设置为蓝色 + cv::Rect roi(10, 10, 40, 40); + image1(roi).setTo(cv::Scalar(255, 0, 0)); + + // 图像取反 + cv::Mat invertedImage; + cv::bitwise_not(image1, invertedImage); + + // 图像差分 + cv::Mat diffImage; + cv::absdiff(image1, image2, diffImage); + + // 显示结果 + cv::imshow("Original Image", image1); + cv::imshow("Inverted Image", invertedImage); + cv::imshow("Difference Image", diffImage); + + cv::waitKey(0); + return 0; +} \ No newline at end of file diff --git a/Cpp_example/B01_basic_method/images/2.png b/Cpp_example/B01_basic_method/images/2.png new file mode 100755 index 0000000000000000000000000000000000000000..87658c3f5bdf5429902aefc1d04caa901fe03b9b Binary files /dev/null and b/Cpp_example/B01_basic_method/images/2.png differ diff --git a/Cpp_example/B01_basic_method/images/All.png b/Cpp_example/B01_basic_method/images/All.png new file mode 100755 index 0000000000000000000000000000000000000000..67b0fbc33c3304cd4665a573fd56726dd1052bfb Binary files /dev/null and b/Cpp_example/B01_basic_method/images/All.png differ diff --git a/Cpp_example/B01_basic_method/images/Difference.png b/Cpp_example/B01_basic_method/images/Difference.png new file mode 100755 index 0000000000000000000000000000000000000000..1e727d4d65d533bbb6120755036226077170bafd Binary files /dev/null and b/Cpp_example/B01_basic_method/images/Difference.png differ diff --git a/Cpp_example/B01_basic_method/images/Inverted.png b/Cpp_example/B01_basic_method/images/Inverted.png new file mode 100755 index 0000000000000000000000000000000000000000..4b70aeae2ad26dd95d4b12c2646036978b824dde Binary files /dev/null and b/Cpp_example/B01_basic_method/images/Inverted.png differ diff --git a/Cpp_example/B01_basic_method/images/Original.png b/Cpp_example/B01_basic_method/images/Original.png new file mode 100755 index 0000000000000000000000000000000000000000..581d5e497f4d6a873a2a1f0cda9a3226ede09284 Binary files /dev/null and b/Cpp_example/B01_basic_method/images/Original.png differ diff --git a/Cpp_example/B02_Image_information_statistics/CMakeLists.txt b/Cpp_example/B02_Image_information_statistics/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..6722a4a8ed14ed8f70553f6cfd0867befd9be9d5 --- /dev/null +++ b/Cpp_example/B02_Image_information_statistics/CMakeLists.txt @@ -0,0 +1,33 @@ +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test-Image-information-statistics) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 图像信息处理 +add_executable(Test-Image-information-statistics Image_information_statistics.cc) +target_include_directories(Test-Image-information-statistics PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-Image-information-statistics PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-Image-information-statistics + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/B02_Image_information_statistics/Image_information_statistics.cc b/Cpp_example/B02_Image_information_statistics/Image_information_statistics.cc new file mode 100755 index 0000000000000000000000000000000000000000..f7c0f07016403e30d51a30d044814c04db26a76d --- /dev/null +++ b/Cpp_example/B02_Image_information_statistics/Image_information_statistics.cc @@ -0,0 +1,38 @@ +#include +#include + +int main() +{ + // 读取图像 + cv::Mat image = cv::imread("example.jpg"); + if (image.empty()) + { + std::cerr << "Error: Could not open image!" << std::endl; + return -1; + } + + // 定义 ROI 并提取 + cv::Rect roiRect(50, 50, 200, 200); + cv::Mat roi = image(roiRect); + + // 转换为灰度图 + cv::Mat grayRoi; + cv::cvtColor(roi, grayRoi, cv::COLOR_BGR2GRAY); + + // 计算均值和标准差 + cv::Scalar mean, stddev; + cv::meanStdDev(grayRoi, mean, stddev); + + // 计算最小值和最大值 + double minVal, maxVal; + cv::Point minLoc, maxLoc; + cv::minMaxLoc(grayRoi, &minVal, &maxVal, &minLoc, &maxLoc); + + // 输出结果 + std::cout << "Mean: " << mean[0] << std::endl; + std::cout << "Standard Deviation: " << stddev[0] << std::endl; + std::cout << "Min Value: " << minVal << " at " << minLoc << std::endl; + std::cout << "Max Value: " << maxVal << " at " << maxLoc << std::endl; + + return 0; +} \ No newline at end of file diff --git a/Cpp_example/B02_Image_information_statistics/README.md b/Cpp_example/B02_Image_information_statistics/README.md new file mode 100755 index 0000000000000000000000000000000000000000..124505f653f47f44a714144669eed1175f55b320 --- /dev/null +++ b/Cpp_example/B02_Image_information_statistics/README.md @@ -0,0 +1,259 @@ +# 使用图像的统计信息 +在图像处理中,统计信息可以帮助我们了解图像的特性,例如区域内的像素分布、颜色转换以及特定区域的分析。本章节将介绍如何提取兴趣区域(ROI)、转换颜色通道、计算均值和标准差,以及查找最小值和最大值,并通过一个综合示例展示其实际应用。 +## 1.基本知识讲解 +### 1.1 图像的兴趣区域(ROI) +- ROI(Region of Interest):指图像中感兴趣的区域,通常用于局部分析或处理。 +- 提取 ROI 的目的是减少数据量并专注于特定区域,从而提高处理效率。 +### 1.2 颜色空间转换 +- 不同的颜色空间适用于不同的任务。例如: + - 灰度图:简化图像处理,常用于边缘检测等任务。 + - HSV:用于颜色分割任务,分离色调、饱和度和亮度。 + - LAB:更接近人类视觉感知,适合颜色校正。 +### 1.3 图像统计信息 +- 均值和标准差:反映图像整体亮度及亮度变化情况。 +- 最小值和最大值:帮助识别图像中的极端像素值及其位置。 + +--- + +## 2.API文档 +### 2.1 头文件 +```c++ +#include +``` +### 2.2 提取兴趣区域(ROI) +```c++ +cv::Mat roi = image(cv::Rect(x, y, w, h)); +``` +- 功能: + - 从图像中提取一个矩形区域。 +- 参数: + - image:输入图像(cv::Mat类型)。 + - (x, y):ROI左上角的坐标。 + - (w, h):ROI的宽高。 +- 返回值: + - 提取出的ROI图像(cv::Mat类型)。 +### 2.3 转换为灰度图 +```c++ +cv::cvtColor(image, grayImage, cv::COLOR_BGR2GRAY); +``` +- 功能: + - 将彩色图像转换为灰度图像。 +- 参数: + - image:输入图像(cv::Mat类型)。 + - grayImage:输出灰度图像(cv::Mat类型)。 + - COLOR_BGR2GRAY:将BGR图像转换为灰度图像。 +- 返回值: + - 无。最后结果储存在grayImage中。 +**注意:** 其中根据不同的转换要求可以使用不同的转换代码,具体如下所示。 + +--- +| 转换方向 | 转换代码 | 描述 | +|------------------------|-----------------------|----------------------------------------| +| **BGR ↔ Grayscale** | `cv::COLOR_BGR2GRAY` | 将 BGR 图像转换为灰度图像 | +| | `cv::COLOR_GRAY2BGR` | 将灰度图像转换为 BGR 图像 | +| **BGR ↔ RGB** | `cv::COLOR_BGR2RGB` | 将 BGR 图像转换为 RGB 图像 | +| | `cv::COLOR_RGB2BGR` | 将 RGB 图像转换为 BGR 图像 | +| **BGR ↔ HSV** | `cv::COLOR_BGR2HSV` | 将 BGR 图像转换为 HSV 图像 | +| | `cv::COLOR_HSV2BGR` | 将 HSV 图像转换为 BGR 图像 | +| **BGR ↔ LAB** | `cv::COLOR_BGR2LAB` | 将 BGR 图像转换为 LAB 图像 | +| | `cv::COLOR_Lab2BGR` | 将 LAB 图像转换为 BGR 图像 | +| **BGR ↔ YUV** | `cv::COLOR_BGR2YUV` | 将 BGR 图像转换为 YUV 图像 | +| | `cv::COLOR_YUV2BGR` | 将 YUV 图像转换为 BGR 图像 | +| **BGR ↔ XYZ** | `cv::COLOR_BGR2XYZ` | 将 BGR 图像转换为 CIE XYZ 图像 | +| | `cv::COLOR_XYZ2BGR` | 将 CIE XYZ 图像转换为 BGR 图像 | +| **BGR ↔ YCrCb** | `cv::COLOR_BGR2YCrCb` | 将 BGR 图像转换为 YCrCb 图像 | +| | `cv::COLOR_YCrCb2BGR` | 将 YCrCb 图像转换为 BGR 图像 | +| **BGR ↔ HLS** | `cv::COLOR_BGR2HLS` | 将 BGR 图像转换为 HLS 图像 | +| | `cv::COLOR_HLS2BGR` | 将 HLS 图像转换为 BGR 图像 | +| **BGR ↔ Luv** | `cv::COLOR_BGR2Luv` | 将 BGR 图像转换为 Luv 图像 | +| | `cv::COLOR_Luv2BGR` | 将 Luv 图像转换为 BGR 图像 | +| **BGR ↔ Bayer** | `cv::COLOR_BayerBG2BGR` | 将 Bayer 格式图像转换为 BGR 图像 | +| **BGR ↔ RGBA** | `cv::COLOR_BGR2RGBA` | 将 BGR 图像转换为 RGBA 图像(添加 Alpha 通道) | +| | `cv::COLOR_RGBA2BGR` | 将 RGBA 图像转换为 BGR 图像 | +--- + +### 2.4 计算均值和标准差: +```c++ +cv::meanStdDev(src, mean, stddev); +``` +- 功能: + - 计算图像或矩阵元素的平均值和标准偏差。 +- 参数: + - src:输入图像或矩阵(cv::Mat类型)。 + - mean:输出平均值(cv::Scalar类型)。 + - stddev:输出标准偏差(cv::Scalar类型)。 +- 返回值: + - 无。最后结果储存在mean和stddev中。 +### 2.5 计算最小值和最大值: +```c++ +cv::minMaxLoc(src, &minVal, &maxVal, &minLoc, &maxLoc); +``` +- 功能: + - 在输入图像或矩阵中找到最小值和最大值。 +- 参数: + - src:输入图像或矩阵(cv::Mat类型)。 + - minVal:输出最小值(double类型)。 + - maxVal:输出最大值(double类型)。 + - minLoc:输出最小值对应的位置(cv::Point类型)。 + - maxLoc:输出最大值对应的位置(cv::Point类型)。 +- 返回值: + - 无。最后结果储存在minVal、maxVal、minLoc和maxLoc中。 + +--- + +## 3.综合代码解析 + +### 3.1 流程图 + + + +### 3.2 代码解释 +- 读取图像文件 +```c++ +cv::Mat image = cv::imread("2.jpg"); +if (image.empty()) { + std::cerr << "Error: Could not open image!" << std::endl; + return -1; +} +``` +- 设定ROI区域 +```c++ +// 定义 ROI 并提取 +cv::Rect roiRect(50, 50, 200, 200); +cv::Mat roi = image(roiRect); +``` +- 转换为灰度图 +```c++ +cv::Mat grayRoi; +cv::cvtColor(roi, grayRoi, cv::COLOR_BGR2GRAY); +``` +- 均值和标准差计算 +```c++ +// 计算均值和标准差 +cv::Scalar mean, stddev; +cv::meanStdDev(grayRoi, mean, stddev); +``` +### 3.3 代码实现 +```c++ +#include +#include + +int main() +{ + // 读取图像 + cv::Mat image = cv::imread("example.jpg"); + if (image.empty()) + { + std::cerr << "Error: Could not open image!" << std::endl; + return -1; + } + + // 定义 ROI 并提取 + cv::Rect roiRect(50, 50, 200, 200); + cv::Mat roi = image(roiRect); + + // 转换为灰度图 + cv::Mat grayRoi; + cv::cvtColor(roi, grayRoi, cv::COLOR_BGR2GRAY); + + // 计算均值和标准差 + cv::Scalar mean, stddev; + cv::meanStdDev(grayRoi, mean, stddev); + + // 计算最小值和最大值 + double minVal, maxVal; + cv::Point minLoc, maxLoc; + cv::minMaxLoc(grayRoi, &minVal, &maxVal, &minLoc, &maxLoc); + + // 输出结果 + std::cout << "Mean: " << mean[0] << std::endl; + std::cout << "Standard Deviation: " << stddev[0] << std::endl; + std::cout << "Min Value: " << minVal << " at " << minLoc << std::endl; + std::cout << "Max Value: " << maxVal << " at " << maxLoc << std::endl; + + return 0; +} +``` + +--- + +## 4.编译过程 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时以正确连接开发板。 +### 4.2 Cmake介绍 +```cmake +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test-Image-information-statistics) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 图像信息处理 +add_executable(Test-Image-information-statistics Image_information_statistics.cc) +target_include_directories(Test-Image-information-statistics PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-Image-information-statistics PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-Image-information-statistics + RUNTIME DESTINATION . +) +``` +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/B02_Image_information_statistics +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` + +在执行完上述命令后,会在build目录下生成可执行文件。 + +--- + +## 5. 例程运行示例 +### 5.1 运行过程 +在凌智视觉模块中输入以下命令: +```shell +chmod 777 Test-Image-information-statistics +./Test-Image-information-statistics +``` +### 5.2 运行效果 +在运行上述代码时,会输出以下结果: +![](./images/images.png) + +--- + +## 6. 总结 +通过上述内容,我们介绍了如何使用 OpenCV 提取图像的 ROI、转换颜色空间、计算统计信息等操作。按照以下步骤,您可以轻松地进行图像的统计分析: +- 提取 ROI:使用 cv::Rect 提取感兴趣区域。 +- 颜色转换:使用 cv::cvtColor 转换颜色空间。 +- 计算统计信息: + - 使用 cv::meanStdDev 计算均值和标准差。 + - 使用 cv::minMaxLoc 查找最小值和最大值及其位置。 +- 综合应用:结合上述方法对图像进行局部分析和全局统计。 \ No newline at end of file diff --git a/Cpp_example/B02_Image_information_statistics/example.jpg b/Cpp_example/B02_Image_information_statistics/example.jpg new file mode 100755 index 0000000000000000000000000000000000000000..87658c3f5bdf5429902aefc1d04caa901fe03b9b Binary files /dev/null and b/Cpp_example/B02_Image_information_statistics/example.jpg differ diff --git a/Cpp_example/B02_Image_information_statistics/images/ALL.png b/Cpp_example/B02_Image_information_statistics/images/ALL.png new file mode 100755 index 0000000000000000000000000000000000000000..0cc0ea5b6d6f81a6220b28d742c09ccfe3acc077 Binary files /dev/null and b/Cpp_example/B02_Image_information_statistics/images/ALL.png differ diff --git a/Cpp_example/B02_Image_information_statistics/images/images.png b/Cpp_example/B02_Image_information_statistics/images/images.png new file mode 100755 index 0000000000000000000000000000000000000000..c3c6b84e3a421bb74e576477e0127efd370b3a29 Binary files /dev/null and b/Cpp_example/B02_Image_information_statistics/images/images.png differ diff --git a/Cpp_example/B03_Draw/CMakeLists.txt b/Cpp_example/B03_Draw/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..7110686ae06142b846669d2167e2954c32b44171 --- /dev/null +++ b/Cpp_example/B03_Draw/CMakeLists.txt @@ -0,0 +1,33 @@ +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test_draw) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 画图示例 +add_executable(Test-Draw Draw.cc) +target_include_directories(Test-Draw PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-Draw PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-Draw + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/B03_Draw/Draw.cc b/Cpp_example/B03_Draw/Draw.cc new file mode 100755 index 0000000000000000000000000000000000000000..13b5241e39cf04014c6c1450fd618cd0ae210413 --- /dev/null +++ b/Cpp_example/B03_Draw/Draw.cc @@ -0,0 +1,31 @@ +#include +#include + +int main() +{ + // 创建一个空白图像 (512x512,3通道) + cv::Mat image = cv::Mat::zeros(512, 512, CV_8UC3); + + // 画线 + cv::line(image, cv::Point(10, 10), cv::Point(100, 100), cv::Scalar(255, 0, 0), 2); + + // 画矩形 + cv::rectangle(image, cv::Rect(50, 50, 100, 100), cv::Scalar(0, 255, 0), 2); + + // 画圆 + cv::circle(image, cv::Point(200, 200), 30, cv::Scalar(0, 0, 255), -1); + + // 画十字 + int cross_x = 300, cross_y = 300, cross_size = 10; + cv::line(image, cv::Point(cross_x - cross_size, cross_y), cv::Point(cross_x + cross_size, cross_y), cv::Scalar(255, 255, 0), 2); + cv::line(image, cv::Point(cross_x, cross_y - cross_size), cv::Point(cross_x, cross_y + cross_size), cv::Scalar(255, 255, 0), 2); + + // 写字 + cv::putText(image, "Hello, OpenCV!", cv::Point(10, 400), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0, 0, 0), 2); + + // 显示图像 + cv::imshow("Drawing Example", image); + cv::waitKey(0); + + return 0; +} \ No newline at end of file diff --git a/Cpp_example/B03_Draw/README.md b/Cpp_example/B03_Draw/README.md new file mode 100755 index 0000000000000000000000000000000000000000..056442170c98109b6ac740766def7c4ab3839d34 --- /dev/null +++ b/Cpp_example/B03_Draw/README.md @@ -0,0 +1,220 @@ +# 画图功能 +在计算机视觉和图像处理中,画图功能是非常重要的工具,尤其是在需要向用户反馈信息时。通过绘制线条、矩形框、圆形以及文字等图形元素,可以直观地展示结果或标注关键信息。本章节将介绍 OpenCV 中常用的画图功能,并通过一个综合示例展示其实际应用。 +## 1.基本知识讲解 +### 1.1 图像画图的重要性 +- 视觉反馈:画图功能可以帮助开发者或用户直观地理解算法的输出结果。 +- 标注目标:例如在目标检测中,可以用矩形框标注检测到的目标。 +- 调试与验证:通过绘制中间结果,可以快速验证算法是否正常工作。 +### 1.2 OpenCV 的画图函数 +OpenCV 提供了多种画图函数,用于在图像上绘制不同的几何图形和文字。这些函数的核心思想是操作像素矩阵,从而生成所需的图形。 + +--- + +## 2.API文档 +### 2.1 依赖的头文件 +```c++ +#include +``` +### 2.2 画线 +```c++ +cv::line(image, cv::Point(x0, y0), cv::Point(x1, y1), color, thickness); +``` +- 参数说明: + - image:画图的图像 + - cv::Point(x0, y0):起点 + - cv::Point(x1, y1):终点 + - color:线的颜色 + - thickness:线的粗细 + +### 2.3 画矩形框 +```c++ +cv::rectangle(image, cv::Rect(x, y, w, h), color, thickness); +``` +- 参数说明: + - image:画图的图像 + - cv::Rect(x, y, w, h):矩形的左上角坐标和宽高 + - color:线的颜色 + - thickness:线的粗细 +### 2.4 画圆 +```c++ +cv::circle(image, cv::Point(x, y), radius, color, thickness); +``` +- 参数说明: + - image:画图的图像 + - cv::Point(x, y):圆心坐标 + - radius:圆的半径 + - color:线的颜色 + - thickness:线的粗细 +### 2.5 写字 +```c++ +cv::putText(image, text, cv::Point(x, y), font, fontScale, color, thickness); +``` +- 参数说明: + - image:画图的图像 + - text:要写的文字 + - cv::Point(x, y):文字的左上角坐标 + - font:字体 + - fontScale:字体大小 + - color:线的颜色 + - thickness:线的粗细 + +--- + +## 3. 综合代码解析 + +### 3.1 流程图 + + + +### 3.2 代码解析 +- 创建512*512黑色画布 +```c++ +// 创建一个空白图像 (512x512,3通道) +cv::Mat image = cv::Mat::zeros(512, 512, CV_8UC3); +``` +- 绘制蓝色线条 +```c++ +// 画线 +cv::line(image, cv::Point(10, 10), cv::Point(100, 100), cv::Scalar(255, 0, 0), 2); +``` +- 绘制绿色矩形 +```c++ +// 画矩形 +cv::rectangle(image, cv::Rect(50, 50, 100, 100), cv::Scalar(0, 255, 0), 2); +``` +- 绘制红色实心圆 +```c++ +// 画圆 +cv::circle(image, cv::Point(200, 200), 30, cv::Scalar(0, 0, 255), -1); +``` +- 绘制黄色十字 +```c++ +int cross_x = 300, cross_y = 300, cross_size = 10; +cv::line(image, cv::Point(cross_x - cross_size, cross_y), cv::Point(cross_x + cross_size, cross_y), cv::Scalar(255, 255, 0), 2); +cv::line(image, cv::Point(cross_x, cross_y - cross_size), cv::Point(cross_x, cross_y + cross_size), cv::Scalar(255, 255, 0), 2); +``` +- 添加黑色文字 +```c++ +// 写字 +cv::putText(image, "Hello, OpenCV!", cv::Point(10, 400), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0, 0, 0), 2); +``` +### 3.3 代码实现 +```c++ +#include +#include + +int main() +{ + // 创建一个空白图像 (512x512,3通道) + cv::Mat image = cv::Mat::zeros(512, 512, CV_8UC3); + + // 画线 + cv::line(image, cv::Point(10, 10), cv::Point(100, 100), cv::Scalar(255, 0, 0), 2); + + // 画矩形 + cv::rectangle(image, cv::Rect(50, 50, 100, 100), cv::Scalar(0, 255, 0), 2); + + // 画圆 + cv::circle(image, cv::Point(200, 200), 30, cv::Scalar(0, 0, 255), -1); + + // 画十字 + int cross_x = 300, cross_y = 300, cross_size = 10; + cv::line(image, cv::Point(cross_x - cross_size, cross_y), cv::Point(cross_x + cross_size, cross_y), cv::Scalar(255, 255, 0), 2); + cv::line(image, cv::Point(cross_x, cross_y - cross_size), cv::Point(cross_x, cross_y + cross_size), cv::Scalar(255, 255, 0), 2); + + // 写字 + cv::putText(image, "Hello, OpenCV!", cv::Point(10, 400), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0, 0, 0), 2); + + // 显示图像 + cv::imshow("Drawing Example", image); + cv::waitKey(0); + + return 0; +} +``` + +--- + +## 4. 编译过程 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时以正确连接开发板。 +### 4.2 Cmake介绍 +```cmake +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test_draw) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 画图示例 +add_executable(Test-Draw Draw.cc) +target_include_directories(Test-Draw PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-Draw PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-Draw + RUNTIME DESTINATION . +) +``` +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/B03_Draw +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` + +在执行完上述命令后,会在build目录下生成可执行文件。 + +--- + +## 5. 例程运行展示 +### 5.1 运行过程 +在凌智视觉模块中输入以下命令: +```shell +chmod 777 Test-Draw +./Test-Draw +``` +### 5.2 运行结果 +运行例程代码,我们可以得到下面的图片。 +![](./images/Drawing.png) + +可以看见其中就包含了画图的基本元素存在。 + +--- + +## 6. 总结 +通过上述内容,我们介绍了 OpenCV 中常用的画图功能及其参数说明,包括: + +- 画线:绘制直线。 +- 画矩形框:绘制矩形框并支持填充。 +- 画圆:绘制圆形并支持填充。 +- 写字:在图像上绘制文字。 + +结合综合示例,您可以轻松地在图像上绘制各种几何图形和文字,从而实现更直观的视觉反馈。这些功能在目标检测、图像标注、调试等场景中非常实用。 diff --git a/Cpp_example/B03_Draw/images/All.png b/Cpp_example/B03_Draw/images/All.png new file mode 100755 index 0000000000000000000000000000000000000000..9611d592215d66faa91f5372bf9fd35e3cc04bce Binary files /dev/null and b/Cpp_example/B03_Draw/images/All.png differ diff --git a/Cpp_example/B03_Draw/images/Drawing.png b/Cpp_example/B03_Draw/images/Drawing.png new file mode 100755 index 0000000000000000000000000000000000000000..44fc4b55e262dd38b5bdf8863b58ebf661019eea Binary files /dev/null and b/Cpp_example/B03_Draw/images/Drawing.png differ diff --git a/Cpp_example/C01_find_blobs/CMakeLists.txt b/Cpp_example/C01_find_blobs/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ca439b7bdfb43a454fd9c43a12af56f375584c66 --- /dev/null +++ b/Cpp_example/C01_find_blobs/CMakeLists.txt @@ -0,0 +1,33 @@ +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test-find-blobs) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 基本图像处理示例 +add_executable(Test-find-blobs find_blobs.cc) +target_include_directories(Test-find-blobs PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-find-blobs PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-find-blobs + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/C01_find_blobs/README.md b/Cpp_example/C01_find_blobs/README.md new file mode 100755 index 0000000000000000000000000000000000000000..df35f051c531cb74f50374a2e82f55283dcc8a25 --- /dev/null +++ b/Cpp_example/C01_find_blobs/README.md @@ -0,0 +1,350 @@ +# 寻找色块 +在传统计算机视觉场景中,颜色识别是目标检测和分割的重要手段之一。通过识别特定颜色的色块,可以在相对纯净的背景下快速定位目标区域。本章节提供了一个简单的色块识别案例,并将其封装为一个自定义函数 find_blobs,方便快速移植和使用。 +## 1. 基本知识讲解 +### 1.1 色块识别的重要性 +- 颜色特征提取:颜色是一种重要的视觉特征,尤其在背景较为单一的情况下,能够快速区分目标区域。 +- 应用场景:广泛应用于机器人导航、工业自动化、物体跟踪等领域。 +- HSV 颜色空间:相比于 RGB 颜色空间,HSV 更适合用于颜色识别,因为它可以将颜色信息(Hue)、饱和度(Saturation)和亮度(Value)分离,便于设置阈值。 +### 1.2 色块识别的流程 +- 获取图像。 +- 将图像从 BGR 转换为 HSV 颜色空间。 +- 创建二值掩码,筛选出符合颜色范围的像素。 +- 使用形态学操作清除噪声。 +- 查找轮廓并筛选符合条件的色块。 +- 计算外接矩形和中心点。 +- 绘制结果并输出。 + +--- + +## 2. API文档 +### 2.1 头文件 +```c++ +#include +``` +### 2.2 生成掩码 +```c++ +cv::inRange(src, lowerb, upperb, dst); +``` +- 参数说明: + - src:输入图像,可以是单通道或三通道的图像。 + - lowerb:颜色下界,是一个Scalar对象,表示要查找的颜色的下限。 + - upperb:颜色上界,是一个Scalar对象,表示要查找的颜色的上限。 + - dst:输出图像,是一个单通道的8位无符号整数图像,表示生成的掩码。 +- 返回值: + - 无 +### 2.3 创建形态学操作所需的结构元素核 +```c++ +cv::getStructuringElement(shape, ksize, anchor); +``` +- 参数说明: + - shape:核形状,可以是RECT、CROSS、ELLIPSE等。 + - ksize:核大小,是一个Size对象,表示核的宽度和高度。 + - anchor:锚点,是一个Point对象,表示核的锚点位置。 +- 返回值: + - 返回一个核,是一个Mat对象。 +### 2.4 形态学操作:清除噪声 +```c++ +cv::morphologyEx(src, dst, op, kernel, anchor, iterations, borderType, borderValue); +``` +- 参数说明: + - src:输入图像,可以是单通道或三通道的图像。 + - dst:输出图像,是一个单通道的8位无符号整数图像,表示生成的掩码。 + - op:操作类型,可以是OPEN、CLOSE、GRADIENT、TOPHAT、BLACKHAT等。 + - kernel:核,是一个Mat对象,表示形态学操作的核。 + - anchor:锚点,是一个Point对象,表示核的锚点位置。 + - iterations:迭代次数,是一个整数,表示形态学操作的迭代次数。 + - borderType:边界类型,可以是BORDER_CONSTANT、BORDER_REPLICATE、BORDER_REFLECT、BORDER_WRAP、BORDER_REFLECT_101等。 + - borderValue:边界值,是一个Scalar对象,表示边界区域的值。 +- 返回值: + - 无 +### 2.5 查找轮廓 +```c++ +cv::findContours(image, contours, hierarchy, mode, method, offset); +``` +- 参数说明: + - image:输入图像,可以是单通道或三通道的图像。 + - contours:输出参数,是一个vector>对象,表示轮廓的集合。 + - hierarchy:输出参数,是一个vector对象,表示轮廓的层级关系。 + - mode:轮廓发现模式,可以是RETR_EXTERNAL、RETR_LIST、RETR_CCOMP、RETR_TREE等。 + - method:轮廓 approximation 方法,可以是CHAIN_APPROX_NONE、CHAIN_APPROX_SIMPLE、CHAIN_APPROX_TC89_L1、CHAIN_APPROX_TC89_KCOS等。 + - offset:轮廓偏移量,是一个Point对象,表示轮廓的偏移量。 +- 返回值: + - 返回一个整数,表示轮廓的数量。 +### 2.6 获取轮廓的外接矩形 +```c++ +cv::boundingRect(points); +``` +- 参数说明: + - points:输入参数,是一个vector对象,表示轮廓的点集合。 +- 返回值: + - 返回一个Rect对象,表示轮廓的外接矩形。 +### 2.7 计算矩阵矩 +```c++ +cv::moments(array, binaryImage); +``` +- 参数说明: + - array:输入参数,是一个Mat对象,表示输入的矩阵。 + - binaryImage:输入参数,是一个布尔值,表示是否将输入的矩阵转换为二值矩阵。 +- 返回值: + - 返回一个 Moments对象,表示矩阵的矩。 +### 2.8 绘制矩形框 +```c++ +cv::rectangle(img, pt1, pt2, color, thickness, lineType, shift); +``` +- 参数说明: + - img:输入参数,是一个Mat对象,表示输入的图像。 + - pt1:输入参数,是一个Point对象,表示矩形的左上角点。 + - pt2:输入参数,是一个Point对象,表示矩形的右下角点。 + - color:输入参数,是一个Scalar对象,表示矩形的颜色。 + - thickness:输入参数,是一个整数,表示矩形的线宽。 + - lineType:输入参数,是一个整数,表示矩形的线类型。 + - shift:输入参数,是一个整数,表示坐标的精度。 +- 返回值: + - 无 +### 2.9 绘制圆 +```c++ +cv::circle(img, center, radius, color, thickness, lineType, shift); +``` +- 参数说明: + - img:输入参数,是一个Mat对象,表示输入的图像。 + - center:输入参数,是一个Point对象,表示圆心。 + - radius:输入参数,是一个整数,表示圆的半径。 + - color:输入参数,是一个Scalar对象,表示圆的颜色。 + - thickness:输入参数,是一个整数,表示圆的线宽。 + - lineType:输入参数,是一个整数,表示圆的线类型。 + - shift:输入参数,是一个整数,表示坐标的精度。 +- 返回值: + - 无 + +--- + +## 3. 综合代码介绍 + +### 3.1 流程图 + + + +### 3.2 核心代码解析 +- BGR转HSV +```c++ +cv::cvtColor(image, hsv_image, cv::COLOR_BGR2HSV); +``` +- 阈值分割 +```c++ +cv::inRange(hsv_image, lower_bound, upper_bound, mask);); +``` +- 形态学处理 +```c++ +cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(kernel_size, kernel_size)); +cv::morphologyEx(mask, mask, cv::MORPH_OPEN, kernel); +``` +- 查找轮廓 +```c++ +cv::findContours(mask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); +``` +自定义函数参数如下所示 +```c++ +std::vector> find_blobs( + const cv::Mat &image, + const cv::Scalar &lower_bound, + const cv::Scalar &upper_bound, + int min_area = 100, + int kernel_size = 5); +``` +- 参数说明: + - image:输入参数,是一个Mat对象,表示输入的图像。 + - lower_bound:输入参数,是一个Scalar对象,表示颜色下界。 + - upper_bound:输入参数,是一个Scalar对象,表示颜色上界。 + - min_area:输入参数,是一个整数,表示最小面积。 + - kernel_size:输入参数,是一个整数,表示核大小。 +- 返回值: + - 返回一个vector>对象,表示找到的色块的点集合。 + +### 3.3 完整代码实现 +```c++ +#include +#include +#include +#include + +std::vector> find_blobs( + const cv::Mat &image, + const cv::Scalar &lower_bound, + const cv::Scalar &upper_bound, + int min_area = 100, + int kernel_size = 5) +{ + + // 转换为 HSV 颜色空间 + cv::Mat hsv_image; + cv::cvtColor(image, hsv_image, cv::COLOR_BGR2HSV); + + // 创建二值掩码 + cv::Mat mask; + cv::inRange(hsv_image, lower_bound, upper_bound, mask); + + // 形态学操作:清除噪声 + cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(kernel_size, kernel_size)); + cv::morphologyEx(mask, mask, cv::MORPH_OPEN, kernel); + + // 查找轮廓 + std::vector> contours; + cv::findContours(mask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + + // 筛选符合条件的色块 + std::vector> filtered_contours; + for (const auto &contour : contours) + { + cv::Rect bounding_rect = cv::boundingRect(contour); + if (bounding_rect.area() >= min_area) + { + filtered_contours.push_back(contour); + } + } + return filtered_contours; +} + +int main() +{ + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + + cv::VideoCapture cap; + int width = 640; // 设置摄像头分辨率宽度 + int height = 480; // 设置摄像头分辨率高度 + cap.set(cv::CAP_PROP_FRAME_WIDTH, width); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); + + // 打开摄像头设备 + cap.open(0); // 参数 0 表示默认摄像头设备 + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + while (true) + { + cv::Mat image; // 存储每一帧图像 + cap >> image; // 获取新的一帧 + + // 定义颜色阈值(例如红色) + cv::Scalar lower_red(170, 100, 100); // 红色下界 + cv::Scalar upper_red(179, 255, 255); // 红色上界 + + // 调用 find_blobs 函数 + int min_area = 100; // 最小面积阈值 + int kernel_size = 1; // 形态学操作核大小 + std::vector> blobs = find_blobs(image, lower_red, upper_red, min_area, kernel_size); + + // 绘制和打印检测到的色块 + for (const auto &contour : blobs) + { + // 计算外接矩形框 + cv::Rect bounding_rect = cv::boundingRect(contour); + + // 绘制矩形框 + cv::rectangle(image, bounding_rect, cv::Scalar(0, 255, 0), 2); + + // 计算中心点 + cv::Moments moments = cv::moments(contour); + int cx = moments.m10 / moments.m00; + int cy = moments.m01 / moments.m00; + + // 绘制中心点 + cv::circle(image, cv::Point(cx, cy), 5, cv::Scalar(0, 0, 255), -1); + + // 打印信息 + std::cout << "Blob detected at (" << cx << ", " << cy << ") with area " << bounding_rect.area() << std::endl; + } + edit.Print(image); + } + return 0; +} +``` + +--- + +## 4. 编译过程 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时以正确连接开发板。 +### 4.2 Cmake介绍 +```cmake +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test-find-blobs) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 基本图像处理示例 +add_executable(Test-find-blobs find_blobs.cc) +target_include_directories(Test-find-blobs PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-find-blobs PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-find-blobs + RUNTIME DESTINATION . +) +``` +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/C01_find_blobs +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` + +在执行完上述命令后,会在build目录下生成可执行文件。 + +--- + +## 5. 例程运行示例 +### 5.1 运行过程 +```shell +chmod 777 Test-find-blobs +./Test-find-blobs +``` +### 5.2 运行效果 +![识别效果](./images/findblob.png) + +--- + +## 6. 总结 +通过上述内容,我们详细介绍了色块识别的流程及相关 API 的使用方法,包括: + +- 生成掩码:筛选符合颜色范围的像素。 +- 形态学操作:清除噪声。 +- 查找轮廓:获取目标区域的轮廓。 +- 筛选与绘制:筛选符合条件的色块并绘制外接矩形和中心点。 + + diff --git a/Cpp_example/C01_find_blobs/find_blobs.cc b/Cpp_example/C01_find_blobs/find_blobs.cc new file mode 100755 index 0000000000000000000000000000000000000000..55f70879431ee888154183365ac1e0cbb286f40d --- /dev/null +++ b/Cpp_example/C01_find_blobs/find_blobs.cc @@ -0,0 +1,103 @@ +#include +#include +#include +#include + +std::vector> find_blobs( + const cv::Mat &image, + const cv::Scalar &lower_bound, + const cv::Scalar &upper_bound, + int min_area = 100, + int kernel_size = 5) +{ + + // 转换为 HSV 颜色空间 + cv::Mat hsv_image; + cv::cvtColor(image, hsv_image, cv::COLOR_BGR2HSV); + + // 创建二值掩码 + cv::Mat mask; + cv::inRange(hsv_image, lower_bound, upper_bound, mask); + + // 形态学操作:清除噪声 + cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(kernel_size, kernel_size)); + cv::morphologyEx(mask, mask, cv::MORPH_OPEN, kernel); + + // 查找轮廓 + std::vector> contours; + cv::findContours(mask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + + // 筛选符合条件的色块 + std::vector> filtered_contours; + for (const auto &contour : contours) + { + cv::Rect bounding_rect = cv::boundingRect(contour); + if (bounding_rect.area() >= min_area) + { + filtered_contours.push_back(contour); + } + } + return filtered_contours; +} + +int main() +{ + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + + cv::VideoCapture cap; + int width = 640; // 设置摄像头分辨率宽度 + int height = 480; // 设置摄像头分辨率高度 + cap.set(cv::CAP_PROP_FRAME_WIDTH, width); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); + + // 打开摄像头设备 + cap.open(0); // 参数 0 表示默认摄像头设备 + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + while (true) + { + cv::Mat image; // 存储每一帧图像 + cap >> image; // 获取新的一帧 + + // 定义颜色阈值(例如红色) + cv::Scalar lower_red(170, 100, 100); // 红色下界 + cv::Scalar upper_red(179, 255, 255); // 红色上界 + + // 调用 find_blobs 函数 + int min_area = 100; // 最小面积阈值 + int kernel_size = 1; // 形态学操作核大小 + std::vector> blobs = find_blobs(image, lower_red, upper_red, min_area, kernel_size); + + // 绘制和打印检测到的色块 + for (const auto &contour : blobs) + { + // 计算外接矩形框 + cv::Rect bounding_rect = cv::boundingRect(contour); + + // 绘制矩形框 + cv::rectangle(image, bounding_rect, cv::Scalar(0, 255, 0), 2); + + // 计算中心点 + cv::Moments moments = cv::moments(contour); + int cx = moments.m10 / moments.m00; + int cy = moments.m01 / moments.m00; + + // 绘制中心点 + cv::circle(image, cv::Point(cx, cy), 5, cv::Scalar(0, 0, 255), -1); + + // 打印信息 + std::cout << "Blob detected at (" << cx << ", " << cy << ") with area " << bounding_rect.area() << std::endl; + } + edit.Print(image); + } + return 0; +} \ No newline at end of file diff --git a/Cpp_example/C01_find_blobs/images/1.png b/Cpp_example/C01_find_blobs/images/1.png new file mode 100755 index 0000000000000000000000000000000000000000..1ac6d28c3c16b2e336927493dce5aafa4dd87bb2 Binary files /dev/null and b/Cpp_example/C01_find_blobs/images/1.png differ diff --git a/Cpp_example/C01_find_blobs/images/findblob.png b/Cpp_example/C01_find_blobs/images/findblob.png new file mode 100755 index 0000000000000000000000000000000000000000..3db42b20a8bfd48452a0846c8de7b0ee208fad2f Binary files /dev/null and b/Cpp_example/C01_find_blobs/images/findblob.png differ diff --git a/Cpp_example/C02_TemplateMatching/CMakeLists.txt b/Cpp_example/C02_TemplateMatching/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..826a02a059d9cdc863e1f0cc683681ec55805113 --- /dev/null +++ b/Cpp_example/C02_TemplateMatching/CMakeLists.txt @@ -0,0 +1,33 @@ +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test-TemplateMatching) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 基本图像处理示例 +add_executable(Test-TemplateMatching TemplateMatching.cc) +target_include_directories(Test-TemplateMatching PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-TemplateMatching PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-TemplateMatching + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/C02_TemplateMatching/README.md b/Cpp_example/C02_TemplateMatching/README.md new file mode 100755 index 0000000000000000000000000000000000000000..ed0758d7d3f4077a27a49b4f8c775e4e6de137ac --- /dev/null +++ b/Cpp_example/C02_TemplateMatching/README.md @@ -0,0 +1,322 @@ +# 模板匹配 +模板匹配是一种在图像中寻找特定模式的技术。它通过滑动一个模板图像(较小的图像)在输入图像上进行比较,找到最相似的区域。本章节提供了一个简单的模板匹配案例,并将其封装为一个自定义函数 performTemplateMatching,方便快速移植和使用。 +## 1. 基本知识讲解 +### 1.1 模板匹配的重要性 +- 目标检测:模板匹配可以用于检测图像中的特定对象。 +- 应用场景:广泛应用于物体识别、工业自动化、机器人导航等领域。 +- 局限性:模板匹配对旋转、缩放和光照变化较为敏感,因此通常需要结合其他技术来提高鲁棒性。 +### 1.2 模板匹配的流程 +- 获取输入图像和模板图像。 +- 使用模板匹配算法(如归一化互相关 NCC)计算相似度。 +- 找到匹配结果中的最大值及其位置。 +- 根据相似度阈值判断匹配是否成功。 +- 绘制矩形框标记匹配区域并显示结果。 + +--- + +## 2. API 文档 +### 2.1 头文件 +```c++ +#include +``` +### 2.2 在输入图像中搜索模板图像的位置 +```c++ +void matchTemplate(InputArray image, InputArray templ, OutputArray result, int method); +``` +- 参数: + - image:待搜索的图像。 + - templ:模板图像。 + - result:搜索结果。 + - method:搜索方法,可以是 TM_SQDIFF、TM_SQDIFF_NORMED、TM_CCORR、TM_CCORR_NORMED、TM_CCOEFF、TM_CCOEFF_NORMED 之一。 +- 返回值: + - 无 +### 2.3 获取匹配结果中的最大值及其位置 +```c++ +void minMaxLoc(InputArray src, double *minVal, double *maxVal, Point *minLoc, Point *maxLoc, InputArray mask = noArray()); +``` +- 参数: + - src:输入矩阵。 + - minVal:最小值。 + - maxVal:最大值。 + - minLoc:最小值位置。 + - maxLoc:最大值位置。 + - mask:可选的掩码矩阵,用于指定要搜索的像素范围。 +- 返回值: + - 无 + +--- + +## 3. 综合代码解析 + +### 3.1 流程图 + + + +### 3.2 代码解释 +- 模板匹配函数应用 +```c++ +double similarityThreshold = 0.7; // 相似度阈值 +bool useGrayscale = false; // 是否使用灰度处理 +bool matchSuccess = performTemplateMatching(img, templ, similarityThreshold, img, useGrayscale); +``` +模板匹配函数具体参数定义如下所示。 +```c++ +bool performTemplateMatching(const Mat &inputImage, const Mat &templateImage, double threshold, Mat &outputImage,bool isGrayscale = false); +``` +- 参数: + - inputImage:输入图像。 + - templateImage:模板图像。 + - threshold:相似度阈值。 + - outputImage:输出图像。 + - isGrayscale:是否进行灰度处理。 +- 返回值: + - true:匹配成功。 + - false:匹配失败。 + +- 输出结果 +```c++ +edit.Print(img); +``` +### 3.3 代码实现 +```c++ +#include +#include +#include + +using namespace cv; +using namespace std; + +// 模板匹配函数 +bool performTemplateMatching(const Mat &inputImage, const Mat &templateImage, double threshold, Mat &outputImage, bool isGrayscale = false) +{ + // 确保模板图像比输入图像小 + if (templateImage.rows > inputImage.rows || templateImage.cols > inputImage.cols) + { + cout << "模板图像不能大于输入图像!" << endl; + return false; + } + + // 创建用于匹配的图像副本 + Mat templ = templateImage.clone(); + Mat img = inputImage.clone(); + + // 如果选择灰度处理,则将输入图像和模板图像转换为灰度 + if (isGrayscale) + { + if (img.channels() == 3) + { + cvtColor(img, img, COLOR_BGR2GRAY); + } + if (templ.channels() == 3) + { + cvtColor(templ, templ, COLOR_BGR2GRAY); + } + } + + // 打印调试信息 + cout << "输入图像尺寸: " << img.size() << ", 通道数: " << img.channels() << endl; + cout << "模板图像尺寸: " << templ.size() << ", 通道数: " << templ.channels() << endl; + + // 创建结果矩阵,用于存储匹配结果 + int resultRows = img.rows - templ.rows + 1; + int resultCols = img.cols - templ.cols + 1; + if (resultRows <= 0 || resultCols <= 0) + { + cout << "结果矩阵尺寸无效!请检查输入图像和模板图像的尺寸。" << endl; + return false; + } + Mat result(resultRows, resultCols, CV_32FC1); + + // 使用归一化互相关(NCC)方法进行模板匹配 + double start = static_cast(getTickCount()); + matchTemplate(img, templ, result, TM_CCOEFF_NORMED); + double end = static_cast(getTickCount()); + double elapsedTime = (end - start) / getTickFrequency(); + cout << "matchTemplate 运行时间: " << elapsedTime << " 秒" << endl; + + // 找到匹配结果中的最大值及其位置 + double minVal, maxVal; + Point minLoc, maxLoc; + minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc); + + // 输出相似度阈值判断 + if (maxVal >= threshold) + { + cout << "匹配成功!最大相似度: " << maxVal << endl; + + // 绘制矩形框标记匹配区域 + rectangle(outputImage, maxLoc, Point(maxLoc.x + templ.cols, maxLoc.y + templ.rows), Scalar(0, 255, 0), 2); + return true; + } + else + { + cout << "匹配失败!最大相似度: " << maxVal << endl; + return false; + } +} + +int main(int argc, char **argv) +{ + // 检查命令行参数数量是否正确 + if (argc != 2) + { + cout << "用法: " << argv[0] << " <模板图像路径>" << endl; + return -1; + } + + // 声明并初始化变量 + string templateImagePath = argv[1]; // 模板图像路径 + + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + + // 初始化摄像头 + cv::VideoCapture cap; + int width = 320; // 设置摄像头分辨率宽度 + int height = 240; // 设置摄像头分辨率高度 + cap.set(cv::CAP_PROP_FRAME_WIDTH, width); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); + + // 打开摄像头设备 + cap.open(0); // 参数 0 表示默认摄像头设备 + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + // 加载模板图像 + Mat templ = imread(templateImagePath, IMREAD_COLOR); // 默认加载彩色模板 + if (templ.empty()) + { + std::cerr << "Error: Could not load template image." << std::endl; + return EXIT_FAILURE; + } + + while (true) + { + // 读取输入图像 + cv::Mat img; + cap >> img; + if (img.empty()) + { + std::cerr << "Error: Captured frame is empty." << std::endl; + break; + } + + // 调用模板匹配函数 + double similarityThreshold = 0.7; // 相似度阈值 + bool useGrayscale = false; // 是否使用灰度处理 + bool matchSuccess = performTemplateMatching(img, templ, similarityThreshold, img, useGrayscale); + + // 显示结果 + imshow("Template Matching Result", img); + if (waitKey(1) == 27) + { // 按 ESC 键退出 + break; + } + + // 输出图像 + edit.Print(img); + } + + return 0; +} +``` + +--- + +## 4. 编译过程 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时以正确连接开发板。 +### 4.2 Cmake介绍 +```cmake +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test-TemplateMatching) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 基本图像处理示例 +add_executable(Test-TemplateMatching TemplateMatching.cc) +target_include_directories(Test-TemplateMatching PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-TemplateMatching PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-TemplateMatching + RUNTIME DESTINATION . +) +``` +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/A01_capture +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` + +在执行完上述命令后,会在build目录下生成可执行文件。 + +--- + +## 5. 例程运行示例 +### 5.1 准备工作 +- 下载凌智视觉模块图片传输助手:[点击下载](https://gitee.com/LockzhinerAI/LockzhinerVisionModule/releases/download/v0.0.0/LockzhinerVisionModuleImageFetcher.exe) +- 我们首先需要进行图像采集,采集一个480*320分辨率下的模板照片。 +### 5.2 运行过程 +在凌智视觉模块中输入以下命令: +```shell +chmod 777 Test-TemplateMatching template_0.png +./Test-TemplateMatching +``` +### 5.3 运行结果 +- 运行结果如下: +![](./images/2025_0421_11_28_33_927.png) + +- 模板照片为 + +![](./images/template_1.png) + +--- + +## 6. 总结 +通过上述内容,我们详细介绍了模板匹配的流程及相关 API 的使用方法,包括: +- 图像读取:加载输入图像和模板图像。 +- 模板匹配:使用归一化互相关方法计算相似度。 +- 查找极值:获取最佳匹配位置。 +- 绘制与显示:标记匹配区域并显示结果。 +希望这份文档能帮助您更好地理解和实现模板匹配功能! +注意事项: +- 在本次例程中摄像头需要运行在480*320分辨率下,否则会出现帧率过低的情况。如需更高帧率,可酌情再降低分辨率。 + diff --git a/Cpp_example/C02_TemplateMatching/TemplateMatching.cc b/Cpp_example/C02_TemplateMatching/TemplateMatching.cc new file mode 100755 index 0000000000000000000000000000000000000000..3bed879933cdcdc8df449c730794e4c3d55e6f33 --- /dev/null +++ b/Cpp_example/C02_TemplateMatching/TemplateMatching.cc @@ -0,0 +1,148 @@ +#include +#include +#include + +using namespace cv; +using namespace std; + +// 模板匹配函数 +bool performTemplateMatching(const Mat &inputImage, const Mat &templateImage, double threshold, Mat &outputImage, bool isGrayscale = false) +{ + // 确保模板图像比输入图像小 + if (templateImage.rows > inputImage.rows || templateImage.cols > inputImage.cols) + { + cout << "模板图像不能大于输入图像!" << endl; + return false; + } + + // 创建用于匹配的图像副本 + Mat templ = templateImage.clone(); + Mat img = inputImage.clone(); + + // 如果选择灰度处理,则将输入图像和模板图像转换为灰度 + if (isGrayscale) + { + if (img.channels() == 3) + { + cvtColor(img, img, COLOR_BGR2GRAY); + } + if (templ.channels() == 3) + { + cvtColor(templ, templ, COLOR_BGR2GRAY); + } + } + + // 打印调试信息 + cout << "输入图像尺寸: " << img.size() << ", 通道数: " << img.channels() << endl; + cout << "模板图像尺寸: " << templ.size() << ", 通道数: " << templ.channels() << endl; + + // 创建结果矩阵,用于存储匹配结果 + int resultRows = img.rows - templ.rows + 1; + int resultCols = img.cols - templ.cols + 1; + if (resultRows <= 0 || resultCols <= 0) + { + cout << "结果矩阵尺寸无效!请检查输入图像和模板图像的尺寸。" << endl; + return false; + } + Mat result(resultRows, resultCols, CV_32FC1); + + // 使用归一化互相关(NCC)方法进行模板匹配 + double start = static_cast(getTickCount()); + matchTemplate(img, templ, result, TM_CCOEFF_NORMED); + double end = static_cast(getTickCount()); + double elapsedTime = (end - start) / getTickFrequency(); + cout << "matchTemplate 运行时间: " << elapsedTime << " 秒" << endl; + + // 找到匹配结果中的最大值及其位置 + double minVal, maxVal; + Point minLoc, maxLoc; + minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc); + + // 输出相似度阈值判断 + if (maxVal >= threshold) + { + cout << "匹配成功!最大相似度: " << maxVal << endl; + + // 绘制矩形框标记匹配区域 + rectangle(outputImage, maxLoc, Point(maxLoc.x + templ.cols, maxLoc.y + templ.rows), Scalar(0, 255, 0), 2); + return true; + } + else + { + cout << "匹配失败!最大相似度: " << maxVal << endl; + return false; + } +} + +int main(int argc, char **argv) +{ + // 检查命令行参数数量是否正确 + if (argc != 2) + { + cout << "用法: " << argv[0] << " <模板图像路径>" << endl; + return -1; + } + + // 声明并初始化变量 + string templateImagePath = argv[1]; // 模板图像路径 + + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + + // 初始化摄像头 + cv::VideoCapture cap; + int width = 320; // 设置摄像头分辨率宽度 + int height = 240; // 设置摄像头分辨率高度 + cap.set(cv::CAP_PROP_FRAME_WIDTH, width); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); + + // 打开摄像头设备 + cap.open(0); // 参数 0 表示默认摄像头设备 + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + // 加载模板图像 + Mat templ = imread(templateImagePath, IMREAD_COLOR); // 默认加载彩色模板 + if (templ.empty()) + { + std::cerr << "Error: Could not load template image." << std::endl; + return EXIT_FAILURE; + } + + while (true) + { + // 读取输入图像 + cv::Mat img; + cap >> img; + if (img.empty()) + { + std::cerr << "Error: Captured frame is empty." << std::endl; + break; + } + + // 调用模板匹配函数 + double similarityThreshold = 0.7; // 相似度阈值 + bool useGrayscale = false; // 是否使用灰度处理 + bool matchSuccess = performTemplateMatching(img, templ, similarityThreshold, img, useGrayscale); + + // 显示结果 + imshow("Template Matching Result", img); + if (waitKey(1) == 27) + { // 按 ESC 键退出 + break; + } + + // 输出图像 + edit.Print(img); + } + + return 0; +} \ No newline at end of file diff --git a/Cpp_example/C02_TemplateMatching/images/2025_0421_11_28_33_927.png b/Cpp_example/C02_TemplateMatching/images/2025_0421_11_28_33_927.png new file mode 100755 index 0000000000000000000000000000000000000000..b43ed71645f279086376ba196364b20e1ddd14a6 Binary files /dev/null and b/Cpp_example/C02_TemplateMatching/images/2025_0421_11_28_33_927.png differ diff --git a/Cpp_example/C02_TemplateMatching/images/All.png b/Cpp_example/C02_TemplateMatching/images/All.png new file mode 100755 index 0000000000000000000000000000000000000000..c69f2511772d2254750908cbe70d96ca667d86e8 Binary files /dev/null and b/Cpp_example/C02_TemplateMatching/images/All.png differ diff --git a/Cpp_example/C02_TemplateMatching/images/template_0.png b/Cpp_example/C02_TemplateMatching/images/template_0.png new file mode 100755 index 0000000000000000000000000000000000000000..503858b8d2cad3393b3cd5a3eee7b9a917188e5a Binary files /dev/null and b/Cpp_example/C02_TemplateMatching/images/template_0.png differ diff --git a/Cpp_example/C02_TemplateMatching/images/template_1.png b/Cpp_example/C02_TemplateMatching/images/template_1.png new file mode 100755 index 0000000000000000000000000000000000000000..e0ca65cd1b7263db3b082858beef9a4600ad3189 Binary files /dev/null and b/Cpp_example/C02_TemplateMatching/images/template_1.png differ diff --git a/Cpp_example/C02_TemplateMatching/images/template_2.png b/Cpp_example/C02_TemplateMatching/images/template_2.png new file mode 100755 index 0000000000000000000000000000000000000000..3febb07f45750f0e801b6b004f26b4204da815e9 Binary files /dev/null and b/Cpp_example/C02_TemplateMatching/images/template_2.png differ diff --git a/Cpp_example/C02_TemplateMatching/images/template_3.png b/Cpp_example/C02_TemplateMatching/images/template_3.png new file mode 100755 index 0000000000000000000000000000000000000000..56d8787de0c6b389a3c5d215446649ebce2ebf42 Binary files /dev/null and b/Cpp_example/C02_TemplateMatching/images/template_3.png differ diff --git a/Cpp_example/C03_Template_Matching_more/CMakeLists.txt b/Cpp_example/C03_Template_Matching_more/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8aef5570821fa1cbb7291b36e63be661e684d5ba --- /dev/null +++ b/Cpp_example/C03_Template_Matching_more/CMakeLists.txt @@ -0,0 +1,33 @@ +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test-TemplateMatching-more) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 基本图像处理示例 +add_executable(Test-TemplateMatching-more Template_Matching_more.cc) +target_include_directories(Test-TemplateMatching-more PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-TemplateMatching-more PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-TemplateMatching-more + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/C03_Template_Matching_more/README.md b/Cpp_example/C03_Template_Matching_more/README.md new file mode 100755 index 0000000000000000000000000000000000000000..ef64d3d598b1de4bddb49b4cfa2e463066b56962 --- /dev/null +++ b/Cpp_example/C03_Template_Matching_more/README.md @@ -0,0 +1,305 @@ +# 多模板匹配 +多模板匹配是一种在图像中同时寻找多个模板的技术。通过对每个模板逐一进行匹配,找到与输入图像最相似的区域,并标记出匹配度最高的结果。本章节提供了一个简单的多模板匹配案例,并将其封装为一个自定义函数 multiTemplateMatching,方便快速移植和使用。 + +## 1. 基本知识讲解 +### 1.1 多模板匹配的重要幸 +- 目标检测:多模板匹配可以用于检测图像中的多个特定对象。 +- 应用场景:广泛应用于物体识别、工业自动化、机器人导航等领域。 +- 优势:支持多个模板的同时匹配,能够灵活处理多种目标。 +- 局限性:对旋转、缩放和光照变化较为敏感,因此通常需要结合其他技术来提高鲁棒性。 +### 1.2 多模板匹配的流程 +- 获取输入图像和多个模板图像。 +- 遍历每个模板,逐一执行模板匹配算法(如归一化互相关 NCC)。 +- 找到每个模板匹配结果中的最大值及其位置。 +- 根据相似度阈值筛选匹配结果,并记录匹配度最高的模板。 +- 绘制矩形框标记匹配区域并显示结果。 + +--- + +## 2. API文档 +### 2.1 头文件 +```c++ +#include +``` +### 2.2 模板匹配 +```c++ +cv::matchTemplate(image, templ, result, method); +``` +- 功能:在输入图像中搜索模板图像的最佳匹配位置。 +- 参数: + - image:输入图像。 + - templ:模板图像。 + - result:匹配结果图像,输出参数。 + - method:匹配方法,可选值有: + - CV_TM_SQDIFF:平方差匹配。 +- 返回值:无。 + +### 2.3 查找机制 +```c++ +cv::minMaxLoc(src, minVal, maxVal, minLoc, maxLoc, mask); +``` +- 功能:查找匹配结果图像中的最小值和最大值及其位置。 +- 参数: + - src:输入矩阵。 + - minVal:输出的最小值。 + - maxVal:输出的最大值。 + - minLoc:最小值的位置。 + - maxLoc:最大值的位置。 + - mask:可选的掩码矩阵。 +- 返回值:无。 + +--- + +## 3. 综合代码解析 + +### 3.1 流程图 + + + +### 3.2 代码解释 +- 使用多模板匹配函数 +```c++ +multiTemplateMatching(img, templates, 0.7, true); +``` +自定义多模板匹配函数具体参数如下所示。 +```c++ +void multiTemplateMatching(const Mat& img, const vector& templatePaths, + double threshold = 0.7, bool isGrayscale = false); +``` +- 功能:对多个模板逐一执行匹配,并标记匹配度最高的区域。 +- 参数: + - img:输入图像。 + - templatePaths:模板图像路径列表。 + - threshold:相似度阈值,默认为 0.7。 + - isGrayscale:是否将输入图像转换为灰度图像,默认为 false。 +- 返回值:无。 +- 输出结果 +```c++ +edit.Print(img); +``` +### 3.3 完整代码实现 +```c++ +#include + +#include +#include +#include +#include + +using namespace cv; +using namespace std; + +// 多模板匹配函数(支持彩色或灰度图像,仅绘制匹配度最高的框) +void multiTemplateMatching(const Mat &img, const vector &templatePaths, + double threshold = 0.7, bool isGrayscale = false) +{ + // 初始化最高匹配度和对应的模板路径、位置 + double bestMatchValue = 0.0; + string bestMatchTemplatePath = ""; + Rect bestMatchRect; + + // 遍历每个模板路径 + for (const auto &templatePath : templatePaths) + { + // 加载模板图像(根据 isGrayscale 决定是灰度还是彩色) + Mat templ = imread(templatePath, isGrayscale ? IMREAD_GRAYSCALE : IMREAD_COLOR); + if (templ.empty()) + { + cerr << "无法加载模板图像: " << templatePath << endl; + continue; + } + + // 如果输入图像是灰度图像,则将彩色图像转换为灰度 + Mat inputImage = img.clone(); + if (isGrayscale && inputImage.channels() == 3) + { + cvtColor(inputImage, inputImage, COLOR_BGR2GRAY); + } + + // 创建结果矩阵 + int result_cols = inputImage.cols - templ.cols + 1; + int result_rows = inputImage.rows - templ.rows + 1; + Mat result(result_rows, result_cols, CV_32FC1); + + // 执行模板匹配 + matchTemplate(inputImage, templ, result, TM_CCOEFF_NORMED); + + // 查找最佳匹配位置 + double minVal, maxVal; + Point minLoc, maxLoc; + minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc); + + // 如果当前模板的最大匹配度高于之前的记录,则更新最佳匹配信息 + if (maxVal > bestMatchValue && maxVal >= threshold) + { + bestMatchValue = maxVal; + bestMatchTemplatePath = templatePath; + bestMatchRect = Rect(maxLoc.x, maxLoc.y, templ.cols, templ.rows); + } + } + + // 如果找到匹配度高于阈值的最佳匹配,则绘制矩形框 + if (!bestMatchTemplatePath.empty()) + { + rectangle(img, bestMatchRect, Scalar(0, 255, 0), 2); // 绿色矩形框 + cout << "匹配到模板: " << bestMatchTemplatePath + << ", 匹配度: " << bestMatchValue << endl; + } + else + { + cout << "未找到匹配度高于阈值的模板。" << endl; + } +} + +int main(int argc, char *argv[]) +{ + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + + cv::VideoCapture cap; + int width = 320; // 设置摄像头分辨率宽度 + int height = 240; // 设置摄像头分辨率高度 + cap.set(cv::CAP_PROP_FRAME_WIDTH, width); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); + + // 打开摄像头设备 + cap.open(0); // 参数 0 表示默认摄像头设备 + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + // 检查命令行参数是否提供了模板路径 + vector templates; + if (argc < 2) + { + cerr << "Usage: " << argv[0] << " template_path1 [template_path2 ...]" << endl; + return EXIT_FAILURE; + } + + // 从命令行读取模板路径 + for (int i = 1; i < argc; ++i) + { + templates.push_back(argv[i]); + } + + while (true) + { + cv::Mat img; // 存储每一帧图像 + cap >> img; // 获取新的一帧 + + // 检查是否成功读取帧 + if (img.empty()) + { + std::cerr << "Warning: Couldn't read a frame from the camera." + << std::endl; + continue; + } + + // 执行多模板匹配(示例中仍使用彩色图像) + multiTemplateMatching(img, templates, 0.7, true); + + // 显示结果 + edit.Print(img); + } + + return 0; +} +``` +### 4.2 Cmake介绍 +```cmake +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test-TemplateMatching-more) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 基本图像处理示例 +add_executable(Test-TemplateMatching-more Template_Matching_more.cc) +target_include_directories(Test-TemplateMatching-more PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-TemplateMatching-more PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-TemplateMatching-more + RUNTIME DESTINATION . +) +``` + +--- + +## 4. 编译结果 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时以正确连接开发板。 +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/C03_TemplateMatching_more +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` + +在执行完上述命令后,会在build目录下生成可执行文件。 + +--- + +## 5. 例程运行示例 +### 5.1 准备工作 +1. 下载凌智视觉模块图片传输助手:[点击下载](https://gitee.com/LockzhinerAI/LockzhinerVisionModule/releases/download/v0.0.0/LockzhinerVisionModuleImageFetcher.exe) + +### 5.2 运行过程 +在凌智视觉模块中输入以下命令: +```shell +chmod 777 Test-TemplateMatching-more +# 在实际运行中,模板数量越少,相对来说运行的帧率越高,如需更高帧率请自行降低分辨率。 +./Test-TemplateMatching-more template_0.png template_1.png template_2.png template_3.png +``` +### 5.3 运行效果 +- 运行程序后,您将看到实时视频流中匹配度最高的区域被绿色矩形框标记出来。如果未找到匹配度高于阈值的模板,则会输出提示信息。 +- ![](./images/2025_0421_11_32_44_406.png) +- 模板照片如下 +![](./images/template_0.png) +![](./images/template_1.png) +![](./images/template_2.png) +![](./images/template_3.png) + +--- + +## 6. 总结 +通过上述内容,我们详细介绍了多模板匹配的流程及相关 API 的使用方法,包括: + +- 图像读取:加载输入图像和多个模板图像。 +- 模板匹配:使用归一化互相关方法计算相似度。 +- 查找极值:获取每个模板的最佳匹配位置。 +- 绘制与显示:标记匹配度最高的区域并显示结果。 diff --git a/Cpp_example/C03_Template_Matching_more/Template_Matching_more.cc b/Cpp_example/C03_Template_Matching_more/Template_Matching_more.cc new file mode 100755 index 0000000000000000000000000000000000000000..76d78aaa5e50da4f207b18d2a05d4649d6ac2838 --- /dev/null +++ b/Cpp_example/C03_Template_Matching_more/Template_Matching_more.cc @@ -0,0 +1,132 @@ +#include + +#include +#include +#include +#include + +using namespace cv; +using namespace std; + +// 多模板匹配函数(支持彩色或灰度图像,仅绘制匹配度最高的框) +void multiTemplateMatching(const Mat &img, const vector &templatePaths, + double threshold = 0.7, bool isGrayscale = false) +{ + // 初始化最高匹配度和对应的模板路径、位置 + double bestMatchValue = 0.0; + string bestMatchTemplatePath = ""; + Rect bestMatchRect; + + // 遍历每个模板路径 + for (const auto &templatePath : templatePaths) + { + // 加载模板图像(根据 isGrayscale 决定是灰度还是彩色) + Mat templ = imread(templatePath, isGrayscale ? IMREAD_GRAYSCALE : IMREAD_COLOR); + if (templ.empty()) + { + cerr << "无法加载模板图像: " << templatePath << endl; + continue; + } + + // 如果输入图像是灰度图像,则将彩色图像转换为灰度 + Mat inputImage = img.clone(); + if (isGrayscale && inputImage.channels() == 3) + { + cvtColor(inputImage, inputImage, COLOR_BGR2GRAY); + } + + // 创建结果矩阵 + int result_cols = inputImage.cols - templ.cols + 1; + int result_rows = inputImage.rows - templ.rows + 1; + Mat result(result_rows, result_cols, CV_32FC1); + + // 执行模板匹配 + matchTemplate(inputImage, templ, result, TM_CCOEFF_NORMED); + + // 查找最佳匹配位置 + double minVal, maxVal; + Point minLoc, maxLoc; + minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc); + + // 如果当前模板的最大匹配度高于之前的记录,则更新最佳匹配信息 + if (maxVal > bestMatchValue && maxVal >= threshold) + { + bestMatchValue = maxVal; + bestMatchTemplatePath = templatePath; + bestMatchRect = Rect(maxLoc.x, maxLoc.y, templ.cols, templ.rows); + } + } + + // 如果找到匹配度高于阈值的最佳匹配,则绘制矩形框 + if (!bestMatchTemplatePath.empty()) + { + rectangle(img, bestMatchRect, Scalar(0, 255, 0), 2); // 绿色矩形框 + cout << "匹配到模板: " << bestMatchTemplatePath + << ", 匹配度: " << bestMatchValue << endl; + } + else + { + cout << "未找到匹配度高于阈值的模板。" << endl; + } +} + +int main(int argc, char *argv[]) +{ + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + + cv::VideoCapture cap; + int width = 320; // 设置摄像头分辨率宽度 + int height = 240; // 设置摄像头分辨率高度 + cap.set(cv::CAP_PROP_FRAME_WIDTH, width); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); + + // 打开摄像头设备 + cap.open(0); // 参数 0 表示默认摄像头设备 + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + // 检查命令行参数是否提供了模板路径 + vector templates; + if (argc < 2) + { + cerr << "Usage: " << argv[0] << " template_path1 [template_path2 ...]" << endl; + return EXIT_FAILURE; + } + + // 从命令行读取模板路径 + for (int i = 1; i < argc; ++i) + { + templates.push_back(argv[i]); + } + + while (true) + { + cv::Mat img; // 存储每一帧图像 + cap >> img; // 获取新的一帧 + + // 检查是否成功读取帧 + if (img.empty()) + { + std::cerr << "Warning: Couldn't read a frame from the camera." + << std::endl; + continue; + } + + // 执行多模板匹配(示例中仍使用彩色图像) + multiTemplateMatching(img, templates, 0.7, true); + + // 显示结果 + edit.Print(img); + } + + return 0; +} \ No newline at end of file diff --git a/Cpp_example/C03_Template_Matching_more/images/2025_0421_11_32_44_406.png b/Cpp_example/C03_Template_Matching_more/images/2025_0421_11_32_44_406.png new file mode 100755 index 0000000000000000000000000000000000000000..7241ead11a2d940013c0ca674cf9c32d30c17851 Binary files /dev/null and b/Cpp_example/C03_Template_Matching_more/images/2025_0421_11_32_44_406.png differ diff --git a/Cpp_example/C03_Template_Matching_more/images/All.png b/Cpp_example/C03_Template_Matching_more/images/All.png new file mode 100755 index 0000000000000000000000000000000000000000..dd57130b7fceecf752fb91034b0e31588f49ac22 Binary files /dev/null and b/Cpp_example/C03_Template_Matching_more/images/All.png differ diff --git a/Cpp_example/C03_Template_Matching_more/images/template_0.png b/Cpp_example/C03_Template_Matching_more/images/template_0.png new file mode 100755 index 0000000000000000000000000000000000000000..503858b8d2cad3393b3cd5a3eee7b9a917188e5a Binary files /dev/null and b/Cpp_example/C03_Template_Matching_more/images/template_0.png differ diff --git a/Cpp_example/C03_Template_Matching_more/images/template_1.png b/Cpp_example/C03_Template_Matching_more/images/template_1.png new file mode 100755 index 0000000000000000000000000000000000000000..e0ca65cd1b7263db3b082858beef9a4600ad3189 Binary files /dev/null and b/Cpp_example/C03_Template_Matching_more/images/template_1.png differ diff --git a/Cpp_example/C03_Template_Matching_more/images/template_2.png b/Cpp_example/C03_Template_Matching_more/images/template_2.png new file mode 100755 index 0000000000000000000000000000000000000000..3febb07f45750f0e801b6b004f26b4204da815e9 Binary files /dev/null and b/Cpp_example/C03_Template_Matching_more/images/template_2.png differ diff --git a/Cpp_example/C03_Template_Matching_more/images/template_3.png b/Cpp_example/C03_Template_Matching_more/images/template_3.png new file mode 100755 index 0000000000000000000000000000000000000000..56d8787de0c6b389a3c5d215446649ebce2ebf42 Binary files /dev/null and b/Cpp_example/C03_Template_Matching_more/images/template_3.png differ diff --git a/Cpp_example/C04_find_contours/CMakeLists.txt b/Cpp_example/C04_find_contours/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..9c76e1e4c47de6d6a19c8caf5beaf85bfaa3ab42 --- /dev/null +++ b/Cpp_example/C04_find_contours/CMakeLists.txt @@ -0,0 +1,43 @@ +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test_find_contours) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 寻找圆型轮廓 +add_executable(Test-find-circle find_circle.cc) +target_include_directories(Test-find-circle PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-find-circle PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) +# 寻找线 +add_executable(Test-find-line find_line.cc) +target_include_directories(Test-find-line PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-find-line PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) +# 寻找多边形 +add_executable(Test-find-polygon find_polygon.cc) +target_include_directories(Test-find-polygon PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-find-polygon PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-find-circle + TARGETS Test-find-line + TARGETS Test-find-polygon + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/C04_find_contours/README.md b/Cpp_example/C04_find_contours/README.md new file mode 100755 index 0000000000000000000000000000000000000000..2191a09e91b153d5838416aa93cd21adf5e990eb --- /dev/null +++ b/Cpp_example/C04_find_contours/README.md @@ -0,0 +1,489 @@ +# 轮廓检测 +本文档展示了如何使用 OpenCV 进行图像处理和特征检测,包括边缘检测、直线检测、圆检测以及多边形拟合。通过这些技术,可以实现对摄像头捕获的实时视频流进行分析,并标记出检测到的特征。 + +--- + +## 1. 基本知识讲解 +### 1.1 图像处理的重要性 +- 目标检测:图像处理技术可以用于检测图像中的特定对象或特征。 +- 应用场景:广泛应用于物体识别、工业自动化、机器人导航、自动驾驶等领域。 +- 常见任务: + - 边缘检测:提取图像中的边界信息。 + - 直线检测:识别图像中的直线结构。 + - 圆检测:识别图像中的圆形结构。 + - 多边形拟合:将轮廓拟合成多边形以简化形状描述。 +### 1.2 图像处理的基本流程 +- 初始化摄像头:打开摄像头设备并设置分辨率。 +- 读取图像帧:从摄像头中获取实时视频帧。 +- 预处理:将图像转换为灰度图、降噪等操作。 +- 特征检测:执行边缘检测、霍夫变换等算法。 +- 结果绘制:在原图上绘制检测到的特征。 +- 显示结果:将处理后的图像输出到屏幕。 + +--- + +## 2. API文档 +### 2.1 头文件 +```c++ +#include +``` +### 2.2 高斯模糊 +```c++ +cv::GaussianBlur(src, dst, Size(3, 3), 0); +``` +- 参数: + - src:输入图像。 + - dst:输出图像。 + - Size(3, 3):卷积核大小。 + - 0:标准差。 +- 返回值: + - 无。 +### 2.3 边缘检测 +```c++ +cv::Canny(src, dst, 50, 150); +``` +- 参数: + - src:输入图像。 + - dst:输出图像。 + - 50:低阈值。 + - 150:高阈值。 + - apertureSize:Sobel 算子的孔径大小(默认为 3)。 + - L2gradient:是否使用 L2 范数计算梯度(默认为 false)。 +- 返回值: + - 无。 +### 2.4 查找图像中的轮廓 +```c++ +cv::findContours(src, contours, hierarchy, mode, method); +``` +- 参数: + - src:输入图像。 + - contours:轮廓列表。 + - hierarchy:轮廓层级信息。 + - mode:轮廓查找模式(默认为 CV_RETR_EXTERNAL)。 + - method:轮廓 approximation 方法(默认为 CV_CHAIN_APPROX_SIMPLE)。 +- 返回值: + - 无 +### 2.5 对轮廓进行多边形拟合 +```c++ +cv::approxPolyDP(contours[i], approx, epsilon, closed); +``` +- 参数: + - contours[i]:轮廓。 + - approx:多边形顶点列表。 + - epsilon:精度参数,表示最大距离,用于控制多边形拟合的精度。 + - closed:是否闭合多边形(默认为 false)。 +- 返回值: + - 无 +### 2.6 使用概率霍夫变换检测直线 +```c++ +cv::HoughLinesP(src, lines, 1, CV_PI / 180, 50, 50, 10); +``` +- 参数: + - src:输入图像。 + - lines:检测到的直线列表。 + - 1:rho 分辨率。 + - CV_PI / 180:theta 分辨率。 + - 50:最小线段长度。 + - 50:最大线段间隔。 + - 10:线段阈值。 +- 返回值: + - 无 +### 2.7 使用霍夫变化检测圆型 +```c++ +cv::HoughCircles(src, circles, CV_HOUGH_GRADIENT, 1, src.rows / 8, 200, 100, 0, 0); +``` +- 参数: + - src:输入图像。 + - circles:检测到的圆列表。 + - CV_HOUGH_GRADIENT:检测方法。 + - 1:rho 分辨率。 + - src.rows / 8:theta 分辨率。 + - 200:最小圆半径。 + - 100:最大圆半径。 + - 0:圆心 x 坐标。 + - 0:圆心 y 坐标。 +- 返回值: + - 无 + +--- + +## 3. 综合代码解析 +### 3.1 识别圆 +#### 3.1.1 流程图 + + + +#### 3.1.2 核心代码解析 +- 灰度转换 +```c++ +cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); +``` +- 高斯模糊 +```c++ +cv::GaussianBlur(gray, gray, cv::Size(5, 5), 0); +``` +- 霍夫圆检测并绘制圆 +```c++ +std::vector circles; +cv::HoughCircles(gray, circles, cv::HOUGH_GRADIENT, 1, gray.rows / 16, 100, 30, 1, 300); +for (const cv::Vec3f &circle : circles) { + cv::Point center(cvRound(circle[0]), cvRound(circle[1])); + int radius = cvRound(circle[2]); + cv::circle(src, center, radius, cv::Scalar(0, 255, 255), 2); // 绘制圆 +} +``` +#### 3.1.3 完整代码实现 +```c++ +#include +#include +#include + +int main() +{ + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + // 初始化摄像头 + cv::VideoCapture cap; + int width = 640; // 设置摄像头分辨率宽度 + int height = 480; // 设置摄像头分辨率高度 + cap.set(cv::CAP_PROP_FRAME_WIDTH, width); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); + + cap.open(0); // 参数 0 表示默认摄像头设备 + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + while (true) + { + // 读取输入图像 + cv::Mat src; + cap >> src; // 获取新的一帧 + if (src.empty()) + { + std::cerr << "Warning: Couldn't read a frame from the camera." << std::endl; + continue; + } + + // 转换为灰度图像 + cv::Mat gray; + cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); + + // 高斯模糊降噪 + cv::GaussianBlur(gray, gray, cv::Size(5, 5), 0); + + // 圆检测(霍夫圆变换) + std::vector circles; + cv::HoughCircles(gray, circles, cv::HOUGH_GRADIENT, 1, gray.rows / 16, 100, 30, 1, 300); + for (const cv::Vec3f &circle : circles) + { + cv::Point center(cvRound(circle[0]), cvRound(circle[1])); + int radius = cvRound(circle[2]); + cv::circle(src, center, radius, cv::Scalar(0, 255, 255), 2); // 绘制圆 + } + + edit.Print(src); + } + + cap.release(); + return 0; +} +``` +### 3.2 识别直线 +#### 3.2.1 流程图 + + + +#### 3.2.2 核心代码解析 +- 将原始图像转换为灰度图像 +```c++ +cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); +``` +- Canny边缘检测 +```c++ +cv::Canny(gray, edges, 50, 150); +``` +- 利用霍夫变换检测直线并在原图像中绘制 +```c++ +cv::HoughLinesP(edges, lines, 1, CV_PI / 180, 50, 50, 10); +for (const cv::Vec4i &line : lines) +{ + cv::line(src, cv::Point(line[0], line[1]), cv::Point(line[2], line[3]), cv::Scalar(255, 0, 0), 2); +} +``` +#### 3.2.3 完整代码实现 +```c++ +#include +#include +#include + +int main() +{ + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + // 初始化摄像头 + cv::VideoCapture cap; + int width = 640; // 设置摄像头分辨率宽度 + int height = 480; // 设置摄像头分辨率高度 + cap.set(cv::CAP_PROP_FRAME_WIDTH, width); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); + + cap.open(0); // 参数 0 表示默认摄像头设备 + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + while (true) + { + // 读取输入图像 + cv::Mat src; + cap >> src; // 获取新的一帧 + if (src.empty()) + { + std::cerr << "Warning: Couldn't read a frame from the camera." << std::endl; + continue; + } + + // 转换为灰度图像 + cv::Mat gray; + cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); + + // 边缘检测(Canny) + cv::Mat edges; + cv::Canny(gray, edges, 50, 150); + + // 直线检测(霍夫变换) + std::vector lines; + cv::HoughLinesP(edges, lines, 1, CV_PI / 180, 50, 50, 10); + for (const cv::Vec4i &line : lines) + { + cv::line(src, cv::Point(line[0], line[1]), cv::Point(line[2], line[3]), cv::Scalar(255, 0, 0), 2); + } + + edit.Print(src); + } + + cap.release(); + return 0; +} +``` +### 3.3 识别多边形 +#### 3.3.1 流程图 + + + +#### 3.3.2 核心代码解析 +- 预处理:包括灰度转换、高斯模糊、Canny边缘检测 +```c++ +cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); +cv::GaussianBlur(gray, gray, cv::Size(5, 5), 0); +cv::Canny(gray, edges, 50, 150); +``` +- 轮廓分析 +```c++ +std::vector> contours; +cv::findContours(edges, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE); +``` +- 多边形拟合 +```c++ +for (size_t i = 0; i < contours.size(); i++) { + std::vector approx; + cv::approxPolyDP(contours[i], approx, + cv::arcLength(contours[i], true) * 0.02, true); + cv::drawContours(polygonImage, + std::vector>{approx}, -1, + cv::Scalar(0, 0, 255), 2); +} +``` +#### 3.3.3 完整代码实现 +```c++ +#include +#include +#include + +int main() +{ + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + // 初始化摄像头 + cv::VideoCapture cap; + int width = 640; // 设置摄像头分辨率宽度 + int height = 480; // 设置摄像头分辨率高度 + cap.set(cv::CAP_PROP_FRAME_WIDTH, width); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); + + cap.open(0); // 参数 0 表示默认摄像头设备 + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + while (true) + { + // 读取输入图像 + cv::Mat src; + cap >> src; // 获取新的一帧 + if (src.empty()) + { + std::cerr << "Warning: Couldn't read a frame from the camera." + << std::endl; + continue; + } + + // 转换为灰度图像 + cv::Mat gray; + cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); + + // 高斯模糊降噪 + cv::GaussianBlur(gray, gray, cv::Size(5, 5), 0); + + // 边缘检测(Canny) + cv::Mat edges; + cv::Canny(gray, edges, 50, 150); + + // 查找轮廓 + std::vector> contours; + cv::findContours(edges, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE); + + // 多边形拟合 + cv::Mat polygonImage = src.clone(); + for (size_t i = 0; i < contours.size(); i++) + { + std::vector approx; + cv::approxPolyDP(contours[i], approx, + cv::arcLength(contours[i], true) * 0.02, true); + cv::drawContours(polygonImage, + std::vector>{approx}, -1, + cv::Scalar(0, 0, 255), 2); + } + + edit.Print(polygonImage); + } + + cap.release(); + return 0; +} +``` + +--- + +## 4. 编译过程 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时以正确连接开发板。 +### 4.2 Cmake介绍 +```cmake +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test_find_contours) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 寻找圆型轮廓 +add_executable(Test-find-circle find_circle.cc) +target_include_directories(Test-find-circle PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-find-circle PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) +# 寻找线 +add_executable(Test-find-line find_line.cc) +target_include_directories(Test-find-line PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-find-line PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) +# 寻找多边形 +add_executable(Test-find-polygon find_polygon.cc) +target_include_directories(Test-find-polygon PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-find-polygon PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-find-circle + TARGETS Test-find-line + TARGETS Test-find-polygon + RUNTIME DESTINATION . +) +``` +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/C04_find_contours +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` + +在执行完上述命令后,会在build目录下生成可执行文件。 + +--- + +## 5. 例程运行示例 +### 5.1 圆识别 +```shell +chmod find_circle +./find_circle +``` +- 识别结果: + +![](./images/circle.png) +### 5.2 直线识别 +```shell +chmod find_line +./find_line +``` +- 识别结果: + +![](./images/line.png) + +### 5.3 多边形识别 +```shell +chmod find_polygon +./find_polygon +``` +- 多边形识别: + +![](./images/polygon.png) + +--- + +## 6. 总结 +- 本文档深入探讨了使用 OpenCV 进行实时图像处理与特征检测的多种方法,展示了如何通过边缘检测、直线检测、圆检测以及多边形拟合等技术对摄像头捕获的视频流进行分析。同时使用传统视觉方法进行图像识别,对环境非常敏感,推荐使用LockAI目标检测方法进行不同物体的识别以取得更好的效果。 diff --git a/Cpp_example/C04_find_contours/find_circle.cc b/Cpp_example/C04_find_contours/find_circle.cc new file mode 100755 index 0000000000000000000000000000000000000000..8a1230bee55f344db0e9286e16e04a71041dc4b8 --- /dev/null +++ b/Cpp_example/C04_find_contours/find_circle.cc @@ -0,0 +1,61 @@ +#include +#include +#include + +int main() +{ + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + // 初始化摄像头 + cv::VideoCapture cap; + int width = 640; // 设置摄像头分辨率宽度 + int height = 480; // 设置摄像头分辨率高度 + cap.set(cv::CAP_PROP_FRAME_WIDTH, width); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); + + cap.open(0); // 参数 0 表示默认摄像头设备 + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + while (true) + { + // 读取输入图像 + cv::Mat src; + cap >> src; // 获取新的一帧 + if (src.empty()) + { + std::cerr << "Warning: Couldn't read a frame from the camera." << std::endl; + continue; + } + + // 转换为灰度图像 + cv::Mat gray; + cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); + + // 高斯模糊降噪 + cv::GaussianBlur(gray, gray, cv::Size(5, 5), 0); + + // 圆检测(霍夫圆变换) + std::vector circles; + cv::HoughCircles(gray, circles, cv::HOUGH_GRADIENT, 1, gray.rows / 16, 100, 30, 1, 300); + for (const cv::Vec3f &circle : circles) + { + cv::Point center(cvRound(circle[0]), cvRound(circle[1])); + int radius = cvRound(circle[2]); + cv::circle(src, center, radius, cv::Scalar(0, 255, 255), 2); // 绘制圆 + } + + edit.Print(src); + } + + cap.release(); + return 0; +} \ No newline at end of file diff --git a/Cpp_example/C04_find_contours/find_line.cc b/Cpp_example/C04_find_contours/find_line.cc new file mode 100755 index 0000000000000000000000000000000000000000..ef4ac12e48f1b98cd7bf9b79b486ddb1c00de723 --- /dev/null +++ b/Cpp_example/C04_find_contours/find_line.cc @@ -0,0 +1,60 @@ +#include +#include +#include + +int main() +{ + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + // 初始化摄像头 + cv::VideoCapture cap; + int width = 640; // 设置摄像头分辨率宽度 + int height = 480; // 设置摄像头分辨率高度 + cap.set(cv::CAP_PROP_FRAME_WIDTH, width); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); + + cap.open(0); // 参数 0 表示默认摄像头设备 + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + while (true) + { + // 读取输入图像 + cv::Mat src; + cap >> src; // 获取新的一帧 + if (src.empty()) + { + std::cerr << "Warning: Couldn't read a frame from the camera." << std::endl; + continue; + } + + // 转换为灰度图像 + cv::Mat gray; + cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); + + // 边缘检测(Canny) + cv::Mat edges; + cv::Canny(gray, edges, 50, 150); + + // 直线检测(霍夫变换) + std::vector lines; + cv::HoughLinesP(edges, lines, 1, CV_PI / 180, 50, 50, 10); + for (const cv::Vec4i &line : lines) + { + cv::line(src, cv::Point(line[0], line[1]), cv::Point(line[2], line[3]), cv::Scalar(255, 0, 0), 2); + } + + edit.Print(src); + } + + cap.release(); + return 0; +} \ No newline at end of file diff --git a/Cpp_example/C04_find_contours/find_polygon.cc b/Cpp_example/C04_find_contours/find_polygon.cc new file mode 100755 index 0000000000000000000000000000000000000000..3ebd64f47f655d690171e0153a2e7cb654bac11b --- /dev/null +++ b/Cpp_example/C04_find_contours/find_polygon.cc @@ -0,0 +1,72 @@ +#include +#include +#include + +int main() +{ + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + // 初始化摄像头 + cv::VideoCapture cap; + int width = 640; // 设置摄像头分辨率宽度 + int height = 480; // 设置摄像头分辨率高度 + cap.set(cv::CAP_PROP_FRAME_WIDTH, width); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); + + cap.open(0); // 参数 0 表示默认摄像头设备 + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + while (true) + { + // 读取输入图像 + cv::Mat src; + cap >> src; // 获取新的一帧 + if (src.empty()) + { + std::cerr << "Warning: Couldn't read a frame from the camera." + << std::endl; + continue; + } + + // 转换为灰度图像 + cv::Mat gray; + cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); + + // 高斯模糊降噪 + cv::GaussianBlur(gray, gray, cv::Size(5, 5), 0); + + // 边缘检测(Canny) + cv::Mat edges; + cv::Canny(gray, edges, 50, 150); + + // 查找轮廓 + std::vector> contours; + cv::findContours(edges, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE); + + // 多边形拟合 + cv::Mat polygonImage = src.clone(); + for (size_t i = 0; i < contours.size(); i++) + { + std::vector approx; + cv::approxPolyDP(contours[i], approx, + cv::arcLength(contours[i], true) * 0.02, true); + cv::drawContours(polygonImage, + std::vector>{approx}, -1, + cv::Scalar(0, 0, 255), 2); + } + + edit.Print(polygonImage); + } + + cap.release(); + return 0; +} \ No newline at end of file diff --git a/Cpp_example/C04_find_contours/images/1.png b/Cpp_example/C04_find_contours/images/1.png new file mode 100755 index 0000000000000000000000000000000000000000..1b0f152683938d5083dc85d659cefdee16668ee8 Binary files /dev/null and b/Cpp_example/C04_find_contours/images/1.png differ diff --git a/Cpp_example/C04_find_contours/images/2.png b/Cpp_example/C04_find_contours/images/2.png new file mode 100755 index 0000000000000000000000000000000000000000..5e22bec4643e5150fa87c3caaacd23efa4012717 Binary files /dev/null and b/Cpp_example/C04_find_contours/images/2.png differ diff --git a/Cpp_example/C04_find_contours/images/3.png b/Cpp_example/C04_find_contours/images/3.png new file mode 100755 index 0000000000000000000000000000000000000000..7f03d5bf8b52388513173da2539e33fb8d7cd894 Binary files /dev/null and b/Cpp_example/C04_find_contours/images/3.png differ diff --git a/Cpp_example/C04_find_contours/images/circle.png b/Cpp_example/C04_find_contours/images/circle.png new file mode 100755 index 0000000000000000000000000000000000000000..4596370a94cebec2b8c2d68ffbbc5ef7556434d8 Binary files /dev/null and b/Cpp_example/C04_find_contours/images/circle.png differ diff --git a/Cpp_example/C04_find_contours/images/line.png b/Cpp_example/C04_find_contours/images/line.png new file mode 100755 index 0000000000000000000000000000000000000000..3ed4094f064036599cf848f6df4d0f76cfc9bc36 Binary files /dev/null and b/Cpp_example/C04_find_contours/images/line.png differ diff --git a/Cpp_example/C04_find_contours/images/polygon.png b/Cpp_example/C04_find_contours/images/polygon.png new file mode 100755 index 0000000000000000000000000000000000000000..24dd53e87d5d154ab147f7457836e8a4ea4b1f87 Binary files /dev/null and b/Cpp_example/C04_find_contours/images/polygon.png differ diff --git a/Cpp_example/C05_Finecolorandshape/CMakeLists.txt b/Cpp_example/C05_Finecolorandshape/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8a6cc82f0d4a7c93fc18053f5c5a0a228235e702 --- /dev/null +++ b/Cpp_example/C05_Finecolorandshape/CMakeLists.txt @@ -0,0 +1,33 @@ +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test-Finecolorandshape) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 寻找色块和轮廓 +add_executable(Test-Finecolorandshape Finecolorandshape.cc) +target_include_directories(Test-Finecolorandshape PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-Finecolorandshape PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-Finecolorandshape + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/C05_Finecolorandshape/Finecolorandshape.cc b/Cpp_example/C05_Finecolorandshape/Finecolorandshape.cc new file mode 100755 index 0000000000000000000000000000000000000000..1cb5cf161ddba9de520e1bb7a493aa23e9c61269 --- /dev/null +++ b/Cpp_example/C05_Finecolorandshape/Finecolorandshape.cc @@ -0,0 +1,122 @@ +#include +#include +#include +#include + +std::vector> find_blobs( + const cv::Mat &image, + const cv::Scalar &lower_bound, + const cv::Scalar &upper_bound, + int min_area = 100, + int kernel_size = 5) +{ + // 转换为 HSV 颜色空间 + cv::Mat hsv_image; + cv::cvtColor(image, hsv_image, cv::COLOR_BGR2HSV); + + // 创建二值掩码 + cv::Mat mask; + cv::inRange(hsv_image, lower_bound, upper_bound, mask); + + // 形态学操作:清除噪声 + cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(kernel_size, kernel_size)); + cv::morphologyEx(mask, mask, cv::MORPH_OPEN, kernel); + + // 查找轮廓 + std::vector> contours; + cv::findContours(mask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + + // 筛选符合条件的色块 + std::vector> filtered_contours; + for (const auto &contour : contours) + { + cv::Rect bounding_rect = cv::boundingRect(contour); + if (bounding_rect.area() >= min_area) + { + filtered_contours.push_back(contour); + } + } + return filtered_contours; +} + +int main() +{ + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + + cv::VideoCapture cap; + int width = 640; // 设置摄像头分辨率宽度 + int height = 480; // 设置摄像头分辨率高度 + cap.set(cv::CAP_PROP_FRAME_WIDTH, width); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); + + // 打开摄像头设备 + cap.open(0); // 参数 0 表示默认摄像头设备 + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + while (true) + { + cv::Mat image; // 存储每一帧图像 + cap >> image; // 获取新的一帧 + + if (image.empty()) + { + std::cerr << "Warning: Couldn't read a frame from the camera." << std::endl; + continue; + } + + // 定义颜色阈值(例如红色) + cv::Scalar lower_red(170, 100, 100); // 红色下界 + cv::Scalar upper_red(179, 255, 255); // 红色上界 + + // 调用 find_blobs 函数 + int min_area = 100; // 最小面积阈值 + int kernel_size = 1; // 形态学操作核大小 + std::vector> blobs = find_blobs(image, lower_red, upper_red, min_area, kernel_size); + + // 绘制和打印检测到的色块,并筛选矩形 + for (const auto &contour : blobs) + { + // 计算外接矩形框 + cv::Rect bounding_rect = cv::boundingRect(contour); + + // 近似多边形拟合 + std::vector approx; + cv::approxPolyDP(contour, approx, cv::arcLength(contour, true) * 0.02, true); + + // 判断是否为四边形 + if (approx.size() == 4) + { + // 绘制矩形框 + cv::rectangle(image, bounding_rect, cv::Scalar(0, 255, 0), 2); + + // 计算中心点 + cv::Moments moments = cv::moments(contour); + int cx = moments.m10 / moments.m00; + int cy = moments.m01 / moments.m00; + + // 绘制中心点 + cv::circle(image, cv::Point(cx, cy), 5, cv::Scalar(0, 0, 255), -1); + + // 打印信息 + std::cout << "Red quadrilateral detected at (" << cx << ", " << cy + << ") with area " << bounding_rect.area() << std::endl; + } + } + + // 显示结果 + edit.Print(image); + } + + cap.release(); + return 0; +} \ No newline at end of file diff --git a/Cpp_example/C05_Finecolorandshape/README.md b/Cpp_example/C05_Finecolorandshape/README.md new file mode 100755 index 0000000000000000000000000000000000000000..26214b9ec273aedd3c6aa70b65f63c98e6a72170 --- /dev/null +++ b/Cpp_example/C05_Finecolorandshape/README.md @@ -0,0 +1,448 @@ +# 同时识别轮廓和色块 +## 1. 项目简介 +### 1.1 色块识别的重要性 +- 颜色特征提取:颜色是一种重要的视觉特征,尤其在背景较为单一的情况下,能够快速区分目标区域。 +- 应用场景:广泛应用于机器人导航、工业自动化、物体跟踪等领域。 +- HSV 颜色空间:相比于 RGB 颜色空间,HSV 更适合用于颜色识别,因为它可以将颜色信息(Hue)、饱和度(Saturation)和亮度(Value)分离,便于设置阈值。 +### 1.2 色块识别的流程 +- 获取图像。 +- 将图像从 BGR 转换为 HSV 颜色空间。 +- 创建二值掩码,筛选出符合颜色范围的像素。 +- 使用形态学操作清除噪声。 +- 查找轮廓并筛选符合条件的色块。 +- 计算外接矩形和中心点。 +- 绘制结果并输出。 +### 1.3 图像处理的重要性 +- 目标检测:图像处理技术可以用于检测图像中的特定对象或特征。 +- 应用场景:广泛应用于物体识别、工业自动化、机器人导航、自动驾驶等领域。 +- 常见任务: + - 边缘检测:提取图像中的边界信息。 + - 直线检测:识别图像中的直线结构。 + - 圆检测:识别图像中的圆形结构。 + - 多边形拟合:将轮廓拟合成多边形以简化形状描述。 +### 1.4 图像处理的基本流程 +- 初始化摄像头:打开摄像头设备并设置分辨率。 +- 读取图像帧:从摄像头中获取实时视频帧。 +- 预处理:将图像转换为灰度图、降噪等操作。 +- 特征检测:执行边缘检测、霍夫变换等算法。 +- 结果绘制:在原图上绘制检测到的特征。 +- 显示结果:将处理后的图像输出到屏幕。 + + +--- +## 2. API 文档 +### 2.1 头文件 +```c++ +#include +``` +### 2.2 生成掩码 +```c++ +cv::inRange(src, lowerb, upperb, dst); +``` +- 参数说明: + - src:输入图像,可以是单通道或三通道的图像。 + - lowerb:颜色下界,是一个Scalar对象,表示要查找的颜色的下限。 + - upperb:颜色上界,是一个Scalar对象,表示要查找的颜色的上限。 + - dst:输出图像,是一个单通道的8位无符号整数图像,表示生成的掩码。 +- 返回值: + - 无 +### 2.3 创建形态学操作所需的结构元素核 +```c++ +cv::getStructuringElement(shape, ksize, anchor); +``` +- 参数说明: + - shape:核形状,可以是RECT、CROSS、ELLIPSE等。 + - ksize:核大小,是一个Size对象,表示核的宽度和高度。 + - anchor:锚点,是一个Point对象,表示核的锚点位置。 +- 返回值: + - 返回一个核,是一个Mat对象。 +### 2.4 形态学操作:清除噪声 +```c++ +cv::morphologyEx(src, dst, op, kernel, anchor, iterations, borderType, borderValue); +``` +- 参数说明: + - src:输入图像,可以是单通道或三通道的图像。 + - dst:输出图像,是一个单通道的8位无符号整数图像,表示生成的掩码。 + - op:操作类型,可以是OPEN、CLOSE、GRADIENT、TOPHAT、BLACKHAT等。 + - kernel:核,是一个Mat对象,表示形态学操作的核。 + - anchor:锚点,是一个Point对象,表示核的锚点位置。 + - iterations:迭代次数,是一个整数,表示形态学操作的迭代次数。 + - borderType:边界类型,可以是BORDER_CONSTANT、BORDER_REPLICATE、BORDER_REFLECT、BORDER_WRAP、BORDER_REFLECT_101等。 + - borderValue:边界值,是一个Scalar对象,表示边界区域的值。 +- 返回值: + - 无 +### 2.5 查找轮廓 +```c++ +cv::findContours(image, contours, hierarchy, mode, method, offset); +``` +- 参数说明: + - image:输入图像,可以是单通道或三通道的图像。 + - contours:输出参数,是一个vector>对象,表示轮廓的集合。 + - hierarchy:输出参数,是一个vector对象,表示轮廓的层级关系。 + - mode:轮廓发现模式,可以是RETR_EXTERNAL、RETR_LIST、RETR_CCOMP、RETR_TREE等。 + - method:轮廓 approximation 方法,可以是CHAIN_APPROX_NONE、CHAIN_APPROX_SIMPLE、CHAIN_APPROX_TC89_L1、CHAIN_APPROX_TC89_KCOS等。 + - offset:轮廓偏移量,是一个Point对象,表示轮廓的偏移量。 +- 返回值: + - 返回一个整数,表示轮廓的数量。 +### 2.6 获取轮廓的外接矩形 +```c++ +cv::boundingRect(points); +``` +- 参数说明: + - points:输入参数,是一个vector对象,表示轮廓的点集合。 +- 返回值: + - 返回一个Rect对象,表示轮廓的外接矩形。 +### 2.7 计算矩阵矩 +```c++ +cv::moments(array, binaryImage); +``` +- 参数说明: + - array:输入参数,是一个Mat对象,表示输入的矩阵。 + - binaryImage:输入参数,是一个布尔值,表示是否将输入的矩阵转换为二值矩阵。 +- 返回值: + - 返回一个 Moments对象,表示矩阵的矩。 +### 2.8 绘制矩形框 +```c++ +cv::rectangle(img, pt1, pt2, color, thickness, lineType, shift); +``` +- 参数说明: + - img:输入参数,是一个Mat对象,表示输入的图像。 + - pt1:输入参数,是一个Point对象,表示矩形的左上角点。 + - pt2:输入参数,是一个Point对象,表示矩形的右下角点。 + - color:输入参数,是一个Scalar对象,表示矩形的颜色。 + - thickness:输入参数,是一个整数,表示矩形的线宽。 + - lineType:输入参数,是一个整数,表示矩形的线类型。 + - shift:输入参数,是一个整数,表示坐标的精度。 +- 返回值: + - 无 +### 2.9 绘制圆 +```c++ +cv::circle(img, center, radius, color, thickness, lineType, shift); +``` +- 参数说明: + - img:输入参数,是一个Mat对象,表示输入的图像。 + - center:输入参数,是一个Point对象,表示圆心。 + - radius:输入参数,是一个整数,表示圆的半径。 + - color:输入参数,是一个Scalar对象,表示圆的颜色。 + - thickness:输入参数,是一个整数,表示圆的线宽。 + - lineType:输入参数,是一个整数,表示圆的线类型。 + - shift:输入参数,是一个整数,表示坐标的精度。 +- 返回值: + - 无 +### 2.10 查找色块函数(自定义) +```c++ +std::vector> find_blobs( + const cv::Mat &image, + const cv::Scalar &lower_bound, + const cv::Scalar &upper_bound, + int min_area = 100, + int kernel_size = 5); +``` +- 参数说明: + - image:输入参数,是一个Mat对象,表示输入的图像。 + - lower_bound:输入参数,是一个Scalar对象,表示颜色下界。 + - upper_bound:输入参数,是一个Scalar对象,表示颜色上界。 + - min_area:输入参数,是一个整数,表示最小面积。 + - kernel_size:输入参数,是一个整数,表示核大小。 +- 返回值: + - 返回一个vector>对象,表示找到的色块的点集合。 +### 2.11 高斯模糊 +```c++ +cv::GaussianBlur(src, dst, Size(3, 3), 0); +``` +- 参数: + - src:输入图像。 + - dst:输出图像。 + - Size(3, 3):卷积核大小。 + - 0:标准差。 +- 返回值: + - 无。 +### 2.12 边缘检测 +```c++ +cv::Canny(src, dst, 50, 150); +``` +- 参数: + - src:输入图像。 + - dst:输出图像。 + - 50:低阈值。 + - 150:高阈值。 + - apertureSize:Sobel 算子的孔径大小(默认为 3)。 + - L2gradient:是否使用 L2 范数计算梯度(默认为 false)。 +- 返回值: + - 无。 +### 2.13 对轮廓进行多边形拟合 +```c++ +cv::approxPolyDP(contours[i], approx, epsilon, closed); +``` +- 参数: + - contours[i]:轮廓。 + - approx:多边形顶点列表。 + - epsilon:精度参数,表示最大距离,用于控制多边形拟合的精度。 + - closed:是否闭合多边形(默认为 false)。 +- 返回值: + - 无 +### 2.14 使用概率霍夫变换检测直线 +```c++ +cv::HoughLinesP(src, lines, 1, CV_PI / 180, 50, 50, 10); +``` +- 参数: + - src:输入图像。 + - lines:检测到的直线列表。 + - 1:rho 分辨率。 + - CV_PI / 180:theta 分辨率。 + - 50:最小线段长度。 + - 50:最大线段间隔。 + - 10:线段阈值。 +- 返回值: + - 无 +### 2.15 使用霍夫变化检测圆型 +```c++ +cv::HoughCircles(src, circles, CV_HOUGH_GRADIENT, 1, src.rows / 8, 200, 100, 0, 0); +``` +- 参数: + - src:输入图像。 + - circles:检测到的圆列表。 + - CV_HOUGH_GRADIENT:检测方法。 + - 1:rho 分辨率。 + - src.rows / 8:theta 分辨率。 + - 200:最小圆半径。 + - 100:最大圆半径。 + - 0:圆心 x 坐标。 + - 0:圆心 y 坐标。 +- 返回值: + - 无 + +## 3. 综合代码介绍 +### 3.1 流程图 + + + +### 3.2 核心代码解析 +- 阈值分割 +```c++ +cv::inRange(hsv_image, lower_bound, upper_bound, mask); +``` +- 形态学开运算 +```c++ +cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(kernel_size, kernel_size)); +cv::morphologyEx(mask, mask, cv::MORPH_OPEN, kernel); +``` +- 轮廓查找 +```c++ +std::vector> contours; +cv::findContours(mask, contours, cv::RETR_EXTERNAL,cv::CHAIN_APPROX_SIMPLE); +``` +- 筛选色块 +```c++ +std::vector> filtered_contours; +for (const auto &contour : contours) +{ + cv::Rect bounding_rect = cv::boundingRect(contour); + if (bounding_rect.area() >= min_area) + { + filtered_contours.push_back(contour); + } +} +``` +### 3.3 完整代码实现 +```c++ +#include +#include +#include +#include + +std::vector> find_blobs( + const cv::Mat &image, + const cv::Scalar &lower_bound, + const cv::Scalar &upper_bound, + int min_area = 100, + int kernel_size = 5) +{ + // 转换为 HSV 颜色空间 + cv::Mat hsv_image; + cv::cvtColor(image, hsv_image, cv::COLOR_BGR2HSV); + + // 创建二值掩码 + cv::Mat mask; + cv::inRange(hsv_image, lower_bound, upper_bound, mask); + + // 形态学操作:清除噪声 + cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(kernel_size, kernel_size)); + cv::morphologyEx(mask, mask, cv::MORPH_OPEN, kernel); + + // 查找轮廓 + std::vector> contours; + cv::findContours(mask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + + // 筛选符合条件的色块 + std::vector> filtered_contours; + for (const auto &contour : contours) + { + cv::Rect bounding_rect = cv::boundingRect(contour); + if (bounding_rect.area() >= min_area) + { + filtered_contours.push_back(contour); + } + } + return filtered_contours; +} + +int main() +{ + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + + cv::VideoCapture cap; + int width = 640; // 设置摄像头分辨率宽度 + int height = 480; // 设置摄像头分辨率高度 + cap.set(cv::CAP_PROP_FRAME_WIDTH, width); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); + + // 打开摄像头设备 + cap.open(0); // 参数 0 表示默认摄像头设备 + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + while (true) + { + cv::Mat image; // 存储每一帧图像 + cap >> image; // 获取新的一帧 + + if (image.empty()) + { + std::cerr << "Warning: Couldn't read a frame from the camera." << std::endl; + continue; + } + + // 定义颜色阈值(例如红色) + cv::Scalar lower_red(170, 100, 100); // 红色下界 + cv::Scalar upper_red(179, 255, 255); // 红色上界 + + // 调用 find_blobs 函数 + int min_area = 100; // 最小面积阈值 + int kernel_size = 1; // 形态学操作核大小 + std::vector> blobs = find_blobs(image, lower_red, upper_red, min_area, kernel_size); + + // 绘制和打印检测到的色块,并筛选矩形 + for (const auto &contour : blobs) + { + // 计算外接矩形框 + cv::Rect bounding_rect = cv::boundingRect(contour); + + // 近似多边形拟合 + std::vector approx; + cv::approxPolyDP(contour, approx, cv::arcLength(contour, true) * 0.02, true); + + // 判断是否为四边形 + if (approx.size() == 4) + { + // 绘制矩形框 + cv::rectangle(image, bounding_rect, cv::Scalar(0, 255, 0), 2); + + // 计算中心点 + cv::Moments moments = cv::moments(contour); + int cx = moments.m10 / moments.m00; + int cy = moments.m01 / moments.m00; + + // 绘制中心点 + cv::circle(image, cv::Point(cx, cy), 5, cv::Scalar(0, 0, 255), -1); + + // 打印信息 + std::cout << "Red quadrilateral detected at (" << cx << ", " << cy + << ") with area " << bounding_rect.area() << std::endl; + } + } + + // 显示结果 + edit.Print(image); + } + + cap.release(); + return 0; +} +``` +## 4. 编译过程 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时以正确连接开发板。 +### 4.2 Cmake介绍 +```cmake +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test-Finecolorandshape) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 寻找色块和轮廓 +add_executable(Test-Finecolorandshape Finecolorandshape.cc) +target_include_directories(Test-Finecolorandshape PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-Finecolorandshape PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-Finecolorandshape + RUNTIME DESTINATION . +) +``` +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/C05_Find_color_and_shape +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` + +在执行完上述命令后,会在build目录下生成可执行文件。 + +--- + +## 5. 例程运行示例 +### 5.1 运行过程 +```shell +chmod 777 Test-Finecolorandshape +./Test-Finecolorandshape +``` +### 5.2 结果展示 +![](./images/2025_0421_11_48_43_095.png) + +--- + +## 6. 总结 +本程序实现了基于 OpenCV 的红色四边形检测功能,具有以下特点: + +- 高效性:通过颜色过滤、形态学处理和轮廓筛选,快速定位目标。 +- 灵活性:支持自定义颜色阈值、最小面积和形态学核大小,适应不同场景需求。 +- 易用性:代码结构清晰,模块化设计便于扩展和维护。 +该程序可作为基础框架,进一步应用于更复杂的视觉任务,例如多目标检测、动态跟踪等。通过调整颜色阈值和形状筛选条件,还可扩展到其他颜色和形状的检测任务。 \ No newline at end of file diff --git a/Cpp_example/C05_Finecolorandshape/images/1.png b/Cpp_example/C05_Finecolorandshape/images/1.png new file mode 100755 index 0000000000000000000000000000000000000000..6189acd5a5d61613d990ce4edf81e0b426dd6b04 Binary files /dev/null and b/Cpp_example/C05_Finecolorandshape/images/1.png differ diff --git a/Cpp_example/C05_Finecolorandshape/images/2025_0421_11_48_43_095.png b/Cpp_example/C05_Finecolorandshape/images/2025_0421_11_48_43_095.png new file mode 100755 index 0000000000000000000000000000000000000000..58d69e6873dbfb634e3dcf7aeb10cf973d3f7b51 Binary files /dev/null and b/Cpp_example/C05_Finecolorandshape/images/2025_0421_11_48_43_095.png differ diff --git a/Cpp_example/C06_test_qr_code_detector/CMakeLists.txt b/Cpp_example/C06_test_qr_code_detector/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..a5e8335ef9c545ccefc201db60156cdbda311884 --- /dev/null +++ b/Cpp_example/C06_test_qr_code_detector/CMakeLists.txt @@ -0,0 +1,43 @@ +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test_qr_code_detector) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 定义 ZXing SDK 路径 +set(ZXing_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/zxing-cpp-v2.2.1-lockzhiner-vision-module") +set(ZXing_DIR "${ZXing_ROOT_PATH}/lib/cmake/ZXing") +set(ZXing_INCLUDE_DIRS "${ZXing_ROOT_PATH}/include") +find_package(ZXing REQUIRED) +set(ZXing_LIBRARIES "${ZXing_LIBS}") + +# 基本图像处理示例 +add_executable(Test-qr_code-detector test_qr_code_detector.cc) +target_include_directories(Test-qr_code-detector PRIVATE + ${ZXing_INCLUDE_DIRS} + ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS} +) +target_link_libraries(Test-qr_code-detector PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES} ${ZXing_LIBRARIES} ) +install( + TARGETS Test-qr_code-detector + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/C06_test_qr_code_detector/README.md b/Cpp_example/C06_test_qr_code_detector/README.md new file mode 100755 index 0000000000000000000000000000000000000000..a2015437b182fbc2f97419ad10d9ca930b4678d3 --- /dev/null +++ b/Cpp_example/C06_test_qr_code_detector/README.md @@ -0,0 +1,256 @@ +# 二维码识别 +二维码识别是视觉模块经常使用到的功能之一,本章节中,我们将教会你如何使用 Lockzhiner Vision Module 进行二维码识别。 +## 1. 基本知识讲解 +### 1.1 二维码简介 +二维码(QR Code)是一种高效的二维条码,能快速存储和读取信息,即使部分损坏也能准确识别。它广泛应用于移动支付、广告、物流、票务等领域,用户只需用智能手机扫描即可获取信息或完成操作,极大提升了效率和便利性。 +### 1.2 二维码识别步骤 +二维码识别主要通过两个步骤完成:图像捕捉和解码。 +- 图像捕捉:使用设备摄像头拍摄包含二维码的图像。 +- 解码:软件处理图像,定位并读取二维码中的数据,转换为原始信息。 + +常用工具如ZXing和OpenCV支持快速集成到应用中,使用户能轻松扫描并获取二维码信息。 + +--- + +## 2. C++ API文档 +### 2.1 QRCodeDetector类 +#### 2.1.1 头文件 +```c++ +#include +``` +- 作用:用于声明QRCodeDetector类,使得QRCodeDetector类可以在当前源文件中使用。 + +#### 2.1.2 构造类对象 +```c++ +lockzhiner_vision_module::vision::QRCodeDetector model; +``` +- 作用:用于实现二维码识别。 +- 参数说明: + - 无 +- 返回值: + - 无 +#### 2.1.3 Predict函数 +```c++ +auto results = model.Predict(input_mat); +``` +- 作用:QRCodeDetector类中的一个函数,用于实现二维码识别。 +- 参数说明: + - input_mat: 输入参数,类型为cv::Mat,表示要分析的输入图像。 +- 返回值: + - 返回一个包含二维码检测结果的对象集合。每个Result对象包含二维码的位置信息和解码后的文本内容。 +### 2.2 Visualize函数 +#### 2.2.1 头文件 +```c++ +#include +``` +- 作用:用于声明Visualize函数,使得Visualize函数可以在当前源文件中使用。 +#### 2.2.2 结果可视化 +```c++ +lockzhiner_vision_module::vision::Visualize(input_image, output_image, + results); +``` +- 参数说明: + - input_image: 输入参数,表示原始输入图像。 + - output_image: 输出参数,用于存储带有可视化结果的输出图像。 + - results: 输入参数,表示二维码检测的结果集。每个Result对象包含二维码的位置信息和解码后的文本内容。 +- 返回值: + - 无 + +--- + +## 3. 综合代码介绍 + +### 3.1 流程图 + + + +### 3.2 核心代码解析 +- 定义检测模型 +```c++ +lockzhiner_vision_module::vision::QRCodeDetector model; +``` +- 调用摄像头捕获图像 +```c++ +cv::VideoCapture cap; +// 设置摄像头获取帧的宽高 +cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); +cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); +cap.open(0); + +// wihile循环中的以下代码用于捕获图像帧 +cap >> input_mat; +if (input_mat.empty()) +{ +continue; +} +``` +- 检测二维码 +```c++ +auto results = model.Predict(input_mat); +``` + +### 3.3 完整代码实现 +```c++ +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace std::chrono; + +lockzhiner_vision_module::vision::QRCodeDetector model; + +int main() +{ + // 初始化 edit 模块 + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + + cv::VideoCapture cap; + // 设置摄像头获取帧的宽高 + cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); + cap.open(0); + + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + cv::Mat input_mat; + + while (true) + { + int read_index = 0; + int time_ms = 0; + + for (int i = 0; i < 30; i++) + { + high_resolution_clock::time_point start_time = high_resolution_clock::now(); + cap >> input_mat; + if (input_mat.empty()) + { + continue; + } + // 使用 model 对象的 Predict 方法对输入图像进行预测,获取二维码检测结果 + auto results = model.Predict(input_mat); + + high_resolution_clock::time_point end_time = high_resolution_clock::now(); + auto time_span = duration_cast(end_time - start_time); + time_ms += time_span.count(); + read_index += 1; + + cv::Mat output_image; + // 调用 Visualize 函数对原始图像和检测结果进行可视化处理,并将结果存储在 output_image 中 + lockzhiner_vision_module::vision::Visualize(input_mat, output_image, results); + edit.Print(output_image); + } + + std::cout << "Frames per second: " << 1000.0 / time_ms * read_index << std::endl; + } + + cap.release(); + return 0; +} +``` + +--- + +## 4. 编译调试 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时以正确连接开发板。 +### 4.2 Cmake介绍 +```cmake +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test_qr_code_detector) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 定义 ZXing SDK 路径 +set(ZXing_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/zxing-cpp-v2.2.1-lockzhiner-vision-module") +set(ZXing_DIR "${ZXing_ROOT_PATH}/lib/cmake/ZXing") +set(ZXing_INCLUDE_DIRS "${ZXing_ROOT_PATH}/include") +find_package(ZXing REQUIRED) +set(ZXing_LIBRARIES "${ZXing_LIBS}") + +# 基本图像处理示例 +add_executable(Test-qr_code-detector test_qr_code_detector.cc) +target_include_directories(Test-qr_code-detector PRIVATE + ${ZXing_INCLUDE_DIRS} + ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS} +) +target_link_libraries(Test-qr_code-detector PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES} ${ZXing_LIBRARIES} ) +install( + TARGETS Test-qr_code-detector + RUNTIME DESTINATION . +) +``` +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/C06_test_qr_code_detector +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` + +在执行完上述命令后,会在build目录下生成可执行文件。 + +## 5. 例程运行示例 +### 5.1 运行前准备 +- 请确保你已经参考 [凌智视觉模块摄像头部署指南](../../../periphery/capture/README.md) 正确下载了凌智视觉模块图片传输助手。 +### 5.2 运行过程 +在凌智视觉模块输入以下命令: +```shell +chmod 777 Test-qr_code-detector +./Test-qr_code-detector +``` +### 5.3 运行效果 +![title](./images/test_capture.png) + +--- + +## 6. 总结 +通过上述内容,我们成功的实现了一个二维码识别系统,包括: + +- 获取并加载包含二维码的图像。 +- 进行二维码的检测和解码,返回检测和解码后的结果。 +- 可视化包含二维码图像的识别结果。 diff --git a/Cpp_example/C06_test_qr_code_detector/images/1.png b/Cpp_example/C06_test_qr_code_detector/images/1.png new file mode 100755 index 0000000000000000000000000000000000000000..767bac6f7c8f45b2c320fc9301cd5d17f60be18e Binary files /dev/null and b/Cpp_example/C06_test_qr_code_detector/images/1.png differ diff --git a/Cpp_example/C06_test_qr_code_detector/images/test_capture.png b/Cpp_example/C06_test_qr_code_detector/images/test_capture.png new file mode 100755 index 0000000000000000000000000000000000000000..a7849202c9b190824dbe94a9df6267450e323311 Binary files /dev/null and b/Cpp_example/C06_test_qr_code_detector/images/test_capture.png differ diff --git a/Cpp_example/C06_test_qr_code_detector/test_qr_code_detector.cc b/Cpp_example/C06_test_qr_code_detector/test_qr_code_detector.cc new file mode 100755 index 0000000000000000000000000000000000000000..e7075905b8cc3eece79d166d46fda560efc665f6 --- /dev/null +++ b/Cpp_example/C06_test_qr_code_detector/test_qr_code_detector.cc @@ -0,0 +1,72 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace std::chrono; + +lockzhiner_vision_module::vision::QRCodeDetector model; + +int main() +{ + // 初始化 edit 模块 + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + + cv::VideoCapture cap; + // 设置摄像头获取帧的宽高 + cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); + cap.open(0); + + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + cv::Mat input_mat; + + while (true) + { + int read_index = 0; + int time_ms = 0; + + for (int i = 0; i < 30; i++) + { + high_resolution_clock::time_point start_time = high_resolution_clock::now(); + cap >> input_mat; + if (input_mat.empty()) + { + continue; + } + // 使用 model 对象的 Predict 方法对输入图像进行预测,获取二维码检测结果 + auto results = model.Predict(input_mat); + + high_resolution_clock::time_point end_time = high_resolution_clock::now(); + auto time_span = duration_cast(end_time - start_time); + time_ms += time_span.count(); + read_index += 1; + + cv::Mat output_image; + // 调用 Visualize 函数对原始图像和检测结果进行可视化处理,并将结果存储在 output_image 中 + lockzhiner_vision_module::vision::Visualize(input_mat, output_image, results); + edit.Print(output_image); + } + + std::cout << "Frames per second: " << 1000.0 / time_ms * read_index << std::endl; + } + + cap.release(); + return 0; +} \ No newline at end of file diff --git a/Cpp_example/C07_test_bar_codeDetector/CMakeLists.txt b/Cpp_example/C07_test_bar_codeDetector/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..12f88ccdae9dbafe8d836570a5494d94472135df --- /dev/null +++ b/Cpp_example/C07_test_bar_codeDetector/CMakeLists.txt @@ -0,0 +1,43 @@ +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test_bar_codeDetector) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 定义 ZXing SDK 路径 +set(ZXing_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/zxing-cpp-v2.2.1-lockzhiner-vision-module") +set(ZXing_DIR "${ZXing_ROOT_PATH}/lib/cmake/ZXing") +set(ZXing_INCLUDE_DIRS "${ZXing_ROOT_PATH}/include") +find_package(ZXing REQUIRED) +set(ZXing_LIBRARIES "${ZXing_LIBS}") + +# 基本图像处理示例 +add_executable(test-bar-codeDetector test_bar_codeDetector.cc) +target_include_directories(test-bar-codeDetector PRIVATE + ${ZXing_INCLUDE_DIRS} + ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS} +) +target_link_libraries(test-bar-codeDetector PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES} ${ZXing_LIBRARIES} ) +install( + TARGETS test-bar-codeDetector + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/C07_test_bar_codeDetector/README.md b/Cpp_example/C07_test_bar_codeDetector/README.md new file mode 100755 index 0000000000000000000000000000000000000000..4f3b74fc061dc91a0e1324f1a19d630dd67cf3a3 --- /dev/null +++ b/Cpp_example/C07_test_bar_codeDetector/README.md @@ -0,0 +1,262 @@ +# 条码识别 +条码识别是视觉模块经常使用到的功能之一,经常用于识别超市的货物信息。本章节中,我们将教会你如何使用 Lockzhiner Vision Module 进行条码识别。 +## 1. 基本知识讲解 +### 1.1 条码简介 +条码是一种通过宽度不同的平行线条和间隔来表示数据的机器可读形式,能够被扫描设备快速读取并转换为数字信号。被广泛应用于零售、物流、医疗和制造等行业。条码技术提高了数据输入的速度和准确性,降低了成本,并且由于其高效性和兼容性强的特点,成为商品标识、库存管理、货物追踪等操作中不可或缺的一部分。无论是简单的产品编码还是一些复杂的包含文字、网址的信息,条码都能提供可靠的支持。 +### 1.2 条码识别步骤 +条码码识别主要通过两个步骤完成:图像捕捉和解码。 +- 图像捕捉:使用设备摄像头拍摄包含条码的图像。 +- 解码:软件处理图像,定位并读取条码中的数据,转换为原始信息。 + +常用工具如ZXing和ZBar提供了便捷的方法来集成条码识别功能,使得开发者可以轻松实现从图像捕捉到数据解码的过程。用户只需简单操作即可快速获取条码中的信息,极大提高了效率和便利性。 + +--- + +## 2. C++ API文档 +### 2.1 Code128Detector类 +#### 2.1.1 头文件 +```c++ +#include +``` +- 作用:用于声明Code128Detector类,使得Code128Detector类可以在当前源文件中使用。 + +#### 2.1.2 构造类对象 +```c++ +lockzhiner_vision_module::vision::Code128Detector model; +``` +- 作用:用于实现条码识别。 +- 参数说明: + - 无 +- 返回值: + - 无 + +#### 2.1.3 Predict函数 +```c++ +auto results = model.Predict(input_mat); +``` +- 作用:Code128Detector类中的一个函数,用于实现条码识别。 +- 参数说明: + - input_mat: 要识别的图像。 +- 返回值: + - 返回一个包含Code 128格式的条码检测结果的对象集合。每个Result对象包含条码的位置信息和解码后的文本内容。 + +### 2.2 Visualize函数 +#### 2.2.1 头文件 +```c++ +#include +``` +- 作用:用于声明Visualize函数,使得Visualize函数可以在当前源文件中使用。 +#### 2.2.2 结果可视化 +```c++ +lockzhiner_vision_module::vision::Visualize(input_image, output_image, + results); +``` +- 参数说明: + - input_image: 输入参数,表示原始输入图像。 + - output_image: 输出参数,用于存储带有可视化结果的输出图像。 + - results: 输入参数,表示条码码检测的结果集。每个Result对象包含条码的位置信息和解码后的文本内容。 +- 返回值: + - 无 + +--- + +## 3. 综合代码介绍 +### 3.1 流程图 + + + +### 3.2 核心代码解析 +- 初始化 +```c++ +lockzhiner_vision_module::vision::Code128Detector model; +lockzhiner_vision_module::edit::Edit edit; +``` +- 调用摄像头捕获图像 +```c++ +cv::VideoCapture cap; +// 设置摄像头获取帧的宽高 +cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); +cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); +cap.open(0); + +// wihile循环中的以下代码用于捕获图像帧 +cap >> input_mat; +if (input_mat.empty()) +{ +continue; +} +``` +- 条码检测 +```c++ +auto results = model.Predict(input_mat); +``` + +### 3.3 完整代码实现 +```c++ +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace std::chrono; + +lockzhiner_vision_module::vision::Code128Detector model; + +int main() +{ + // 初始化 edit 模块 + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + + std::cout << "Device connected successfully." << std::endl; + + cv::VideoCapture cap; + // 设置摄像头获取帧宽高 + cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); + cap.open(0); + + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + cv::Mat input_mat; + + while (true) + { + int read_index = 0; + int time_ms = 0; + + for (int i = 0; i < 30; i++) + { + // 获取当前时间点作为开始时间 + high_resolution_clock::time_point start_time = high_resolution_clock::now(); + cap >> input_mat; + if (input_mat.empty()) + { + continue; + } + // 使用 model 对象的 Predict 方法对输入图像进行预测,获取条码检测结果 + auto results = model.Predict(input_mat); + // 获取当前时间点作为结束时间 + high_resolution_clock::time_point end_time = high_resolution_clock::now(); + auto time_span = duration_cast(end_time - start_time); + time_ms += time_span.count(); + read_index += 1; + + cv::Mat output_image; + // 调用 Visualize 函数对原始图像和检测结果进行可视化处理,并将结果存储在 output_image 中 + lockzhiner_vision_module::vision::Visualize(input_mat, output_image, results); + edit.Print(output_image); + } + + std::cout << "Frames per second: " << 1000.0 / time_ms * read_index << std::endl; + } + + cap.release(); + return 0; +} +``` + +--- + +## 4. 编译调试 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时以正确连接开发板。 +### 4.2 Cmake介绍 +```cmake +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +project(test_bar_codeDetector) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +# 定义 ZXing SDK 路径 +set(ZXing_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/zxing-cpp-v2.2.1-lockzhiner-vision-module") +set(ZXing_DIR "${ZXing_ROOT_PATH}/lib/cmake/ZXing") +set(ZXing_INCLUDE_DIRS "${ZXing_ROOT_PATH}/include") +find_package(ZXing REQUIRED) +set(ZXing_LIBRARIES "${ZXing_LIBS}") + +# 基本图像处理示例 +add_executable(test-bar-codeDetector test_bar_codeDetector.cc) +target_include_directories(test-bar-codeDetector PRIVATE + ${ZXing_INCLUDE_DIRS} + ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS} +) +target_link_libraries(test-bar-codeDetector PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES} ${ZXing_LIBRARIES} ) +install( + TARGETS test-bar-codeDetector + RUNTIME DESTINATION . +) +``` +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/C07_test_bar_codeDetector +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` + +在执行完上述命令后,会在build目录下生成可执行文件。 + +--- + +## 5. 例程运行示例 +### 5.1 运行前准备 +- 请确保你已经参考 [凌智视觉模块摄像头部署指南](../../../periphery/capture/README.md) 正确下载了凌智视觉模块图片传输助手。 +### 5.2 运行过程 +在凌智视觉模块输入以下命令: +```shell +chmod 777 test-bar-codeDetector +./test-bar-codeDetector +``` +### 5.3 运行效果 +![title](./images/Bar_Code_result.png) + +--- + +## 6. 总结 +通过上述内容,我们成功的实现了一个条码识别系统,包括: + +- 获取并加载包含code 128格式的条码图像。 +- 进行条码的检测和解码,返回检测和解码后的结果。 +- 可视化包含条码图像的识别结果。 diff --git a/Cpp_example/C07_test_bar_codeDetector/images/1.png b/Cpp_example/C07_test_bar_codeDetector/images/1.png new file mode 100755 index 0000000000000000000000000000000000000000..9b4497a9af0ce5740b0baa760bda0364553131c6 Binary files /dev/null and b/Cpp_example/C07_test_bar_codeDetector/images/1.png differ diff --git a/Cpp_example/C07_test_bar_codeDetector/images/Bar_Code_result.png b/Cpp_example/C07_test_bar_codeDetector/images/Bar_Code_result.png new file mode 100755 index 0000000000000000000000000000000000000000..523c0d3f3f0c5896193ecc4aaf5d52931c14a006 Binary files /dev/null and b/Cpp_example/C07_test_bar_codeDetector/images/Bar_Code_result.png differ diff --git a/Cpp_example/C07_test_bar_codeDetector/test_bar_codeDetector.cc b/Cpp_example/C07_test_bar_codeDetector/test_bar_codeDetector.cc new file mode 100644 index 0000000000000000000000000000000000000000..d0f7fef50528050e98ac6566f55a430234f36eef --- /dev/null +++ b/Cpp_example/C07_test_bar_codeDetector/test_bar_codeDetector.cc @@ -0,0 +1,74 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace std::chrono; + +lockzhiner_vision_module::vision::Code128Detector model; + +int main() +{ + // 初始化 edit 模块 + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + + std::cout << "Device connected successfully." << std::endl; + + cv::VideoCapture cap; + // 设置摄像头获取帧宽高 + cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); + cap.open(0); + + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + cv::Mat input_mat; + + while (true) + { + int read_index = 0; + int time_ms = 0; + + for (int i = 0; i < 30; i++) + { + // 获取当前时间点作为开始时间 + high_resolution_clock::time_point start_time = high_resolution_clock::now(); + cap >> input_mat; + if (input_mat.empty()) + { + continue; + } + // 使用 model 对象的 Predict 方法对输入图像进行预测,获取条码检测结果 + auto results = model.Predict(input_mat); + // 获取当前时间点作为结束时间 + high_resolution_clock::time_point end_time = high_resolution_clock::now(); + auto time_span = duration_cast(end_time - start_time); + time_ms += time_span.count(); + read_index += 1; + + cv::Mat output_image; + // 调用 Visualize 函数对原始图像和检测结果进行可视化处理,并将结果存储在 output_image 中 + lockzhiner_vision_module::vision::Visualize(input_mat, output_image, results); + edit.Print(output_image); + } + + std::cout << "Frames per second: " << 1000.0 / time_ms * read_index << std::endl; + } + + cap.release(); + return 0; +} \ No newline at end of file diff --git a/Cpp_example/D01_test_detection/CMakeLists.txt b/Cpp_example/D01_test_detection/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..2d519a7b3a3b1d337e5059fb9121f3fde146fbea --- /dev/null +++ b/Cpp_example/D01_test_detection/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.10) + +project(D01_test_detection) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +add_executable(Test-detection test_detection.cc) +target_include_directories(Test-detection PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-detection PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-detection + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/D01_test_detection/README.md b/Cpp_example/D01_test_detection/README.md new file mode 100755 index 0000000000000000000000000000000000000000..7ea22dbd4b281569ca4e54274de2b42f0526f1df --- /dev/null +++ b/Cpp_example/D01_test_detection/README.md @@ -0,0 +1,300 @@ +# 目标检测 +本文档展示了如何使用 lockzhiner_vision_module::vision::PaddleDet 类进行目标检测,并通过 +lockzhiner_vision_module::vision::Visualize 函数将检测结果可视化。 + +--- + +## 1. 基础知识讲解 +### 1.1 目标检测的基本介绍 +目标检测是计算机视觉领域中的一个关键任务,它不仅需要识别图像中存在哪些对象,还需要定位这些对象的位置。具体来说,目标检测算法会输出每个检测到的对象的边界框(Bounding Box)以及其所属类别的概率或置信度得分。 +- 应用场景:目标检测技术广泛应用于多个领域,包括但不限于安全监控、自动驾驶汽车、智能零售和医疗影像分析。 +### 1.2 PaddleDetection 的基本介绍 +PaddleDetection 是基于百度飞桨深度学习框架开发的一个高效的目标检测库,支持多种先进的目标检测模型,如 YOLO 系列、SSD、Faster R-CNN、Mask R-CNN 等。它提供了简单易用的接口,使得开发者能够快速部署高性能的目标检测应用。 +- 特点: + - 高性能:优化了推理速度,在保持高精度的同时实现了快速响应。 + - 灵活性:支持多种预训练模型,可以根据具体需求选择合适的模型架构。 + - 易于集成:提供 C++ API,便于嵌入式系统或桌面应用程序中使用。 + - 丰富的模型库:涵盖单阶段(One-stage)和双阶段(Two-stage)检测模型,满足不同场景的需求。 +- 适用场景:适用于需要对视频流或图像进行实时分析的应用场景,例如安防监控、智能交通系统、工业自动化等。 + +--- + +## 2. API 文档 +### 2.1 PaddleDetection 类 +#### 2.1.1 头文件 +```cpp +#include +``` +#### 2.1.2 构造函数 +```cpp +lockzhiner_vision_module::vision::PaddleDetection(); +``` +- 作用: + - 创建一个 PaddleDetection 对象,并初始化相关成员变量。 +- 参数: + - 无 +- 返回值: + - 无 +#### 2.1.3 Initialize函数 +```cpp +bool Initialize(const std::string& model_path); +``` +- 作用: + - 加载预训练的 PaddleDetection 模型。 +- 参数: + - model_path:模型路径,包含模型文件和参数文件。 +- 返回值: + - true:模型加载成功。 + - false:模型加载失败。 +#### 2.1.4 SetThreshold函数 +```cpp +void SetThreshold(float score_threshold = 0.5, float nms_threshold = 0.3); +``` +- 作用: + - 设置目标检测的置信度阈值和NMS阈值。 +- 参数: + - score_threshold:置信度阈值,默认值为0.5。 + - nms_threshold:NMS阈值,默认值为0.3。 +- 返回值: + - 无 +#### 2.1.5 Predict函数 +```cpp +std::vector Predict(const cv::Mat& image); +``` +- 作用: + - 使用加载的模型对输入图像进行目标检测,返回检测结果。 +- 参数: + - input_mat (const cv::Mat&): 输入的图像数据,通常是一个 cv::Mat 变量。 +- 返回值: + - 返回一个包含多个 DetectionResult 对象的向量,每个对象表示一个检测结果。 +### 2.2 DetectionResult 类 +#### 2.2.1 头文件 +```cpp +#include +``` +#### 2.2.2 box函数 +```cpp +lockzhiner_vision_module::vision::Rect box() const; +``` +- 作用: + - 获取目标检测结果的边界框。 +- 参数: + - 无 +- 返回值: + - 返回一个 lockzhiner_vision_module::vision::Rect 对象,表示目标检测结果的边界框。 +#### 2.2.3 score函数 +```cpp +float score() const; +``` +- 作用: + - 获取目标检测结果的置信度得分。 +- 参数: + - 无 +- 返回值: + - 返回一个 float 类型的置信度得分。 +#### 2.2.4 label_id函数 +- 作用: + - 获取目标检测结果的标签ID。 +- 参数: + - 无 +- 返回值: + - 返回一个整数,表示目标检测结果的标签ID。 +### 2.3 Visualize 函数 +### 2.3.1 头文件 +```cpp +#include +``` +### 2.3.2 函数定义 +```cpp +void lockzhiner_vision_module::vision::Visualize( + const cv::Mat& input_mat, + cv::Mat& output_image, + const std::vector& results, + const std::vector& labels = {}, + float font_scale = 0.4 +); +``` +- 作用: + - 将目标检测结果可视化到输入图像上,并返回可视化后的图像。 +- 参数: + - input_mat (const cv::Mat&): 输入图像。 + - output_image (cv::Mat&): 输出图像,包含标注后的结果。 + - results (const std::vector&): 检测结果列表。 + - labels (const std::vector&): 可选的标签列表,用于标注类别名称,默认为空。 + - font_scale (float): 字体大小比例,默认为 0.4。 +- 返回值: + - 无 + +--- + +## 3. 示例代码解析 + +### 3.1 流程图 + + + +### 3.2 核心代码解析 +- 初始化模型 +```cpp +lockzhiner_vision_module::vision::PaddleDet model; +if (!model.Initialize(argv[1])) { + std::cout << "Failed to initialize model." << std::endl; + return 1; +} +``` +- 模型推理 +```cpp +auto results = model.Predict(input_mat); +``` +- 可视化推理结果 +```cpp +cv::Mat output_image; +lockzhiner_vision_module::vision::Visualize(input_mat, output_image, results); + +edit.Print(output_image); +``` +### 3.3 完整代码实现 +```cpp +#include +#include +#include +#include +#include +#include + +using namespace std::chrono; + +int main(int argc, char* argv[]) { + if (argc != 2) { + std::cerr << "Usage: Test-PaddleDet model_path" << std::endl; + return 1; + } + + // 初始化模型 + lockzhiner_vision_module::vision::PaddleDet model; + if (!model.Initialize(argv[1])) { + std::cout << "Failed to initialize model." << std::endl; + return 1; + } + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + // 打开摄像头 + cv::VideoCapture cap; + cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); + cap.open(0); + + if (!cap.isOpened()) { + std::cerr << "Error: Could not open camera." << std::endl; + return 1; + } + + cv::Mat input_mat; + while (true) { + // 捕获一帧图像 + cap >> input_mat; + if (input_mat.empty()) { + std::cerr << "Warning: Captured an empty frame." << std::endl; + continue; + } + + // 调用模型进行预测 + high_resolution_clock::time_point start_time = high_resolution_clock::now(); + auto results = model.Predict(input_mat); + high_resolution_clock::time_point end_time = high_resolution_clock::now(); + + // 计算推理时间 + auto time_span = duration_cast(end_time - start_time); + std::cout << "Inference time: " << time_span.count() << " ms" << std::endl; + + // 可视化结果 + cv::Mat output_image; + lockzhiner_vision_module::vision::Visualize(input_mat, output_image, results); + + edit.Print(output_image); + } + + cap.release(); + return 0; +} +``` + +--- + +## 4. 编译过程 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时以正确连接开发板。 +### 4.2 Cmake介绍 +```cmake +cmake_minimum_required(VERSION 3.10) + +project(D01_test_detection) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +add_executable(Test-detection test_detection.cc) +target_include_directories(Test-detection PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-detection PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-detection + RUNTIME DESTINATION . +) +``` +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/D01_test_detection +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` + +在执行完上述命令后,会在build目录下生成可执行文件。 + +--- + +## 5. 例程运行示例 +### 5.1 运行 +```shell +chmod 777 Test-detection +# 在实际应用的过程中LZ-Picodet需要替换为下载的或者你的rknn模型 +./Test-detection LZ-Picodet +``` +### 5.2 结果展示 +- 可以看到我们正确识别了绿色的方块,同时打印了标签和置信度。 +![](./images/2025_0421_11_51_17_647.png) + +--- + +## 6. 总结 +本文档详细介绍了目标检测的基础知识及 PaddleDetection 的基本概念,并提供了详细的API文档说明,帮助开发者理解和实现目标检测与可视化功能。通过上述流程,可以构建高效的实时目标检测系统,满足多种应用场景的需求。 \ No newline at end of file diff --git a/Cpp_example/D01_test_detection/images/1.png b/Cpp_example/D01_test_detection/images/1.png new file mode 100755 index 0000000000000000000000000000000000000000..ba82d6e505fe1f7d56a3b817900c79bb7be38622 Binary files /dev/null and b/Cpp_example/D01_test_detection/images/1.png differ diff --git a/Cpp_example/D01_test_detection/images/2025_0421_11_51_17_647.png b/Cpp_example/D01_test_detection/images/2025_0421_11_51_17_647.png new file mode 100755 index 0000000000000000000000000000000000000000..9bb2263414612a4b441b9a314d9d65e52d30369f Binary files /dev/null and b/Cpp_example/D01_test_detection/images/2025_0421_11_51_17_647.png differ diff --git a/Cpp_example/D01_test_detection/test_detection.cc b/Cpp_example/D01_test_detection/test_detection.cc new file mode 100755 index 0000000000000000000000000000000000000000..5a52e847c3dcb49f17bf4a025b6a2819cba90111 --- /dev/null +++ b/Cpp_example/D01_test_detection/test_detection.cc @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include +#include + +using namespace std::chrono; + +int main(int argc, char *argv[]) +{ + if (argc != 2) + { + std::cerr << "Usage: Test-PaddleDet model_path" << std::endl; + return 1; + } + + // 初始化模型 + lockzhiner_vision_module::vision::PaddleDet model; + if (!model.Initialize(argv[1])) + { + std::cout << "Failed to initialize model." << std::endl; + return 1; + } + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + // 打开摄像头 + cv::VideoCapture cap; + cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); + cap.open(0); + + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return 1; + } + + cv::Mat input_mat; + while (true) + { + // 捕获一帧图像 + cap >> input_mat; + if (input_mat.empty()) + { + std::cerr << "Warning: Captured an empty frame." << std::endl; + continue; + } + + // 调用模型进行预测 + high_resolution_clock::time_point start_time = high_resolution_clock::now(); + auto results = model.Predict(input_mat); + high_resolution_clock::time_point end_time = high_resolution_clock::now(); + + // 计算推理时间 + auto time_span = duration_cast(end_time - start_time); + std::cout << "Inference time: " << time_span.count() << " ms" << std::endl; + + // 可视化结果 + cv::Mat output_image; + lockzhiner_vision_module::vision::Visualize(input_mat, output_image, results); + + edit.Print(output_image); + } + + cap.release(); + return 0; +} \ No newline at end of file diff --git a/Cpp_example/D02_DigitHandRecog/CMakeLists.txt b/Cpp_example/D02_DigitHandRecog/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..085cd7eeedb3f12b4ed68a032e5b1d622d60b271 --- /dev/null +++ b/Cpp_example/D02_DigitHandRecog/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.10) + +project(test_DigitHandRecog) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +add_executable(Test-DigitHandRecog DigitHandRecog.cc) +target_include_directories(Test-DigitHandRecog PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-DigitHandRecog PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-DigitHandRecog + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/D02_DigitHandRecog/DigitHandRecog.cc b/Cpp_example/D02_DigitHandRecog/DigitHandRecog.cc new file mode 100755 index 0000000000000000000000000000000000000000..3ccb96bffcd44878981b1e267bd0abc3e08cd8f7 --- /dev/null +++ b/Cpp_example/D02_DigitHandRecog/DigitHandRecog.cc @@ -0,0 +1,114 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::chrono; + +lockzhiner_vision_module::vision::PaddleClas model; + +int TestCapture() +{ + // 初始化 edit 模块 + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + + cv::VideoCapture cap; + // 设置摄像头长宽 + cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); + cap.open(0); + + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + cv::Mat input_mat; + while (true) + { + int read_index = 0; + int time_ms = 0; + for (int i = 0; i < 30; i++) + { + // 获取当前时间点作为开始时间 + high_resolution_clock::time_point start_time = + high_resolution_clock::now(); + cap >> input_mat; + if (input_mat.empty()) + { + continue; + } + // 使用 model 对象的 Predict 方法对输入图像进行预测 + auto result = model.Predict(input_mat); + // 获取当前时间点作为结束时间 + high_resolution_clock::time_point end_time = high_resolution_clock::now(); + auto time_span = duration_cast(end_time - start_time); + time_ms += time_span.count(); + read_index += 1; + + std::cout << "score is " << result.score << ";label_id is " + << result.label_id << std::endl; + + cv::Mat output_image; + lockzhiner_vision_module::vision::Visualize(input_mat, output_image, + result); + // 使用 edit 模块处理帧 + edit.Print(output_image); + } + std::cout << "Frames per second: " << 1000.0 / time_ms * read_index + << std::endl; + } + cap.release(); + return 0; +} + +int TestImage(const std::string &image_path) +{ + cv::Mat input_image = cv::imread(image_path); + auto result = model.Predict(input_image); + std::cout << "score is " << result.score << ";label_id is " << result.label_id + << std::endl; + cv::Mat output_image; + lockzhiner_vision_module::vision::Visualize(input_image, output_image, + result); + cv::imwrite("cls_result.png", output_image); + return 0; +} + +int main(int argc, char *argv[]) +{ + if (argc != 3) + { + std::cerr << "Usage: Test-PaddleClas model_path " + << std::endl; + return 1; + } + + if (!model.Initialize(argv[1])) + { + std::cout << "Failed to initialize model." << std::endl; + return 1; + } + + std::string argument(argv[2]); + if (argument == "Capture") + { + return TestCapture(); + } + else + { + return TestImage(argument); + } + return 0; +} \ No newline at end of file diff --git a/Cpp_example/D02_DigitHandRecog/README.md b/Cpp_example/D02_DigitHandRecog/README.md new file mode 100755 index 0000000000000000000000000000000000000000..ac6119c2223b8396d90fb645db897291fceda567 --- /dev/null +++ b/Cpp_example/D02_DigitHandRecog/README.md @@ -0,0 +1,293 @@ +# 手写数字识别 +手写数字识别是一种经典的模式识别和图像处理问题,旨在通过计算机自动识别用户手写的数字。本章节中,我们将教会你如何使用 Lockzhiner Vision Module 进行手写数字识别。 +## 1. 基本知识讲解 +### 1.1 手写数字识别简介 +手写数字识别是一种利用计算机视觉和机器学习技术自动识别手写数字的过程。它通过图像预处理、特征提取和模型训练来实现高效准确的数字识别。被广泛应用于银行支票处理、邮政编码识别及考试评分等场景。这项技术不仅提高了数据处理的速度和准确性,还极大地简化了输入流程,为金融、邮政和教育等行业带来了显著的便利。 +### 1.2 手写数字识别常用方法 +目前,实现手写数字识别方法有很多,常用的方法如下: +- 卷积神经网络(CNN):最流行的方法之一,能够自动从图像中学习特征。适用于复杂背景和不同书写风格的手写数字识别。 +- 支持向量机(SVM):一种传统的机器学习方法,通过提取图像的特征(如HOG特征)进行分类,适合处理较为规范的手写数字。 +- K近邻算法(KNN):基于相似度的分类方法,通过比较待识别数字与训练样本的距离来进行分类,简单但计算成本较高。 + +--- + +## 2. C++ API文档 +### 2.1 PaddleClas类 +#### 2.1.1 头文件 +```c++ +#include +``` +- 作用:用于声明PaddleClas类,使得PaddleClas类可以在当前源文件中使用。 + +#### 2.1.2 构造类函数 +```c++ +lockzhiner_vision_module::vision::PaddleClas model; +``` +- 作用:用于实现手写数字识别。 +- 参数说明: + - 无 +- 返回值: + - 无 + +#### 2.1.3 Predict函数 +```c++ +auto result = model.Predict(input_mat); +``` +- 作用:PaddleClas类中的一个函数,用于实现手写数字识别。 +- 参数说明: + - input_mat:要识别的图像。 +- 返回值: + - 返回一个包含手写数字分类结果的对象。该Result对象包含预测得分(score)和对应的标签ID(label_id),即识别出的手写数字。 + +### 2.2 Visualize函数 +#### 2.2.1 头文件 +```c++ +#include +``` +- 作用:用于声明Visualize函数,使得Visualize函数可以在当前源文件中使用。 +#### 2.2.2 结果可视化 +```c++ +lockzhiner_vision_module::vision::Visualize(input_mat, output_image, result); +``` +- 参数说明: + - input_mat:表示原始输入图像。 + - output_image:用于存储带有可视化结果的输出图像。 + - result:输入参数,表示手写数字识别的结果。该Result对象包含预测得分(score)和对应的标签ID(label_id)。 +- 返回值: + - 无 + +--- + +## 3. 综合代码解析 +### 3.1 流程图 + + + +### 3.2 核心代码解析 +- 初始化分类模型 +```cpp +lockzhiner_vision_module::vision::PaddleClas model; +``` +自定义函数参数如下 +- 图片手写数字识别 +```c++ +int TestImage(const std::string& image_path) +``` +- 参数说明: + - image_path:输入参数,表示包含手写数字的图像文件路径。 +- 返回值: + - 返回0表示成功执行,并保存执行结果为"cls_result.png"。 + +- 摄像头实时手写数字识别 +```c++ +int TestCapture() +``` +- 参数说明: + - 无 +- 返回值: + - 返回0表示执行成功,并将检测结果绘制在原始图像上。程序会持续从摄像头读取帧并进行处理,直到手动终止程序。 + +### 3.3 完整代码实现 +```c++ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::chrono; + +lockzhiner_vision_module::vision::PaddleClas model; + +int TestCapture() +{ + // 初始化 edit 模块 + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + + cv::VideoCapture cap; + // 设置摄像头长宽 + cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); + cap.open(0); + + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + cv::Mat input_mat; + while (true) + { + int read_index = 0; + int time_ms = 0; + for (int i = 0; i < 30; i++) + { + // 获取当前时间点作为开始时间 + high_resolution_clock::time_point start_time = + high_resolution_clock::now(); + cap >> input_mat; + if (input_mat.empty()) + { + continue; + } + // 使用 model 对象的 Predict 方法对输入图像进行预测 + auto result = model.Predict(input_mat); + // 获取当前时间点作为结束时间 + high_resolution_clock::time_point end_time = high_resolution_clock::now(); + auto time_span = duration_cast(end_time - start_time); + time_ms += time_span.count(); + read_index += 1; + + std::cout << "score is " << result.score << ";label_id is " + << result.label_id << std::endl; + + cv::Mat output_image; + lockzhiner_vision_module::vision::Visualize(input_mat, output_image, + result); + // 使用 edit 模块处理帧 + edit.Print(output_image); + } + std::cout << "Frames per second: " << 1000.0 / time_ms * read_index + << std::endl; + } + cap.release(); + return 0; +} + +int TestImage(const std::string &image_path) +{ + cv::Mat input_image = cv::imread(image_path); + auto result = model.Predict(input_image); + std::cout << "score is " << result.score << ";label_id is " << result.label_id + << std::endl; + cv::Mat output_image; + lockzhiner_vision_module::vision::Visualize(input_image, output_image, + result); + cv::imwrite("cls_result.png", output_image); + return 0; +} + +int main(int argc, char *argv[]) +{ + if (argc != 3) + { + std::cerr << "Usage: Test-PaddleClas model_path " + << std::endl; + return 1; + } + + if (!model.Initialize(argv[1])) + { + std::cout << "Failed to initialize model." << std::endl; + return 1; + } + + std::string argument(argv[2]); + if (argument == "Capture") + { + return TestCapture(); + } + else + { + return TestImage(argument); + } + return 0; +} +``` + +--- + +## 4. 编译调试 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时已经正确连接开发板。 +### 4.2 Cmake介绍 +```cmake +cmake_minimum_required(VERSION 3.10) + +project(test_DigitHandRecog) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +add_executable(Test-DigitHandRecog DigitHandRecog.cc) +target_include_directories(Test-DigitHandRecog PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-DigitHandRecog PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-DigitHandRecog + RUNTIME DESTINATION . +) +``` +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/D02_DigitHandRecog +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` + +在执行完上述命令后,会在build目录下生成可执行文件。 + +--- + +## 5. 例程运行示例 +### 5.1 运行前准备 +- 请确保你已经下载了 [凌智视觉模块手写数字分类模型](https://gitee.com/LockzhinerAI/LockzhinerVisionModule/releases/download/v0.0.0/LZ-DigitHandRecog.rknn) +### 5.2 运行过程 +在凌智视觉模块输入以下命令: +```shell +chmod 777 Test-DigitHandRecog +# 调用摄像头实时识别 +./Test-DigitHandRecog LZ-DigitHandRecog.rknn Capture +# 单张图像识别 +./Test-DigitHandRecog LZ-DigitHandRecog.rknn image_path +``` +### 5.3 运行效果 +- 图像识别效果图 + ![title](./images/cls_result.png) +- 摄像头实时识别效果图 + ![title](./images/Capture_7.png) + +--- + +## 6. 总结 +通过上述内容,我们成功的实现了一个手写数字识别系统,包括: + +- 获取并加载包含手写数字的图像。 +- 进行手写数字的检测和分类,返回检测和分类后的结果。 +- 可视化包含手写数字图像的识别结果。 \ No newline at end of file diff --git a/Cpp_example/D02_DigitHandRecog/images/1.png b/Cpp_example/D02_DigitHandRecog/images/1.png new file mode 100755 index 0000000000000000000000000000000000000000..7e7119623962ecbfa23801af7fb427d74056e31a Binary files /dev/null and b/Cpp_example/D02_DigitHandRecog/images/1.png differ diff --git a/Cpp_example/D02_DigitHandRecog/images/Capture_7.png b/Cpp_example/D02_DigitHandRecog/images/Capture_7.png new file mode 100755 index 0000000000000000000000000000000000000000..bae885b073c9448ae2dd51a86adae3d71f7ac4ee Binary files /dev/null and b/Cpp_example/D02_DigitHandRecog/images/Capture_7.png differ diff --git a/Cpp_example/D02_DigitHandRecog/images/cls_result.png b/Cpp_example/D02_DigitHandRecog/images/cls_result.png new file mode 100755 index 0000000000000000000000000000000000000000..b410a1f02741c70b03517b845291d3e90eda1e51 Binary files /dev/null and b/Cpp_example/D02_DigitHandRecog/images/cls_result.png differ diff --git a/Cpp_example/D03_face_recognition_system/CMakeLists.txt b/Cpp_example/D03_face_recognition_system/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8a3eda59bca4f08633e1a9072af45bdb2030ec20 --- /dev/null +++ b/Cpp_example/D03_face_recognition_system/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.10) + +project(D03_face_recognition_system) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +add_executable(Test-face-recognition-system face_recognition_system.cc) +target_include_directories(Test-face-recognition-system PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-face-recognition-system PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-face-recognition-system + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/D03_face_recognition_system/README.md b/Cpp_example/D03_face_recognition_system/README.md new file mode 100755 index 0000000000000000000000000000000000000000..5ab0c2b82ccd7ff551344a716b6a611f41b919fd --- /dev/null +++ b/Cpp_example/D03_face_recognition_system/README.md @@ -0,0 +1,275 @@ +# 人脸识别系统 +本章节基于 Lockzhiner Vision Module 的 LZ-Picodet 模型训练的人脸检测模型 LZ-Face,以及ArcFace人脸识别模型,实现了一个人脸识别系统。 +## 1. 基本知识讲解 +### 1.1 人脸识别简介 +人脸识别是一种利用人的脸部特征进行身份识别的生物识别技术。它通过检测图像或视频中的人脸,提取如眼睛、鼻子和嘴巴等关键特征点,并将这些信息转化为面部特征向量,进而与已知人脸数据库中的数据比对来确认个人身份。被广泛应用于安全监控、门禁系统、移动设备解锁及社交媒体等领域。 +### 1.2 人脸识别常用方法 +人脸识别主要涉及到以下几个关键步骤:人脸检测、特征提取和匹配识别。以下是实现人脸识别的常用方法: +- 深度学习方法:现代的人脸识别系统大多采用深度学习方法,并结合大规模人脸数据库和高性能计算资源,实现了非常高的识别精度。 +- 基于模板匹配的方法:通过将待识别人脸与预定义的标准人脸模板进行比较来实现识别。 + +--- + +## 2. C++ API 文档 +### 2.1 FaceRecognitionSystem类 +#### 2.1.1 头文件 +```c++ +#include +``` +- 作用:用于声明FaceRecognitionSystem类,使得FaceRecognitionSystem类可以在当前文件中使用。 + +#### 2.1.2 构造类函数 +```c++ +lockzhiner_vision_module::vision::FaceRecognitionSystem face_system; +``` +- 作用:用于实现人脸识别。 +- 参数说明: + - 无 +- 返回值: + - 无 + +#### 2.1.3 Predict函数 +```c++ +auto result = face_system.Predict(input_mat); +``` +- 作用:FaceRecognitionSystem类中的一个函数,用于实现人脸识别。 +- 参数说明: + - input_mat:要识别的图像。 +- 返回值: + - 返回一个包含人脸识别结果的对象。该对象包含人脸的id,置信度和人脸的位置信息。 + +### 2.2 Visualize函数 +#### 2.2.1 头文件 +```c++ +#include +``` +- 作用:用于声明Visualize函数,使得Visualize函数可以在当前源文件中使用。 + +#### 2.2.2 结果可视化 +```c++ +lockzhiner_vision_module::vision::Visualize(input_mat, output_image, result); +``` +- 参数说明: + - input_mat:原始输入图像。 + - output_image:用于存储带有可视化结果的输出图像。 + - result:输入参数,表示人脸识别的结果。该result对象包含人脸的id,置信度和人脸的位置信息。 +- 返回值: + - 无 + +--- + +## 3. 综合代码解析 +### 3.1 流程图 + + + +### 3.2 核心代码解析 +- 初始化人脸识别模型 +```cpp +lockzhiner_vision_module::vision::FaceRecognitionSystem face_system; +``` +- 构建人脸数据库 +```cpp +if (!face_system.BuildDatabase(argv[3], argv[4])) { + std::cout << "Failed to build database." << std::endl; + return 1; +} +``` +- 调用摄像头捕获图像 +```c++ +cv::VideoCapture cap; +// 设置摄像头获取帧的宽高 +cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); +cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); +cap.open(0); + +// wihile循环中的以下代码用于捕获图像帧 +cap >> input_mat; +if (input_mat.empty()) +{ +continue; +} +``` +- 模型推理 +```cpp +auto result = face_system.Predict(input_mat); +``` + +### 3.3 完整代码实现 +```c++ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::chrono; + +lockzhiner_vision_module::vision::FaceRecognitionSystem face_system; + +int main(int argc, char *argv[]) +{ + if (argc != 5) + { + std::cerr << "Usage: Test-Face-Recognition-System det_model_path " + "rec_model_path database_root crop_root" + << std::endl; + return 1; + } + + if (!face_system.Initialize(argv[1], argv[2])) + { + std::cout << "Failed to initialize face system." << std::endl; + return 1; + } + + if (!face_system.BuildDatabase(argv[3], argv[4])) + { + std::cout << "Failed to build database." << std::endl; + return 1; + } + // 初始化 edit 模块 + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + + cv::VideoCapture cap; + // 设置摄像头捕获帧的宽高 + cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); + cap.open(0); + + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + cv::Mat input_mat; + while (true) + { + int read_index = 0; + int time_ms = 0; + for (int i = 0; i < 30; i++) + { + // 获取当前时间点作为开始时间 + high_resolution_clock::time_point start_time = + high_resolution_clock::now(); + cap >> input_mat; + if (input_mat.empty()) + { + continue; + } + // 使用 model 对象的 Predict 方法对输入图像进行预测 + auto result = face_system.Predict(input_mat); + // 获取当前时间点作为结束时间 + high_resolution_clock::time_point end_time = high_resolution_clock::now(); + auto time_span = duration_cast(end_time - start_time); + time_ms += time_span.count(); + read_index += 1; + + cv::Mat output_image; + lockzhiner_vision_module::vision::Visualize(input_mat, output_image, + result); + // 使用 edit 模块处理帧 + edit.Print(output_image); + } + std::cout << "Frames per second: " << 1000.0 / time_ms * read_index + << std::endl; + } + // 释放摄像头资源 + cap.release(); + return 0; +} +``` + +--- + +## 4. 编译调试 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时已经正确连接开发板。 +### 4.2 Cmake介绍 +```cmake +cmake_minimum_required(VERSION 3.10) + +project(D03_face_recognition_system) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +add_executable(Test-face-recognition-system face_recognition_system.cc) +target_include_directories(Test-face-recognition-system PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-face-recognition-system PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-face-recognition-system + RUNTIME DESTINATION . +) +``` +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/D03_face_recognition_system +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` + +在执行完上述命令后,会在build目录下生成可执行文件。 + +--- + +## 5. 例程运行示例 +### 5.1 运行前准备 +- 请确保你已经下载了 [凌智视觉模块人脸检测模型](https://gitee.com/LockzhinerAI/LockzhinerVisionModule/releases/download/v0.0.3/LZ-Face.rknn) +- 请确保你已经下载了 [凌智视觉模块人脸识别模型](https://gitee.com/LockzhinerAI/LockzhinerVisionModule/releases/download/v0.0.0/LZ-ArcFace.rknn) +### 5.2 运行过程 +在凌智视觉模块输入以下命令: +```shell +chmod 777 Test-face-recognition-system +./Test-face-recognition-system LZ-Face LZ-ArcFace BaseDataset CropDataset +``` +### 5.3 运行效果 +![title](./images/result.png) +### 5.4 注意事项 +上面提到的BaseDataset和CropDataset需要提前创建。BaseDataset用于存储已有的人脸图像,同一个人的人脸图像保存在BaseDataset的一个子文件夹下。CropDataset文件夹创建时为空,用于保存裁剪后的人脸图像,目录结构和BaseDataset相同。 + +--- + +## 6. 总结 +通过上述内容,我们成功实现了一个高效的人脸识别系统,包括: + +- 获取并加载包含人脸的图像。 +- 进行人脸检测和识别。 +- 可视化人脸识别结果。 \ No newline at end of file diff --git a/Cpp_example/D03_face_recognition_system/face_recognition_system.cc b/Cpp_example/D03_face_recognition_system/face_recognition_system.cc new file mode 100755 index 0000000000000000000000000000000000000000..5d897718792124ac41a6df6f4406988b70c3e464 --- /dev/null +++ b/Cpp_example/D03_face_recognition_system/face_recognition_system.cc @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::chrono; + +lockzhiner_vision_module::vision::FaceRecognitionSystem face_system; + +int main(int argc, char *argv[]) +{ + if (argc != 5) + { + std::cerr << "Usage: Test-Face-Recognition-System det_model_path " + "rec_model_path database_root crop_root" + << std::endl; + return 1; + } + + if (!face_system.Initialize(argv[1], argv[2])) + { + std::cout << "Failed to initialize face system." << std::endl; + return 1; + } + + if (!face_system.BuildDatabase(argv[3], argv[4])) + { + std::cout << "Failed to build database." << std::endl; + return 1; + } + // 初始化 edit 模块 + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + std::cout << "Device connected successfully." << std::endl; + + cv::VideoCapture cap; + // 设置摄像头捕获帧的宽高 + cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); + cap.open(0); + + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return EXIT_FAILURE; + } + + cv::Mat input_mat; + while (true) + { + int read_index = 0; + int time_ms = 0; + for (int i = 0; i < 30; i++) + { + // 获取当前时间点作为开始时间 + high_resolution_clock::time_point start_time = + high_resolution_clock::now(); + cap >> input_mat; + if (input_mat.empty()) + { + continue; + } + // 使用 model 对象的 Predict 方法对输入图像进行预测 + auto result = face_system.Predict(input_mat); + // 获取当前时间点作为结束时间 + high_resolution_clock::time_point end_time = high_resolution_clock::now(); + auto time_span = duration_cast(end_time - start_time); + time_ms += time_span.count(); + read_index += 1; + + cv::Mat output_image; + lockzhiner_vision_module::vision::Visualize(input_mat, output_image, + result); + // 使用 edit 模块处理帧 + edit.Print(output_image); + } + std::cout << "Frames per second: " << 1000.0 / time_ms * read_index + << std::endl; + } + // 释放摄像头资源 + cap.release(); + return 0; +} \ No newline at end of file diff --git a/Cpp_example/D03_face_recognition_system/images/1.png b/Cpp_example/D03_face_recognition_system/images/1.png new file mode 100755 index 0000000000000000000000000000000000000000..6c73599c07a8e1c6199d2a73b6d838fb50505262 Binary files /dev/null and b/Cpp_example/D03_face_recognition_system/images/1.png differ diff --git a/Cpp_example/D03_face_recognition_system/images/result.png b/Cpp_example/D03_face_recognition_system/images/result.png new file mode 100755 index 0000000000000000000000000000000000000000..7f388b139844399d26af9debc1f985b6dd67982d Binary files /dev/null and b/Cpp_example/D03_face_recognition_system/images/result.png differ diff --git a/Cpp_example/D04_distance/CMakeLists.txt b/Cpp_example/D04_distance/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..4f56b10aa489d2c8cfa75bc40537f3f0d3d053aa --- /dev/null +++ b/Cpp_example/D04_distance/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.10) + +project(test_distance) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +add_executable(Test-distance distance.cc) +target_include_directories(Test-distance PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-distance PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-distance + RUNTIME DESTINATION . +) \ No newline at end of file diff --git a/Cpp_example/D04_distance/README.md b/Cpp_example/D04_distance/README.md new file mode 100755 index 0000000000000000000000000000000000000000..eb0a402d9949fdf403a48026ad065bc896b6aeb2 --- /dev/null +++ b/Cpp_example/D04_distance/README.md @@ -0,0 +1,350 @@ +# 测距 +本系统基于单目摄像头和目标检测模型,通过目标的真实物理尺寸和其在图像中的像素大小之间的关系,计算目标与摄像头之间的距离。该方案适用于嵌入式设备或高性能计算平台,并支持实时测距和可视化显示。 +## 1. 基础知识 +### 1.1 测距原理 +利用单目摄像头成像几何关系,测距公式如下: +$ 距离 = \frac{真实物体大小 \times 相机焦距}{目标在图像中的像素大小} $ +- 真实物体大小:目标的实际物理尺寸(单位:米)。 +- 相机焦距:需要通过标定获得,或者通过实验调整得到合适的值。 +- 目标在图像中的像素大小:通过目标检测模型获取目标的边界框宽度和高度,并取其平均值作为目标的像素大小。 +### 1.2 实现步骤 +在实现测距系统的过程中,最重要的一步就是先检测到目标,通过判断目标的像素和实际大小的一个转换关系,从而得到目标与摄像头之间的距离。在本次实验中,我们就综合目标检测来做测距实验。 +- 初始化目标检测模型: + - 加载预训练的目标检测模型(如 PaddleDet)。 + - 初始化外部设备连接(如串口通信模块)。 +- 捕获视频流: + - 打开摄像头,设置分辨率(如 640x480)。 + - 捕获每一帧图像。 +- 目标检测与测距: + - 使用目标检测模型对每一帧图像进行预测,获取目标的边界框信息。 + - 根据边界框的宽度和高度计算目标的像素大小。 + - 使用测距公式计算目标与摄像头的距离。 +- 结果可视化: + - 在图像上绘制目标的边界框。 + - 将计算出的距离信息标注在图像上。 + - 将处理后的图像发送到外部设备显示。 +- 循环运行: + - 实时处理每一帧图像,直到用户退出程序。 + +--- + +## 2. API说明 +### 2.1 PaddleDetection 类 +#### 2.1.1 头文件 +```cpp +#include +``` +#### 2.1.2 构造函数 +```cpp +lockzhiner_vision_module::vision::PaddleDetection(); +``` +- 作用: + - 创建一个 PaddleDetection 对象,并初始化相关成员变量。 +- 参数: + - 无 +- 返回值: + - 无 +#### 2.1.3 Initialize函数 +```cpp +bool Initialize(const std::string& model_path); +``` +- 作用: + - 加载预训练的 PaddleDetection 模型。 +- 参数: + - model_path:模型路径,包含模型文件和参数文件。 +- 返回值: + - true:模型加载成功。 + - false:模型加载失败。 +#### 2.1.4 SetThreshold函数 +```cpp +void SetThreshold(float score_threshold = 0.5, float nms_threshold = 0.3); +``` +- 作用: + - 设置目标检测的置信度阈值和NMS阈值。 +- 参数: + - score_threshold:置信度阈值,默认值为0.5。 + - nms_threshold:NMS阈值,默认值为0.3。 +- 返回值: + - 无 +#### 2.1.5 Predict函数 +```cpp +std::vector Predict(const cv::Mat& image); +``` +- 作用: + - 使用加载的模型对输入图像进行目标检测,返回检测结果。 +- 参数: + - input_mat (const cv::Mat&): 输入的图像数据,通常是一个 cv::Mat 变量。 +- 返回值: + - 返回一个包含多个 DetectionResult 对象的向量,每个对象表示一个检测结果。 +### 2.2 DetectionResult 类 +#### 2.2.1 头文件 +```cpp +#include +``` +#### 2.2.2 box函数 +```cpp +lockzhiner_vision_module::vision::Rect box() const; +``` +- 作用: + - 获取目标检测结果的边界框。 +- 参数: + - 无 +- 返回值: + - 返回一个 lockzhiner_vision_module::vision::Rect 对象,表示目标检测结果的边界框。 +#### 2.2.3 score函数 +```cpp +float score() const; +``` +- 作用: + - 获取目标检测结果的置信度得分。 +- 参数: + - 无 +- 返回值: + - 返回一个 float 类型的置信度得分。 +#### 2.2.4 label_id函数 +- 作用: + - 获取目标检测结果的标签ID。 +- 参数: + - 无 +- 返回值: + - 返回一个整数,表示目标检测结果的标签ID。 +### 2.3 Visualize 函数 +```cpp +void lockzhiner_vision_module::vision::Visualize( + const cv::Mat& input_mat, + cv::Mat& output_image, + const std::vector& results, + const std::vector& labels = {}, + float font_scale = 0.4 +); +``` +- 作用: + - 将目标检测结果可视化到输入图像上,并返回可视化后的图像。 +- 参数: + - input_mat (const cv::Mat&): 输入图像。 + - output_image (cv::Mat&): 输出图像,包含标注后的结果。 + - results (const std::vector&): 检测结果列表。 + - labels (const std::vector&): 可选的标签列表,用于标注类别名称,默认为空。 + - font_scale (float): 字体大小比例,默认为 0.4。 +- 返回值: + - 无 +--- + +## 3. 综合代码解析 + +### 3.1 流程图 + + + +### 3.2 核心代码解析 +- 初始化模型 +```cpp +lockzhiner_vision_module::vision::PaddleDet model; +if (!model.Initialize(argv[1])) { + std::cout << "Failed to initialize model." << std::endl; + return 1; +} +``` +- 模型推理 +```cpp +auto results = model.Predict(input_mat); +``` +- 计算物体距离 +```cpp +for (size_t i = 0; i < results.size(); ++i) { + int width = results[i].box.width; + int height = results[i].box.height; + // 计算目标的平均像素大小 + float pixel_size = (width + height) / 2.0f; + // 计算距离 + float distance = CalculateDistance(pixel_size, REAL_OBJECT_SIZE, FOCAL_LENGTH); + // 将距离信息存储到 label_id 中(需要转换为整数) + results[i].label_id = static_cast(distance * 100); // 单位:厘米 +} +``` +CalculateDistance函数具体参数定义如下所示。 +```c++ +float CalculateDistance(float pixel_size, float real_size, float focal_length) +``` +- 参数: + - pixel_size:目标在图像中的像素大小,单位为像素。 + - real_size:目标的实际物理尺寸,单位为米。 + - focal_length:相机焦距,单位为像素。 +- 返回值:目标与摄像头之间的距离,单位为米。 + +### 3.3 完整代码实现 +```c++ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const float REAL_OBJECT_SIZE = 0.02f; +const float FOCAL_LENGTH = 800.0f; + +float CalculateDistance(float pixel_size, float real_size, float focal_length) { + return (real_size * focal_length) / pixel_size; +} + +int main(int argc, char *argv[]) { + if (argc != 2) { + std::cerr << "Usage: Test-PaddleDet model_path" << std::endl; + return 1; + } + + // 初始化模型和设备连接 + lockzhiner_vision_module::vision::PaddleDet model; + if (!model.Initialize(argv[1])) { + std::cout << "Failed to initialize model." << std::endl; + return 1; + } + + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + + cv::VideoCapture cap; + cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); + cap.open(0); + + if (!cap.isOpened()) { + std::cerr << "Error: Could not open camera." << std::endl; + return 1; + } + + cv::Mat input_mat; + while (true) { + cap >> input_mat; + if (input_mat.empty()) { + std::cerr << "Warning: Captured an empty frame." << std::endl; + continue; + } + + // 预测并可视化结果 + auto start_time = std::chrono::high_resolution_clock::now(); + auto results = model.Predict(input_mat); + auto end_time = std::chrono::high_resolution_clock::now(); + auto time_span = std::chrono::duration_cast(end_time - start_time); + std::cout << "Inference time: " << time_span.count() << " ms" << std::endl; + + cv::Mat output_image; + lockzhiner_vision_module::vision::Visualize(input_mat, output_image, results); + + // 在每个检测框上绘制距离信息 + for (size_t i = 0; i < results.size(); ++i) { + int width = results[i].box.width; + int height = results[i].box.height; + float pixel_size = (width + height) / 2.0f; + float distance = CalculateDistance(pixel_size, REAL_OBJECT_SIZE, FOCAL_LENGTH); + + // 格式化距离文本(保留两位小数) + std::stringstream ss; + ss << std::fixed << std::setprecision(2) << distance << " m"; + std::string distance_text = ss.str(); + + // 在检测框左上方绘制距离 + cv::Rect box = results[i].box; + cv::putText( + output_image, + distance_text, + cv::Point(box.x, box.y - 25), // 在框上方5像素处显示 + cv::FONT_HERSHEY_SIMPLEX, + 0.5, // 字体大小 + cv::Scalar(0, 255, 0), // 绿色文本 + 2 // 线宽 + ); + } + + // 显示结果到外部设备 + edit.Print(output_image); + } + + cap.release(); + return 0; +} +``` + +--- + +## 4. 编译过程 +### 4.1 编译环境搭建 +- 请确保你已经按照 [开发环境搭建指南](../../../../docs/introductory_tutorial/cpp_development_environment.md) 正确配置了开发环境。 +- 同时以正确连接开发板。 +### 4.2 Cmake介绍 +```cmake +cmake_minimum_required(VERSION 3.10) + +project(test_distance) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 定义项目根目录路径 +set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..") +message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH}) + +include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake") + +# 定义 OpenCV SDK 路径 +set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module") +set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4") +find_package(OpenCV REQUIRED) +set(OPENCV_LIBRARIES "${OpenCV_LIBS}") + +# 定义 LockzhinerVisionModule SDK 路径 +set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk") +set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module") +find_package(LockzhinerVisionModule REQUIRED) + +add_executable(Test-distance distance.cc) +target_include_directories(Test-distance PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}) +target_link_libraries(Test-distance PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES}) + +install( + TARGETS Test-distance + RUNTIME DESTINATION . +) +``` +### 4.3 编译项目 +使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目 +```bash +# 进入Demo所在目录 +cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/D04_distance +# 创建编译目录 +rm -rf build && mkdir build && cd build +# 配置交叉编译工具链 +export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf" +# 使用cmake配置项目 +cmake .. +# 执行编译项目 +make -j8 && make install +``` + +在执行完上述命令后,会在build目录下生成可执行文件。 + +--- + +## 5. 例程运行示例 +### 5.1 运行 +```shell +chmod 777 Test-distance +# 实际使用过程中 LZ-Picodet 需要替换为你需要的rknn模型。 +./Test-distance LZ-Picodet +``` +### 5.2 结果展示 +- 我们可以看见,在label_id中显示了实时距离,同时正确识别了绿色方块。 +![](./images/2025_0421_10_25_53_469.png) + +--- + +## 6. 总结 +本系统通过结合目标检测模型和测距公式,实现了基于单目摄像头的实时测距功能。但是需要注意的是单目摄像头的测距只是大致测距,收到环境和检测质量的影响非常大,同时误差也几乎是不可控的。如有精确的测距需求,建议采用双目摄像头或其他测距方案实现。 diff --git a/Cpp_example/D04_distance/distance.cc b/Cpp_example/D04_distance/distance.cc new file mode 100755 index 0000000000000000000000000000000000000000..557e7d612e066a8af2034db1bb834ae7523d3b8f --- /dev/null +++ b/Cpp_example/D04_distance/distance.cc @@ -0,0 +1,106 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const float REAL_OBJECT_SIZE = 0.02f; +const float FOCAL_LENGTH = 800.0f; + +float CalculateDistance(float pixel_size, float real_size, float focal_length) +{ + return (real_size * focal_length) / pixel_size; +} + +int main(int argc, char *argv[]) +{ + if (argc != 2) + { + std::cerr << "Usage: Test-PaddleDet model_path" << std::endl; + return 1; + } + + // 初始化模型和设备连接 + lockzhiner_vision_module::vision::PaddleDet model; + if (!model.Initialize(argv[1])) + { + std::cout << "Failed to initialize model." << std::endl; + return 1; + } + + lockzhiner_vision_module::edit::Edit edit; + if (!edit.StartAndAcceptConnection()) + { + std::cerr << "Error: Failed to start and accept connection." << std::endl; + return EXIT_FAILURE; + } + + cv::VideoCapture cap; + cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); + cap.open(0); + + if (!cap.isOpened()) + { + std::cerr << "Error: Could not open camera." << std::endl; + return 1; + } + + cv::Mat input_mat; + while (true) + { + cap >> input_mat; + if (input_mat.empty()) + { + std::cerr << "Warning: Captured an empty frame." << std::endl; + continue; + } + + // 预测并可视化结果 + auto start_time = std::chrono::high_resolution_clock::now(); + auto results = model.Predict(input_mat); + auto end_time = std::chrono::high_resolution_clock::now(); + auto time_span = std::chrono::duration_cast(end_time - start_time); + std::cout << "Inference time: " << time_span.count() << " ms" << std::endl; + + cv::Mat output_image; + lockzhiner_vision_module::vision::Visualize(input_mat, output_image, results); + + // 在每个检测框上绘制距离信息 + for (size_t i = 0; i < results.size(); ++i) + { + int width = results[i].box.width; + int height = results[i].box.height; + float pixel_size = (width + height) / 2.0f; + float distance = CalculateDistance(pixel_size, REAL_OBJECT_SIZE, FOCAL_LENGTH); + + // 格式化距离文本(保留两位小数) + std::stringstream ss; + ss << std::fixed << std::setprecision(2) << distance << " m"; + std::string distance_text = ss.str(); + + // 在检测框左上方绘制距离 + cv::Rect box = results[i].box; + cv::putText( + output_image, + distance_text, + cv::Point(box.x, box.y - 25), // 在框上方5像素处显示 + cv::FONT_HERSHEY_SIMPLEX, + 0.5, // 字体大小 + cv::Scalar(0, 255, 0), // 绿色文本 + 2 // 线宽 + ); + } + + // 显示结果到外部设备 + edit.Print(output_image); + } + + cap.release(); + return 0; +} \ No newline at end of file diff --git a/Cpp_example/D04_distance/images/1.png b/Cpp_example/D04_distance/images/1.png new file mode 100755 index 0000000000000000000000000000000000000000..9068d2a3a2a172463ef1d7bc9a795476d25f3735 Binary files /dev/null and b/Cpp_example/D04_distance/images/1.png differ diff --git a/Cpp_example/D04_distance/images/2025_0421_10_25_53_469.png b/Cpp_example/D04_distance/images/2025_0421_10_25_53_469.png new file mode 100755 index 0000000000000000000000000000000000000000..d1f7c42577a0536bbabbe5b5afd2375012c618c5 Binary files /dev/null and b/Cpp_example/D04_distance/images/2025_0421_10_25_53_469.png differ