浅易理解:非极大抑制NMS

news2025/1/10 23:43:10

什么是非极大抑制NMS

非极大值抑制(Non-Maximum Suppression,简称NMS)是一种在计算机视觉和图像处理领域中广泛使用的后处理技术,特别是在目标检测任务中。它的主要目的是解决目标检测过程中出现的重复检测问题,即对于同一个物体,算法可能会预测出多个重叠或相似的边界框(bounding boxes)。

在目标检测算法得出一系列候选边界框及其对应的类别得分(confidence score)之后,NMS过程如下:

  1. 排序:首先根据每个边界框的得分进行降序排序,选取得分最高的边界框作为保留的对象。

  2. 抑制:对于排序后的边界框列表,对每一个框i,检查其与得分低于它的所有其他框j之间的重叠程度。通常使用交并比(Intersection over Union,IoU)来量化两个框的重叠面积占它们并集面积的比例。

  3. 剔除:如果框i与某个框j的IoU超过预设的阈值(比如0.5),则认为框j是冗余的,将其剔除(抑制)。

  4. 迭代:重复上述步骤,直到处理完所有候选边界框,最终剩下的边界框集合即是经过非极大值抑制后的结果,这些框代表了各自区域内最有可能对应真实物体的检测结果。

总之,非极大值抑制确保了对同一物体只有一个最精确的边界框被保留下来,从而减少误报和重复检测,提高了目标检测的精度。

在目标检测中,NMS的目的就是要去除冗余的检测框,保留最好的一个

非极大抑制的概念只需要看这两幅图就知道了:

下图是经过非极大抑制的。

下图是未经过非极大抑制的。

NMS的原理是对于预测框的集合S及其对应的置信度score(这里的置信度就是softmax得出的概率值,它的含义是多大的把握预测正确,也就是有多大的把握确定检测框中存在真正的目标),选择具有最大score的检测框,记为M,将其从集合S中移除并加入到最终的检测结果集合中.并且将集合S中剩余检测框中与检测框M的IoU大于阈值的框从集合S中移除.重复这个过程,直到集合S为空。

使用流程如下图所示:

首先是检测出一系列的检测框

将检测框按照类别进行分类

对同一类别的检测框应用NMS获取最终的检测结果

代码:

NMS 算法一般是为了去掉模型预测后的多余框,其一般设有一个nms_threshold=0.5,具体的实现思路如下:

  • 选取这类box中scores最大的哪一个,它的index记为 i ,并保留它;
  • 计算 boxes[i] 与其余的 boxes 的 IOU 值;
  • 如果其 IOU>0.5 了,那么就舍弃这个box(由于可能这两个box表示同一目标,所以保留分数高的哪一个);
  • 从最后剩余的boxes中,再找出最大scores的哪一个,如此循环往复。
def nms(boxes, scores, threshold=0.5, top_k=200):
    '''
    Args:
        boxes: 预测出的box, shape[M,4]
        scores: 预测出的置信度,shape[M]
        threshold: 阈值
        top_k: 要考虑的box的最大个数
    Return:
        keep: nms筛选后的box的新的index数组
        count: 保留下来box的个数
    '''
    keep = scores.new(scores.size(0)).zero_().long()
    x1 = boxes[:, 0]
    y1 = boxes[:, 1]
    x2 = boxes[:, 2]
    y2 = boxes[:, 3]
 
    area = (x2-x1)*(y2-y1)  # 面积,shape[M]
    _, idx = scores.sort(0, descending=True) # 降序排列scores的值大小
    # 取前top_k个进行nms
    idx = idx[:top_k]
 
    count = 0
 
    while idx.numel():
        # 记录最大score值的index
        i = idx[0]
        # 保存到keep中
        keep[count] = i
        # keep 的序号
        count += 1
 
        if idx.size(0) == 1: # 保留框只剩一个
            break
 
        idx = idx[1:] # 移除已经保存的index
 
        # 计算boxes[i]和其他boxes之间的iou
        xx1 = x1[idx].clamp(min=x1[i])
        yy1 = y1[idx].clamp(min=y1[i])
        xx2 = x2[idx].clamp(max=x2[i])
        yy2 = y2[idx].clamp(max=y2[i])
 
        w = (xx2 - xx1).clamp(min=0)
        h = (yy2 - yy1).clamp(min=0)
 
        # 交集的面积
        inter = w * h  # shape[M-1]
        iou = inter / (area[i] + area[idx] - inter)
 
        # iou满足条件的idx
        idx = idx[iou.le(threshold)] # Shape[M-1]
 
    return keep, count

其中:

  • torch.numel(): 表示一个张量总元素的个数
  • torch.clamp(min, max): 设置上下限
  • tensor.le(x): 返回tensor<=x的判断 

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//

通过一个例子看些NMS的使用方法,假设定位车辆,算法就找出了一系列的矩形框,我们需要判别哪些矩形框是没用的,需要使用NMS的方法来实现。

假设集合S中有A、B、C、D、E 5个候选框,每个框旁边的数字是它的置信度,我们设定NMS的iou阈值是0.5,接下来进行迭代计算:

第一轮:因为B是得分最高的(即B的置信度最高),在集合S的其余候选框中,如果与B的IoU>0.5会被删除。A,C,D,E中现在分别与B计算IoU,DE结果>0.5,剔除DE(说明BDE检测的是同一个目标,保留置信度最大的候选框;而AC可能检测的是另一个目标),B作为一个预测结果,从集合S中移除,并放入最终的检测结果集合中。此时新的集合S中只剩下候选框A,C

第二轮:在新的集合S中,A的置信度得分最高,将集合S中剩下的候选框分别与A计算IoU,因为A与C的iou>0.5,所以剔除C,A作为另外一个预测结果从集合S中移除,并放入最终的检测结果集合中,此时集合S为空,所以循环结束。

最终结果为在这个5个中检测出了两个目标为A和B。

单类别的NMS的实现方法如下所示:

import numpy as np
def nms(bboxes, confidence_score, threshold):
    """非极大抑制过程
    :param bboxes: 同类别候选框坐标
    :param confidence: 同类别候选框分数(即置信度)
    :param threshold: iou阈值
    :return:
    """
    # 1、没有传入候选框则返回空列表
    if len(bboxes) == 0:
        return [], []
    #强制转换为numpy类型的数组,这样才可以进行切片等numpy所支持的操作
    bboxes = np.array(bboxes)
    score = np.array(confidence_score)
 
    # 取出所有候选框的左上角坐标和右下角坐标
    x1 = bboxes[:, 0]
    y1 = bboxes[:, 1]
    x2 = bboxes[:, 2]
    y2 = bboxes[:, 3]
 
    # 2、对候选框进行NMS筛选
    # 返回的框坐标和分数
    picked_boxes = []
    picked_score = []
    # 对置信度进行排序, 获取排序后的下标序号, argsort默认从小到大排序
    order = np.argsort(score)
    #计算所有候选框的面积 
    areas = (x2 - x1) * (y2 - y1)
    while order.size > 0:
        # 将当前置信度最大的候选框的索引,加入返回值列表中,因为是从小到大排序,所有最后一个值最大,即 order[-1]表示最后一个元素
        index = order[-1]
        #将置信度最大的候选框及其置信度值加入返回值列表中
        picked_boxes.append(bboxes[index])
        picked_score.append(score[index])
 
        # 获取当前置信度最大的候选框与其他任意候选框的相交面积,这里的order[:-1]表示除了最后一个元素之外的所有元素,np.max和np.maximum的实现功能是不同的
        #np.maximum的用法:np.maximum([2,4,7],[3,1,5])输出的结果是array([3, 4, 7]);np.maximum([2],[3,1,5])的输出结果是array([3, 2, 5])
        #np.max的用法:np.max([2,4,7])输出结果是7
        
        x11 = np.maximum(x1[index], x1[order[:-1]])
        y11 = np.maximum(y1[index], y1[order[:-1]])
        x22 = np.minimum(x2[index], x2[order[:-1]])
        y22 = np.minimum(y2[index], y2[order[:-1]])
        # 计算相交的面积,不重叠时面积设为0
        w = np.maximum(0.0, x22 - x11)
        h = np.maximum(0.0, y22 - y11)
        inter_area = w * h
 
        # 计算交并比
        iou = inter_area / (areas[index] + areas[order[:-1]] - inter_area)
        # 获取IoU小于阈值的候选框的索引
        keep_boxes = np.where(iou < threshold)
        #更新order,以便保留IoU小于阈值的框,
        order = order[keep_boxes]
    # 返回NMS后的框及分类结果   
    return picked_boxes, picked_score

假设有检测结果如下:当阈值threshold设置的越大,则保留越多的候选框

  • 当threshold取0.3时:

bounding = [(187, 82, 337, 317), (150, 67, 305, 282), (246, 121, 368, 304)]
confidence_score = [0.9, 0.65, 0.8]
threshold = 0.3
picked_boxes, picked_score = nms(bounding, confidence_score, threshold)
print('阈值threshold为:', threshold)
print('NMS后得到的bbox是:', picked_boxes)
print('NMS后得到的bbox的confidences是:', picked_score)

返回结果:

阈值threshold为: 0.3
NMS后得到的bbox是: [array([187,  82, 337, 317])]
NMS后得到的bbox的confidences是: [0.9]

当threshold取0.5时:

bounding = [(187, 82, 337, 317), (150, 67, 305, 282), (246, 121, 368, 304)]
confidence_score = [0.9, 0.65, 0.8]
threshold = 0.5
picked_boxes, picked_score = nms(bounding, confidence_score, threshold)
print('阈值threshold为:', threshold)
print('NMS后得到的bbox是:', picked_boxes)
print('NMS后得到的bbox的confidences是:', picked_score)

返回结果:

阈值threshold为: 0.5
NMS后得到的bbox是: [array([187,  82, 337, 317]), array([246, 121, 368, 304])]
NMS后得到的bbox的confidences是: [0.9, 0.8]

上述所讲的NMS方法都是先将检测框按照类别进行分类,然后对对同一类别的检测框应用NMS。但是在实际的任务中,如果所预测的类别很多时,那么这种效率非常低。所以有些时候我们会使用新的方法进行NMS:它的大致思想是先将不同类别的预测框在坐标位置上尽可能的区分开,然后就可以一次性对所有预测框进行NMS(此时不用先进行分类,然后分别对每一个类别依次做NMS)

,比如下图所示,蓝色方框的类别索引是1,黄色方框的类别索引是2,这些不同类别的预测框在位置上靠的很近,此时如果直接对所有类别同时做NMS,效果就很差。所以我们会设法将蓝色方框和黄色方框分离开,本例的方法是首先找到所有方框中坐标值最大的数值max_value,比如这里是81,

然后使用类别索引 indxs与val_value相乘,得到不同类别框的偏移量offsets,它的公式是:offsets=indxs*max_value

比如对于类别索引为1的方框,它的偏移量是offsets=indxs*max_value=1*81=81,对于类别索引为1的方框,它的偏移量是offsets=indxs*max_value=2*81=162

计算完每个类别的偏移量后,我们就得到新的预测框的坐标以及其对于的新位置,如下所示。然后就可以一次性对所有预测框进行NMS(此时不用先进行分类,然后分别对每一个类别依次做NMS)

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++//

概括非极大抑制的功能就是:

筛选出一定区域内属于同一种类得分最大的框。

1、非极大抑制NMS的实现过程
本博文实现的是多分类的非极大抑制:
输入shape为[ batch_size, all_anchors, 5+num_classes ]

第一个维度是图片的数量。
第二个维度是所有的预测框。
第三个维度是所有的预测框的预测结果。

非极大抑制的执行过程如下所示:
1、对所有图片进行循环。
2、找出该图片中得分大于门限函数的框。在进行重合框筛选前就进行得分的筛选可以大幅度减少框的数量。
3、判断第2步中获得的框的种类与得分。取出预测结果中框的位置与之进行堆叠。此时最后一维度里面的内容由5+num_classes变成了4+1+2,四个参数代表框的位置,一个参数代表预测框是否包含物体,两个参数分别代表种类的置信度与种类。
4、对种类进行循环,非极大抑制的作用是筛选出一定区域内属于同一种类得分最大的框,对种类进行循环可以帮助我们对每一个类分别进行非极大抑制。
5、根据得分对该种类进行从大到小排序。
6、每次取出得分最大的框,计算其与其它所有预测框的重合程度,重合程度过大的则剔除。

视频中实现的代码是numpy形式,而且库比较久远。这里改成pytorch的形式,且适应当前的库。

实现代码如下:

def bbox_iou(self, box1, box2, x1y1x2y2=True):
    """
        计算IOU
    """
    if not x1y1x2y2:
        b1_x1, b1_x2 = box1[:, 0] - box1[:, 2] / 2, box1[:, 0] + box1[:, 2] / 2
        b1_y1, b1_y2 = box1[:, 1] - box1[:, 3] / 2, box1[:, 1] + box1[:, 3] / 2
        b2_x1, b2_x2 = box2[:, 0] - box2[:, 2] / 2, box2[:, 0] + box2[:, 2] / 2
        b2_y1, b2_y2 = box2[:, 1] - box2[:, 3] / 2, box2[:, 1] + box2[:, 3] / 2
    else:
        b1_x1, b1_y1, b1_x2, b1_y2 = box1[:, 0], box1[:, 1], box1[:, 2], box1[:, 3]
        b2_x1, b2_y1, b2_x2, b2_y2 = box2[:, 0], box2[:, 1], box2[:, 2], box2[:, 3]

    inter_rect_x1 = torch.max(b1_x1, b2_x1)
    inter_rect_y1 = torch.max(b1_y1, b2_y1)
    inter_rect_x2 = torch.min(b1_x2, b2_x2)
    inter_rect_y2 = torch.min(b1_y2, b2_y2)

    inter_area = torch.clamp(inter_rect_x2 - inter_rect_x1, min=0) * \
                torch.clamp(inter_rect_y2 - inter_rect_y1, min=0)
                
    b1_area = (b1_x2 - b1_x1) * (b1_y2 - b1_y1)
    b2_area = (b2_x2 - b2_x1) * (b2_y2 - b2_y1)
    
    iou = inter_area / torch.clamp(b1_area + b2_area - inter_area, min = 1e-6)

    return iou

def non_max_suppression(self, prediction, num_classes, input_shape, image_shape, letterbox_image, conf_thres=0.5, nms_thres=0.4):
    #----------------------------------------------------------#
    #   将预测结果的格式转换成左上角右下角的格式。
    #   prediction  [batch_size, num_anchors, 85]
    #----------------------------------------------------------#
    box_corner          = prediction.new(prediction.shape)
    box_corner[:, :, 0] = prediction[:, :, 0] - prediction[:, :, 2] / 2
    box_corner[:, :, 1] = prediction[:, :, 1] - prediction[:, :, 3] / 2
    box_corner[:, :, 2] = prediction[:, :, 0] + prediction[:, :, 2] / 2
    box_corner[:, :, 3] = prediction[:, :, 1] + prediction[:, :, 3] / 2
    prediction[:, :, :4] = box_corner[:, :, :4]

    output = [None for _ in range(len(prediction))]
    for i, image_pred in enumerate(prediction):
        #----------------------------------------------------------#
        #   对种类预测部分取max。
        #   class_conf  [num_anchors, 1]    种类置信度
        #   class_pred  [num_anchors, 1]    种类
        #----------------------------------------------------------#
        class_conf, class_pred = torch.max(image_pred[:, 5:5 + num_classes], 1, keepdim=True)

        #----------------------------------------------------------#
        #   利用置信度进行第一轮筛选
        #----------------------------------------------------------#
        conf_mask = (image_pred[:, 4] * class_conf[:, 0] >= conf_thres).squeeze()

        #----------------------------------------------------------#
        #   根据置信度进行预测结果的筛选
        #----------------------------------------------------------#
        image_pred = image_pred[conf_mask]
        class_conf = class_conf[conf_mask]
        class_pred = class_pred[conf_mask]
        if not image_pred.size(0):
            continue
        #-------------------------------------------------------------------------#
        #   detections  [num_anchors, 7]
        #   7的内容为:x1, y1, x2, y2, obj_conf, class_conf, class_pred
        #-------------------------------------------------------------------------#
        detections = torch.cat((image_pred[:, :5], class_conf.float(), class_pred.float()), 1)

        #------------------------------------------#
        #   获得预测结果中包含的所有种类
        #------------------------------------------#
        unique_labels = detections[:, -1].cpu().unique()

        if prediction.is_cuda:
            unique_labels = unique_labels.cuda()
            detections = detections.cuda()

        for c in unique_labels:
            #------------------------------------------#
            #   获得某一类得分筛选后全部的预测结果
            #------------------------------------------#
            detections_class = detections[detections[:, -1] == c]

            # #------------------------------------------#
            # #   使用官方自带的非极大抑制会速度更快一些!
            # #------------------------------------------#
            # keep = nms(
            #     detections_class[:, :4],
            #     detections_class[:, 4] * detections_class[:, 5],
            #     nms_thres
            # )
            # max_detections = detections_class[keep]
            
            # 按照存在物体的置信度排序
            _, conf_sort_index = torch.sort(detections_class[:, 4]*detections_class[:, 5], descending=True)
            detections_class = detections_class[conf_sort_index]
            # 进行非极大抑制
            max_detections = []
            while detections_class.size(0):
                # 取出这一类置信度最高的,一步一步往下判断,判断重合程度是否大于nms_thres,如果是则去除掉
                max_detections.append(detections_class[0].unsqueeze(0))
                if len(detections_class) == 1:
                    break
                ious = self.bbox_iou(max_detections[-1], detections_class[1:])
                detections_class = detections_class[1:][ious < nms_thres]
            # 堆叠
            max_detections = torch.cat(max_detections).data
            
            # Add max detections to outputs
            output[i] = max_detections if output[i] is None else torch.cat((output[i], max_detections))
        
        if output[i] is not None:
            output[i]           = output[i].cpu().numpy()
            box_xy, box_wh      = (output[i][:, 0:2] + output[i][:, 2:4])/2, output[i][:, 2:4] - output[i][:, 0:2]
            output[i][:, :4]    = self.yolo_correct_boxes(box_xy, box_wh, input_shape, image_shape, letterbox_image)
    return output

 参考文章:【SSD算法】史上最全代码解析-核心篇 - 知乎

参考文章:睿智的目标检测31——非极大抑制NMS与Soft-NMS-CSDN博客

参考文章:NMS(非极大值抑制)_nms非极大值抑制-CSDN博客

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

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

相关文章

Sunday 算法介绍

1. Sunday 算法介绍 「Sunday 算法」 是一种在字符串中查找子串的算法&#xff0c;是 Daniel M.Sunday 于1990年提出的字符串模式匹配算法。 Sunday 算法思想&#xff1a;对于给定文本串 T 与模式串 p&#xff0c;先对模式串 p 进行预处理。然后在匹配的过程中&#xff0c;当发…

spy分析文件另存为弹框【selenium】

有时需要下载多个文件&#xff0c;但是不想保存在同一个目录下&#xff0c;需要做两步 selenium设置浏览器默认下载路径&#xff0c;这个路径需要是个不存在的路径操作文件另存为弹框 文章目录 selenium设置浏览器默认下载路径操作文件另存为弹框 selenium设置浏览器默认下载路…

Golang中map数据结构字段解析

Golang里map底层数据结构具体如下图所示&#xff1a; map其实就是一个指向 hmap 的指针&#xff0c;占用了8个字节 hmap各自段存放的字段意义如下&#xff1a; 字段含义countmap中元素的个数&#xff0c;对应len (map)的值flags状态标志位&#xff0c;标记map的一些状态B桶数…

MySQL大小写敏感、MySQL设置字段大小写敏感

文章目录 一、MySQL大小写敏感规则二、设置数据库及表名大小写敏感2.1、查询库名及表名是否大小写敏感2.2、修改库名及表名大小写敏感 三、MySQL列名大小写不敏感四、lower_case_table_name与校对规则4.1、验证校对规则影响大小写敏感4.1、验证校对规则影响排序 五、设置字段内…

FPGA静态时序分析与约束(三)、读懂vivado时序报告

系列文章目录 FPGA静态时序分析与约束&#xff08;一&#xff09;、理解亚稳态 FPGA静态时序分析与约束&#xff08;二&#xff09;、时序分析 文章目录 系列文章目录前言一、时序分析回顾二、打开vivado任意工程2.1 工程布局路由成功后&#xff0c;点击vivado左侧**IMPLEMENT…

SpringMVC 02

这里先附上前一篇的地址,以上系列均为博主的学习路线,仅供参考 初识Spring MVC-CSDN博客 下面我们从SpringMVC传递数组开始讲起 1.传递数组 传递数组的方式和传递普通变量的方式其实是相同的,下面我们附上传递的图片 RequestMapping("/r7")public String r1(String[…

【Git】本地仓库关联远程仓库

Git 本地项目关联远程仓库 本地 本地已有项目 ● 项目 07.GitLocalTest 包含有一个js ○ test.js 远程仓库 ● 远程仓库地址 ○ https://github.com/Sonnenlicht77/gitTest.git ○ 仓库只有一个 readme.md 关联 1.本地 1.1 本地仓库 ● git init ● git add . ● gi…

考研复习C语言进阶(3)

结构体 1 结构体的声明 1.1 结构的基础知识 结构是一些值的集合&#xff0c;这些值称为成员变量。结构的每个成员可以是不同类型的变量。 1.2 结构的声明 struct tag { member-list; }variable-list; 例如描述一个学生&#xff1a; struct Stu { char name[20];//名字 int ag…

Java开发从入门到精通(九):Java的面向对象OOP:成员变量、成员方法、类变量、类方法、代码块、单例设计模式

Java大数据开发和安全开发 &#xff08;一)Java的变量和方法1.1 成员变量1.2 成员方法1.3 static关键字1.3.1 static修饰成员变量1.3.1 static修饰成员变量的应用场景1.3.1 static修饰成员方法1.3.1 static修饰成员方法的应用场景1.3.1 static的注意事项1.3.1 static的应用知识…

03-java基础-运算符(数据类型转换)、原码、补码、反码

一、运算符 一、1、算术运算符 在代码中如果有小数参与运算&#xff0c;结果有可能会不精确。 一、1.1、数字相加 一、1.1.1、类型转换的分类&#xff08;2种&#xff09; 一、1.1.1.1、类型转换的分类1-----隐式转换 一、1.1.1.1、类型转换的分类2-----强制转换 一、1.2、字符…

海外媒体宣发套餐推广:如何选择最佳方案-华媒舍

在信息时代&#xff0c;传播和宣传已经成为各个行业发展的关键部分。尤其对于拓展国际市场的企业来说&#xff0c;海外媒体宣发更是至关重要。由于各种原因&#xff0c;很多企业在选择海外媒体宣发套餐时感到困惑。本文将为您介绍如何选择最佳的海外媒体宣发方案。 1.了解目标市…

工匠的发展与兴衰趋势-机器人篇

这是一篇纯纯调侃的博客&#xff0c;如有雷同纯属意外。 之前&#xff0c;写过&#xff1a; 从2050回顾2020&#xff0c;职业规划与技术路径&#xff08;节选&#xff09; 从2050回顾2020&#xff0c;职业规划与技术路径&#xff08;节选&#xff09;补充 未来以“工”为主的…

LarkXR上新了 | Apollo多终端与XR体验的优化创新

作为领先的数字平行世界产品技术提供方&#xff0c;「Paraverse平行云」一直致力于为企业和开发者提供企业级实时云渲染解决方案。其多终端接入产品LarkXR Apollo&#xff0c;基于底层Runtime技术&#xff0c;实现了在Windows、Linux、MacOS、Android、iOS等多种操作系统下&…

centos破解root密码以及如何防止他人破解root密码

目录 破解root密码 服务器重启 1.再重启页面上下选择第一个按e进入内核编辑模式 2.找到linux16开头的一行&#xff0c;光标移动到最后添加 init/bin/sh Ctrlx 保存 3.进入单用户模式 4.重新挂在根分区 5.关闭selinux 6.更新密码 passwd 7.在根分区下面创建一个隐藏文件…

字符串函数(C语言详解)

1.字符串简介 字符串是一串连续的且以\0结尾的字符 char arr[]"zhangsan";//将字符串存到数组里面 char*a"lisi";//常量字符串 char arr1[]{z,h,a,n,g};//字符数组 注意&#xff1a; 1.以第一种形式初始化字符串时&#xff0c;计算机会自动在字符串末尾加…

leetcode-打家劫舍专题系列(动态规划)

198.打家劫舍 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 给定一个代表每个…

C# WPF中设置图标时出现TypeConverterMarkupExtension异常

异常内容为&#xff1a;System.Windows.Baml2006.TypeConverterMarkupExtension 是因为有些地方比如菜单和左上角默认的图标等&#xff0c;只能使用ico格式的文件&#xff0c;如果设置的是png格式的文件&#xff0c;就会出现此错误&#xff01;通过在线转ico的方式把png转换一…

【Maven学习笔记】Maven入门教程(适合新手反复观看学习)

Maven学习笔记 Maven的简要介绍Maven的安装和配置Maven的安装Maven安装的常用配置 Maven的使用入门编写pom编写主代码编写测试代码打包和运行使用Archetype生成项目骨架 Maven核心概念的阐述坐标案例分析依赖依赖的范围传递性依赖依赖范围依赖调节可选依赖Maven依赖常用的技巧 …

《ARM汇编与逆向工程》读书心得与实战体验

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 &#x1f4d8; 一、引言 &#x1f4dd; 二、…