【视觉项目】【day4】8.24号实验记录(消除瓶子内部“边缘”)

思路分析以及代码

思路1:使用findContours函数,设置轮廓为最外部RETR_EXTERNAL,结果发现结果仍然是所有轮廓。
思路2:先二值化,然后进行闭操作,然后canny,得到的轮廓确实比之前少很多,但是有个缺点:瓶子的边缘由于二值化的影响失真了。

原图
直接canny得到的图
二值化后canny图

思路3:先二值化,然后进行闭操作,然后填充较小的黑色区域,然后再进行腐蚀操作,得到了比原本瓶子要小一圈的mask,然后对canny后的图进行去轮廓操作(mask区域为黑),这样或许就可以了。

结果:确实好很多,但是有两张图片的没有结果,观察:有一个是因为孔洞填充选取的点有问题,还有一个 debug发现也是孔洞填充出现了问题。暂时现将大棕瓶区别对待。因为他的大津二值化结果有点特殊。
验证结果,发现效果确实比直接模板匹配canny后的好,但是仍然会有误判,发现误判主要出现在将大一点的瓶子的某部分识别成小瓶子,所以需要从瓶子像素多少来进行限制,规则是:不能识别成像素比模板瓶子像素少的类别。
所要做的:总结出模板图中瓶子像素值,得到每张测试图的瓶子像素(这里使用mask2的像素个数需要用连通域来进行甄别是否为,比实际瓶子要小)
空洞填充函数
输入:待处理图像的二值图
参数:背景颜色(黑还是白) 漫水填充的起始点(填充的是背景,一般选择(0,0))
输出:填充后的二值图

void My_hole_filling(Mat& srcImage, Mat& dstImage,int color,Point &startPoint)
{// Floodfill from point (0, 0) 以点(0,0)为种子点,进行漫水填充/*int x = startPoint.x;int y = startPoint.y;*///srcImage.at<char>(x, y)if ( color== 255)		//背景为白{srcImage = ~srcImage;}Mat im_floodfill = srcImage.clone();floodFill(im_floodfill, startPoint, Scalar(255));//255// Invert floodfilled image 反转图像Mat im_floodfill_inv;bitwise_not(im_floodfill, im_floodfill_inv);// Combine the two images to get the foreground. 获得前景dstImage = (srcImage | im_floodfill_inv);dstImage = ~dstImage;
}

获取瓶子外轮廓的函数
输入:原图灰度图 canny阈值 输出:轮廓图 mask2图

void get_external_Contours_function(Mat& srcImage, Mat& dstImage,Mat& dstmask, int canny_thred)
{//模糊化降噪blur(srcImage, srcImage, Size(5, 5));Mat mask;//大津二值化threshold(srcImage, mask, 100, 255, THRESH_OTSU);//闭操作int Abs_offset = 2;Mat element = getStructuringElement(MORPH_ELLIPSE, Size(Abs_offset * 2 + 1, Abs_offset * 2 + 1), Point(Abs_offset, Abs_offset));	//返回的是内核矩阵morphologyEx(mask, mask, MORPH_CLOSE, element);//孔洞填充Point startpoint = Point(40,40);My_hole_filling(mask, mask, 0, startpoint);//将mask缩小一圈Mat mask2;Mat element_erode = getStructuringElement(MORPH_ELLIPSE, Size(Abs_offset * 2 + 1, Abs_offset * 2 + 1), Point(Abs_offset, Abs_offset));	//返回的是内核矩阵morphologyEx(mask, mask2, MORPH_DILATE, element_erode);dstmask = mask2;//mask2就是我们的掩膜//对模糊后的灰度图进行canny检测Canny(srcImage, dstImage, canny_thred, canny_thred * 2, 3);//将在mask2内的所有为白的像素置为黑int height = dstImage.rows;int width = dstImage.cols;for (int j = 0; j < height; j++){for (int i = 0; i < width; i++){if (mask2.at<uchar>(j, i) == 0 && dstImage.at<uchar>(j, i) == 255){dstImage.at<uchar>(j, i) = 0;}}}
}

连通域测试代码,找出mask2中面积最大的连通域的像素个数

int main()
{cv::Mat srcMat = imread("D:\\opencv_picture_test\\视觉项目resize后的图片夹\\测试图片夹\\测试图\\10+麻点.jpg",1);//cv::Mat srcMat = imread("D:\\opencv_picture_test\\视觉项目resize后的图片夹\\测试图片夹\\测试图\\大棕瓶.jpg", 1);//cv::Mat srcMat = imread("D:\\opencv_picture_test\\视觉项目resize后的图片夹\\测试图片夹\\均衡化前的测试图\\方肩+肩薄.jpg", 1);Mat dstMat;Mat mask2;int thred = 40;//转换成灰度cvtColor(srcMat, dstMat, COLOR_BGR2GRAY);get_external_Contours_function(dstMat, dstMat, mask2, thred);//观察连通域个数,同时选出最大的那个连通域,之前对mask2进行反色mask2 = 255 - mask2;Mat lableMat;Mat statsMat;Mat centerMat;int nComp = cv::connectedComponentsWithStats(mask2,lableMat,statsMat,centerMat,8,CV_32S);//找出连通域像素个数最多的那个,然后记录下像素个数int max_pixels = 0;int max_pixels_label = 0;if (nComp == 1) max_pixels = statsMat.at<int>(1, 4);else{//找到像素点最多的连通域标记vector<int > pixels_nums;for (int i = 1; i < nComp; i++){pixels_nums.push_back(statsMat.at<int>(i, 4));	//将连通域面积入vector}//找到最大的值并且返回它在vector的位置auto maxPosition = max_element(pixels_nums.begin(), pixels_nums.end());max_pixels = *maxPosition;max_pixels_label = maxPosition - pixels_nums.begin();}cout <<"连通域个数(算上背景)="<< nComp << endl;cout << "max_pixels = " << max_pixels << endl;cout << endl;imshow("die_on_chip", dstMat);waitKey(0);return 0;
}

结果:

获取地址成功
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\10+波纹.jpg
0 max_pixels = 14347
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\10+麻点.jpg
1 max_pixels = 13617
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\10+气泡+瓶口破裂.jpg
2 max_pixels = 13368
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\10.jpg
3 max_pixels = 14335
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\10_2.jpg
4 max_pixels = 12677
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\11.jpg
5 max_pixels = 11718
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\12.jpg
6 max_pixels = 12413
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\13+炸口.jpg
7 max_pixels = 8002
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\14.jpg
8 max_pixels = 8870
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\2+料纹.jpg
9 max_pixels = 18144
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\3.jpg
10 max_pixels = 16658
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\3_2.jpg
11 max_pixels = 15836
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\4+厚底.jpg
12 max_pixels = 17636
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\4+厚底2.jpg
13 max_pixels = 16468
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\4+炸肩.jpg
14 max_pixels = 15504
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\5+脖夹料.jpg
15 max_pixels = 19443
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\5+肩薄.jpg
16 max_pixels = 18623
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\5+气泡.jpg
17 max_pixels = 19209
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\5+炸口.jpg
18 max_pixels = 20063
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\5+皱纹气泡.jpg
19 max_pixels = 19552
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\6.jpg
20 max_pixels = 14913
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\6_2.jpg
21 max_pixels = 15616
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\6_3.jpg
22 max_pixels = 15653
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\7+厚底.jpg
23 max_pixels = 15158
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\7+厚底2.jpg
24 max_pixels = 13383
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\8.jpg
25 max_pixels = 14950
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\8_2.jpg
26 max_pixels = 15271
D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图\9.jpg
27 max_pixels = 13192

统计每个瓶子的像素个数(取最小的再减去2000作为标准)

判断序列像素个数
1暂不统计
218144
315836
415504
518623
614913
713383
814950
913192
10(A)12677
11(B)11718
12©12413
13(D)8002
14(E)8870

实现思路:获取测试图后,得到其mask2像素个数。与pixels_num_criterion[ ]中的元素相比较。
记录下满足(test_mask_pxiels>=pixels_num_criterion[i]-pixels_num_sub)的序号i,放入新的vector:prepare_template_num然后用序号属于其中的模板去匹配。
代码好像有点问题,存个档,明天检查一下:

//将测试图转换成与模板图相匹配的函数
//输入:测试图 canny算子阈值 输出:外轮廓图 
//返回值:该测试图mask2中的瓶子像素个数
int test_covertTo_Outer_contour(Mat& srcImg, Mat& dstImg, int thred)
{//这里我们批量处理Mat mask2;//int thred = 40;//转换成灰度cvtColor(srcImg, dstImg, COLOR_BGR2GRAY);get_external_Contours_function(dstImg, dstImg, mask2, thred);//观察连通域个数,同时选出最大的那个连通域,之前对mask2进行反色mask2 = 255 - mask2;Mat lableMat;Mat statsMat;Mat centerMat;int nComp = cv::connectedComponentsWithStats(mask2,lableMat,statsMat,centerMat,8,CV_32S);//找出连通域像素个数最多的那个,然后记录下像素个数int max_pixels = 0;int max_pixels_label = 0;if (nComp == 1) max_pixels = statsMat.at<int>(1, 4);else{//找到像素点最多的连通域标记vector<int > pixels_nums;//0是背景for (int i = 1; i < nComp; i++){pixels_nums.push_back(statsMat.at<int>(i, 4));	//将连通域面积入vector}//找到最大的值并且返回它在vector的位置,然后还需要+1才是在连通域label中的位置auto maxPosition = max_element(pixels_nums.begin(), pixels_nums.end());max_pixels = *(maxPosition);max_pixels_label = (maxPosition - pixels_nums.begin() + 1);}return max_pixels;
}
int main()
{//改变控制台字体颜色system("color 02");//******************************************【0】获取测试文件夹路径和模板文件夹路径********************************************************////获取测试文件夹路径和模板文件夹路径cv::String path_test = "D:/opencv_picture_test/视觉项目resize后的图片夹/测试图片夹/测试图/";        cv::String path_template = "D:/opencv_picture_test/视觉项目resize后的图片夹/模板图片夹/template外轮廓/";    cout << "获取地址成功" << endl;//******************************************【1】加载模板图像********************************************************////创建模板vectorvector<Mat>tempMat;//插入模板元素Mat srcImage;std::vector<cv::String> temp_filenames;cv::glob(path_template, temp_filenames);                 //opencv里面用来读取指定路径下文件名的一个很好用的函数for (int i = 0; i < temp_filenames.size(); i++){srcImage = cv::imread(temp_filenames[i],0);tempMat.push_back(srcImage);cout << temp_filenames[i] << endl;}//获取模板数目int tempMat_Nums = tempMat.size();//******************************************【2】加载测试图像********************************************************////创建测试vectorvector<Mat>testMat;//插入测试元素std::vector<cv::String> test_filenames;cv::glob(path_test, test_filenames);                 //opencv里面用来读取指定路径下文件名的一个很好用的函数for (int i = 0; i < test_filenames.size(); i++){srcImage = cv::imread(test_filenames[i]);testMat.push_back(srcImage);//cout << test_filenames[i] << endl;}//获取测试图数目int testMat_Nums = testMat.size();//******************************************【3】对每张测试图进行模板匹配********************************************************//for (int j = 0;j < testMat_Nums;j++){cout <<"第"<< j <<"张测试图片的测试"<< endl;Mat resultMat;Mat CompareMat;Mat dispMat;//将测试图转换成与模板图相匹配的类型int test_mask_pxiels = 0;test_mask_pxiels=test_covertTo_Outer_contour(testMat[j], CompareMat, 40);cout << "test_mask_pxiels" << test_mask_pxiels << endl;int match_method = TM_CCORR_NORMED;		//经过试错发现此参数较好。//用每个模板去匹配测试图,并且找出每次结果的最佳匹配值,将值存入vector中vector<double>goodval;vector<Point>goodlock;int matchnum = 0;Point matchLoc;vector<int>prepare_template_num;cout << "可能的模板序号" << endl;for (int i = 0;i < 14;i++){if (test_mask_pxiels >= (pixels_num_criterion[i] - pixels_num_sub)){//将符合规则的模板序号导入vector中prepare_template_num.push_back(i);cout << i <<" ";}}cout << endl;for (int x = 0;x < prepare_template_num.size();x++){cout << prepare_template_num[x] << " ";}cout << endl;for (int i = 0;i < tempMat_Nums;i++){//采用模板与目标图像像素与各自图像的平均值计算dot product,正值越大匹配度越高,负值越大图像的区别越大,但如果图像没有明显的特征(即图像中的像素值与平均值接近)则返回值越接近0;matchTemplate(CompareMat, tempMat[i], resultMat, match_method);//不归一化,因为不同模板归一化后的最佳值皆为1,无法比较//normalize(resultMat, resultMat, 0, 1, NORM_MINMAX, -1, Mat());	//归一化double minVal; double maxVal; Point minLoc; Point maxLoc;	//定义最大值最小值以及它们的位置变量minMaxLoc(resultMat, &minVal, &maxVal, &minLoc, &maxLoc, Mat());	//从结果矩阵中找到匹配度最大以及最小的值并且确定其位置//对于方法SQDIFF和SQDIFF_NORMED两种方法来讲,越小的值就有着更高的匹配结果//而其余的方法则是数值越大匹配效果越好if (match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED){//将不符合像素数目规则的模板的可能性置1vector<int>::iterator result = find(prepare_template_num.begin(), prepare_template_num.end(), i); //查找该模板是否食欲符合规则的模板if (result == prepare_template_num.end()) //没找到{minVal = 1;}goodlock.push_back(minLoc);goodval.push_back(minVal);}else{//将不符合像素数目规则的模板的可能性置0vector<int>::iterator result = find(prepare_template_num.begin(), prepare_template_num.end(), i); //查找该模板是否食欲符合规则的模板if (result == prepare_template_num.end()) //没找到{maxVal = 0;}goodlock.push_back(maxLoc);goodval.push_back(maxVal);}show_probability(i, maxVal);//cout << i << "  " << maxVal << endl;}//找到goodval中最佳的一组if (match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED){auto goodPosition = min_element(goodval.begin(), goodval.end());matchnum = distance(begin(goodval), goodPosition);}else{auto goodPosition = max_element(goodval.begin(), goodval.end());matchnum = distance(begin(goodval), goodPosition);}show_text(matchnum, test_filenames[j]);matchLoc = goodlock[matchnum];testMat[j].copyTo(dispMat);//以最佳匹配点为中心绘制与模板相同大小的框rectangle(dispMat, matchLoc, Point(matchLoc.x + tempMat[matchnum].cols, matchLoc.y + tempMat[matchnum].rows), Scalar::all(255), 2, 8, 0);namedWindow("testMat", WINDOW_NORMAL);//WINDOW_NORMAL允许用户自由伸缩imshow("testMat", dispMat);waitKey(30);}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/378381.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

国产操作系统和linux 之间的关系,为何国产系统大多基于开源Linux?操作系统从0做起到底有多难?...

今年貌似是国产操作系统的“爆发”之年&#xff0c;除了老牌的银河麒麟、中标麒麟、深度之外&#xff0c;中兴近日发布了自己的“新支点”&#xff0c;华为也公开了自研的操作系统“鸿蒙”。纵观这些国产操作系统&#xff0c;大多基于开源的Linux。那么为什么我们不可以从0开始…

js 第四课

正则表达式&#xff1a;RegExp对象 正则表达式描述一个字符模式的对象&#xff0c;或者说用某种模式去匹配一类字符串的一个公式。 1.创建 可以用RegExp构造函数和直接量两种方式。正则表达式直接量被包含在一对"/"中. 1 var partern1 RegExp(\\d*); 2 …

解析法实现一元线性回归、多元线性回归以及数据模型可视化操作

目录【1】解析法实现一元线性回归python列表实现利用Numpy实现利用TensorFlow实现数据和模型可视化【2】解析法实现多元线性回归利用Numpy实现需要用到的NumPy数组运算函数数据和模型可视化绘制空间点集&#xff1a;绘制空间平面图&#xff1a;绘制线框图并且与散点图对比&…

32位系统win2008+mssql2008 6G内存折腾纪实

十年没搞硬件了&#xff0c;现在计算机发展到大硬盘大内存的时代了。一直都少搞服务器配置、运营&#xff0c;以前弄服务器都是普通的PC来当服务器。公司原来的一个业务系统用的是mssql2000好几年了&#xff0c;由于业务数据越积压越多最大的一张表已经有7000多万条记录了&…

前台用js、jquery出现错误很多是由于IE缓存

例如&#xff1a;当你用jquery进行异步请求数据时&#xff0c;如果浏览器发现请求的地址不变&#xff0c;或者参数也不改变的情况下 IE默认是取原来的缓存中的数据&#xff0c;而不进行重新请求数 解决的方法是是在地址栏的后面加上一个随机参数值&#xff0c;IE发现地址改变&a…

WebC.BBS 项目参与新人必读

开发环境: 采用Visual Studio 2010&#xff0c;MVC版本采用Asp.Net MVC3&#xff0c;数据库采用Sql2005 2008,扩展技术包括jQuery。 SVN的相关信息&#xff1a; SVN-Url&#xff1a;svn://svn.cyqdata.com/project_bbs 账户申请&#xff1a;请将自己的密码发给组长&#xff0c;…

利用梯度下降法求解一元线性回归和多元线性回归

文章目录原理以及公式【1】一元线性回归问题【2】多元线性回归问题【3】学习率【4】流程分析&#xff08;一元线性回归&#xff09;【5】流程分析&#xff08;多元线性回归&#xff09;归一化原理以及每种归一化适用的场合一元线性回归代码以及可视化结果多元线性回归代码以及可…

Javascript之创建对象(原型模式)

我们创建的每个函数都有一个prototype(原型)属性&#xff0c;这个属性是一个指针&#xff0c;指向一个对象&#xff0c;它的用途是包含可以有特定类型的所有实例共享的属性和方法。 prototype就是通过构造函数而创建的那个对象的原型对象。使用原型的好处就是可以让所有对象实例…

两种方法将Android NDK samples中hello-neon改成C++

一、第一种方法&#xff1a;1.修改helloneon.c 中代码 a.将 char* str; 改为 char str[512] {0}; b.将 asprintf(&str, "FIR Filter benchmark:\nC version : %g ms\n", time_c); 改为 sprintf(str, "FIR Filter benchmark:\nC ve…

【视觉项目】【day6】8.26关于matchTemplate()以及NCC的思考整理

NCC与matchTemplate()函数中match_method TM_CCOEFF_NORMED是否一样&#xff1f; 先看公式&#xff1a; TM_CCOEFF_NORMED NCCTM_CCOEFF_NORMED:归一化的相关性系数匹配方法 NCC:normalized cross correlation:归一化互相关系数 公式是一样的。 参考&#xff1a; 模板匹配的几…

HTTP 状态代码

如果向您的服务器发出了某项请求要求显示您网站上的某个网页&#xff08;例如&#xff0c;当用户通过浏览器访问您的网页或在 Googlebot 抓取该网页时&#xff09;&#xff0c;那么&#xff0c;您的服务器会返回 HTTP 状态代码以响应该请求。 此状态代码提供了有关请求状态的信…

TensorFlow的可训练变量和自动求导机制

文章目录一些概念、函数、用法TensorFlow实现一元线性回归TensorFlow实现多元线性回归一些概念、函数、用法 对象Variable 创建对象Variable&#xff1a; tf.Variable(initial_value,dtype)利用这个方法&#xff0c;默认整数为int32&#xff0c;浮点数为float32&#xff0c;…

django第二个项目--使用模板做一个站点访问计数器

上一节讲述了django和第一个项目HelloWorld&#xff0c;这节我们讲述如何使用模板&#xff0c;并做一个简单的站点访问计数器。 1、建立模板 在myblog模块文件夹&#xff08;即包含__init__.py的文件夹)下面新建一个文件夹templates&#xff0c;用于存放HTML模板&#xff0c;在…

c语言math乘法,JavaScript用Math.imul()方法进行整数相乘

1. 基本概念Math.imul()方法用于计算两个32位整数的乘积&#xff0c;它的结果也是32位的整数。JavaScript的Number类型同时包含了整数和浮点数&#xff0c;它没有专门的整型和浮点型。因此&#xff0c;Math.imul()方法能提供类似C语言的整数相乘的功能。我们将Math.imul()方法的…

梯度下降法预测波士顿房价以及简单的模型评估

目录原理代码关于归一化的思考原理 观察数据可知属性之间差距很大&#xff0c;为了平衡所有的属性对模型参数的影响&#xff0c;首先进行归一化处理。 每一行是一个记录&#xff0c;每一列是个属性&#xff0c;所以对每一列进行归一化。 二维数组归一化&#xff1a;1、循环方式…

Windows Phone 内容滑动切换实现

在新闻类的APP中&#xff0c;有一个经常使用的场景&#xff1a;左右滑动屏幕来切换上一条或下一条新闻。 那么通常我们该使用哪种方式去实现呢&#xff1f;可以参考一下Demo的实现步骤。 1&#xff0c;添加Windows Phone用户自定义控件。例如&#xff1a; 这里我为了演示的方便…

使用鸢尾花数据集实现一元逻辑回归、多分类问题

目录鸢尾花数据集逻辑回归原理【1】从线性回归到广义线性回归【2】逻辑回归【3】损失函数【4】总结TensorFlow实现一元逻辑回归多分类问题原理独热编码多分类的模型参数损失函数CCETensorFlow实现多分类问题独热编码计算准确率计算交叉熵损失函数使用花瓣长度、花瓣宽度将三种鸢…

【神经网络计算】——神经网络实现鸢尾花分类

本blog为观看MOOC视频与网易云课堂所做的笔记 课堂链接&#xff1a; 人工智能实践:TensorFlow笔记 吴恩达机器学习 疑问与思考 为什么按照batch喂入数据 之前看的视频里面处理数据都是一次性将所有数据喂入&#xff0c;现在看的这个视频对数据进行了分组投入。这是为何&#…

c# xaml语言教程,c#学习之30分钟学会XAML

1.狂妄的WPF相对传统的Windows图形编程&#xff0c;需要做很多复杂的工作&#xff0c;引用许多不同的API。例如&#xff1a;WinForm(带控件表单)、GDI(2D图形)、DirectXAPI(3D图形)以及流媒体和流文档等&#xff0c;都需要不同的API来构建应用程序。WPF就是看着上面的操作复杂和…

.NET 小结之内存模型

.NET 小结之内存模型 为什么要解.NET 的内存模型 在.NET下的内存管理、垃圾回收其实大部分不需要我们操心&#xff0c;因为大部分.NET已经帮我们做了&#xff0c;通常情况下也不需要考虑这些。但是如果想要了解一些.NET一些稍微“底层”的原理&#xff0c;如&#xff1a;“装箱…