当前位置: 首页 > news >正文

5.29-6.4解决问题归纳

1.CLion配置opencv

在opencv官网下载对应版本的opencv。
下载地址
计算机电脑内配置环境
在这里插入图片描述

验证是否安装成功

cmd终端输入以下命令成功,成功显示版本号则为成功。

python
import cv2
print( cv2.__version__ )

在这里插入图片描述

CLion编写CPP连接opencv

GPT建议文件夹目录为:

MyProject/
├── CMakeLists.txt               # 顶层 CMake 构建文件
├── README.md                    # 项目说明文档
├── third_party/                 # 第三方库(如 OpenCV 可通过 find_package,也可源码放此)
│   └── ...
├── include/                     # 公共头文件(暴露给外部使用的 API 接口)
│   └── my_project/              # 命名空间子目录(避免污染)
│       └── my_header.h
├── src/                         # 源代码
│   ├── main.cpp                 # 入口程序
│   └── my_module.cpp
├── lib/                         # 项目生成的静态库或动态库(可选)
│   └── ...
├── bin/                         # 可执行文件输出目录
│   └── ...
├── test/                        # 单元测试(如使用 GoogleTest)
│   ├── CMakeLists.txt
│   └── my_module_test.cpp
├── tools/                       # 工具程序或脚本(如数据生成、图像标注工具等)
│   └── data_preprocessor.cpp
├── data/                        # 测试样例数据或图片(不编译)
│   └── image01.jpg
├── docs/                        # 项目文档
│   └── design.md
└── config/                      # 配置文件(如 YAML、JSON)└── default.yaml

本demo不涉及太多代码,文件夹目录为:

project/
├── bin/
├──imgs/
├── include/
│   └── feature_detection.h
├── src/
│   ├── feature_detection.cpp
│   └── main.cpp
├── CMakeLists.txt   

编写CMakeLists文件

cmake_minimum_required(VERSION 3.10)
project(project) # 项目名称# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 17) # C++ 标准版本# 查找 OpenCV
find_package(OpenCV REQUIRED)  # 查找 OpenCV 库
include_directories(${OpenCV_INCLUDE_DIRS}   # 包含 OpenCV 头文件目录${PROJECT_SOURCE_DIR}/include  # 包含头文件目录
)# 设置源文件路径
file(GLOB SRC_FILES${PROJECT_SOURCE_DIR}/src/*.cpp
)# 添加可执行文件
add_executable(project ${SRC_FILES})  # 创建名为 project 的可执行文件,包含所有源文件# 链接 OpenCV 库
target_link_libraries(project ${OpenCV_LIBS}) # 链接 OpenCV 库到可执行文件
  • cmake_minimum_required(VERSION 3.10)设置cmake最低版本要求
  • project(project)设置项目名称
  • set(CMAKE_CXX_STANDARD 17)设置C++标准版本
  • find_package(OpenCV REQUIRED) 查找opencv库
  • include_directories( ${OpenCV_INCLUDE_DIRS} # 包含 OpenCV 头文件目录 ${PROJECT_SOURCE_DIR}/include # 包含头文件目录 )include_directories是用来添加头文件的,${OpenCV_INCLUDE_DIRS}在配置的opencv环境中找opencv的头文件库,${PROJECT_SOURCE_DIR}/include找项目根目录中叫include的子目录中的头文件。
  • file(GLOB SRC_FILES ${PROJECT_SOURCE_DIR}/src/*.cpp )设置指定目录下的所有cpp文件为SRC_FIES
  • add_executable(project ${SRC_FILES})把项目里SRC_FILES都添加为可执行文件。
  • target_link_libraries添加链接opencv库到可执行文件。

CLion编译器内配置

工具链选择MSVC,记得和opencv版本相匹配。
在这里插入图片描述
Cmake配置选项表内填入

-B build -DOpenCV_DIR=C:\opencv\build\x64\vc16\lib

在这里插入图片描述

  • -B build:-B 表示指定构建输出目录,也就是构建了一个叫build的存放构建中间文件和生成文件的目录。
  • -DOpenCV_DIR=-D 表示定义一个变量,OpenCV_DIR 是 OpenCV 的 CMake 配置路径变量。

.h文件编写

#ifndef 头文件名的大写宏
#define 头文件名的大写宏// 头文件内容...#endif

当你在多个 .cpp 文件中包含同一个头文件(比如 feature_detection.h)时,为了防止这个头文件内容被重复包含造成编译错误,就加上这种宏判断。

简单来说:防止重复加载头文件。

与program one区别

特性#ifndef/#define#pragma once
机制通过宏名判断是否重复编译器自动确保只包含一次
可移植性完全标准、跨平台非标准,但主流编译器都支持
重复命名风险可能有宏冲突没有冲突
易写性要手动命名宏简洁一行
性能稍慢(需宏判断)稍快(编译器优化)

编码格式问题

当你编写好你的代码准备构建运行时会发现类似这样的报错。

warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失

虽然是个 警告(warning) 而不是致命错误,但它 常常会导致 VS 编译器处理失败或生成乱码结果。这与文件的 编码格式 和当前系统的 代码页 设置有关。
解决办法:
要编译执行的文件添加编码BOM
在这里插入图片描述
报错原理:
用 CLion + CMake + MSVC (cl.exe) 编译 C++ 项目时,这其实是 Windows 编译器(cl.exe)在处理非 ASCII 字符时的一个“编码问题”。Windows 上的 MSVC 编译器 cl.exe 默认使用 系统的当前代码页(Code Page) 来读取和解释源文件内容。cl.exe 会默认把 .cpp 文件当成 GBK 编码来读取
BOM 是 UTF-8 文件的开头特殊标记:

EF BB BF

这 3 个字节告诉读取程序:这是一个 UTF-8 编码文件!当 MSVC(cl.exe)看到 BOM 时,它会自动切换到 UTF-8 解码模式,不再用默认的 GBK 来读文件。

DLL文件导出

想要把图像识别相关算法封装好让其他语言调用,其中一种方法就是生成动态链接库。与之对应的是静态链接库。

  • 相同点:
项目描述
功能作用都用于将通用代码封装起来,供多个程序共享或重用。
组织结构都可以包含函数、类、数据等。
编译依赖使用它们的程序在编译时都需要有对应的库头文件和接口声明。
目的提高代码复用性、降低维护成本、模块化开发。
  • 不同点:
比较项静态链接库(.lib / .a)动态链接库(.dll / .so / .dylib)
链接时间编译时链接(静态链接)程序运行时加载(动态链接)
生成文件.lib(Windows),.a(Linux).dll(Windows),.so(Linux),.dylib(macOS)
可执行文件大小较大(库内容被复制到可执行文件)较小(库代码不嵌入可执行文件)
运行依赖无需依赖外部库文件必须确保动态库存在于运行环境
更新维护更新库需重新编译程序只需替换 DLL,即可更新功能
内存使用每个程序有自己的副本多个程序可共享同一份 DLL
调试复杂度调试简单相对复杂,特别是跨模块调试
跨平台移植可控性较强需要确保目标系统兼容动态库

.h文件接口

#ifdef _WIN32
#define DLL_EXPORT extern "C" __declspec(dllexport)
#else
#define DLL_EXPORT extern "C"
#endif
  • #ifdef _WIN32 检查当前编译环境是否是 Windows。
  • __declspec(dllexport)表示这个符号(函数、变量)需要被导出到 DLL 中,供其他程序调用。
  • extern "C"用于告诉编译器采用 C语言的方式导出符号名(不进行 C++ 名字重整 name mangling),这样 DLL 可以被 C/C++ 或其他语言调用,否则会乱码。
    For a example:
DLL_EXPORT int detect_feature_points(const char* imagePath,PointResult** results,int* resultCount,const char* saveFolderPath = "result"
);
参数名类型含义
const char* imagePath输入图像文件路径,用于加载图像
PointResult** results输出返回的特征点数组的指针(通过内存分配)
int* resultCount输出返回的特征点个数
const char* saveFolderPath = "result"可选输入结果保存路径(默认是 “result” 文件夹)

.cpp接口

在生产DLL文件时报错了,我就有了这个疑问,为什么.h里面写好接口,.cpp文件还要进一步加上导出关键字。
查阅相关资料了解到:.h 头文件中的 extern "C" __declspec(dllexport) 只是声明;.cpp 文件中如果定义函数时没有加上 extern "C",链接器可能无法识别并导出正确的符号。extern "C" 是“声明约定”不是“自动继承”,这只是告诉编译器:“这个函数应该用 C语言的方式导出名字”。这个声明不会影响 .cpp 文件中函数定义的 链接方式,它并不会自动“套用”到实现上。
因此在.cpp文件上也需要加上导出关键字:

extern "C" __declspec(dllexport)
int detect_feature_points(const char* imagePath, PointResult** results, int* resultCount, const char* saveFolderPath) {if (!imagePath || !results || !resultCount) return -1;try {FeatureDetector detector;std::vector<PointData> points = detector.detectContours(imagePath, saveFolderPath ? saveFolderPath : "result");if (points.empty()) {*resultCount = 0;*results = nullptr;return 0; // 无检测点也算成功}// 分配内存给 C#,使用 new[]PointResult* temp = new PointResult[points.size()];for (size_t i = 0; i < points.size(); ++i) {temp[i].name = _strdup(points[i].name.c_str()); // 分配 name 的内存temp[i].pixel_x = points[i].pixel.x;temp[i].pixel_y = points[i].pixel.y;temp[i].xz_x = points[i].xz.x;temp[i].xz_z = points[i].xz.y;}*results = temp;*resultCount = static_cast<int>(points.size());return 0;}catch (...) {return -2; // 其他错误}
}

CMakeLists文件重新编写

cmake_minimum_required(VERSION 3.10)
project(feature_detection_dll)
set(CMAKE_CXX_STANDARD 17)
# 查找 OpenCV
find_package(OpenCV REQUIRED)
# 头文件目录
include_directories(${PROJECT_SOURCE_DIR}/include${OpenCV_INCLUDE_DIRS}
)# 源码文件(生成 DLL)
file(GLOB DLL_SRC_FILES${PROJECT_SOURCE_DIR}/src/feature_detection.cpp
)
# 生成 DLL
add_library(feature_detection SHARED ${DLL_SRC_FILES})
target_link_libraries(feature_detection ${OpenCV_LIBS})
  • add_library(... SHARED ...)创建共享库(DLL)。

C#调用

生成的DLL文件要放到C#项目的对应文件夹去调用。调试代码类似这种:

using System;
using System.Runtime.InteropServices;
class Program
{// C# 对应 C++ 的结构体[StructLayout(LayoutKind.Sequential)]public struct PointResult{public IntPtr name;       // char*public int pixel_x;public int pixel_y;public double xz_x;public double xz_z;}// 将 char* 转为 C# stringstatic string PtrToString(IntPtr ptr) => Marshal.PtrToStringAnsi(ptr);// 导入 detect_feature_points[DllImport("feature_detection.dll", CallingConvention = CallingConvention.Cdecl)]public static extern int detect_feature_points(string imagePath,out IntPtr results,out int resultCount,string saveFolderPath);// 导入 free_feature_points[DllImport("feature_detection.dll", CallingConvention = CallingConvention.Cdecl)]public static extern void free_feature_points(IntPtr results);static void Main(){string imgPath = @"C:\\project\\PythonProject\\imgs\\AlgorithmData\\1.jpg";string savePath = @"C:\\project\\DLL_test\\test3\\imgs\\result";IntPtr resultPtr;int count;int status = detect_feature_points(imgPath, out resultPtr, out count, savePath);if (status != 0){Console.WriteLine($"DLL 调用失败,错误码: {status}");return;}Console.WriteLine($"成功检测到 {count} 个点:");int size = Marshal.SizeOf(typeof(PointResult));for (int i = 0; i < count; i++){IntPtr itemPtr = IntPtr.Add(resultPtr, i * size);PointResult point = Marshal.PtrToStructure<PointResult>(itemPtr);string name = PtrToString(point.name);Console.WriteLine($"{name}: 像素({point.pixel_x}, {point.pixel_y}), XZ({point.xz_x:F2}, {point.xz_z:F2})");}Console.WriteLine("\n按任意键退出...");Console.ReadKey();// 释放由 DLL 分配的内存free_feature_points(resultPtr);}
}

any cpu和x64的区别

第一次直接跑代码并没有成功,发现any cpu和x64是有区别的。Any CPU 是 .NET 平台的中立选项,但一旦调用了本地(如 C++)的 DLL,就必须保证 C# 和 DLL 是在相同位数平台(x86/x64)下运行,否则会报错。
在这里插入图片描述
而我们这种方式生成的一个 64 位 DLL。

CMake + OpenCV + MSVC x64 编译器

any cpu 虽然有x64 但visual stdio的项目属性里面会默认勾选首选32位。因此运行报错。
在这里插入图片描述

release和debug的区别

  • Debug 模式 用于开发和调试,包含调试信息、运行较慢;
  • Release 模式 用于发布或上线,优化性能,但不包含调试信息。
项目Debug 模式Release 模式
目的开发、调试、测试发布、部署、生产环境
优化❌ 不开启编译器优化(保留原始结构)✅ 开启各种性能优化
调试信息✅ 含调试符号(.pdb 文件)❌ 默认不含调试符号(可以手动开启)
运行速度🐢 慢(未优化)🚀 快(优化代码执行)
生成的文件大小📦 较大(保留变量名、调试信息)💡 较小(去除无用信息)
断点调试支持✅ 支持断点、变量查看、单步运行❌ 不支持或极其困难
安全检查✅ 启用运行时断言、越界检测等❌ 通常关闭这些检查以提升性能
可读性(反汇编)✅ 结构清晰,接近源代码❌ 优化过度,难以跟踪

相关文章:

  • ‘pnpm‘ 不是内部或外部命令,也不是可运行的程序
  • Linux系统iptables防火墙实验拓补
  • 亚马逊站内信规则2025年重大更新:避坑指南与合规策略
  • 制造业数智化:R²AIN SUITE 如何打通提效闭环
  • 使用 useSearchParams 的一个没有触发控制台报错的错误用法
  • 某校体育场馆结构自动化监测
  • LeetCode 2297. 跳跃游戏 VIII(中等)
  • 【电赛培训课程】电子设计竞赛工程基础知识
  • Git常用命令完全指南:从入门到精通
  • 【Redis实战:缓存与消息队列的应用】
  • 30 C 语言递归算法详解:基准条件、递归逻辑、循环对比、经典案例(斐波那契、猴子吃桃、汉诺塔、二分查找等)
  • 防火墙iptables项目实战
  • golang常用库之-go-feature-flag库(特性开关(Feature Flags))
  • 关于面试找工作的总结(四)
  • Linux容器篇、第一章_02Rocky9.5 系统下 Docker 的持久化操作与 Dockerfile 指令详解
  • 电子电路:共集电极放大器原理与作用解析
  • MySQL JSON 查询中的对象与数组技巧
  • nginx配置
  • DeviceNET从站转EtherNET/IP主站在盐化工行业的创新应用
  • STM32L0看门狗设置LL库
  • 邢台地区网站建设/西安seo经理
  • 北京网站建设还公司/阿里巴巴推广
  • 新能源网站建设哪家好/网站友情链接
  • 广州割双眼皮网站建设/怎样在百度上宣传自己的产品
  • 厦门学网站设计/雅虎搜索引擎入口
  • 后台管理网站模板/国内高清视频素材网站推荐