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

【opencv】示例-grabcut.cpp 使用OpenCV库的GrabCut算法进行图像分割

41f08706f303066b2ff6f1dc27fbfc16.png

left mouse button - set rectangle

33bae4d5ad6bf1e6d3d10211a597a22c.png

bd1a7f237d6a0e78aeb8b84c86f86330.png

SHIFT+left mouse button - set GC_FGD pixels

CTRL+left mouse button - set GC_BGD pixels

这段代码是一个使用OpenCV库的GrabCut算法进行图像分割的C++程序。它允许用户通过交互式方式选择图像中的一个区域,并利用GrabCut算法尝试将其分割出来。代码中包含用户操作指南、颜色定义、事件处理以及GrabCut算法的迭代过程,实现了用户通过鼠标交互和按键控制来迭代分割效果。程序逻辑结构清晰,采用面向对象的方式封装了GrabCut分割相关的操作。

// 引入OpenCV库中处理图像编解码的相关头文件
#include "opencv2/imgcodecs.hpp"
// 引入OpenCV库中高级GUI的相关头文件
#include "opencv2/highgui.hpp"
// 引入OpenCV库中图像处理的相关头文件
#include "opencv2/imgproc.hpp"// 引入输入输出流的库文件
#include <iostream>// 使用标准命名空间std,免去std::前缀
using namespace std;
// 使用OpenCV命名空间cv,免去cv::前缀
using namespace cv;// 定义静态辅助函数,用于显示程序帮助信息
static void help(char** argv)
{cout << "\nThis program demonstrates GrabCut segmentation -- select an object in a region\n""and then grabcut will attempt to segment it out.\n""Call:\n"<< argv[0] << " <image_name>\n" // 显示如何调用程序"\nSelect a rectangular area around the object you want to segment\n"// 以下是热键操作说明"\nHot keys: \n""\tESC - quit the program\n" // 按ESC键退出程序"\tr - restore the original image\n" // 按'r'键还原到原始图像"\tn - next iteration\n" // 按'n'键执行下一迭代步骤"\n"// 以下是鼠标操作说明"\tleft mouse button - set rectangle\n" // 左键点击设置矩形区域"\n""\tCTRL+left mouse button - set GC_BGD pixels\n" // 按住CTRL键同时左键点击设置背景像素"\tSHIFT+left mouse button - set GC_FGD pixels\n" // 按住SHIFT键同时左键点击设置前景像素"\n""\tCTRL+right mouse button - set GC_PR_BGD pixels\n" // 按住CTRL键同时右键点击设置可能的背景像素"\tSHIFT+right mouse button - set GC_PR_FGD pixels\n" << endl; // 按住SHIFT键同时右键点击设置可能的前景像素
}// 定义一些标量常量表示不同的颜色
const Scalar RED = Scalar(0,0,255);
const Scalar PINK = Scalar(230,130,255);
const Scalar BLUE = Scalar(255,0,0);
const Scalar LIGHTBLUE = Scalar(255,255,160);
const Scalar GREEN = Scalar(0,255,0);// 定义背景键和前景键的标志,用于鼠标操作事件中
const int BGD_KEY = EVENT_FLAG_CTRLKEY;
const int FGD_KEY = EVENT_FLAG_SHIFTKEY;// 声明静态函数,用于从复合掩模图中提取二值掩模图
static void getBinMask( const Mat& comMask, Mat& binMask )
{// 确保comMask非空且类型为CV_8UC1,否则抛出错误if( comMask.empty() || comMask.type()!=CV_8UC1 )CV_Error( Error::StsBadArg, "comMask is empty or has incorrect type (not CV_8UC1)" );// 如果binMask为空或大小与comMask不一致,则重新创建与comMask大小一致的binMaskif( binMask.empty() || binMask.rows!=comMask.rows || binMask.cols!=comMask.cols )binMask.create( comMask.size(), CV_8UC1 );// 将comMask中的最低位复制到binMask中binMask = comMask & 1;
}// 声明GrabCut应用类,用于实现GrabCut算法的交互操作
// 定义用于GrabCut算法的应用类
class GCApplication
{
public:// 定义选择状态的枚举类型enum{ NOT_SET = 0, IN_PROCESS = 1, SET = 2 };// 定义圆的半径static const int radius = 2;// 定义圆的线条厚度,-1表示填充圆static const int thickness = -1;// 成员函数声明void reset();  // 重置函数void setImageAndWinName( const Mat& _image, const string& _winName );  // 设置图像和窗口名void showImage() const;  // 展示图像void mouseClick( int event, int x, int y, int flags, void* param );  // 鼠标点击事件处理函数int nextIter();  // 迭代处理函数int getIterCount() const { return iterCount; }  // 获取迭代次数的函数private:// 私有成员函数和变量声明void setRectInMask();  // 设置矩形到掩模中void setLblsInMask( int flags, Point p, bool isPr );  // 设置标签到掩模中的函数const string* winName;  // 窗口名称const Mat* image;  // 图像引用Mat mask;  // 掩模矩阵Mat bgdModel, fgdModel;  // 背景和前景模型uchar rectState, lblsState, prLblsState;  // 矩形和标签状态变量bool isInitialized;  // 是否已经初始化的标志Rect rect;  // 矩形区域vector<Point> fgdPxls, bgdPxls, prFgdPxls, prBgdPxls;  // 记录前景和背景像素位置的向量int iterCount;  // 迭代计数
};// 成员函数定义
// 重置GCApplication
void GCApplication::reset()
{if( !mask.empty() )mask.setTo(Scalar::all(GC_BGD));  // 如果掩模非空,设置掩模的所有值为GC_BGD// 清除前景、背景像素位置的记录bgdPxls.clear(); fgdPxls.clear();prBgdPxls.clear();  prFgdPxls.clear();// 重置相关的状态标志和迭代计数isInitialized = false;rectState = NOT_SET;lblsState = NOT_SET;prLblsState = NOT_SET;iterCount = 0;
}// 设置图像和窗口名
void GCApplication::setImageAndWinName( const Mat& _image, const string& _winName  )
{if( _image.empty() || _winName.empty() )return;  // 如果图像或窗口名为空,直接返回image = &_image;  // 设置图像引用winName = &_winName;  // 设置窗口名mask.create( image->size(), CV_8UC1);  // 创建与图像相同大小的掩模矩阵reset();  // 重置GCApplication
}// 展示图像
void GCApplication::showImage() const
{if( image->empty() || winName->empty() )return;  // 如果图像为空或窗口名为空,直接返回Mat res;Mat binMask;image->copyTo( res );  // 复制图像到resif( isInitialized ){getBinMask( mask, binMask);  // 如果已经初始化,获取二值掩模Mat black (binMask.rows, binMask.cols, CV_8UC3, cv::Scalar(0,0,0));black.setTo(Scalar::all(255), binMask);  // 设置黑色图像的掩模区域为白色addWeighted(black, 0.5, res, 0.5, 0.0, res);  // 将黑色图像与原图混合}vector<Point>::const_iterator it;// 绘制背景和前景像素位置for( it = bgdPxls.begin(); it != bgdPxls.end(); ++it )circle( res, *it, radius, BLUE, thickness );for( it = fgdPxls.begin(); it != fgdPxls.end(); ++it )circle( res, *it, radius, RED, thickness );for( it = prBgdPxls.begin(); it != prBgdPxls.end(); ++it )circle( res, *it, radius, LIGHTBLUE, thickness );for( it = prFgdPxls.begin(); it != prFgdPxls.end(); ++it )circle( res, *it, radius, PINK, thickness );// 绘制矩形框if( rectState == IN_PROCESS || rectState == SET )rectangle( res, Point( rect.x, rect.y ), Point(rect.x + rect.width, rect.y + rect.height ), GREEN, 2);imshow( *winName, res );  // 显示图像
}   // GCApplication类的成员函数:设置矩形框到掩模中
void GCApplication::setRectInMask()
{CV_Assert( !mask.empty() ); // 确认掩模不为空mask.setTo( GC_BGD ); // 将掩模的全部像素值设置为背景// 修正矩形坐标,确保矩形框不会超出图像范围rect.x = max(0, rect.x);rect.y = max(0, rect.y);rect.width = min(rect.width, image->cols-rect.x);rect.height = min(rect.height, image->rows-rect.y);// 将矩形框内的像素值设置为可能的前景(mask(rect)).setTo( Scalar(GC_PR_FGD) );
}// GCApplication类的成员函数:根据用户输入,在掩模上设置前景或背景标签
void GCApplication::setLblsInMask( int flags, Point p, bool isPr )
{vector<Point> *bpxls, *fpxls; // 指向背景和前景像素点的向量uchar bvalue, fvalue; // 背景和前景的像素值// 根据是否是概率图(isPr)设置不同的指针和像素值if( !isPr ){bpxls = &bgdPxls;fpxls = &fgdPxls;bvalue = GC_BGD;fvalue = GC_FGD;}else{bpxls = &prBgdPxls;fpxls = &prFgdPxls;bvalue = GC_PR_BGD;fvalue = GC_PR_FGD;}// flags中对应的按键标志位被设置,更新背景像素点向量,并在掩模上画圆if( flags & BGD_KEY ){bpxls->push_back(p);circle( mask, p, radius, bvalue, thickness );}// flags中对应的按键标志位被设置,更新前景像素点向量,并在掩模上画圆if( flags & FGD_KEY ){fpxls->push_back(p);circle( mask, p, radius, fvalue, thickness );}
}// GCApplication类的成员函数:处理鼠标点击事件
void GCApplication::mouseClick( int event, int x, int y, int flags, void* )
{switch( event ){// 左键按下事件:设置矩形或者GC_BGD/GC_FGD像素标签case EVENT_LBUTTONDOWN: {bool isb = (flags & BGD_KEY) != 0,isf = (flags & FGD_KEY) != 0;if( rectState == NOT_SET && !isb && !isf ){rectState = IN_PROCESS; // 开始绘制矩形框rect = Rect( x, y, 1, 1 ); // 初始化矩形框大小}if ( (isb || isf) && rectState == SET )lblsState = IN_PROCESS; // 开始设置标签状态}break;// 右键按下事件:设置GC_PR_BGD/GC_PR_FGD像素标签case EVENT_RBUTTONDOWN: {bool isb = (flags & BGD_KEY) != 0,isf = (flags & FGD_KEY) != 0;if ( (isb || isf) && rectState == SET )prLblsState = IN_PROCESS; // 开始设置概率标签状态}break;// 左键释放事件:完成矩形绘制或者设置像素标签case EVENT_LBUTTONUP:if( rectState == IN_PROCESS ){// 如果起点和终点一样,则不设置矩形框if(rect.x == x || rect.y == y){rectState = NOT_SET;}else{rect = Rect( Point(rect.x, rect.y), Point(x,y) );rectState = SET; // 设置矩形框状态为已设置setRectInMask(); // 在掩模上绘制矩形框CV_Assert( bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty() );}showImage(); // 显示图像}if( lblsState == IN_PROCESS ){setLblsInMask(flags, Point(x,y), false); // 设置像素标签lblsState = SET; // 设置标签状态为已设置nextIter(); // 进行一次迭代showImage(); // 显示图像}else{// 如果矩形框状态已设置,进行一次迭代后显示图像if(rectState == SET){nextIter();showImage();}}break;// 右键释放事件:完成概率标签的设置case EVENT_RBUTTONUP:if( prLblsState == IN_PROCESS ){setLblsInMask(flags, Point(x,y), true); // 设置概率标签prLblsState = SET; // 设置概率标签状态为已设置}// 如果矩形框状态已设置,进行一次迭代后显示图像if(rectState == SET){nextIter();showImage();}break;// 鼠标移动事件:更新矩形框大小或者继续设置像素标签case EVENT_MOUSEMOVE:if( rectState == IN_PROCESS ){rect = Rect( Point(rect.x, rect.y), Point(x,y) );CV_Assert( bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty() );showImage(); // 显示图像}else if( lblsState == IN_PROCESS ){setLblsInMask(flags, Point(x,y), false); // 设置像素标签showImage(); // 显示图像}else if( prLblsState == IN_PROCESS ){setLblsInMask(flags, Point(x,y), true); // 设置概率标签showImage(); // 显示图像}break;}
}// 主函数入口
int main( int argc, char** argv )
{// 解析命令行参数cv::CommandLineParser parser(argc, argv, "{@input| messi5.jpg |}");// 调用help函数显示帮助信息help(argv);// 获取图片文件名string filename = parser.get<string>("@input");// 如果文件名为空,输出错误信息并返回1if( filename.empty() ){cout << "\nDurn, empty filename" << endl;return 1;}// 读取图片Mat image = imread(samples::findFile(filename), IMREAD_COLOR);// 如果读取失败,输出错误信息并返回1if( image.empty() ){cout << "\n Durn, couldn't read image filename " << filename << endl;return 1;}// 创建窗口const string winName = "image";namedWindow( winName, WINDOW_AUTOSIZE );// 设置鼠标回调函数setMouseCallback( winName, on_mouse, 0 );// 设置gcapp对象的图片和窗口名称gcapp.setImageAndWinName( image, winName );// 显示图片gcapp.showImage();// 无限循环,等待用户按键for (;;){// 等待按键事件char c = (char)waitKey(0);switch (c){case '\x1b': // 按Esc键退出cout << "Exiting ..." << endl;goto exit_main;case 'r': // 按'r'键重置cout << endl;gcapp.reset(); // 调用重置函数gcapp.showImage(); // 显示图像break;case 'n': // 按'n'键进行下一次迭代int iterCount = gcapp.getIterCount(); // 获取当前迭代次数cout << "<" << iterCount << "... ";int newIterCount = gcapp.nextIter(); // 调用下一个迭代if (newIterCount > iterCount){gcapp.showImage(); // 显示新的图像cout << iterCount << ">" << endl;}elsecout << "rect must be determined>" << endl; // 提示需要先确定矩形框break;}}exit_main:// 销毁窗口并退出destroyWindow( winName );return 0;
}

4396662620bffcce3c85a06b8f6b8519.png

vector<Point>::const_iterator it;// 绘制背景和前景像素位置for (it = bgdPxls.begin(); it != bgdPxls.end(); ++it)circle(res, *it, radius, BLUE, thickness);for (it = fgdPxls.begin(); it != fgdPxls.end(); ++it)circle(res, *it, radius, RED, thickness);for (it = prBgdPxls.begin(); it != prBgdPxls.end(); ++it)circle(res, *it, radius, LIGHTBLUE, thickness);for (it = prFgdPxls.begin(); it != prFgdPxls.end(); ++it)circle(res, *it, radius, PINK, thickness);

f7e7b43a3df3c230bdc740bf725eeb2f.png

de922bc6f830506138ace52857ca324d.png

grabCut(*image, mask, rect, bgdModel, fgdModel, 1);

70dc097b43f56b2b7747b1df365b07ad.png

// 如果标签已经设置或概率标签已经设置,用mask初始化grabCutif (lblsState == SET || prLblsState == SET)grabCut(*image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_MASK);else// 否则,用rect初始化grabCutgrabCut(*image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_RECT);

a69d4dac7b64bbaebfd262eea01fcebe.png

相关文章:

【opencv】示例-grabcut.cpp 使用OpenCV库的GrabCut算法进行图像分割

left mouse button - set rectangle SHIFTleft mouse button - set GC_FGD pixels CTRLleft mouse button - set GC_BGD pixels 这段代码是一个使用OpenCV库的GrabCut算法进行图像分割的C程序。它允许用户通过交互式方式选择图像中的一个区域&#xff0c;并利用GrabCut算法尝试…...

GEE数据集——巴基斯坦国家级土壤侵蚀数据集(2005 年和 2015 年)

简介 巴基斯坦国家级土壤侵蚀数据集&#xff08;2005 年和 2015 年&#xff09; 该数据集采用修订的通用土壤流失方程 (RUSLE)&#xff0c;并考虑了六个关键影响因素&#xff1a;降雨侵蚀率 (R)、土壤可侵蚀性 (K)、坡长 (L)、坡陡 (S)、覆盖管理 (C) 和保护措施 (P)&#xff…...

服务器代理

服务器代理 配置&#xff1a;64G内存1 3090&#xff08;24g&#xff09;1P4000&#xff08;8g&#xff09; SSH连接 工作路径&#xff1a;/home/ubuntu/workspace/python Anaconda路径&#xff1a;/home/Ubuntu 1.在工作路径下创建自己的文件夹作为workspace 2.以用户ubunbtu登…...

【SGDR】《SGDR:Stochastic Gradient Descent with Warm Restarts》

arXiv-2016 code: https://github.com/loshchil/SGDR/blob/master/SGDR_WRNs.py 文章目录 1 Background and Motivation2 Related Work3 Advantages / Contributions4 Method5 Experiments5.1 Datasets and Metric5.2 Single-Model Results5.3 Ensemble Results5.4 Experiment…...

如何将arping以及所有依赖打包安装到另外一台离线ubuntu机器

ubuntu系统下可以使用arping命令检测局域网内一些ip是否冲突&#xff0c;使用方式为&#xff1a; arping xx.xx.xx.xx 在线情况下&#xff0c;可以使用下面命令下载arping&#xff0c;然后使用即可 apt install arping 但是有些情况下机器可能不能上网&#xff0c;这时就需要将…...

mac上如何安装python3

mac上如何安装python3&#xff1f; 安装homebrew 在终端执行命令 /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 执行完成后&#xff0c;homebrew和pip等工具就自动安装好了。 接下来安装python3.在终端…...

Java 那些诗一般的 数据类型 (下篇)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人能接…...

WEB3.0:互联网的下一阶段

随着互联网的发展&#xff0c;WEB3.0时代正在逐步到来。本文将深入探讨WEB3.0的定义、特点、技术应用以及未来展望&#xff0c;为读者带来全新的思考。 一、什么是WEB3.0&#xff1f; WEB3.0可以被理解为互联网发展的下一阶段&#xff0c;是当前WEB2.0的升级版。相较于2.0时代…...

Fastgpt配合chatglm+m3e或ollama+m3e搭建个人知识库

概述&#xff1a; 人工智能大语言模型是近年来人工智能领域的一项重要技术&#xff0c;它的出现标志着自然语言处理领域的重大突破。这些模型利用深度学习和大规模数据训练&#xff0c;能够理解和生成人类语言&#xff0c;为各种应用场景提供了强大的文本处理能力。AI大语言模…...

如何使用选择器精确地控制网页中每一个元素的样式?

1. 基础知识 什么是 CSS 元素选择器 CSS 元素选择器是一种在网页中通过元素类型来应用样式的方法。 简单来说&#xff0c;它就像是一个指挥棒&#xff0c;告诉浏览器哪些 HTML 元素需要应用我们定义的 CSS 样式规则。 为何要使用 CSS 元素选择器 使用元素选择器可以让我们…...

各个微前端框架的优劣浅谈

各个微前端框架都有其独特的优势和劣势&#xff0c;下面我将针对几个主流的微前端框架进行简要的优劣分析&#xff1a; single-spa 优势&#xff1a; 轻量级&#xff1a;single-spa是一个非常轻量级的微前端框架&#xff0c;它主要提供了一个加载和管理微应用的机制&#xff0c…...

自动化运维(二十二)Ansible实战 之Jenkins模块

Ansible提供了一些模块,可以用来与Jenkins进行交互,执行各种操作,如创建任务、触发构建、获取构建结果等。通过使用这些模块,我们可以将Jenkins的配置和管理集成到Ansible的自动化流程中。 以下是一些常用的Ansible Jenkins模块: 1、jenkins_job模块 jenkins_job模块用于创建…...

Python数据分析与应用 |第4章 使用pandas进行数据预处理 (实训)

表1-1healthcare-dataset-stroke.xlsx 部分中风患者的基础信息和体检数据 编号性别高血压是否结婚工作类型居住类型体重指数吸烟史中风9046男否是私人城市36.6以前吸烟是51676女否是私营企业农村N/A从不吸烟是31112男否是私人农村32.5从不吸烟...

基于双向长短期神经网络BILSTM的线损率预测,基于gru的线损率预测

目录 背影 摘要 LSTM的基本定义 LSTM实现的步骤 BILSTM神经网络 基于双向长短期神经网络BILSTM的线损率预测,基于gru的线损率预测 完整代码:基于双向长短期神经网络BILSTM的线损率预测,基于gru的线损率预测(代码完整,数据齐全)资源-CSDN文库 https://download.csdn.net/d…...

智能售货机:引领便捷生活

智能售货机&#xff1a;引领便捷生活 在这个科技迅速进步的时代&#xff0c;便捷已成为生活的必需。智能售货机作为技术与便利完美结合的产物&#xff0c;正逐渐改变我们的购物方式&#xff0c;为都市生活增添新的活力。 智能售货机的主要优势是它的极致便利性。不论是在地铁…...

正向代理和反向代理

正向代理和反向代理是网络中常见的两种代理方式&#xff0c;它们在网络通信中扮演着不同的角色。 正向代理&#xff1a; 正向代理是代理服务器位于客户端和目标服务器之间的一种代理方式。 客户端向代理服务器发送请求&#xff0c;然后代理服务器将请求转发给目标服务器&…...

kimichat使用技巧:用语音对话聊天

kimichat之前是只能用文字聊天的&#xff0c;不过最近推出了语音新功能&#xff0c;也可以用语音畅快的对话聊天了。 这个功能目前支持手机app版本&#xff0c;所以首先要在手机上下载安装kimi智能助手。已经安装的&#xff0c;要点击检查更新&#xff0c;更新到最新的版本。 …...

机器学习-09-图像处理02-PIL+numpy+OpenCV实践

总结 本系列是机器学习课程的系列课程&#xff0c;主要介绍机器学习中图像处理技术。 参考 【人工智能】PythonOpenCV图像处理&#xff08;一篇全&#xff09; 一文讲解方向梯度直方图&#xff08;hog&#xff09; 【杂谈】计算机视觉在人脸图像领域的十几个大的应用方向&…...

应急响应-战前反制主机HIDSElkeid蜜罐系统HFish

知识点 战前-反制-平台部署其他更多项目&#xff1a; https://github.com/birdhan/SecurityProduct HIDS&#xff1a;主机入侵检测系统&#xff0c;通常会有一个服务器承担服务端角色&#xff0c;其他主机就是客户端角色&#xff0c;客户端加入到服务端的检测范围里&#xff…...

C#:24小时制和12小时制之间的转换

任务描述 本关任务&#xff1a;编写一个程序&#xff0c;利用求余运算完成24小时制和12小时制之间的转换。 注意&#xff1a;要求输入的数字是0到24之间的整数。 测试说明 平台会对你编写的代码进行测试&#xff1a; 测试输入&#xff1a;4 预期输出&#xff1a; 现在是上午4…...

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...

Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storms…...

消防一体化安全管控平台:构建消防“一张图”和APP统一管理

在城市的某个角落&#xff0c;一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延&#xff0c;滚滚浓烟弥漫开来&#xff0c;周围群众的生命财产安全受到严重威胁。就在这千钧一发之际&#xff0c;消防救援队伍迅速行动&#xff0c;而豪越科技消防一体化安全管控平台构建的消防“…...