漯河河南网站建设,网站建设包含美工,三网合一网站源代码,关于网站建设的调查报告一、概述
在视觉任务中#xff0c;图像分割任务是一个很广泛的领域#xff0c;应用于交互式分割#xff0c;边缘检测#xff0c;超像素化#xff0c;感兴趣目标生成#xff0c;前景分割#xff0c;语义分割#xff0c;实例分割#xff0c;泛视分割等。 交互式分割图像分割任务是一个很广泛的领域应用于交互式分割边缘检测超像素化感兴趣目标生成前景分割语义分割实例分割泛视分割等。 交互式分割这种分割任务它允许用户手动细化掩码来分割任意类型的对象。然而这种方法需要用户的不断参与和指导类似于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::vectorsam_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::vectorsam_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::vectorsam_result_t faceobjects, std::vectorint picked, float nms_threshold)
{picked.clear();const int n faceobjects.size();std::vectorfloat 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::vectorsam_result_t proposals, std::vectorint 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::vectorncnn::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::vectorsam_result_t sam_results, float pred_iou_thresh, float stability_score_thresh)
{std::vectorncnn::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::vectorstd::pairfloat, int scores_vec;for (int i 1; i scores.w; i) {scores_vec.push_back(std::pairfloat, int(scores[i], i));}std::sort(scores_vec.begin(), scores_vec.end(), std::greaterstd::pairfloat, 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::vectorstd::vectorcv::Point contours;std::vectorcv::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::vectorstd::pairfloat,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::pairfloat,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代码没有实现这一部分。
开放式点 点击要分割目标的中间分割包含该点的物体会按最小分割的结果展示出来如果想分割的物体大于展示的结果可以在物体的其他部分也点击下 选择矩形框 使用鼠标拖动在目标选择分割目标
分割一切 将图片中所有物体的分割都展示出来