一、下载并安装OpenCV
访问OpenCV官网:首先,你需要访问OpenCV的官方网站(OpenCV官网),在“Releases”部分找到适合你的操作系统的版本进行下载。对于Windows用户,通常会选择Windows预编译的二进制文件。
下载并安装:下载适合你系统的OpenCV版本后,按照安装向导进行安装。注意选择一个不包含中文和特殊字符的路径,以避免潜在的问题。
二、配置系统环境变量
打开系统属性:在Windows搜索栏中输入“系统属性”,然后选择“打开”以进入系统属性界面。
编辑环境变量:点击“高级”选项卡下的“环境变量”按钮。在“系统变量”区域找到名为“Path”的变量,选择它并点击“编辑”。
添加OpenCV路径:点击“新建”并添加OpenCV的bin目录路径到你的Path变量中。这个路径通常类似于C:\opencv\build\x64\vc16\bin(注意替换为你的实际安装路径)。
保存并重启:点击“确定”保存更改,并重启你的计算机以确保环境变量生效。
三、在Visual Studio中配置项目属性
创建或打开项目:在Visual Studio 2022中创建一个新的C++项目或打开一个现有的项目。
配置项目属性:
右键点击项目名称,选择“属性”。
在左侧导航栏中,选择“VC++目录”。
在“包含目录”中添加OpenCV的include目录路径,如C:\opencv\build\include和C:\opencv\build\include\opencv2。
在“库目录”中添加OpenCV的lib目录路径,如C:\opencv\build\x64\vc16\lib(注意根据你的Visual Studio版本和OpenCV构建配置选择正确的目录)。
配置链接器:
在项目属性中,选择“链接器”->“输入”。
在“附加依赖项”中添加你需要的OpenCV库文件,如opencv_worldXXXd.lib(XXX是版本号,d表示debug版本,对于release版本则不需要d)。注意,如果你没有使用opencv_world这样的统一库,而是分别链接了不同的模块,那么你需要添加相应模块的库文件。
四、编写和测试代码
编写代码:在你的C++项目中编写使用OpenCV的代码。例如,一个简单的读取并显示图片的代码示例如下:
#include <opencv2/opencv.hpp>
using namespace cv;
int main() {
Mat img = imread("G:\\111111111111111111111111111\\213213.jpg"); // 替换为你的图片路径
if (img.empty()) {
std::cerr << "Error: Loading image" << std::endl;
return -1;
}
imshow("Image Display", img);
waitKey(0);
return 0;
}
编译并运行:编译你的项目并运行它。如果一切配置正确,你应该能够看到你的图片在一个窗口中显示。
注意
确保你的OpenCV版本与你的Visual Studio版本兼容。
如果你在配置过程中遇到任何问题,检查你的路径设置是否正确,特别是环境变量和项目属性中的路径。
如果你使用的是预编译的OpenCV库,确保它们是为你的Visual Studio版本和Windows SDK版本构建的。如果不是,你可能需要从源代码自行编译OpenCV。
2cv::imshow(“Original Image”, img); 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误 LNK2019 无法解析的外部符号 “void __cdecl cv::imshow(class std::basic_string<char,struct std::char_traits,class std::allocator > const &,class cv::debug_build_guard::_InputArray const &)” (?imshow@cv@@YAXAEBV? b a s i c s t r i n g @ D U ? basic_string@DU? basicstring@DU?char_traits@D@std@@V?$allocator@D@2@@std@@AEBV_InputArray@debug_build_guard@1@@Z),函数 main 中引用了该符号 opencv1 G:\VS2022_programs\Opencv_0726_VS2022\opencv1\opencv1\opencv1.obj 1
答:错误 LNK2019 表示链接器无法找到在代码中引用的某个函数或符号的实现。在你的情况下,链接器无法找到 cv::imshow 函数的实现,这通常是因为OpenCV库没有被正确地链接到你的项目中。
这里有几个步骤可以帮助你解决这个问题:
确认OpenCV库文件已被添加到项目中:
确保你已经将OpenCV的库文件(.lib 文件)添加到了项目的链接器输入中。这通常在项目的属性设置中的“链接器”->“输入”->“附加依赖项”中完成。
检查库文件的路径:
在项目的属性中,确保“VC++目录”->“库目录”中包含了OpenCV库文件的路径。这样,链接器就能在这些目录中查找库文件。
确认使用的是正确的库文件:
如果你使用的是OpenCV的Debug版本,那么你需要链接到带有d后缀的库文件(例如opencv_world451d.lib),而对于Release版本,则应该链接不带d后缀的库文件(例如opencv_world451.lib)。
检查项目配置:
确保你的项目配置(Debug或Release)与你链接的库文件配置相匹配。
重新生成项目:
在Visual Studio中,尝试清理并重新生成你的项目。这可以通过“生成”菜单中的“清理解决方案”和“重新生成解决方案”来完成。
检查OpenCV的安装:
如果以上步骤都不能解决问题,可能需要检查你的OpenCV安装是否完整或是否有损坏。你可以尝试重新安装OpenCV,或者从其他来源获取库文件。
检查Visual Studio的版本和更新:
确保你的Visual Studio和所有相关的组件都已更新到最新版本。
检查项目设置中的字符集:
在某些情况下,如果你的项目设置为使用多字节字符集(MBCS),而OpenCV库是使用Unicode字符集编译的,这也可能导致链接问题。尝试将项目的字符集设置为“使用Unicode字符集”。
如果以上步骤仍然无法解决问题,你可能需要查看更详细的错误信息或日志,以获取更多关于为什么链接器无法找到 cv::imshow 的线索。此外,确保你的OpenCV版本与你的Visual Studio版本兼容。如果你使用的是较新版本的Visual Studio,可能需要从源代码编译OpenCV或使用预编译的二进制文件,这些二进制文件是为较新版本的Visual Studio编译的。
如果是UE5进行配置,那么除了上面的步骤(除去链接库更改外,还需要更改.Build.cs文件),如下:
// Copyright Epic Games, Inc. All Rights Reserved.
using System.IO;
using UnrealBuildTool;
public class Ceshi_0726 : ModuleRules
{
public Ceshi_0726(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
// 假设你的静态库头文件在 Source/ThirdParty/MyLib/Include 下
// 静态库文件在 Source/ThirdParty/MyLib/Lib 目录下(假设为Windows平台)
string ThirdPartyPath = Path.GetFullPath(Path.Combine(ModuleDirectory, "..", "ThirdParty"));
string LibIncludePath = Path.Combine(ThirdPartyPath, "MyLib", "Include");
string LibPath = Path.Combine(ThirdPartyPath, "MyLib", "Lib", Target.Platform.ToString());
// 添加包含目录
PublicIncludePaths.AddRange(new string[] { LibIncludePath });
PublicIncludePaths.AddRange(new string[] { LibIncludePath ,"opencv2"});
PublicIncludePaths.AddRange(new string[] { LibIncludePath, "opencv2" ,"core"});
PublicIncludePaths.AddRange(new string[] { LibIncludePath, "opencv2", "core" , "cuda" });
// 静态库文件,注意这里假设库文件名为 MyLib.lib(Windows平台)
// 对于其他平台,如Linux或Mac,你可能需要调整文件名和路径
if (Target.Platform == UnrealTargetPlatform.Win64)
{
PublicAdditionalLibraries.Add(Path.Combine(LibPath, "StaticLib1.lib"));
// PublicAdditionalLibraries.Add(Path.Combine(LibPath, "opencv1.lib"));
PublicAdditionalLibraries.Add(Path.Combine(LibPath, "opencv_world4100d.lib"));
// PublicAdditionalLibraries.Add(Path.Combine(LibPath, "opencv_world4100d.lib"));
}
// 如果有多个静态库或不同平台的库,可以按上述方式继续添加
// Uncomment if you are using Slate UI
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
// Uncomment if you are using online features
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
// 注意:对于动态链接库(DLLs),你可能需要使用PublicLibraryPaths和PublicDelayLoadDLLs
PrivateDependencyModuleNames.AddRange(new string[] { });
// Uncomment if you are using Slate UI
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
// Uncomment if you are using online features
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
}
}
但是由于opencv的代码需要一个while(1),所以放到UE里并不适用,不如在外部将opencv项目写成一个udp服务器,把UE项目写成udp客户端。
下面的代码是udp传输面部位置的代码:
#include <opencv2/opencv.hpp>
#include <iostream>
#include <memory>
#include <winsock2.h>
#include <string>
#include <thread> // 用于多线程
#include <chrono> // 用于时间操作
#include <mutex>
#include <ws2tcpip.h>
std::mutex mtx; // 用于保护 abcd 和 pShared
#pragma comment(lib, "ws2_32.lib") // 链接Winsock库
#define PORT 7509
#define BUFSIZE 1024
#define SEND_INTERVAL 1000 // 发送间隔时间,单位毫秒
WSADATA wsaData;
SOCKET sock;
struct sockaddr_in serverAddr, clientAddr, clientAddr2;
int bytesReceived;
char buffer[BUFSIZE];
int addrLen;
class EMA {
private:
double alpha;
double previous;
public:
EMA(double a) : alpha(a), previous(0.0) {}
double update(double newValue) {
double ema = alpha * newValue + (1 - alpha) * previous;
previous = ema;
return ema;
}
};
void sendMessageContinuously(SOCKET sock, sockaddr_in clientAddr, std::shared_ptr<std::string> pShared) {
sockaddr_in destAddr;
memset(&destAddr, 0, sizeof(destAddr)); // 初始化结构体
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(7503); // 假设你希望发送到这个端口
// 使用inet_pton将IP地址字符串转换为二进制形式
if (inet_pton(AF_INET, "127.0.0.1", &destAddr.sin_addr) <= 0) {
// 处理错误
// std::cerr << "Invalid address/Address not supported\n";
return;
}
while (true) {
addrLen = sizeof(clientAddr);
//sendto(sock, pShared->c_str(), pShared->length(), 0, (struct sockaddr*)&clientAddr, addrLen);//这个要加上接收哪个才有用
//sendto(sock, pShared->c_str(), pShared->length(), 0, (struct sockaddr*)&clientAddr, sizeof(clientAddr));//这个不用收,但是地址是0.0.0.0
sendto(sock, pShared->c_str(), pShared->length(), 0, (struct sockaddr*)&destAddr, sizeof(destAddr));
// std::cout << "Continuous message sent.:::"<< pShared->c_str() <<std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
int main() {
EMA xEMA(0.1); // 假设alpha为0.1
EMA yEMA(0.1);
// 初始化摄像头
cv::VideoCapture capture(0);
// 检查摄像头是否成功打开
if (!capture.isOpened()) {
std::cerr << "Error: Unable to open camera\n";
return -1;
}
// 加载人脸检测的Haar级联分类器
cv::CascadeClassifier face_cascade;
// if (!face_cascade.load("E:\\Program Files\\Opencv\\opencv\\build\\etc\\haarcascades\\haarcascade_frontalface_default.xml")) {
if (!face_cascade.load("E:\\Program Files\\Opencv\\opencv\\sources\\data\\haarcascades_cuda\\haarcascade_frontalface_default.xml")) {
std::cerr << "Error: Unable to load face cascade classifier\n";
return -1;
}
// 创建一个窗口
cv::namedWindow("Camera Feed with Faces", cv::WINDOW_AUTOSIZE);
cv::Mat frame, gray;
// 初始化Winsock
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
std::cerr << "Winsock初始化失败,错误代码: " << WSAGetLastError() << std::endl;
return 1;
}
// 创建UDP socket
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
std::cerr << "创建socket失败,错误代码: " << WSAGetLastError() << std::endl;
WSACleanup();
return 1;
}
std::cout << "Socket创建成功。\n";
// 设置服务器地址结构
clientAddr.sin_family = AF_INET;
clientAddr.sin_addr.s_addr = INADDR_ANY;
clientAddr.sin_port = htons(7503);
// 设置服务器地址结构
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = INADDR_ANY;
serverAddr.sin_port = htons(7500);
// 绑定socket
if (bind(sock, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
std::cerr << "绑定失败,错误代码: " << WSAGetLastError() << std::endl;
closesocket(sock);
WSACleanup();
return 1;
}
std::cout << "绑定成功。\n";
const char* p;
//std::shared_ptr<std::string> pShared = std::make_shared<std::string>("p$x:12.234632$y:54.812842$z:60.3453\n");
std::shared_ptr<std::string> pShared = std::make_shared<std::string>("p$x:20$y:54$z:60.3453\n");
// 创建并启动发送线程
std::thread senderThread(sendMessageContinuously, sock, clientAddr, pShared);
int abc = 0;
// int x, y;
double x, y;
while (true) {
// 从摄像头捕获一帧
capture >> frame;
// 如果帧是空的,跳出循环
if (frame.empty()) {
std::cerr << "Error: Couldn't receive a frame (stream end?). Exiting ..." << std::endl;
break;
}
// 转换为灰度图,因为人脸检测通常在灰度图上进行
cvtColor(frame, gray, cv::COLOR_BGR2GRAY);
// 检测人脸
std::vector<cv::Rect> faces;
face_cascade.detectMultiScale(gray, faces, 2, 3, 0 | cv::CASCADE_SCALE_IMAGE, cv::Size(30, 30));
// 在原图上绘制矩形框
for (size_t i = 0; i < faces.size(); i++) {
cv::rectangle(frame, faces[0], cv::Scalar(255, 0, 0), 2, 8, 0);
// std::cout << "Face #" << i + 1 << ": Left=" << faces[i].x << ", Top=" << faces[i].y << std::endl;
std::stringstream ss;
x = xEMA.update(faces[i].x * (-0.2) + 160);
y = yEMA.update(faces[i].y * (-0.416667) + 125);
//y = faces[0].y * (-0.416667) + 125;
//x = faces[0].x * (-0.2) + 160;
/*
if (faces[i].y * (-0.416667) + 125 - y > 10) { y = y + 10; }
else if (faces[i].y * (-0.416667) + 125 - y < 10) { y = y - 10; }
if (faces[i].x * (-0.2) + 160 - x> 10) { x = x + 10; }
else if (faces[i].x * (-0.2) + 160 - x < 10) { x = x - 10; }
*/
if (x > 100) { x = 100; }
if (x < 0) { x = 0; }
if(y > 100) { y = 100; }
if (y < 0) { y = 0; }
std::lock_guard<std::mutex> lock(mtx);
ss << "p$x:" << x << "$y:" << y << "$z:20.3453";
*pShared = ss.str();
}
}
// 释放摄像头资源
capture.release();
// 关闭所有OpenCV窗口
cv::destroyAllWindows();
// 清理
closesocket(sock);
WSACleanup();
return 0;
}