图像万物分割——Segment Anything算法解析与模型推理
一、概述
在视觉任务中,图像分割任务是一个很广泛的领域,应用于交互式分割,边缘检测,超像素化,感兴趣目标生成,前景分割,语义分割,实例分割,泛视分割等。
交互式分割,这种分割任务,它允许用户手动细化掩码来分割任意类型的对象。然而,这种方法需要用户的不断参与和指导,类似于ps里面的抠图快速选择工具。

实例分割任务是它能够自动分割特定类别的对象,例如行人,狗,电视或椅子,但需要大量的手动标注数据,标注样本要以上万个样本,然后要经过大量的计算资源和代码算法知识来训练模型。这种方式应用最广泛应该是人像自动抠图:

为了 解决这些分割任务的局限性,Meta 推出了「分割一切」AI 算法Segment Anything,为分割任务提供一种通用的、全自动的分割解决方案。
二、Segment Anything 万物分割
1.算法摘要
作者介绍了Segment Anything (SA) 项目,这是一个旨在进行图像分割的新任务,同时提供了相应的模型和数据集。在该项目中,作者采用了一种高效的模型来进行数据收集,以构建迄今为止最大的分割数据集。他们在超过1,100万张公开的图像上进行了标注,生成了超过10亿个掩码。
这个模型(SAM)训练完成之后,以使其具备"promptable"(可提示)的性质,因此意味着它可以零样本(zero-shot)地适应新的数据集和任务,而无需先对数据进行标注和训练。作者对该模型进行了广泛的评估,发现它在许多任务上的零样本表现通常与完全监督的性能相媲美,甚至更好。作者公开了他们的模型(SAM),还发布了相应的图像数据集(SA-1B)。

2. 算法介绍
LLM的出现,让研究人员感受到,使用互联网规模的数据集上预训练的大型语言模型已经改变了自然语言处理(NLP)领域,因为它们表现出强大的零样本和少样本泛化能力,可以应对未在训练中出现的任务和数据分布。这种泛化通常通过提示工程(prompt engineering)来实现,其中手工制作的文本提示可以引导语言模型生成有效的文本响应。这些基础模型在使用丰富的互联网文本语料库进行预训练时,表现出令人惊讶的零样本和少样本性能,有时甚至可以与经过精细调整(fine-tune)的模型相媲美。研究经验表明,这种零样本和少样本性能会随着模型规模、数据集大小和总训练计算量的增加而改善。
在计算机视觉领域也在探索基础模型的应用,例如,CLIP和ALIGN使用对比学习来训练文本和图像编码器,经过训练后,这些编码器可以用于零样本泛化到新的视觉概念和数据分布。这些编码器还可以有效地与其他模块结合,用于解决下游任务,比如图像生成。然而,计算机视觉领域涉及的问题远不止这些,而且许多问题缺乏丰富的训练数据。
在这项研究工作中,SAM作者的目标是建立一个图像分割的基础模型,也就是一个可提示的模型,它可以在广泛的数据集上进行预训练以实现强大的泛化能力。一旦有了这个模型,作者进一步探索如何通过快速流程来解决各种新的数据分布上的下游分割问题。
这个计划的成功取决于三个关键要素:任务、模型和数据。作者需要解决以下关于图像分割的问题:
-
什么样的视觉分割任务可以实现零样本泛化?
-
为了实现这一个分割任务,对应的模型架构应该是什么样的?
-
哪些数据可以支持这个任务和模型的预训练?
作者首先定义了一个可提示的分割任务,这个任务足够通用,可以作为强大的预训练目标,同时也可以支持广泛的下游应用。这个任务要求一个支持多种提示的模型,并能够实时生成分割掩码,以支持交互式使用。然而,互联网上目前尚没有足够大规模的分割数据集来满足这个任务的需求。作者提出了“数据引擎”来应对这个问题,即通过模型辅助数据收集和不断迭代来改进数据,以填补数据的不足。这个方法可以在模型训练和数据收集之间进行交互,以实现更好的性能。
- 分割任务
在自然语言处理和计算机视觉领域,基础模型具有很大的前景,因为它们可以用于执行零样本学习和少样本学习,通过利用提示来适应新的数据集和任务。受到这种思路的启发,本文提出了一个称为"可提示分割任务"的新领域,其主要目标是在给定分割提示的情况下生成有效的分割掩码(如图1a所示)。
这些分割提示可以简单地指定图像中要分割的对象,例如,提示可以包括对象的位置信息或文本描述。
这里的"有效输出掩码"意味着,即使提示信息模糊不清,可能指向多个不同对象(例如,在图像上一个点可能表示衬衫或穿衬衫的人),生成的分割掩码也应该合理,至少应该包括这些对象中的一个。
在这项研究中,作者将可提示分割任务作为预训练目标,然后使用提示工程方法来解决各种不同的下游分割任务。这种方法有望为计算机视觉领域带来一种强大的学习范式,可以在面对新任务时从有限的提示信息中进行学习,而不需要大量的标记数据。这对于处理多样化和复杂的视觉任务可能具有很大的潜力。
- 模型选择
可提示分割任务对模型的架构提出了一些严格的要求,这包括对提示的支持灵活性、实时计算的需求,以便允许交互使用,以及能够处理歧义。作者提出了一个简单的模型设计,可以满足这些要求,被称为"Segment Anything"模型,简称SAM(见图1b)。SAM的架构包括以下组成部分: - 图像编码器:这是一个强大的模型,负责将输入图像转化为图像嵌入(image embedding),以捕捉图像的特征信息。
- 提示编码器:这是一个用于嵌入提示信息的模型,它将提示信息转化为提示嵌入,以使模型能够理解提示中的内容。
- 控码解码器:这是一个轻量级的模型,负责将图像嵌入和提示嵌入结合,然后预测分割掩码。这一部分的设计使得SAM可以实现对相同图像嵌入的不同提示信息的分配,从而使模型能够处理多样性的提示。
SAM的设计还允许它在不超过50毫秒的时间内从提示符中预测掩码,实现了实时性能,这对于实际应用和交互式任务非常重要。
作者的主要关注点包括边界框、关键点和分割掩码提示。为了解决歧义问题,SAM被设计成能够预测多个掩码,即使给定相同的提示。这使得SAM可以自然地处理提示中的歧义,比如前文提到的衬衫和穿衬衫的人之间的歧义示例。这个能力对于处理复杂的图像场景和多义性提示非常有帮助。
- 数据引擎
为了使SAM能够在新的数据分布上实现强大的泛化能力,需要在一个大型数据集上进行训练,该数据集应该覆盖各种不同的分割任务和场景。然而,典型的训练方法通常依赖于在线获取数据,而掩码标注信息通常相对稀缺,因此需要采用替代策略。作者提出的解决方案是构建一个称为"数据引擎"的系统,这个引擎包括三个主要阶段:辅助手动、半自动和全自动。
-
辅助手动阶段:在这个阶段,SAM与人工注释人员协作,类似于传统的交互式分割设置。人工注释人员手动为图像中的对象生成掩码,同时SAM提供辅助信息,例如提示信息,以帮助人工注释人员完成掩码的生成。这一阶段有助于收集一些基本的分割标注。
-
半自动阶段:在这个阶段,SAM能够自动为图像中的对象的某些子区域生成掩码。它会根据已有的掩码和提示信息,自动预测可能的对象位置,并生成相应的掩码。这减轻了人工注释人员的工作负担,因为他们可以专注于注释剩余的对象,从而提高了标注的多样性。
-
全自动阶段:在最后一个阶段,作者采用一种规则网格提示SAM,用于生成大量高质量掩码。这个提示方法能够为每张图像平均产生约100个掩码,以增加数据的多样性和覆盖不同的情况。
通过这种数据引擎的阶段性设计,作者能够有效地利用协作注释和自动化方法,以构建一个大规模的数据集,为SAM的训练提供了足够丰富和多样的标注数据,从而使其在新的数据分布上实现强大的泛化能力。这种方法有助于克服标注数据稀缺性的问题,尤其是对于复杂的分割任务。
3.数据集
作者最终的数据集SA-1B,包括来自1100万张经许可和隐私保护图像的超过10亿个掩码(见图2)。SA-1B使用作者的数据引擎的最后阶段完全自动收集,比现有的最大分割数据集拥有400多倍的掩码,并且作者广泛验证,掩码具有高质量和多样性。作者希望SA-1B能够成为一种有价值的资源,用于建立新的基础模型。
4.实验
作者广泛地评估SAM。首先,在23个分割数据集上的测试,作者发现SAM从单个前景点生成了高质量的掩码,通常仅略低于手动注释的真实值。其次,作者在使用提示工程的零样本传输协议(zero-shot transfer protocol)下的各种下游任务上发现了持续强大的定量和定性结果,包括边缘检测、感兴趣目标生成、实例分割和文本到掩码预测。这些结果表明,SAM可以在即时工程中开箱即用,解决涉及SAM训练数据之外的图像分布的各种任务。
三、模型C++推理
1.实现代码
#include "include/segment_anything.h"
namespace sam{
SegmentAnything::~SegmentAnything()
{image_encoder_net_.clear();mask_decoder_net_.clear();
}static inline float intersection_area(const sam_result_t& a, const sam_result_t& b)
{cv::Rect_<float> inter = a.box & b.box;return inter.area();
}static void qsort_descent_inplace(std::vector<sam_result_t>& faceobjects, int left, int right)
{int i = left;int j = right;float p = faceobjects[(left + right) / 2].iou_pred;while (i <= j){while (faceobjects[i].iou_pred > p)i++;while (faceobjects[j].iou_pred < p)j--;if (i <= j){// swapstd::swap(faceobjects[i], faceobjects[j]);i++;j--;}}#pragma omp parallel sections{#pragma omp section{if (left < j) qsort_descent_inplace(faceobjects, left, j);}#pragma omp section{if (i < right) qsort_descent_inplace(faceobjects, i, right);}}
}static void qsort_descent_inplace(std::vector<sam_result_t>& faceobjects)
{if (faceobjects.empty())return;qsort_descent_inplace(faceobjects, 0, faceobjects.size() - 1);
}static void nms_sorted_bboxes(const cv::Mat& bgr,const std::vector<sam_result_t>& faceobjects, std::vector<int>& picked, float nms_threshold)
{picked.clear();const int n = faceobjects.size();std::vector<float> areas(n);for (int i = 0; i < n; i++){areas[i] = faceobjects[i].box.area();}cv::Mat img = bgr.clone();for (int i = 0; i < n; i++){const sam_result_t& a = faceobjects[i];int keep = 1;for (int j = 0; j < (int)picked.size(); j++){const sam_result_t& b = faceobjects[picked[j]];// intersection over unionfloat inter_area = intersection_area(a, b);float union_area = areas[i] + areas[picked[j]] - inter_area;// float IoU = inter_area / union_areaif (inter_area / union_area > nms_threshold){keep = 0;}}if (keep)picked.push_back(i);}
}
int SegmentAnything::NMS(const cv::Mat& bgr, std::vector<sam_result_t>& proposals, std::vector<int>& picked, float nms_threshold)
{qsort_descent_inplace(proposals);nms_sorted_bboxes(bgr, proposals, picked, nms_threshold);return 0;
}int SegmentAnything::Load(const std::string& image_encoder_param, const std::string& image_encoder_bin, const std::string& mask_decoder_param, const std::string& mask_decoder_bin)
{int ret = 0;ret = image_encoder_net_.load_param(image_encoder_param.c_str());if (ret < 0)return -1;ret = image_encoder_net_.load_model(image_encoder_bin.c_str());if (ret < 0)return -1;ret = mask_decoder_net_.load_param(mask_decoder_param.c_str());if (ret < 0)return -1;ret = mask_decoder_net_.load_model(mask_decoder_bin.c_str());if (ret < 0)return -1;return 0;
}
int SegmentAnything::ImageEncoder(const cv::Mat& bgr, ncnn::Mat& image_embeddings, image_info_t& image_info)
{const int target_size = 1024;int img_w = bgr.cols;int img_h = bgr.rows;int w = img_w;int h = img_h;float scale = 1.f;if (w > h){scale = (float)target_size / w;w = target_size;h = h * scale;}else{scale = (float)target_size / h;h = target_size;w = w * scale;}ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, ncnn::Mat::PIXEL_BGR2RGB, img_w, img_h, w, h);int wpad = target_size - w;int hpad = target_size - h;ncnn::Mat in_pad;ncnn::copy_make_border(in, in_pad, 0, hpad, 0, wpad, ncnn::BORDER_CONSTANT, 0.f);in_pad.substract_mean_normalize(means_, norms_);ncnn::Extractor image_encoder_ex = image_encoder_net_.create_extractor();image_encoder_ex.input("image", in_pad);image_encoder_ex.extract("image_embeddings", image_embeddings);image_info.img_h = img_h;image_info.img_w = img_w;image_info.pad_h = h;image_info.pad_w = w;image_info.scale = scale;return 0;
}int SegmentAnything::embed_masks(const prompt_info_t& prompt_info, ncnn::Mat& mask_input, ncnn::Mat& has_mask)
{mask_input = ncnn::Mat(256, 256, 1);mask_input.fill(0.f);has_mask = ncnn::Mat(1);has_mask.fill(0.f);return 0;
}
int SegmentAnything::transform_coords(const image_info_t& image_info, ncnn::Mat& point_coords)
{for(int h = 0; h < point_coords.h; ++h){float* ptr = point_coords.row(h);ptr[0] *= image_info.scale;ptr[1] *= image_info.scale;}return 0;
}
int SegmentAnything::embed_points(const prompt_info_t& prompt_info, std::vector<ncnn::Mat>& point_labels, ncnn::Mat& point_coords)
{int num_points = prompt_info.points.size() / 2;point_coords = ncnn::Mat(num_points * 2, (void*)prompt_info.points.data()).reshape(2, num_points).clone();ncnn::Mat point_labels1 = ncnn::Mat(256, num_points);ncnn::Mat point_labels2 = ncnn::Mat(256, num_points);ncnn::Mat point_labels3 = ncnn::Mat(256, num_points);ncnn::Mat point_labels4 = ncnn::Mat(256, num_points);ncnn::Mat point_labels5 = ncnn::Mat(256, num_points);ncnn::Mat point_labels6 = ncnn::Mat(256, num_points);point_labels1.row_range(0, num_points - 1).fill(1.f);point_labels1.row_range(num_points - 1, 1).fill(0.f);for (int i = 0; i < num_points - 1; ++i) {if (prompt_info.labels[i] == -1)point_labels2.row_range(i, 1).fill(1.f);elsepoint_labels2.row_range(i, 1).fill(0.f);}point_labels2.row_range(num_points - 1, 1).fill(1.f);for (int i = 0; i < num_points - 1; ++i) {if (prompt_info.labels[i] == 0)point_labels3.row_range(i, 1).fill(1.f);elsepoint_labels3.row_range(i, 1).fill(0.f);}point_labels3.row_range(num_points - 1, 1).fill(0.f);for (int i = 0; i < num_points - 1; ++i) {if (prompt_info.labels[i] == 1)point_labels4.row_range(i, 1).fill(1.f);elsepoint_labels4.row_range(i, 1).fill(0.f);}point_labels4.row_range(num_points - 1, 1).fill(0.f);for (int i = 0; i < num_points - 1; ++i) {if (prompt_info.labels[i] == 2)point_labels5.row_range(i, 1).fill(1.f);elsepoint_labels5.row_range(i, 1).fill(0.f);}point_labels5.row_range(num_points - 1, 1).fill(0.f);for (int i = 0; i < num_points - 1; ++i) {if (prompt_info.labels[i] == 3)point_labels6.row_range(i, 1).fill(1.f);elsepoint_labels6.row_range(i, 1).fill(0.f);}point_labels6.row_range(num_points - 1, 1).fill(0.f);point_labels.push_back(point_labels1);point_labels.push_back(point_labels2);point_labels.push_back(point_labels3);point_labels.push_back(point_labels4);point_labels.push_back(point_labels5);point_labels.push_back(point_labels6);return 0;
}
int SegmentAnything::MaskDecoder(const ncnn::Mat& image_embeddings, image_info_t& image_info, const prompt_info_t& prompt_info, std::vector<sam_result_t>& sam_results, float pred_iou_thresh, float stability_score_thresh)
{std::vector<ncnn::Mat> point_labels;ncnn::Mat point_coords;embed_points(prompt_info, point_labels, point_coords);transform_coords(image_info, point_coords);ncnn::Mat mask_input, has_mask;embed_masks(prompt_info, mask_input, has_mask);ncnn::Extractor mask_decoder_ex = mask_decoder_net_.create_extractor();mask_decoder_ex.input("mask_input", mask_input);mask_decoder_ex.input("point_coords", point_coords);mask_decoder_ex.input("point_labels1", point_labels[0]);mask_decoder_ex.input("point_labels2", point_labels[1]);mask_decoder_ex.input("point_labels3", point_labels[2]);mask_decoder_ex.input("point_labels4", point_labels[3]);mask_decoder_ex.input("point_labels5", point_labels[4]);mask_decoder_ex.input("point_labels6", point_labels[5]);mask_decoder_ex.input("image_embeddings", image_embeddings);mask_decoder_ex.input("has_mask_input", has_mask);ncnn::Mat scores;mask_decoder_ex.extract("scores", scores);ncnn::Mat masks;mask_decoder_ex.extract("masks", masks);//postprocessstd::vector<std::pair<float, int>> scores_vec;for (int i = 1; i < scores.w; ++i) {scores_vec.push_back(std::pair<float, int>(scores[i], i));}std::sort(scores_vec.begin(), scores_vec.end(), std::greater<std::pair<float, int>>());if (scores_vec[0].first > pred_iou_thresh) {sam_result_t sam_result;ncnn::Mat mask = masks.channel(scores_vec[0].second);cv::Mat cv_mask_32f = cv::Mat::zeros(cv::Size(mask.w, mask.h), CV_32F);std::copy((float*)mask.data, (float*)mask.data + mask.w * mask.h, (float*)cv_mask_32f.data);cv::Mat single_mask_32f;cv::resize(cv_mask_32f(cv::Rect(0, 0, image_info.pad_w, image_info.pad_h)), single_mask_32f, cv::Size(image_info.img_w,image_info.img_h), 0, 0, 1);float stable_score = calculate_stability_score(single_mask_32f);if (stable_score < stability_score_thresh)return -1;single_mask_32f = single_mask_32f > 0;single_mask_32f.convertTo(sam_result.mask, CV_8UC1, 1, 0);if (postprocess_mask(sam_result.mask, sam_result.box) < 0)return -1;sam_results.push_back(sam_result);}else {return -1;}return 0;
}
int SegmentAnything::postprocess_mask(cv::Mat& mask, cv::Rect& box)
{std::vector<std::vector<cv::Point>> contours;std::vector<cv::Vec4i> hierarchy;cv::findContours(mask.clone(), contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);if(contours.size() == 0)return -1;if (contours.size() > 1) {float max_area = 0;int max_idx = 0;std::vector<std::pair<float,int>> areas;for (size_t i = 0; i < contours.size(); ++i) {float area = cv::contourArea(contours[i]);if (area > max_area) {max_idx = i;max_area = area;}areas.push_back(std::pair<float,int>(area,i));}for (size_t i = 0; i < areas.size(); ++i) {//if (i == max_idx)// continue;//else {// cv::drawContours(mask, contours, i, cv::Scalar(0), -1);//}if(areas[i].first < max_area * 0.3){cv::drawContours(mask, contours, i, cv::Scalar(0), -1);}else{box = box | cv::boundingRect(contours[i]);}}}else {box = cv::boundingRect(contours[0]);}return 0;
}
float SegmentAnything::calculate_stability_score(cv::Mat& mask, float mask_threshold, float stable_score_offset)
{float intersections = (float)cv::countNonZero(mask > (mask_threshold + stable_score_offset));float unions = (float)cv::countNonZero(mask > (mask_threshold - stable_score_offset));return intersections / unions;
}
}
2. 交互方法
分割交互方式中有好四种,开放式点,可以多个点组合, 矩形框, 分割一切,还有文字提示这几种方式。但文字提示效果不太稳定,C++代码没有实现这一部分。
开放式点
点击要分割目标的中间,分割包含该点的物体,会按最小分割的结果展示出来,如果想分割的物体大于展示的结果,可以在物体的其他部分也点击下:

选择矩形框
使用鼠标拖动在目标选择,分割目标:

分割一切
将图片中所有物体的分割都展示出来:

相关文章:
图像万物分割——Segment Anything算法解析与模型推理
一、概述 在视觉任务中,图像分割任务是一个很广泛的领域,应用于交互式分割,边缘检测,超像素化,感兴趣目标生成,前景分割,语义分割,实例分割,泛视分割等。 交互式分割&am…...
Redis实战篇笔记(最终篇)
Redis实战篇笔记(七) 文章目录 Redis实战篇笔记(七)前言达人探店发布和查看探店笔记点赞点赞排行榜 好友关注关注和取关共同关注关注推送关注推荐的实现 总结 前言 本系列文章是Redis实战篇笔记的最后一篇,那么到这里…...
游戏配置表的导入使用
游戏配置表是游戏策划的标配,如下图: 那么程序怎么把这张配置表导入使用? 1.首先,利用命令行把Excel格式的文件转化成Json格式: json-excel\json-excel json Tables\ Data\copy Data\CharacterDefine.txt ..\Clien…...
❀dialog命令运用于linux❀
目录 ❀dialog命令运用于linux❀ msgbox部件(消息框) yesno部件(yesno框) inputbox部件(输入文本框) textbox部件(文本框) menu部件(菜单框) fselect部…...
【算法】蓝桥杯2013国C 横向打印二叉树 题解
文章目录 题目链接题目描述输入格式输出格式样例自己的样例输入自己的样例输出 思路整体思路存储二叉搜索树中序遍历并存储计算目标数的行号dfs遍历并写入数组初始化和处理输入输出初始化处理输入处理输出 完整的代码如下 结束语更新初始化的修改存储二叉搜索树的修改中序遍历和…...
XunSearch 讯搜 error: storage size of ‘methods_bufferevent’ isn’t known
报错: error: storage size of ‘methods_bufferevent’ isn’t known CentOS8.0安装迅搜(XunSearch)引擎报错的解决办法 比较完整的文档 http://www.xunsearch.com/download/xs_quickstart.pdf 官方安装文档 http://www.xunsearch.com/doc/php/guide/start.in…...
基于AWS Serverless的Glue服务进行ETL(提取、转换和加载)数据分析(三)——serverless数据分析
3 serverless数据分析 大纲 3 serverless数据分析3.1 创建Lambda3.2 创建API Gateway3.3 结果3.4 总结 3.1 创建Lambda 在Lambda中,我们将使用python3作为代码语言。 步骤图例1、入口2、创建(我们选择使用python3.7)3、IAM权限(…...
08、分析测试执行时间及获取pytest帮助
官方用例 # content of test_slow_func.py import pytest from time import sleeppytest.mark.parametrize(delay,(1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,1.0,0.1,0.2,0,3)) def test_slow_func(delay):print("test_slow_func {}".format(delay))sleep(delay)assert…...
视频集中存储/智能分析融合云平台EasyCVR平台接入rtsp,突然断流是什么原因?
安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快,可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等,以及支持厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…...
JavaScript 复杂的<三元运算符和比较操作>的组合--案例(一)
在逆向的时候,碰上有些复杂的js代码,逻辑弄得人有点混; 因此本帖用来记录一些棘手的代码,方便自己记忆,也让大家拓展认识~ ----前言 内容: function(e, t, n) {try {1 (e "{" e[0] ? JSON.parse(e) : JSON.parse(webInstace.shell(e))).Status || 200 e.Code…...
uniapp搭建内网映射测试https域名
搭建Https域名服务器 使用github的frp搭建,使用宝塔申请免费https证书,需要先关闭宝塔nginx的反向代理,申请完域名后再开启反向代理即可。 教程 新版frp搭建教程 启动命令 服务器端 sudo systemctl start frps本地 cd D:\软件安装包\f…...
国防科技大博士招生入学考试【50+论文主观题】
目录 回答模板大意创新和学术价值启发 论文分类(根据问题/场景分类)数学问题Efficient Multiset Synchronization(高效的多集同步【简单集合/可逆计数Bloom过滤器】)大意创新和学术价值启发 An empirical study of Bayesian netwo…...
CUDA简介——编程模式
1. 引言 前序博客: CUDA简介——基本概念 CPU是用于控制的。即,host控制整个程序流程: 1)程序以Host代码main函数开始,然后顺序执行。 Host代码是顺序执行的,并执行在CPU之上。Host代码会负责Launch ke…...
Linux 软件安装
目录 一、Linux 1、Linux异常解决 1、JDK安装 1、Linux卸载JDK 2、Linux安装JDK 2、Redis安装 一、Linux 1、Linux异常解决 1、Another app is currently holding the yum lock; waiting for it to exit... 解决办法: rm -f /var/run/yum.pid1、杀死这个应用程序 ps a…...
flask之邮件发送
一、安装Flask-Mail扩展 pip install Flask-Mail二、配置Flask-Mail 格式:app.config[参数]值 三、实现方法 3.1、Mail类 常用类方法 3.2、Message类,它封装了一封电子邮件。构造函数参数如下: flask-mail.Message(subject, recipient…...
【Filament】Filament环境搭建
1 前言 Filament 是一个实时物理渲染引擎,用于 Android、iOS、Linux、macOS、Windows 和 WebGL 平台。该引擎旨在提供高效、实时的图形渲染,并被设计为在 Android 平台上尽可能小而尽可能高效。Filament 支持基于物理的渲染(PBR)&…...
外包干了2个月,技术倒退2年。。。。。
先说一下自己的情况,本科生,20年通过校招进入深圳某软件公司,干了接近4年的功能测试,今年国庆,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…...
使用 python ffmpeg 批量检查 音频文件 是否损坏或不完整
自用工具,检查下载的音乐是否有损坏 或 下载不完整 使用方法,把 in_dir r’D:\158首无损珍藏版’ 改成你自己的音乐文件夹路径 如果发现文件有损坏,则会在命令行打印错误文件的路径 注意,要求 ffmpeg 命令可以直接在命令行调用…...
Django:通过user-agent判断请求是来自移动端还是PC端(电脑端)
第一种思路: 根据博文 Djano的request.META是什么?的研究成果,先判断有无键HTTP_SEC_CH_UA_MOBILE,如果没有,再去按博文 网站如何判断请求是来自手机-移动端还是PC-电脑端?如何让网站能适应不同的客户端&am…...
Linux中ssh远程登录系统和远程拷贝
本章主要介绍ssh远程登录系统和远程拷贝的方法 ssh的基本用法打开远程图形化界面ssh无密码登录和安全操作Windows远程登录远程拷贝 很多时候服务器并没有显示器,我们也不可能每次都通过控制台去管理服务器,这时就需 要远程登录。远程登录到服务器可以通…...
JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...
大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
