MFC开发中遇到的问题
①decoderLogin(ip, user, code)函数返回值不为0
一般由于摄像头登录失败引起。 error返回错误码为3:SDK未初始化 解决办法:在MainApp中初始化SDK
//初始化摄像头
bool tmp = SDK_Init();
海康威视错误码表 https://open.hikvision.com/hardware/definitions/NET_DVR_GetLastError.html
②PinNetCheck一直返回false
涉及到ping ip问题的部分有: 1.MainApp属性页->链接器->输入->附加依赖项加入ws2_32.lib,取消勾选从父类或项目默认设置继承; 2.MainApp属性页->C/C+±>常规->SDL检查设置否; 3.PinNetCheck.h中加入:
#include <winsock2.h>
#pragma comment (lib, "ws2_32.lib")
PinNetCheck.cpp中使用:
WSADATA WSAData;
WSAStartup(MAKEWORD(1, 1), &WSAData);
rawSocket = WSASocket(AF_INET,
SOCK_RAW,
IPPROTO_ICMP,
NULL, 0, 0);
rawSocket一直返回2^64-1适用这条;
③项目VC目录,数据库纪要
④ VS2015调整布局界面卡死
来源于windows更新兼容性问题,卸载KB5000803后恢复
⑤无法启动此程序,因为计算机中丢失opencv_world341.dll。请尝试重新安装改程序已解决此问题。
把opencv底下E:\opencv\build\x64\vc14\bin的文件opencv_world341.dll复制到电脑C:\Windows\System32底下
⑥项目运行提示找不到cv::xx函数
一般是链接错了动态库文件,或者没正确include 1.在release环境下附加依赖项应该选择opencv_world451.lib;debug模式下应该为opencv_world451d.lib。 2.opencv目录找到这个函数在的文件,将其加入项目文件表头,例:
#include <opencv2\opencv.hpp>
#include <opencv2\imgproc.hpp>
⑦项目程序退出后出现大量内存泄漏的警告
实践证明,这是误报的内存泄漏。
链接: 解决MFC使用OpenCV动态库会误报内存泄露的问题。
采用方法二成功解决。
链接1: 简单内存泄漏检测方法 解决 Detected memory leaks! 问题. 链接2: 内存管理:_CrtDumpMemoryLeaks和_CrtSetBreakAlloc.
检查是否资源泄漏的办法之一: 在任务管理器 进程 查看 选择列 里面选择:内存使用、虚拟内存大小、句柄数、线程数、USER对象、GDI对象 让你的程序(进程)不退出,循环执行主流程很多遍,越多越好,比如1000000次甚至无限循环,记录以上各数值,再隔至少一小时,越长越好,比如一个月,再记录以上各数值。如果以上两组数值的差较大或随时间流逝不断增加,则铁定有对应资源的资源泄漏!搜“GDI泄露检测”
⑧opencv4.0中“未定义标识符cvNamedWindow或CV_WINDOW_AUTOSIZE”的解决方法
比如:“在代码开头加入头文件#include <opencv2/highgui/highgui_c.h>”之类的方法,就完全没解决问题。
经过探索,终于找到了解决之道。
原来这是由于opencv4.0和之前的版本中有一些命令发生了变化,比如在之前的版本中cvNamedWindow是用来创建窗口的,然而到了4.0版本中,创建窗口的命令改成了namedWindow,所以只要把命令替换以下就可以运行了。
同样的老版本中“cvResizeWindow”改成了“resizeWindow”。类似的情况还有很多,就不一一列举了。opencv3以后CV_XXX_XXX函数,改为了XXX_XXX,去掉前面的CV_就是新版本中同样的。 ———————————————— 版权声明:本文为CSDN博主「Leroy Sane」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接: Link
⑨OpenCV 行列值与坐标的对应关系
OpenCV像素坐标系如下图所示:
*行列与坐标系对应关系
行:Y
列:X
注意!注意!注意!
在Mat类型变量访问时下标是反着写的,即:按照(y, x)的关系形式访问,下面通过代码展示来说明这一点
下面展示一些 代码片。
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
Mat mat_src = Mat::eye(3, 4, CV_8UC1);
cout << "mat_src :" << endl;
cout << mat_src << endl;
cout << endl;
cout << "Rows : " << mat_src.rows << endl;
cout << "Cols : " << mat_src.cols << endl;
//注: mat_src.at<float>(y, x), 下标关系为: y-x
mat_src.at<float>(0, 2) = 2;
mat_src.at<float>(2, 0) = 4;
cout << endl;
cout << "mat_src :" << endl;
cout << mat_src << endl;
return 0;
}
执行结果:
转载至: link.
⑩opencv的Copyto函数
首先对于openCv中copyTo()的用法有以下两种:
image.copyTo(imageROI)。作用是把image的内容复制粘贴到imageROI上;
image.copyTo(imageROI,mask)。 作用是把mask和image重叠以后把mask中像素值为0(black)的点对应的image中的点变为透明,而保留其他点。
⑩①多线程单类多变量实现多窗口中其它窗口给此类发消息无法传递的BUG
项目体系如下: 主窗口类为CMainDlg,分别创建三个CCameraThread线程类对象,在三个线程对象中分别创建子窗口类CCamera对象。 此时CCamera类下有子窗口类CExtractDlg,当从CExtractDlg类中使用SendMessage发送消息时:
CCamera* p = (CCamera*)GetParent();
int b = p->m_targetInfo.number;
LRESULT a = ::SendMessage(p->GetSafeHwnd(), DESTROYDLG, 1, 0);
// 此处加断点后,a=0,b=2071849968
//正常情况应为,a为非零值,b = 摄像头编号
这个BUG是由于项目结构导致
CCamera* p = (CCamera*)GetParent();
语句找不到具体对象,返回新的一个CCamera类对象。 发送消息修改为如下后可正常发送消息:
pDlg->m_Thread2->m_Dlg1->SendMessage(DESTROYDLG, NULL, NULL);
//pDLg是CMainDlg类指针,m_Thread2是CCameraThread类对象
//m_Dlg1是CCamera对象,对应摄像头1
⑩②海康工业相机SDK开发资料
这是黑色摄像头的!海康威视工业相机 机器型号MV-CA060-10GC,工业相机网址不在海康官网,要搜索HIKROBOT SDK开发见如下链接: 链接: 海康威视工业相机SDK二次开发(VS+Opencv+QT+海康SDK+C++)(一).
链接: 海康威视工业相机SDK二次开发.
⑩③海康工业相机MVS抓图并转换为mat
下面展示一些 内联代码片。
Mat CameraSDK::getM_pic()
{
m_nSaveImageType = MV_Image_Bmp;
int nRet = SaveImage();
if (!nRet)
{
bool isMono;//判断是否为黑白图像
switch (stImageInfo.enPixelType)
{
case PixelType_Gvsp_Mono8:
case PixelType_Gvsp_Mono10:
case PixelType_Gvsp_Mono10_Packed:
case PixelType_Gvsp_Mono12:
case PixelType_Gvsp_Mono12_Packed:
isMono = true;
break;
default:
isMono = false;
break;
}
if (isMono)
{
m_pic = Mat(stImageInfo.nHeight, stImageInfo.nWidth, CV_8UC1, m_pBufForDriver);
}
else
{
//转换图像格式为BGR8
MV_CC_PIXEL_CONVERT_PARAM stConvertParam = { 0 };
memset(&stConvertParam, 0, sizeof(MV_CC_PIXEL_CONVERT_PARAM));
stConvertParam.nWidth = stImageInfo.nWidth;
stConvertParam.nHeight = stImageInfo.nHeight;
stConvertParam.pSrcData = m_pBufForDriver;
stConvertParam.nSrcDataLen = stImageInfo.nFrameLen;
stConvertParam.enSrcPixelType = stImageInfo.enPixelType;
stConvertParam.enDstPixelType = PixelType_Gvsp_BGR8_Packed;
//stConvertParam.enDstPixelType = PixelType_Gvsp_RGB8_Packed;
stConvertParam.pDstBuffer = m_pBufForSaveImage;
stConvertParam.nDstBufferSize = m_nBufSizeForSaveImage;
m_pcMyCamera->ConvertPixelType(&stConvertParam);
m_pic = Mat(stImageInfo.nHeight, stImageInfo.nWidth, CV_8UC3, m_pBufForSaveImage);
//bool result = imwrite("test1.png",m_pic);
return m_pic;
}
ShowErrorMsg(TEXT("failed"), nRet);
return m_pic;
}
}
// 保存图片
int CameraSDK::SaveImage()
{
// ch:获取1张图
unsigned int nRecvBufSize = 0;
int nRet = MV_OK;
// ch:仅在第一次保存图像时申请缓存,在 CloseDevice 时释放
if (NULL == m_pBufForDriver)
{
// ch:从相机中获取一帧图像大小
nRet = m_pcMyCamera->GetIntValue("PayloadSize", &nRecvBufSize);
if (nRet != MV_OK)
{
ShowErrorMsg(TEXT("failed in get PayloadSize"), nRet);
return nRet;
}
// ch:一帧数据大小
m_nBufSizeForDriver = nRecvBufSize;
m_pBufForDriver = (unsigned char *)malloc(m_nBufSizeForDriver);
if (NULL == m_pBufForDriver)
{
ShowErrorMsg(TEXT("malloc m_pBufForDriver failed, run out of memory"), 0);
return nRet;
}
}
stImageInfo = { 0 };
memset(&stImageInfo, 0, sizeof(MV_FRAME_OUT_INFO_EX));
unsigned int nDataSize = nRecvBufSize;
unsigned int nImageNum = 1;
unsigned int nDataLen = 0;
while (nImageNum)
{
nRet = m_pcMyCamera->GetOneFrameTimeout(m_pBufForDriver, &nDataLen, m_nBufSizeForDriver, &stImageInfo, 1000);
if (nRet == MV_OK)
{
nImageNum--;
// ch:仅在第一次保存图像时申请缓存,在 CloseDevice 时释放
if (NULL == m_pBufForSaveImage)
{
// ch:BMP图片大小:width * height * 3 + 2048(预留BMP头大小)
m_nBufSizeForSaveImage = stImageInfo.nWidth * stImageInfo.nHeight * 3 + 2048;
m_pBufForSaveImage = (unsigned char*)malloc(m_nBufSizeForSaveImage);
if (NULL == m_pBufForSaveImage)
{
break;
}
}
// ch:设置对应的相机参数 | en:Set camera parameter
MV_SAVE_IMAGE_PARAM_EX stParam = { 0 };
stParam.enImageType = m_nSaveImageType;
// ch:需要保存的图像类型 | en:Image format to save
stParam.enPixelType = stImageInfo.enPixelType;
// ch:相机对应的像素格式 | en:Camera pixel type
stParam.nWidth = stImageInfo.nWidth;
// ch:相机对应的宽 | en:Width
stParam.nHeight = stImageInfo.nHeight;
// ch:相机对应的高 | en:Height
stParam.nDataLen = stImageInfo.nFrameLen;
stParam.pData = m_pBufForDriver;
stParam.pImageBuffer = m_pBufForSaveImage;
stParam.nBufferSize = m_nBufSizeForSaveImage;
// ch:存储节点的大小 | en:Buffer node size
stParam.nJpgQuality = 80;
// ch:jpg编码,仅在保存Jpg图像时有效。保存BMP时SDK内忽略该参数
nRet = m_pcMyCamera->SaveImage(&stParam);
if (MV_OK != nRet)
{
break;
}
}
else
{
break;
}
}
return nRet;
}
⑩④ 使用CreateThread方式创建线程的问题
下面展示一些 内联代码片。
//全局声明线程
DWORD WINAPI PingNetThread(LPVOID lpParam);
//在代码中启动线程
HANDLE h_ping = CreateThread(NULL,0, PingNetThread, this, 0, NULL);
CloseHandle(h_ping);
CreateThread的线程无法手动关闭,只能通过系统关闭自动关闭,若在线程中与对话框类有交互,并有“sleep+循环”的形式,如:
CCamera *p = (CCamera*)lpParam;
//使用了CCamera对话框类
while (p != NULL)
{//while循环
……
Sleep(1000);
}
当系统关闭时可能线程正处在执行之中,而相对应的p对象被delete了,导致内存访问错误。debug发现,当注释sleep函数时,代码循环速度快,从而能通过while判断语句调查发现p对象空了。暂时找不到其他办法,只能在循环中多加入几次if(p!=NULL)的判断。
⑩⑤ CPP程序调用MATLAB ENGINE的问题
环境配置:cpp项目包含目录下: D:\program files\Matlab2016a\extern\include\win64 D:\program files\Matlab2016a\extern\includecpp项目库目录下: D:\program files\Matlab2016a\extern\lib\win64\microsoft系统环境变量下PATH D:\program files\Matlab2016a\bin\win64链接器输入附加依赖项 libeng.lib libmx.lib libmat.lib mclmcrrt.lib libmex.lib出现引擎调用失败问题 原因主要是matlab在安装时并未初始注册; 解决方案:用管理员权限打开cmd,cd到matlab目录输入命令:matlab /regserverc文件头
#include "engine.h"
Engine *m_engine; //创建Matlab引擎
//m_engine = NULL; //初始化引擎
if ( !(m_engine = engOpen("\0")) ) //打开引擎,此时会打开一个Matlab命令行窗口
{
cout << "Can't start Matlab engine!" << endl;
exit(1);
return 0;
}
engSetVisible(m_engine, 0); //将命令行窗口设为不可见
char engBuffer[256] = { '\0' };
engEvalString(m_engine, "cd('E:\\Color_correction')"); //进入Matlab代码的路径
engEvalString(m_engine, "[color_ms_e] = bptest2();");
mxArray *result = engGetVariable(m_engine, "color_ms_e"); // 2. get the result
double *p = mxGetPr(result); //p[0]就是计算结果
cout << "C++ output: the sum of all the elements is: " << p[0] << endl;
mxDestroyArray(result);
if (m_engine) //关闭Matlab引擎
{
engClose(m_engine);
m_engine = NULL;
}
return 0;
⑩⑥ 海康威视网络摄像头SDK开发
目录配置如③所示; 在【附加依赖项】中添加:
HCNetSDK.lib
GdiPlus.lib
HCAlarm.lib
HCCore.lib
HCGeneralCfgMgr.lib
HCPreview.lib
PlayCtrl.lib
动态库添加到可执行文件目录:E:\MFC\test\opencvtest\x64\Debug
AudioRender.dll
HCCore.dll
HCNetSDK.dll
PlayCtrl.dll
SuperRender.dll
参考链接:海康威视摄像机的实时读取篇二(海康SDK开发环境配置)
⑩⑦ WaitForSingleObject一直阻塞
对图像处理线程:
if (m_pic != NULL)//确保线程不在挂起状态
{
int liv_tmp = ResumeThread(m_pic->m_hThread);
while (liv_tmp >= 1)
{
liv_tmp = ResumeThread(m_pic->m_hThread);
}
m_exitPicThread = true;
m_pic->PostThreadMessage(WM_QUIT, NULL, NULL);
WaitForSingleObject(m_pic->m_hThread, INFINITE); //无限长
}
WaitForSingleObject设置为无限长时,程序一直阻塞,无法正常退出。 这是由于 WaitForSingleObject会阻塞对话框线程(Dialog thread),同时也会导致了对话框的消息循环机制被阻塞 。 而我在线程函数中会对对话框有一些UI操作(SetPos, SetWindowText),这些对对话框的UI操作实际上是通过线程向控件发送消息得到的 ( SendMessage(m_hWnd, PBM_SETPOS, nPos, 0L) )。 因此WaitForSingleObject阻塞了消息循环,也就导致 SendMessage无法返回,卡住了线程 ,WaitForSingleObject也就无法返回了。
p->set_colorpic(_Result);//此处用到了 SetWindowText
解决办法:对话框中WaitForSingleObject等待线程退出导致程序阻塞的原因及解决;链接2.
DWORD dwRet = 0;
MSG msg;
while (TRUE)
{
dwRet = WaitForSingleObject(m_pic->m_hThread, 500);
switch (dwRet)
{
case WAIT_OBJECT_0:
break; //break the loop
case WAIT_TIMEOUT:
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
continue;
default:
break; // unexpected failure
}
break;
}
根据上面的流程,合理的解释应该是:MsgWaitForMultipleObject等待过程中Worker thread仍然被阻塞,当Dialog中有消息到来时MsgWaitForMultipleObject返回,此时调用PeekMessage,消息处理函数被激活,处理 PBM_SETPOS消息,workder thread中的SetPos函数返回。 然而,究竟是怎么样 PeekMessage会激活消息处理函数?我思考了很久在《windows核心编程》第26章“窗口消息”找到了答案:对于接收消息的线程而言(本文中为 Dlg thread ),如果该线程正在执行代码,并且没有等待消息 (如调用GetMessage、PeekMessage或WaitMessage)时,发送过来的消息不会被该线程处理,而发送消息的线程(本文中为worker thread)中的SendMessage函数也不会返回。
根据上面的文字,也就是说,在执行 对话框中某个函数时,如果函数没有退出,并且没有执行 GetMessage、PeekMessage或WaitMessage函数,那么从别的线程发送来的SendMessage来的消息将不会被处理。
也就是说,PeekMessage这个函数所做的工作不止是从队列中拿出posted message,还在此之前会先处理nonqueued messages(send message)。 找到了send message的处理流程,也就不难解释上面程序执行的流程了。
还可以看出:MsgWaitForMultipleObjects实际上在这其中并没有起到什么实质性的作用,只是定期返回而已,那么根据此,可以用固定时间WaitForSingleObject来代替MsgWaitForMultipleObjects ,并且循环等待 ; 同时,我们也没有必要手动DispatchMessage,只需要PeekMessage即可,需要注意的是如果删除了 DispatchMessage, 在 PeekMessage时不能将消息从队列取走。
———————————————— 版权声明:本文为CSDN博主「silvervi」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:
⑩⑧ 程序退出时线程报异常
项目中有一个界面类的工作线程中用到了另一个界面类的变量,当程序在退出时,VS反馈异常在
pWnd = GetGDlgItem(IDC_CAMERA1_COLOR1);//参数为控件ID
pWnd->GetClientRect(&rc);//rc为控件的大小。
这句,提示GetGDlgItem函数获取的句柄为空;初步判断为此时界面已经注销,控件已经销毁。
第一,OnOK()和OnCancel()是CDialog基类的成员函数,而OnClose()和OnDestroy()是CWnd基类的成员函数,即WM消息响应函数。从应用程序结构的角度,拿对话框来说,红色的X对应的是CWnd,而处于对话框中的“确定”、“取消”按钮则对应了CDialog。
第二,OnClose()和OnDestroy() 在单视图程序中,根据<<深入浅出MFC>>所讲,程序退出时执行的操作顺序为(从点X按钮开始) (1)用户点击X退出按钮,发送了WM_CLOSE消息----->响应OnClose() (2)在WM_CLOSE消息的处理函数中,调用DestroyWindow()----->销毁与指定CWnd窗口对象关联的窗口,但未销毁CWnd对象 (3)在DestroyWindow()中发送了WM_DESTROY消息----->窗口销毁后响应OnDestroy() (4)在WM_DESTROY消息中调用PostQuitMessage(),发送WM_QUIT消息,结束消息循环 可以看到,程序的退出过程,是先响应OnClose(),然后响应OnDestroy(),在响应OnDestroy()之前,窗口对象已经被销毁。OnDestroy()到底干了什么呢?它就像一个teller,先通知CWnd对象告诉它即将被销毁,尔后OnDestroy的真正运行是在CWnd对象已经从屏幕上清除以后被调用的。
第三,OnOK()、OnCancel()()、OnClose()、OnDestroy() CDialog::OnOK首先调用UpdateData(TRUE)将数据传给对话框成员变量,然后调用CDialog::EndDialog关闭对话框; CDialog::OnCancel只调用CDialog::EndDialog关闭对话框; OnClose()是响应 WM_CLOSE 的.一定程度上可以说CDialog::EndDialog()和OnClose()完成类似的工作,但处理的机制不一样,前者是CDialog的对象机制,后者是WM的消息映射机制。 CDialog::EndDialog()-------->OnDestroy() OnClose()-------->OnDestroy() EndDialog()和OnClose()属于“同级别”的,所以我们在按下OK按钮的时候,程序是不会执行OnClose()的,但两种机制都必须经过OnDestroy()。
因此线程的退出得先放在OnDestroy()消息响应中,从而可以调整不同对话框中线程退出的顺序。若放在对话框类析构函数中进行,则会产生对话框已经销毁但线程还未退出的错误!
⑩⑨ static和const
static局部变量 将一个变量声明为函数的局部变量,那么这个局部变量在函数执行完成之后不会被释放,而是继续保留在内存中 static 全局变量 表示一个变量在当前文件的全局内可访问 static 函数 表示一个函数只能在当前文件中被访问 static 类成员变量 表示这个成员为全类所共有 static 类成员函数 表示这个函数为全类所共有,而且只能访问静态成员变量 (1)函数体内static变量的作用范围为该函数体,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值; (2)在模块内的static全局变量和函数可以被模块内的函数访问,但不能被模块外其它函数访问; (3)在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝; (4)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。
const 常量:定义时就初始化,以后不能更改。 const 形参:func(const int a){};该形参在函数里不能改变 const修饰类成员函数:该函数对成员变量只能进行只读操作 (1)阻止一个变量被改变 (2)声明常量指针和指针常量 (3)const修饰形参,表明它是一个输入参数,在函数内部不能改变其值; (4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量; (5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为”左值”。
②〇 栈空间
一个由C/C++编译ling的程序占用的内存分为以下几个部分: 1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈 2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。 3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放 4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放 5、程序代码区—存放函数体的二进制代码。
在单元测试时,发现了比较多的线程堆栈空间不够导致 COREDUMP 的问题,这个问题的原因是由于线程独立拥有一个可配置大小的堆栈,一个线程内所有函数使用到的堆栈都依赖于这个栈,如果太多的变量、参数需要使用栈,可能 导致栈溢出从而COREDUMP。目前基础平台子系统通过配置环境变量,将默认堆栈大小设置为128K,可以减少这个问题的出现,但业务系统在编码时仍然 需要注意栈的使用,避免出现问题。 包括: 1、不要在函数内部定义过大的局部变量,如过大的结构体变量,联合变量,过大的字符串,数组等; 2、函数调用的深度也需要注意,如果函数 A 调用 B, B 再调用 C,而A/B/C每个函数定义了 10 K的局部变量,则总的栈空间需求将超过 30K; 3、不要直接将大的结构变量通过函数参数传递,这样也会消耗栈空间,可以通过指针或者引用的方式传递; 4、在unit_test/callsvc 目录下提供了一个 rs 程序,可以 misc16_pkg.h 和 particle_stru.h 中结构局部变量的大小,如果一个函数中使用了这里边的结构定义局部变量,可以通过 “rs [变量类型] ”计算总的栈内存需求。建议每个函数内部定义的变量大小控制在4-8K以下; 5、如果在运行中 COREDUMP,并且通过 GDB 的 WHERE 命令时看到刚进入某个函数就报错,连函数内的第一条调试语句都无法指向,则基本可以认为是栈空间不够导致的,可以尝试将栈空间配置大一点,如果问题不再出现,则可以确定问题。这时需要按照前面几点的要求修改代码,减少栈的使用。
链接: 设置线程堆栈大小 链接: Stack overflow 编译能通过,运行时出现Stack overflow