yolov7模型输出层预测方法解读

本文从代码的角度分析模型训练阶段输出层的预测包括以下几个方面:

  • 标注数据(下文统称targets)的正样本分配策略,代码实现位于find_3_positive。
  • 候选框的生成,会介绍输出层的预测值、GT、grid、 anchor之间的联系
  • 损失函数的计算

参数介绍

3个输出层

在这里插入图片描述
p传递的是3个输出层的预测值, (8,3,80,80,11)表示8个batch, 3个anchor, 特征图大小(80 * 80), 6分类对应的一个bbox向量维度是11。

标签targets

在这里插入图片描述
在这里插入图片描述
targets(42, 6) ,对应8batch的标注数据一共有42个,每个标注数据的信息用6维向量表示。分别是标签所在的batch id、标签的分类id、归一化的坐标框。

find_3_positive

find_3_positive实现了正样本分配策略。通过标注数据往左上或者右下偏移,能够增加正样本的数量。正样本对应的grid坐标和anchor id用来参与输出层的预测值计算。

  def find_3_positive(self, p, targets):
        # Build targets for compute_loss(), input targets(image,class,x,y,w,h)
        na, nt = self.na, targets.shape[0]  # number of anchors, targets
        indices, anch = [], []
        gain = torch.ones(7, device=targets.device).long()  # 7表示原标签6个+框ID(属于哪个大小的anchor)  normalized to gridspace gain
        ai = torch.arange(na, device=targets.device).float().view(na, 1).repeat(1, nt)  # same as .repeat_interleave(nt)
        targets = torch.cat((targets.repeat(na, 1, 1), ai[:, :, None]), 2)  # 就是最后加了一个维度,表示anchorID, append anchor indices

        g = 0.5  # bias 一会要玩漂移,
        off = torch.tensor([[0, 0],
                            [1, 0], [0, 1], [-1, 0], [0, -1],  # j,k,l,m
                            # [1, 1], [1, -1], [-1, 1], [-1, -1],  # jk,jm,lk,lm
                            ], device=targets.device).float() * g  # offsets

        for i in range(self.nl):#有3个输出层,分别做
            anchors = self.anchors[i]#当前输出层对应anchor
            gain[2:6] = torch.tensor(p[i].shape)[[3, 2, 3, 2]]  # 赋值,一会用,xyxy gain

            # Match targets to anchors,这块在遍历看看这些GT到底放在哪个的输出层合适
            t = targets * gain#归一化的标签映射到特征图上
            if nt:
                # Matches
                r = t[:, :, 4:6] / anchors[:, None]  # 每一个GT与anchor大宽高比大小,wh ratio
                j = torch.max(r, 1. / r).max(2)[0] < self.hyp['anchor_t']  # 0.25<比例<4才会被保留 compare
                # j = wh_iou(anchors, t[:, 4:6]) > model.hyp['iou_t']  # iou(3,n)=wh_iou(anchors(3,2), gwh(n,2))
                t = t[j]  # filter

                # Offsets
                gxy = t[:, 2:4]  # 到左上角的距离 grid xy
                gxi = gain[[2, 3]] - gxy  # 到右下角的距离 inverse
                j, k = ((gxy % 1. < g) & (gxy > 1.)).T#离左上角近的选出来,而且不能是边界
                l, m = ((gxi % 1. < g) & (gxi > 1.)).T#离右下角近的选出来,而且不能是边界
                j = torch.stack((torch.ones_like(j), j, k, l, m))#5个,因为自己所在实际位置一定为true
                t = t.repeat((5, 1, 1))[j]#相当于原来就1个 现在还要考虑2个邻居 target必然增多
                offsets = (torch.zeros_like(gxy)[None] + off[:, None])[j]#对应区域玩对应漂移大小 都是0.5个单位
            else:
                t = targets[0]
                offsets = 0

            # Define
            b, c = t[:, :2].long().T  # batch, class
            gxy = t[:, 2:4]  # grid xy
            gwh = t[:, 4:6]  # grid wh
            gij = (gxy - offsets).long()#漂移后 整数部分就是格子的索引
            gi, gj = gij.T  # grid xy indices

            # Append
            a = t[:, 6].long()  # 每一个target对应的anchor indices
            indices.append((b, a, gj.clamp_(0, gain[3] - 1), gi.clamp_(0, gain[2] - 1)))  # batch, anchor, grid indices
            anch.append(anchors[a])  # anchors大小

        return indices, anch

ai

已知模型有3个输出层,每个输出层有3个尺寸的anchor。对于targets我们初始化一个ai(3,42),用来表示targets和anchor可能存在的对应关系。
在这里插入图片描述

  • torch.arange(na, device=targets.device):这个函数创建了一个从0到na(不包括na)的一维张量,其中na是一个整数。这个张量被创建在targets.device上,这意味着它会在targets张量所在的设备上(例如CPU或GPU)。
  • .float():这个方法将上一步创建的张量转换为浮点数类型。这是因为torch.arange默认生成整数类型的张量,而.float()可以确保后续操作中数值的精度。
  • .view(na, 1):.view()方法用于改变张量的形状而不改变其数据。在这里,它将一维张量重新塑形为一个na x 1的二维张量。每个元素都变成了一个单独的行。
  • .repeat(1, nt):.repeat()方法用于沿着指定的维度重复张量。在这里,它将上一步得到的二维张量在第二维(列)上重复nt次。结果是一个na x nt的二维张量,其中每一行都是原始arange张量的副本。

targets增加anchor信息

这一步操作的目的就是为了把anchor id添加到 targets中,将targets张量维度从[42, 6]—> [3, 42 , 7]。
在这里插入图片描述

  • targets.repeat(na, 1, 1):在第一个维度重复na边,第二和第三个维度保持不变 [42, 6]–>[3,42,6]
  • ai[:, : , None] :该切片操作是在None的维度增加一维,但是元素的个数保持不变,用来扩充张量的维度,方便拼接。[3, 42, 1]

targets与anchor尺寸不匹配则滤掉

 # Matches
 r = t[:, :, 4:6] / anchors[:, None]  # 每一个GT与anchor大宽高比大小,wh ratio
 j = torch.max(r, 1. / r).max(2)[0] < self.hyp['anchor_t']  # 0.25<比例<4才会被保留 compare
 # j = wh_iou(anchors, t[:, 4:6]) > model.hyp['iou_t']  # iou(3,n)=wh_iou(anchors(3,2), gwh(n,2))
 t = t[j]  # filter

t[:, :, 4:6] 取标注数据的w和h与3个anchor的w和h做除法,大小不能超过4倍。过滤后匹配anchor大小的标注数据剩下39个。一个target可能对应多个anchor,所以过滤后的数据可能比开始的标注数据多。
在这里插入图片描述

计算offset是左上/右下

 # Offsets
                gxy = t[:, 2:4]  # 到左上角的距离 grid xy
                gxi = gain[[2, 3]] - gxy  # 到右下角的距离 inverse
                j, k = ((gxy % 1. < g) & (gxy > 1.)).T#离左上角近的选出来,而且不能是边界
                l, m = ((gxi % 1. < g) & (gxi > 1.)).T#离右下角近的选出来,而且不能是边界
                j = torch.stack((torch.ones_like(j), j, k, l, m))#5个,因为自己所在实际位置一定为true
                t = t.repeat((5, 1, 1))[j]#相当于原来就1个 现在还要考虑2个邻居 target必然增多
                offsets = (torch.zeros_like(gxy)[None] + off[:, None])[j]#对应区域玩对应漂移大小 都是0.5个单位

torch.stack()将多个张量按照新的维度进行堆叠。

计算新增样本grid索引

b, c = t[:, :2].long().T  # batch, class
            gxy = t[:, 2:4]  # grid xy
            gwh = t[:, 4:6]  # grid wh
            gij = (gxy - offsets).long()#漂移后 整数部分就是格子的索引
            gi, gj = gij.T  # grid xy indices

append新增正样本

在这里插入图片描述

find_3_positive返回值

返回结果是anchor所在的grid的位置信息,以及是3个anchor中的anchor id。
在这里插入图片描述

build_target

gt、grid、anchor

  • 如下图所示黄色圆点表示grid,在特征图大小为80 * 80的输出层能用来预测目标框的grid的数量也有80 * 80个。
  • 每个grid上有3个尺寸的anchor可以用,如图中3个叠加的红框所示。
  • gt所在的grid用来生成预测值,不可能80 * 80个grid都用来预测目标框。gt所在的grid如何获取参考find_3_positive.
  • gt 和 anchor尺寸超过4倍, 那么用来生成预测值的要素(gt、 grid、 anchor)会增加。 因此一个gt可能对应多个anchor。
    在这里插入图片描述

候选框预测值的生成

经过函数find_3_position我们得到了更多的gt以及它的grid、anchor信息。这些信息和输出层输出的预测值需要搭配使用,这个步骤如下图所示(只看yolov7部分):
在这里插入图片描述

公式中的参数含义:

  • tx, ty, tw, th(变量fg_pred ):这些值从模型输出层(变量pi)中索引得到的。索引即上文中计算得到的targets所在的grid坐标。我自己强行理解了这个grid坐标的作用:即target所在的gird本来就可以生成预测框,因此需要该grid在输出层中索引候选框的坐标。但是模型输出层不能一下输出正确的预测值,模型需要训练。因此使用上图公式,加上anchor的辅助计算能够得到更加合理的预测值。最后为了训练模型更新参数需要与标注数据计算LOSS。并且通过不断的迭代将LOSS降到最低。
  • cx,cy(变量grid): 所在grid的坐标
  • bx, by, bw, bh(变量pxywh ):目标的坐标框预测值,需要计算获得
  • pw, ph(变量anch): 尺寸匹配的anchor的宽、高
fg_pred = pi[b, a, gj, gi]  #取对应target位置的预测结果
grid = torch.stack([gi, gj], dim=1)
pxy = (fg_pred[:, :2].sigmoid() * 2. - 0.5 + grid) * self.stride[i] #中心点在当前格子偏移量,-0.5到1.5之间 再还原 / 8.
pwh = (fg_pred[:, 2:4].sigmoid() * 2) ** 2 * anch[i][idx] * self.stride[i] #之前是考虑四倍,这也得同步  / 8.
pxywh = torch.cat([pxy, pwh], dim=-1)
pxyxy = xywh2xyxy(pxywh)
pair_wise_iou = box_iou(txyxy, pxyxy)#计算GT与所有候选正样本的IOU
pair_wise_iou_loss = -torch.log(pair_wise_iou + 1e-8)#IOU损失

build_targets

附源码:

       def build_targets(self, p, targets, imgs):
        
        #indices, anch = self.find_positive(p, targets)
        indices, anch = self.find_3_positive(p, targets)
        #indices, anch = self.find_4_positive(p, targets)
        #indices, anch = self.find_5_positive(p, targets)
        #indices, anch = self.find_9_positive(p, targets)

        matching_bs = [[] for pp in p]
        matching_as = [[] for pp in p]
        matching_gjs = [[] for pp in p]
        matching_gis = [[] for pp in p]
        matching_targets = [[] for pp in p]
        matching_anchs = [[] for pp in p]
        #p是list,每个list存放不同尺寸的预测头的预测值
        # p[0]:[8,3,80,80,11] 
        # p[1]:[8,3,40,40,11] 
        # p[2]:[8,3,20,20,11]
        nl = len(p)    
    
        for batch_idx in range(p[0].shape[0]):
        	# targets[42, 6]表示一个8batch的gt
            b_idx = targets[:, 0]==batch_idx
            #this_target表示输入当前图像的gt索引
            #eg:this_target[2,6] 2表示有两个标注框,6表示标注框具体的值
            this_target = targets[b_idx]#当前图像里的标注框GT
            if this_target.shape[0] == 0:
                continue
                
            txywh = this_target[:, 2:6] * imgs[batch_idx].shape[1]#得到实际大小
            txyxy = xywh2xyxy(txywh)

            pxyxys = []
            p_cls = []
            p_obj = []
            from_which_layer = []
            all_b = []
            all_a = []
            all_gj = []
            all_gi = []
            all_anch = []
            
            for i, pi in enumerate(p):#遍历每一个输出层
                
                b, a, gj, gi = indices[i]
                idx = (b == batch_idx)
                b, a, gj, gi = b[idx], a[idx], gj[idx], gi[idx]                
                all_b.append(b)
                all_a.append(a)
                all_gj.append(gj)
                all_gi.append(gi)
                all_anch.append(anch[i][idx])
                from_which_layer.append(torch.ones(size=(len(b),)) * i)#来自哪个输出层
                
                fg_pred = pi[b, a, gj, gi]  #取对应target位置的预测结果
                p_obj.append(fg_pred[:, 4:5])
                p_cls.append(fg_pred[:, 5:])
                
                grid = torch.stack([gi, gj], dim=1)
                pxy = (fg_pred[:, :2].sigmoid() * 2. - 0.5 + grid) * self.stride[i] #中心点在当前格子偏移量,-0.5到1.5之间 再还原 / 8.
                #pxy = (fg_pred[:, :2].sigmoid() * 3. - 1. + grid) * self.stride[i]
                pwh = (fg_pred[:, 2:4].sigmoid() * 2) ** 2 * anch[i][idx] * self.stride[i] #之前是考虑四倍,这也得同步  / 8.
                pxywh = torch.cat([pxy, pwh], dim=-1)
                pxyxy = xywh2xyxy(pxywh)
                pxyxys.append(pxyxy)
            
            pxyxys = torch.cat(pxyxys, dim=0)
            if pxyxys.shape[0] == 0:
                continue
            p_obj = torch.cat(p_obj, dim=0)
            p_cls = torch.cat(p_cls, dim=0)
            from_which_layer = torch.cat(from_which_layer, dim=0)
            all_b = torch.cat(all_b, dim=0)
            all_a = torch.cat(all_a, dim=0)
            all_gj = torch.cat(all_gj, dim=0)
            all_gi = torch.cat(all_gi, dim=0)
            all_anch = torch.cat(all_anch, dim=0)
        	#txyxy2各真实值  pxyxys:18个候选框
            pair_wise_iou = box_iou(txyxy, pxyxys)#计算GT与所有候选正样本的IOU

            pair_wise_iou_loss = -torch.log(pair_wise_iou + 1e-8)#IOU损失

            top_k, _ = torch.topk(pair_wise_iou, min(10, pair_wise_iou.shape[1]), dim=1)#多的话选10个,少的话有几个算几个
            dynamic_ks = torch.clamp(top_k.sum(1).int(), min=1)#累加,相当于有些可能太小的我不需要,宁缺毋滥?
			#gt_cls_per_image[2,18,6]含义:18个候选框,2个gt,6分类,每个候选框对于每个gt,它的分类是什么
            gt_cls_per_image = (
                F.one_hot(this_target[:, 1].to(torch.int64), self.nc)
                .float()
                .unsqueeze(1)
                .repeat(1, pxyxys.shape[0], 1)#onehot后重复候选框数量次
            )

            num_gt = this_target.shape[0]
            # p_obj 目标置信度,预测类别的时候做了个加权,即是个目标物体的前提,预测你的类别是什么
            cls_preds_ = (
                p_cls.float().unsqueeze(0).repeat(num_gt, 1, 1).sigmoid_()
                * p_obj.unsqueeze(0).repeat(num_gt, 1, 1).sigmoid_()
            )#预测类别情况
			#把类别的真实值和预测值传进去做交叉熵损失函数
            y = cls_preds_.sqrt_()
            pair_wise_cls_loss = F.binary_cross_entropy_with_logits(
               torch.log(y/(1-y)) , gt_cls_per_image, reduction="none"
            ).sum(-1)#类别差异
            del cls_preds_
        	#候选框复筛,考虑IOU损失和类别损失的加权影响
            cost = (
                pair_wise_cls_loss
                + 3.0 * pair_wise_iou_loss
            )#候选框里要开始选了,要看他们的IOU情况和分类情况 综合考虑

            matching_matrix = torch.zeros_like(cost)

            for gt_idx in range(num_gt):
                _, pos_idx = torch.topk(
                    cost[gt_idx], k=dynamic_ks[gt_idx].item(), largest=False
                )
                matching_matrix[gt_idx][pos_idx] = 1.0

            del top_k, dynamic_ks
            anchor_matching_gt = matching_matrix.sum(0)#竖着加
            if (anchor_matching_gt > 1).sum() > 0:#一个正样本匹配到了多个GT的情况
                _, cost_argmin = torch.min(cost[:, anchor_matching_gt > 1], dim=0)#那就比较跟哪个一个损失最小,删除其他
                matching_matrix[:, anchor_matching_gt > 1] *= 0.0#其他删除
                matching_matrix[cost_argmin, anchor_matching_gt > 1] = 1.0#最小的那个保留
            fg_mask_inboxes = matching_matrix.sum(0) > 0.0#哪些是正样本
            matched_gt_inds = matching_matrix[:, fg_mask_inboxes].argmax(0)#每个正样本对应的真实框索引
        
            from_which_layer = from_which_layer[fg_mask_inboxes]
            #from_which_layer = from_which_layer.to(fg_mask_inboxes.device)
            all_b = all_b[fg_mask_inboxes]#对应的batch索引
            all_a = all_a[fg_mask_inboxes]#对应的anchor索引
            all_gj = all_gj[fg_mask_inboxes]
            all_gi = all_gi[fg_mask_inboxes]
            all_anch = all_anch[fg_mask_inboxes]
        
            this_target = this_target[matched_gt_inds]#匹配到正样本的GT
        
            for i in range(nl):#得到每一层的正样本
                layer_idx = from_which_layer == i
                matching_bs[i].append(all_b[layer_idx])
                matching_as[i].append(all_a[layer_idx])
                matching_gjs[i].append(all_gj[layer_idx])
                matching_gis[i].append(all_gi[layer_idx])
                matching_targets[i].append(this_target[layer_idx])
                matching_anchs[i].append(all_anch[layer_idx])

        for i in range(nl):#合并
            if matching_targets[i] != []:
                matching_bs[i] = torch.cat(matching_bs[i], dim=0)
                matching_as[i] = torch.cat(matching_as[i], dim=0)
                matching_gjs[i] = torch.cat(matching_gjs[i], dim=0)
                matching_gis[i] = torch.cat(matching_gis[i], dim=0)
                matching_targets[i] = torch.cat(matching_targets[i], dim=0)
                matching_anchs[i] = torch.cat(matching_anchs[i], dim=0)
            else:
                matching_bs[i] = torch.tensor([], device='cuda:0', dtype=torch.int64)
                matching_as[i] = torch.tensor([], device='cuda:0', dtype=torch.int64)
                matching_gjs[i] = torch.tensor([], device='cuda:0', dtype=torch.int64)
                matching_gis[i] = torch.tensor([], device='cuda:0', dtype=torch.int64)
                matching_targets[i] = torch.tensor([], device='cuda:0', dtype=torch.int64)
                matching_anchs[i] = torch.tensor([], device='cuda:0', dtype=torch.int64)

        return matching_bs, matching_as, matching_gjs, matching_gis, matching_targets, matching_anchs           

损失函数计算

iou损失

pair_wise_iou_loss = -torch.log(pair_wise_iou + 1e-8)#IOU损失

分类损失

fg_pred = pi[b, a, gj, gi]  #取对应target位置的预测结果
p_cls.append(fg_pred[:, 5:])
num_gt = this_target.shape[0]
cls_preds_ = (
    p_cls.float().unsqueeze(0).repeat(num_gt, 1, 1).sigmoid_()
    * p_obj.unsqueeze(0).repeat(num_gt, 1, 1).sigmoid_()
)#预测类别情况
 y = cls_preds_.sqrt_()
 pair_wise_cls_loss = F.binary_cross_entropy_with_logits(
    torch.log(y/(1-y)) , gt_cls_per_image, reduction="none"
 ).sum(-1)#类别差异

损失加权

cost = (
        pair_wise_cls_loss
         + 3.0 * pair_wise_iou_loss
     )#候选框里要开始选了,要看他们的IOU情况和分类情况 综合考虑

总结

本文主要目的是为了梳理yolov7输出层预测的目标框坐标的整个过程。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【原创】springboot+mysql疫苗预约管理系统设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…

《Kubernetes部署篇:基于Kylin V10+ARM架构CPU+外部etcd使用containerd部署K8S 1.26.15容器版集群(多主多从)》

总结&#xff1a;整理不易&#xff0c;如果对你有帮助&#xff0c;可否点赞关注一下&#xff1f; 更多详细内容请参考&#xff1a;企业级K8s集群运维实战 1、在当前实验环境中安装K8S1.25.14版本&#xff0c;出现了一个问题&#xff0c;就是在pod中访问百度网站&#xff0c;大…

ollama大语言模型

查看已经安装的大语言模型 ollama list运行大语言模型 ollama run llama2:latest

【EI会议征稿通知】2024年图像处理、机器学习与模式识别国际学术会议(IPMLP 2024)

2024年图像处理、机器学习与模式识别国际学术会议&#xff08;IPMLP 2024) 2024 International Conference on Image Processing, Machine Learning and Pattern Recognition 重要信息 大会官网&#xff1a;www.ipmlp.net&#xff08;点击参会/投稿/了解会议详情&#xff09;…

Elasticsearch:简化 KNN 搜索

作者&#xff1a;来自 Elastic Panagiotis Bailis 在这篇博客文章中&#xff0c;我们将深入探讨我们为了使 KNN 搜索的入门体验变得更加简单而做出的努力&#xff01; 向量搜索 向量搜索通过在 Elasticsearch 中引入一种新的专有的 KNN 搜索类型&#xff0c;已经可以使用一段…

蓝桥杯2024年第十五届省赛真题-数字接龙

思路&#xff1a;DFS&#xff0c;因为输入的i&#xff0c;j的顺序导致&#xff0c;方向向量中x是行编号&#xff0c;y是列编号。方向向量可能和直觉上不同。 错的 //int dx[8]{0,1,1,1,0,-1,-1,-1}; //int dy[8]{1,1,0,-1,-1,-1,0,1}; 对的 int dx[]{-1,-1,0,1,1,1,0,-1}; int…

论文复现《SplaTAM: Splat, Track Map 3D Gaussians for Dense RGB-D SLAM》

前言 SplaTAM算法是首个开源的基于RGB-D数据&#xff0c;生成高质量密集3D重建的SLAM技术。 通过结合3DGS技术和SLAM框架&#xff0c;在保持高效性的同时&#xff0c;提供精确的相机定位和场景重建。 代码仓库&#xff1a;spla-tam/SplaTAM: SplaTAM: Splat, Track & Map 3…

算法一:数字 - 两数之和

给定一个整数数组 nums 和一个目标值 target&#xff0c;请你在该数组中找出和为目标值的那 两个 整数&#xff0c;并返回他们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素不能使用两遍。 来源&#xff1a;力扣(LeetCode) 链接&#xf…

政安晨:【Keras机器学习示例演绎】(一)—— 利用类 U-Net 架构进行图像分割

目录 下载数据 准备输入图像的路径和目标分割掩码 一幅输入图像和相应的分割掩码是什么样子的&#xff1f; 准备数据集&#xff0c;以加载和矢量化成批数据 准备 U-Net Xception 风格模型 预留验证分割 训练模型 可视化预测 政安晨的个人主页&#xff1a;政安晨 欢迎 &…

4.18学习总结

多线程补充 等待唤醒机制 现在有两条线程在运行&#xff0c;其中一条线程可以创造一个特殊的数据供另一条线程使用&#xff0c;但这个数据的创建也有要求&#xff1a;在同一时间只允许有一个这样的特殊数据&#xff0c;那么我们要怎样去完成呢&#xff1f;如果用普通的多线程…

FTP客户端Transmit 5 for Mac中文激活版

Transmit 5是一款功能强大的Mac FTP客户端软件&#xff0c;它由Panic公司开发&#xff0c;为用户提供简单、高效的文件传输体验。 Transmit 5 for Mac中文激活版下载 Transmit 5支持多种传输协议&#xff0c;如FTP、SFTP、WebDAV和Amazon S3等&#xff0c;满足用户不同的文件传…

eCongnition 获取特征(shp)

目录 1、加载数据和分割的shp文件 2、将专题(导入的shp)转换为对象 3、导出特征 1、加载数据和分割的shp文件 我们加载数据&#xff0c;在第二个框&#xff08;Thematic La..&#xff09;里加载矢量shp 导入的.shp文件称为专题层(Thematic Layer), 显示方式如下所示&#x…

深入探索:Facebook如何重塑社交互动

在当代社会中&#xff0c;社交互动已成为日常生活的核心组成部分。而在众多的社交媒体平台中&#xff0c;Facebook凭借其卓越的用户基础和创新的功能&#xff0c;已经成为了全球最大的社交媒体平台。本文将深入探讨Facebook如何通过其独特的特性和功能&#xff0c;重塑了人们的…

Python 字符串 Base64

因消息传输的需要&#xff0c;我们需要对大量文本的字符串进行一下 Base64 转换。 这样的好处是因为在传输的字符串中可能有存在一些特殊字符&#xff0c;这些特殊在经过网络传输的时候会出现编码的问题&#xff0c;并且会影响传输稳定性。 使用 Base64 可以避免这个问题。 方…

数据库--Sqlite3

1、思维导图 2sqlite3在linux中是实现数据的增删&#xff0c;改 #include<myhead.h> int main(int argc, const char *argv[]) { //1、定义一个数据库句柄指针 sqlite3* ppDb NULL; //2、创建或打开数据库 if(sqlite3_open("./mydb…

深入解析Apache Hadoop YARN:工作原理与核心组件

什么是YARN&#xff1f; YARN&#xff08;Yet Another Resource Negotiator&#xff09;是Apache Hadoop生态系统中的一个重要组件&#xff0c;用于资源管理和作业调度。它是Hadoop 2.x版本中的一个关键特性&#xff0c;取代了旧版本中的JobTracker和TaskTracker。YARN的设计目…

ElasticSearch实战之项目搜索高亮

文章目录 1. 前情配置2、数据操作2.1 操作API2.2 数据入库 3. 高亮搜索3.1 方法封装3.2 高亮搜索 1. 前情配置 为满足ElasticSearch可在项目中实现搜索高亮&#xff0c;我们需要先做一些前情配置 导入ElasticSearch依赖 <dependency><groupId>org.springframewor…

【Flutter】多语言方案一:flutter_localizations 与 GetX 配合版

系列文章目录 多语言方案&#xff1a;flutter_localizations 与 GetX 配合版&#xff0c;好处&#xff1a;命令行生成多语言字符串的引用常量类&#xff0c;缺点&#xff1a;切换语言以后&#xff0c;主界面需要手动触发setState&#xff0c;重绘将最新的Locale数据设置给GetM…

【Leetcode每日一题】 分治 - 排序数组(难度⭐⭐)(60)

1. 题目解析 题目链接&#xff1a;912. 排序数组 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 算法思路&#xff1a; 快速排序作为一种经典的排序算法&#xff0c;其核心思想在于通过“分而治之”的策略&#xff…

Idea修改【Help->Edit Custom VM Options...】后,导致idea无法正常启动的解决方法

一、错误场景: 二、解决方法&#xff1a; 修改文件路径&#xff1a;C:\Users\tianjm&#xff08;写自己的用户名&#xff09;\AppData\Roaming\JetBrains\IdeaIC2024.1&#xff08;选自己安装的版本&#xff09;
最新文章