MFC知识点(一)

1、GetDlgItem
GetDlgItem是父窗口用来获取它的子窗口的句柄的。
如果是在一个对话框里想要获取另一个对话框中元素的句柄,假设B对话框的指针为pWnd,则pCtrl = pWnd->GetDlgItem(IDC_BUTTON);
2、 C/C++ 什么是句柄:
句柄是一个指向指针的指针。

一个指向指针的指针,保存的是另一个指针的地址,我们可以通过第一个指针来找到另一个指针,然后通过另一个指针访问到具体的数据。

Windows 需要经常在物理内存和虚拟内存之间来回移动数据。数据被移动意味着他的地址发生变化,如果我们还用之前的地址来访问它,那么肯定是会出错的。如何解决这个问题?

为了解决这个问题,Windows 系统专门腾出一块内存空间来保存数据在内存中的地址变化,Windows 每次移动数据,都要把数据的新地址告知这个内存空间来保存。而记录地址变化的这块内存空间的地址是保持不变的。

我们把这个不变的地址叫作句柄。我们便可以通过改句柄找到数据的最新地址,然后访问该数据。

当然,每次重启程序的时候,Windows 操作系统分配的句柄都是不一样的,就像每次抽奖抽到的号码不同一样,但也有可能,不过几率太小。
3、CWnd pWnd = GetDlgItem(IDB_BUT_RECOGNIZE);*

pWnd->GetClientRect(&rect); //指该控件自身客户区的矩形,原点为控件左上角,长宽为整个控件的尺寸

ClientToScreen(&rect); // 坐标原点从控件所属窗口客户区移动到屏幕左上角,长宽为整个控件的尺寸,相当于把控件放在它所属的客户区的左上角时,在屏幕坐标系中的位置

pWnd->GetWindowRect(&rect); //该控件相对电脑屏幕的坐标,,原点为屏幕左上角,长宽为整个控件的尺寸

ScreenToClient(&rect); //移动窗口时未改变,所以是相对位置,从屏幕坐标变换到客户区坐标,相当于该控件到客户区

GetClientRect(&rect); //整个窗口自身客户区的坐标 ,原点为客户区左上角,长宽为客户区的尺寸

ClientToScreen(&rect); // 坐标系从客户区变换到屏幕, 该窗口客户区在屏幕坐标系中的位置

GetWindowRect(&rect); //整个窗口相对于电脑屏幕的坐标,,原点为屏幕左上角,长宽为整个窗口的尺寸

ScreenToClient(&rect); //整个窗口在客户区坐标系下的位置
4、设备描述表(DC):是Windows中的一种数据结构,它包含GDI(图形设备接口)需要的所有关于显示界面情况的描述字段,包括相连的物理设备和各种各样的状态信息。

通过设备描述表的句柄(HDC)访问DC数据结构:设备描述表是一种数据结构,它包括了一个设备(如显示器和打印机)的绘制属性相关的信息。所有的绘制操作通过设备描述表进行。设备描述表与大多WIN32结构不同,应用程序不能直接访问设备描述表,只能由各种相关API函数通过设备描述表的句柄(HDC)间接访问该结构。

CDC是MFC的DC的一个类。CDC等设备上下分类,都含有一个类的成员变量:m_nHdc;即HDC类型的句柄。

如何编程:获取PIC的ID,通过该ID指向用户CRect,再通过该ID指向DC,通过CDC进行具体操作。
需要知道的类:
CWnd类是MFC的一个窗口类, 这个类里几乎封装了所有关于窗口操作的API函数
CRect类
CDC类

CImage image;
image.Load(_T("result.bmp"));//获取图资源
 
CWnd* pWnd;
pWnd = GetDlgItem(IDC_Image_2);    // 获取PIC的ID,即获取picture控件ID句柄
 
CRect rect_frame;
pWnd->GetClientRect(&rect_frame);    // 通过该ID指向用户CRect,即获取picture控件的区域CRect
 
CDC* pDC = pWnd->GetDC();    // 通过CDC进行具体操作
HDC hDC = pDC->m_hDC;
 
::SetStretchBltMode(hDC, HALFTONE);
::SetBrushOrgEx(hDC, 0, 0, NULL);
 
image.Draw(hDC, rect_frame);
ReleaseDC(pDC);//释放picture控件的DC
// 显示
/*
输入 
    CString strFilePath
    pic_ID IDC_Image_1
*/
CImage image;
image.Load(strFilePath);					// 获取图资源
 
CWnd* pWnd;
pWnd = GetDlgItem(IDC_Image_1);				// 获取picture控件ID句柄
 
CRect rect_frame;
pWnd->GetClientRect(&rect_frame);			// 获取picture控件的区域CRect
 
CDC* pDC = pWnd->GetDC();					// 获取picture控件的DC设备描述表——CDC是DC的一个类,CDC中包含HDC类型的句柄,HDC hDC = pDC->m_hDC;
 
pDC->SetStretchBltMode(STRETCH_HALFTONE);	//HALFTONE 保持不失真
 
image.Draw(pDC->m_hDC, rect_frame);
 
ReleaseDC(pDC);								//释放picture控件的DC

5、CImage类介绍

 CImage是MFC和ATL共享的新类,它能从外部磁盘中调入一个JPEG、GIF、BMP和PNG格式的图像文件加以显示,而且这些文件格式可以相互转换。
 CImage是VC.NET中定义的一种MFC/ATL共享类,也是ATL的一种工具类,它提供增强型的(DDB和DIB)位图支持,可以装入、显示、转换和保存多种格式的图像文件,包括BMP、GIF、JPG、PNG、TIF等。CImage是一个独立的类,没有基类。(CImage类是基于GDI+的,从VC.NET起引进,VC 6.0中没有。)
  ATL(Active Template Library,活动模板库)是一套基于模板的 C++ 类,用以简化小而快的 COM 对象的编写。

为了在MFC程序中使用CImage类,必须包含ATL的图像头文件atlimage.h:(在VS08 SP1中不用包含)
#include <atlimage.h>
6、函数原型:int SetStretchBItMode(HDC hdc,int iStretchMode)

参数:

hdc: 设备环境句柄

iStretchMode: 指定拉伸模式,可以取下列值

BLACKONWHITE:使用消除和现在的像素颜色值进行逻辑AND(与)操作运算。如果该位图是单色位图,那么该模式以牺牲白色像素为代价,保留黑色像素点。
COLORONCOLOR:删除像素。该模式删除所有消除的像素行,不保留其信息。
HALFTONE:将源矩形区中的像素映射到目标矩形区的像素块中,覆盖目标像素块的一般颜色与源像素的颜色接近。在设置完HALFTONE拉伸模之后,应用程序
https://learn.microsoft.com/zh-cn/windows/win32/api/wingdi/nf-wingdi-setstretchbltmode?redirectedfrom=MSDN
7、BOOL Draw( HDC hDestDC, int xDest, int yDest,int nDestWidth, int nDestHeight, int xSrc, int ySrc,int nSrcWidth, int nSrcHeight );
其中,hDestDC用来指定绘制的目标设备环境句柄,(xDest, yDest)和pointDest用来指定图像显示的位置,这个位置和源图像的左上角点相对应。

nDestWidth和nDestHeight分别指定图像要显示的高度和宽度,xSrc、ySrc、nSrcWidth和nSrcHeight用来指定要显示的源图像的某个部分所在的位置和大小。

rectDest和rectSrc分别用来指定目标设备环境上和源图像所要显示的某个部分的位置和大小。

需要说明的是,Draw方法综合了StretchBlt、TransparentBlt和AlphaBlend函数的功能。默认时,Draw的功能和 StretchBlt相同。但当图像含有透明色或Alpha通道时,它的功能又和TransparentBlt、AlphaBlend相同。因此,在一般情况下,我们都应该尽量调用CImage::Draw方法来绘制图像。
8、cimage.Create(nWidth, nHeight, 8 * nChannels); //默认图像像素单通道占用1个字节
9、CImage类提供了GetBits()函数来读取数据区,GetBits()函数返回的是图片最后一行第一个像素的地址
网上有人说返回指针的起始位置是不同的,有些图片返回的是左上角像素的地址,有些是左下角像素的地址,跟图片内部顺序有关。
GetPitch( ) 图像的间距。 如果返回值为负,位图是一个从下到上 DIB,并且原点是左下角。 如果返回值为正的,位图是一组 DIB,并且原点为左上角
两个函数GetPitch()和GetHeight()一起使用就可以得到图片数据取得起始位置

img_Data=(BYTE )m_Image.GetBits()+(m_Image.GetPitch()(m_Image.GetHeight()-1));

这样,img_Data就是图片数据区的起始位置,这个公式是从codeproject里看到的,介绍的很精辟,可以从google里搜索到。

其中GetHeight()函数返回图片的高度(以像素为单位)。GetPitch()返回图像的斜度,如果图像的顺序是从下到上(也就是GetBits()返回左上角像素的地址),
这时GetPitch()返回一个负值,大小为图像宽所占有的字节数,例如24位800600的图片,返回值应该是正或负的8003。
这样用每一行的字节数乘行数就可以得到起始位置了。
10、CImage::GetColorTable
void GetColorTable(
UINT iFirstColor,
UINT nColors,
RGBQUAD* prgbColors
) const throw( );
参数
iFirstColor
检索的第一个输入的颜色表索引。
nColors
颜色表的项数检索的。
prgbColors
对数组的指针检索颜色表项目的 RGBQUAD 结构。
11、ColorTable=new RGBQUAD[256]; //申请颜色表需要的空间

for( i=0;i<256;i++) //对于256个灰度等级调色板索引进行赋值
{
pColorTable[i].rgbBlue=i;
pColorTable[i].rgbGreen=i;
pColorTable[i].rgbRed=i;
pColorTable[i].rgbReserved=0;
}
12、Mat.ptr(int i=0) 获取图像像素矩阵指针,i表示从第几行开始,从0开始计行数
saturate_cast(-100) 返回0
saturate_cast(288),返回255
saturate_cast(150),返回150
这个函数saturate_cast确保了RGB的值在0~255之间
————————————————
版权声明:本文为CSDN博主「Nikola desian」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_41887615/article/details/89059798
13、 cv::namedWindow(title, cv::WINDOW_AUTOSIZE);
HWND hWnd = (HWND)cvGetWindowHandle(title.c_str());
HWND hParent = GetParent(hWnd);
SetParent(hWnd, new_hParent);
ShowWindow(hParent, SW_HIDE);
上面的代码是创建一个标题是title的窗口,并更换这个窗口的父对象为HWND new_hParent代表的对象,同时隐藏原来的父对象。之后只要记住这个title,用cv::imshow(title, mat)就可以在其上显示图片了。
14、C++中的using namespace std是什么意思呢?为了理解其中的含义,我们首先需要学习一个概念叫做命名空间。
 我们都知道,C语言规定变量名不能与关键字冲突,但是并没有规定变量名不能与库中的变量名冲突,但这在实际的操作的过程中就会产生意想不到的错误:
 例如,将一个time变量放在main函数中,根据局部优先的原则,time变量就会被理解为一个局部变量。但当time作为一个全局变量出现的时候,由于time库中也含有一个名叫time的函数名,头文件被展开后,此time被理解为变量名还是函数名就会产生歧义。
————————————————
版权声明:本文为CSDN博主「罅隙`」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/whc18858/article/details/124588910
15、vector 容器是包含 T 类型元素的序列容器,和 array<T,N> 容器相似,不同的是 vector 容器的大小可以自动增长,从而可以包含任意数量的元素;因此类型参数 T 不再需要模板参数 N。只要元素个数超出 vector 当前容量,就会自动分配更多的空间。只能在容器尾部高效地删除或添加元素。

vector 容器可以方便、灵活地代替数组。在大多数时候,都可以用 vector 代替数组存放元素。只要能够意识到,vector 在扩展容量,以 及在序列内部删除或添加元素时会产生一些开销;但大多数情况下,代码不会明显变慢。 为了使用 vector 容器模板,需要在代码中包含头文件 vector。
**16、C语言中的const string &T:**const表示参数传进来后不能被修改
&表示引用的形式传递参数
意思就是以引用的形式传递常量字符串string T
在函数内常量字符串不能被修改
17、ofstream和ifstream详细用法
在C++中,有一个stream这个类,所有的I/O都以这个“流”类为基础的,包括我们要认识的文件I/O,stream这个类有两个重要的运算符:

1、插入器(<<)  向流输出数据。比如说系统有一个默认的标准输出流(cout),一般情况下就是指的显示器,所以,cout<<“Write Stdout”<<‘\n’;就表示把字符串"Write Stdout"和换行字符(‘\n’)输出到标准输出流。

2、析取器(>>)  从流中输入数据。比如说系统有一个默认的标准输入流(cin),一般情况下就是指的键盘,所以,cin>>x;就表示从标准输入流中读取一个指定类型(即变量x的类型)的数据。

在C++中,对文件的操作是通过stream的子类fstream(file stream)来实现的,所以,要用这种方式操作文件,就必须加入头文件fstream.h。下面就把此类的文件操作过程一一道来。

c++中输出和输入导屏幕和键盘的类别声明包含再标题文件<iostrream.h>中,而磁盘类文件的 I/O则声明再包含标题文件<fstream.h>内。

输入和输出格式:

输出到磁盘 ofsteam 识别字(“文件名”)

从磁盘读文件 ifsteam 识别字("文件名“)
————————————————
版权声明:本文为CSDN博主「魏波.」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weibo1230123/article/details/80032814
18、C++ STL vector添加元素(push_back()和emplace_back())详解
要知道,向 vector 容器中添加元素的唯一方式就是使用它的成员函数,如果不调用成员函数,非成员函数既不能添加也不能删除元素。这意味着,vector 容器对象必须通过它所允许的函数去访问,迭代器显然不行。

在 《STL vector容器详解》一节中,已经给大家列出了 vector 容器提供的所有成员函数,在这些成员函数中,可以用来给容器中添加元素的函数有 2 个,分别是 push_back() 和 emplace_back() 函数。

push_back()
该成员函数的功能是在 vector 容器尾部添加一个元素
emplace_back()
该函数是 C++ 11 新增加的,其功能和 push_back() 相同,都是在 vector 容器的尾部添加一个元素。
19、torch::NoGradGuard no_grad;可以预防梯度计算,减小显存
20、cv::Mat img_input = img.clone();
clone是把所有的都复制过来,不论你是否设置了ROI、COI等影响,clone都会原封不动的克隆过来。用clone复制后,如果源图像在内存中消失,复制的图像也变了,而用copy复制,源图像消失后,复制的图像不变。
(1)OpenCV中的clone()和直接赋值(=)都会导致共享数据区,也就是相当于C++的引用(&),使用copy,才会获取新的Mat。及具有相同的type和size的Mat变量,copyTo和直接赋值(=)不会为目标矩阵重新分配内存,而clone总是会为目标矩阵重新分配内存。
(2)copyTo是实现图像roi操作的正确方法。

roi=src.clone(); //1
src.copyTo(roi); //2
对于语句1:不管roi在之前有没有分配内存,clone都会为其分配新内存。如果roi指向某图像img的某个rect,此语句并不能实现对img(rect)的操作,clone分配新内存后,roi不再指向img(rect).

语句2:如果roi在之前未分配内存,copyTo会为其分配新内存,若roi已分配内存,copyTo不再为其分配。

(3)对于Mat之间赋值传递,opencv分为两种情况一种是浅层拷贝比如Mat A=imread(“x.jpg”); Mat B=A;时候B就是浅层拷贝A,B只拷贝了A的的头部和地址,当B被操作后A也随之改变;第二种既是深层拷贝,例如Mat A=imread(“x.jpg”); Mat B=A.clone();这时候B是开辟了新的内存完全的复制了A的内容,操作B不会对A造成影响;
————————————————
版权声明:本文为CSDN博主「紫金小飞侠」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yangshengwei230612/article/details/102758136
21、cv::cvtColor函数将图像从BGR(蓝绿红)颜色空间转换为RGB(红绿蓝)颜色空间。

在OpenCV中,BGR是默认的颜色空间,而在其他常见的图像处理库和软件中,RGB通常是默认的颜色空间。因此,有时需要将图像从BGR转换为RGB,以便在不同的应用程序中正确显示颜色。
22、torch::Tensor imgTensor = torch::from_blob(img.data, { img.rows, img.cols,3 }, torch::kByte).to(device_type);
代码使用OpenCV库中的cv::Mat图像数据指针img.data和指定的图像形状{ img.rows, img.cols, 3 }来创建一个新的PyTorch张量对象imgTensor。这个张量的大小是img.rows x img.cols x 3,对应于图像的高度、宽度和通道数,其中3代表红、绿、蓝三个通道。

最后,使用.to方法将张量对象imgTensor移动到指定的设备类型device_type上,以便在该设备上进行后续计算。
**23、imgTensor = imgTensor.permute({ 2,0,1 });
imgTensor = imgTensor.toType(torch::kFloat);**两行代码的作用是将张量imgTensor的维度重新排列,并将其数据类型转换为float32,以便进行后续的深度学习计算。
24、imgTensor = imgTensor.div(255);
imgTensor = imgTensor.unsqueeze(0);

imgTensor.div(255) 表示对imgTensor中的所有元素都除以255,即对每个像素的RGB值进行归一化处理,将其值缩放到[0,1]的范围内。这个操作可以提高模型训练的稳定性和收敛速度。

imgTensor.unsqueeze(0) 表示在imgTensor的第0个维度(最前面)增加一个维度。这个操作通常用于扩展张量的维度,以便于将单张图片作为批次中的一部分进行处理。这里,将imgTensor从一个形状为(通道数,高度,宽度)的张量变为一个形状为(1,通道数,高度,宽度)的四维张量,其中1表示批次大小为1。

因此,这两行代码的作用是对张量imgTensor进行归一化处理,并将其形状扩展为一个四维张量,以便于后续的深度学习计算
25、torch::Tensor preds = module.forward({ imgTensor }).toTuple()->elements()[0].toTensor();
module.forward({ imgTensor }) 返回的是一个 torch::jit::IValue 类型的对象,其中包含了模型的输出结果。toTuple() 将这个对象转换为一个 torch::ivalue::Tuple 类型的元组对象,其中包含了模型的所有输出。然后通过 elements()[0] 取出第一个输出(因为在这个例子中模型只有一个输出),再使用 toTensor() 方法将这个输出转换为 torch::Tensor 类型的对象,并将结果赋值给变量 preds。因此,preds 就是模型的输出结果,是一个 torch::Tensor 类型的对象。
26、torch::Tensor scores = pred.select(1, 4) * std::get<0>(torch::max(pred.slice(1, 5, pred.sizes()[1]), 1));
这行代码的作用是计算预测框的置信度得分,用于后续的筛选。具体来说,假设 pred 是一个大小为 (N, C) 的张量,其中 N 表示预测框的个数,C 表示每个预测框的通道数,这些通道包括预测框的坐标和置信度等信息。那么,pred.select(1, 4) 就是选取 pred 中所有预测框的置信度信息(假设置信度信息在第 4 个通道),结果是一个大小为 (N,) 的张量。

接下来,pred.slice(1, 5, pred.sizes()[1]) 表示选取 pred 中每个预测框的第 5 到最后一个通道(即预测框的类别概率信息),结果是一个大小为 (N, C-5) 的张量。torch::max() 函数对该张量进行操作,返回一个大小为 (N,) 的张量 max_scores 和一个大小为 (N,) 的张量 max_idxs,分别表示每个预测框的最大类别概率和对应的类别下标。

最后,pred.select(1, 4) * std::get<0>(torch::max(pred.slice(1, 5, pred.sizes()[1]), 1)) 就是将每个预测框的置信度信息与其对应的最大类别概率相乘,得到一个大小为 (N,) 的张量 scores,即每个预测框的最终得分。
27、pred = torch::index_select(pred, 0, torch::nonzero(scores > score_thresh).select(1, 0));
这行代码使用 PyTorch C++ 接口中的 torch::index_select 函数和 torch::nonzero 函数来从一个预测结果张量 pred 中筛选出置信度得分高于 score_thresh 阈值的预测结果,并将筛选结果保存在变量 pred 中。

具体地,假设 pred 是一个大小为 (N, C) 的张量,表示有 N 个预测结果,每个预测结果有 C 个类别得分。scores 是一个大小为 N 的张量,表示每个预测结果的置信度得分。score_thresh 是一个标量值,表示阈值。

代码中的 scores > score_thresh 是一个比较运算,返回一个大小为 N 的布尔型张量,表示每个预测结果的置信度是否高于阈值。torch::nonzero(scores > score_thresh) 函数将这个布尔型张量中所有为真(即置信度高于阈值)的元素的索引提取出来,并返回一个大小为 (M, 1) 的张量,其中 M 表示有多少个预测结果的置信度高于阈值,每个行向量是一个满足条件的元素的索引,即 (i, )。

接下来,torch::nonzero(scores > score_thresh).select(1, 0) 将这个张量中所有行的第一个元素(即索引 i)提取出来,得到一个新的大小为 (M,) 的一维张量,表示所有满足条件的元素的索引 i。

最后,torch::index_select(pred, 0, …) 使用这个一维张量来选取 pred 中相应的预测结果,并将筛选结果保存在变量 pred 中。这个函数的第一个参数是要被选择的张量,第二个参数是选择维度的索引,这里是 0,表示选择第 0 维度(即按行选择)。第三个参数是索引的一维张量,它指定了哪些行要被选择。例如,如果 pred 是一个大小为 (N, C) 的张量,而上面提到的索引张量是大小为 (M,) 的张量 [i1, i2, …, iM],那么 torch::index_select(pred, 0, …) 的输出将是一个大小为 (M, C) 的张量,其中第 i1 行、第 i2 行、…、第 iM 行分别对应于原来的第 i1 个、第 i2 个、…、第 iM 个预测结果。
28、std::tuple<torch::Tensor, torch::Tensor> max_tuple = torch::max(pred.slice(1, 5, pred.sizes()[1]), 1) 的意思是对 pred 中每个预测结果的后 C-5 个类别得分求最大值,得到一个大小为 (N,) 的张量,表示每个预测结果的最大得分。同时,返回一个包含两个张量的 std::tuple 类型变量 max_tuple,其中第一个张量是最大得分的张量,第二个张量是最大得分的索引的张量。

最后,std::get<0>(max_tuple) 用于获取 max_tuple 的第一个张量,即最大得分的张量,std::get<1>(max_tuple) 用于获取 max_tuple 的第二个张量,即最大得分的索引的张量。
29、torch::Tensor dets = pred.slice(1, 0, 6);
这行代码用于从张量 pred 中提取每个预测结果的前 6 个元素,即每个预测结果的类别、得分、以及边界框的坐标。具体地,假设 pred 是一个大小为 (N, C) 的张量,表示有 N 个预测结果,每个预测结果有 C 个元素,其中前 4 个元素表示边界框的坐标,第 4 个元素表示边界框的置信度得分,后面的元素表示各个类别的置信度得分。在这种情况下,pred.slice(1, 0, 6) 表示在第 1 维度上选择索引为 0 到 5 的切片,即选择张量 pred 中每个预测结果的前 6 个元素,将结果保存在张量 dets 中。因此,dets 是一个大小为 (N, 6) 的张量,其中每行表示一个预测结果的类别、得分、以及边界框的坐标。
**30、**pred.select(1, 4) 是一个 PyTorch 张量(Tensor) pred 的方法调用,用于选择张量的第一个维度上的第4个元素。具体来说,它使用 PyTorch 的 select() 方法,该方法的第一个参数表示要选择的维度,第二个参数表示要选择的索引。

例如,假设 pred 是一个形状为 (3, 5) 的二维张量,它有三行和五列。pred.select(1, 4) 表示选择第一个维度(即行)上的第四个元素,因此它将返回一个形状为 (3,) 的一维张量,其中包含了 pred 的第四列的所有元素。如果要选择第二个维度(即列)上的第三个元素,则可以使用 pred.select(0, 2),它将返回一个形状为 (5,) 的一维张量,其中包含了 pred 的第三行的所有元素。
31、std::tuple<torch::Tensor, torch::Tensor> max_tuple = torch::max(pred.slice(1, 5, pred.sizes()[1]), 1);
使用了 PyTorch 的 max() 方法和 slice() 方法。它的作用是对张量 pred 进行通道维度上的切片操作,取出第 5 个通道之后所有通道中的最大值和它们所对应的索引。
32、std::tuple<torch::Tensor, torch::Tensor> indexes_tuple = torch::sort(dets.select(1, 4), 0, 1);
这行代码使用了 PyTorch 的 sort() 方法和 select() 方法。它的作用是对包含检测结果的张量 dets 在第二个维度(即置信度)上进行排序,并返回排好序的置信度和对应的索引。

具体来说,dets.select(1, 4) 使用 select() 方法在第二个维度上选取索引为 4 的那一列数据,即包含检测结果置信度的列。然后,sort() 方法对这列数据进行排序。第一个参数是要排序的张量,第二个参数 0 表示在第一个维度(即列维度)上排序,第三个参数 1 表示升序排序。这样,就可以得到一个排好序的置信度张量和对应的索引张量。

最终,返回的结果是一个包含两个张量的元组,分别是排好序的置信度张量和对应的索引张量。这些张量可以用于进一步的后处理,比如获取置信度最高的几个检测结果的信息。
33、indexes[0].item().toInt()
indexes[0]是一个torch::Tensor张量的第一个元素,item()方法将该张量中的一个标量值提取出来,然后toInt()方法将其转换为C++的整数类型。因此,indexes[0].item().toInt()是将张量中第一个元素提取为C++整数类型的值。在上下文中,它是用于获取选定的检测结果的索引。
34、widths[i] = std::max(float(0), rights[i].item().toFloat() - lefts[i].item().toFloat());
代码计算了每个框与当前选定框(即indexes[0])之间的宽度,并将结果存储在widths张量中的第i个位置。具体地说,它使用右边界rights[i]和左边界lefts[i]之间的差来计算宽度,但如果计算出的宽度小于0,则将宽度设置为0。这是因为宽度不可能是负数。
35、indexes = torch::index_select(indexes, 0, torch::nonzero(ious <= iou_thresh).select(1, 0) + 1);
代码的作用是根据 IoU 阈值筛选出重叠度较小的框的索引,并更新 indexes 张量,以便下一轮的筛选。具体来说,torch::nonzero(ious <= iou_thresh) 返回一个二维张量,其中第一维表示满足条件 ious <= iou_thresh 的元素在 ious 张量中的索引,第二维为固定值 0。select(1, 0) 操作则返回了这个二维张量的第一维,即满足条件的框在 ious 张量中的索引。最后 + 1 操作是因为在上一步筛选时,indexes 张量的第一个元素已经被保留下来了,所以在更新 indexes 张量时需要将这个元素索引加上。最终的结果是,indexes 张量中只保留了与第一个框的 IoU 值小于等于阈值的框的索引。
36、float left = dets[0][i][0].item().toFloat() * image.cols / 640;
代码的作用是从 dets 中获取一个 bounding box 的左边界坐标,并将其映射回原始图像的尺度。具体来说,代码中的 dets 是一个二维的浮点张量,其中第一个维度表示检测到的 bounding box 的数量,第二个维度为 5,分别表示 bounding box 的左上角和右下角的坐标以及置信度得分。而 i 则是一个表示当前要获取的 bounding box 的索引的变量。

这行代码中 dets[0][i][0] 表示获取 dets 张量中第 i 个 bounding box 的左上角的横坐标,.item() 则表示获取该张量中对应元素的值,并将其转换为 C++ 中的浮点数类型。然后该值乘以图像的列数 image.cols 再除以 640,就可以将其映射回原始图像的尺度,其中 640 是模型训练时使用的图像尺寸。最后,将计算得到的值赋给 left 变量。
37、CImage的draw函数显示图片后,再显示下一张图片,使得上一张图片消失。

CRect rect;									//定义矩形类  
	CWnd* pWnd = GetDlgItem(IDC_SHOW);			//获取控件句柄  
	pWnd->GetClientRect(&rect);					//获取句柄指向控件区域的大小  
	CDC* pDc = pWnd->GetDC();					//获取picture的DC  
	int nWindowW = rect.Width();				//获取窗口宽度 
	int nWindowH = rect.Height();				//获取窗口高度  
	int nImageW = mImage.cols;					//获取图片宽度  
	int nImageH = mImage.rows;					//获取图片高度
	//使图片在控件中居中全部显示
	float ratioW = (float)nWindowW / nImageW;
	float ratioH = (float)nWindowH / nImageH;

	CImage ImageCam;
	MatToCImage(mImage, ImageCam);
	pDc->BitBlt(0, 0, nWindowW, nWindowH, NULL, 0, 0, WHITENESS);	//显示前先清除上一次的图像
	pDc->SetStretchBltMode(COLORONCOLOR);		//防止失真
	if (ratioW < ratioH)
		ImageCam.Draw(pDc->m_hDC, 0, (int)(nWindowH - nImageH * ratioW) / 2, nWindowW, (int)(nImageH * ratioW), 0, 0, nImageW, nImageH);
	else
		ImageCam.Draw(pDc->m_hDC, (int)(nWindowW - nImageW * ratioH) / 2, 0, (int)(nImageW * ratioH), nWindowH, 0, 0, nImageW, nImageH);
	ReleaseDC(pDc);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: MFC ChartCtrl是一个用于绘制图表的控件,它是MFC中的一部分,用于构建图形用户界面(GUI)应用程序。 如果您想学习如何使用MFC ChartCtrl,以下是一些教程步骤: 1. 首先,您需要创建一个MFC应用程序项目。在Visual Studio中,选择“新建项目”并选择MFC应用程序向导。按照向导的指示创建项目。 2. 在创建项目时,您可以选择使用对话框作为主窗口。对话框是一个常用的GUI元素,通常用于显示和交互。 3. 打开对话框资源文件(.dlg文件),将ChartCtrl控件添加到对话框中。您可以在工具箱中找到ChartCtrl控件,将它拖放到对话框上。 4. 在代码中,包含ChartCtrl的头文件,并创建一个ChartCtrl对象。您可以在对话框类的成员变量中声明一个ChartCtrl对象,然后在DoDataExchange函数中进行映射绑定。 5. 在OnInitDialog函数中,初始化ChartCtrl对象。您可以设置图表类型(如柱状图、折线图、饼图等)以及标签、图例等属性。 6. 在需要更新图表的地方,调用ChartCtrl的相应函数来添加数据点、刷新图表等。您可以使用AddDataPoint来添加数据点,并使用Invalidate函数使控件无效并重绘。 7. 可以根据需要,设置图表的其他属性,例如轴的刻度、标题、颜色等。 这是一个简单的教程,介绍了如何使用MFC ChartCtrl来创建和显示图表。您可以进一步深入学习ChartCtrl的各种功能和用法,探索更高级的图表设置和交互功能。 ### 回答2: MFC ChartCtrl 是 Microsoft Foundation Class (MFC) 库中包含的一个图表控件,用于在 Windows 平台上创建图表和数据展示。MFC 是微软针对 Windows 操作系统开发的一套 C++ 库,提供了很多与图形用户界面 (GUI) 相关的类和函数。 学习 MFC ChartCtrl 教程可以帮助开发者了解如何在 MFC 应用程序中使用图表控件。以下是一些学习 MFC ChartCtrl 的步骤和要点: 1. 环境设置:首先,在 Visual Studio IDE 中创建一个 MFC 应用程序项目,并确保已安装 MFC 库。然后,将 ChartCtrl 控件库添加到项目中。 2. 创建图表:在 MFC 应用程序中,可以使用资源编辑器或动态创建方式来创建 ChartCtrl 控件。通过设置 ChartCtrl 的属性,如背景颜色、坐标轴标签和标题等,可以定制图表的外观。 3. 添加数据:通过调用 ChartCtrl 的函数,可以添加数据到图表中。可以添加不同类型的数据,如曲线图、柱状图、饼图等。还可以设置数据的样式、颜色和图例。 4. 数据绑定:MFC ChartCtrl 支持数据绑定,可以将图表与数据模型关联起来。通过将数据源与 ChartCtrl 控件进行绑定,当数据发生变化时,图表会自动更新。 5. 事件处理:MFC ChartCtrl 也支持事件处理。通过定义图表控件的事件处理函数,在用户交互或数据变化等事件发生时,可以执行相应的操作。 6. 导出图表:MFC ChartCtrl 提供了导出图表的功能,可以将图表保存为图片格式(如 BMP、JPEG)或使用其他方法进行导出。 通过学习 MFC ChartCtrl 教程,开发者可以掌握使用图表控件创建各种类型的图表,并实现数据的展示和交互功能。这对于开发涉及数据可视化的应用程序非常有帮助,提升用户体验和数据分析的效果。 ### 回答3: MFC ChartCtrl是一个用于创建和显示图表的MFC控件。它可以在MFC程序中创建和定制各种类型的图表,例如折线图、柱状图、饼图等。 学习MFC ChartCtrl的教程可以帮助开发人员了解如何使用该控件来创建和呈现图表。以下是一些学习MFC ChartCtrl的步骤和内容: 1. 环境搭建:在开始学习MFC ChartCtrl之前,要先在MFC开发环境中安装相应的图表控件库。 2. 控件的基本使用:学习如何在MFC应用程序中添加ChartCtrl控件,并对其进行基本的属性设置,如大小、位置、背景颜色等。 3. 数据绑定:学习如何将数据与图表控件进行绑定,以便可以动态地显示和更新图表。可以使用不同的数据源,如数组、数据库等。 4. 图表类型和样式:了解MFC ChartCtrl提供的不同的图表类型和样式,如折线图、柱状图、饼图等,以及如何根据需要选择并设置相应的样式。 5. 数据处理和展示:学习如何对数据进行处理和计算,以便在图表中显示出有意义和易于理解的结果。例如,可以添加坐标轴、数据标签、图例等。 6. 交互和操作:了解如何通过鼠标事件等交互方式对图表进行操作和交互,如缩放、平移、选中等。 7. 定制化和高级功能:学习如何根据实际需求对图表进行定制化和高级功能的实现,如添加动画效果、自定义绘制等。 总结来说,学习MFC ChartCtrl教程可以帮助开发人员掌握在MFC应用程序中创建和展示图表的技巧。逐步学习和实践这些知识,可以让开发人员更好地利用MFC ChartCtrl控件来实现图表功能,并提升应用程序的用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值