保姆级 Keras 实现 YOLO v3 二

保姆级 Keras 实现 YOLO v3 二

  • 一. 数据准备
  • 二. 从 xml 或者 json 文件中读出标注信息
  • 三. K-Means 计算 anchor box 聚类尺寸
    • 读出所有标注框尺寸
    • K-Means 聚类
  • 四. 代码下载

上一篇 文章中, 我们完成了 YOLO v3 的网络定义, 相当于完成了前向计算功能, 但此时网络中的参数处于随机状态, 预测并没有任何意义. 接下来的工作就是要从头开始训练, 让网络调整其参数以达到预期的效果

这里需要定义一些常量, 因为后面的函数会用到, 训练自己的数据集时也需要修改的

# 模型配置
LONG_SIDE = 416        # 输入图像缩放长边尺寸
STRIDES = (8, 16, 32)  # 每种特征图的下采样倍数
CLUSTER_K = 9          # anchor box 聚类中心数量
NEG_THRES = 0.4        # 负样本阈值, 这个值按你喜欢的来改
POS_THRES = 0.7        # 正样本阈值, 这个是为了增加更多的正样本而设置, 后面会有解释

# 类别列表, 不分先后
CATEGORIES = ("aeroplane", "bicycle", "bird", "boat", "bottle",
              "bus", "car", "cat", "chair", "cow",
              "diningtable", "dog", "horse", "motorbike", "person",
              "pottedplant", "sheep", "sofa", "train", "tvmonitor")

DATA_PATH = "data_set" # 这样写表示相对路径, 也可以写成绝对路径, 你喜欢就好

一. 数据准备

一般我们都是为了训练自己的数据集, 这就涉及到做标签的问题, 不过这不是本文的重点, 可以参考《保姆级 Keras 实现 Faster R-CNN 一》, 里面有说明如何标注. 标注完成后, 图像和标签文件放到同一个目录中, 方便处理. 假设放到了 data_set 中

如果是使用已经标注好的数据, 那就不用再标注, 只是要理解标注文件中的信息, 像 VOC2007 标注文件是 xml 格式的, 什么格式无所谓, 只要能读出来就行. 我们关注的是标注文件中目标框的坐标和类别.《保姆级 Keras 实现 Faster R-CNN 二》中也有相应的说明, 这里就不再重复讲了. 也一样将图像和对应的标注文件放到一个文件夹中, 假设放到了 data_set 中, 如下图这样

data_set
因为我们把图像和对应的标注文件放到了同一个文件夹中, 接下就就需要将各个图像和标注文件的路径列出来, 再划分训练集和验证集

# 取得图像和标注文件路径
# data_set_path: 数据集所在路径
# split_rate: 这些文件中用于训练, 验证, 测试所占的比例
#             如果为 None, 则不区分, 直接返回全部
#             如果只写一个小数, 如 0.8, 则表示 80% 为训练集, 20% 为验证集, 没有测试集
#             如果是一个 tuple 或 list, 只有一个元素的话, 同上面的一个小数的情况
# shuffle_enable: 是否要打乱顺序
# 返回训练集, 验证集和验证集路径列表
def get_data_set(data_set_path, split_rate = (0.7, 0.2, 0.1), shuffle_enable = True):
    data_set = []
    files = os.listdir(data_set_path)
    
    for f in files:
        ext = osp.splitext(f)[1]
        if ext in (".jpg", ".png", ".bmp"):            
            img_path = osp.join(data_set_path, f)
            
            ann_type = "" # 标注文件类型
            ann_path = img_path.replace(ext, ".json")
            
            if osp.exists(ann_path):
                ann_type = "json"
            else:
                ann_path = img_path.replace(ext, ".xml")
                if osp.exists(ann_path):
                    ann_type = "xml"
                
            if "" == ann_type:
                continue
                
            data_set.append((img_path, ann_path, ann_type))
        
    if shuffle_enable:
        shuffle(data_set)
        
    if None == split_rate:
        return data_set

    total_num = len(data_set)

    if isinstance(split_rate, float) or 1 == len(split_rate):
        if isinstance(split_rate, float):
            split_rate = [split_rate]
        train_pos = int(total_num * split_rate[0])
        train_set = data_set[: train_pos]
        valid_set = data_set[train_pos: ]

        return train_set, valid_set

    elif isinstance(split_rate, tuple) or isinstance(split_rate, list):
        list_len = len(split_rate)
        assert(list_len > 1)

        train_pos = int(total_num * split_rate[0])
        valid_pos = int(total_num * (split_rate[0] + split_rate[1]))

        train_set = data_set[0: train_pos]
        valid_set = data_set[train_pos: valid_pos]
        test_set = data_set[valid_pos: ]

        return train_set, valid_set, test_set

上面的函数中, 区分了标注文件的类型, VOC2007 是 xml, 如何使用 Labelme 标注的话, 标注文件则是 json, 下面测试一下

# 取得目录
train_set, valid_set, test_set = get_data_set(DATA_PATH, split_rate = (0.8, 0.1, 0.1))

print("Total number:", len(train_set) + len(valid_set) + len(test_set),
      " Train number:", len(train_set),
      " Valid number:", len(valid_set),
      " Test number:", len(test_set))

# 输出第一个元素
print("First element:", train_set[0])

输出如下

Total number: 5010  Train number: 4008  Valid number: 501  Test number: 501
First element: ('data_set\\003885.jpg', 'data_set\\003885.xml', 'xml')

因为 YOLO 的性能已经经过了验证, 所以其实不需要测试集, 这样参与训练的图像就会多一点, 训练出来的模型也会好一点, 划分是就可以这样

# 取得目录
train_set, valid_set, test_set = get_data_set(DATA_PATH, split_rate = 0.95)

就像征性的留一点用作验证集了

二. 从 xml 或者 json 文件中读出标注信息

前面讲过, 我们需要的是标注框的坐标和类别, 所以只需要从标注文件中读取相关信息即可

# 从 xml 或 json 文件中读出 ground_truth
# data_set: get_data_set 函数返回的列表
# categories: 类别列表
# file_type: 标注文件类型
# 返回 ground_truth 坐标与类别
def get_ground_truth(label_path, file_type, categories):
    ground_truth = []
    with open(label_path, 'r', encoding = "utf-8") as f:
        if "json" == file_type:
            jsn = f.read()
            js_dict = json.loads(jsn)        
            shapes = js_dict["shapes"] # 取出所有图形

            for shape in shapes:
                if shape["label"] in categories:                
                    pts = shape["points"]
                    x1 = round(pts[0][0])
                    x2 = round(pts[1][0])
                    y1 = round(pts[0][1])
                    y2 = round(pts[1][1])

                    # 防止有些人标注的时候喜欢从右下角拉到左上角
                    if x1 > x2:
                        x1, x2 = x2, x1
                    if y1 > y2:
                        y1, y2 = y2, y1
                        
                    bnd_box = [x1, y1, x2, y2]
                    cls_id = categories.index(shape["label"])

                    # 把 bnd_box 和 cls_id 组合在一起, 后面可有会用得上
                    ground_truth.append([bnd_box, cls_id])
        elif "xml" == file_type:
            tree = et.parse(f)
            root = tree.getroot()
            for obj in root.iter("object"):

                cls_id = obj.find("name").text
                cls_id = categories.index(cls_id) # 类别 id

                bnd_box = obj.find("bndbox")
                bnd_box = [int(bnd_box.find("xmin").text),
                           int(bnd_box.find("ymin").text),
                           int(bnd_box.find("xmax").text),
                           int(bnd_box.find("ymax").text)]

                # 把 bnd_box 和 cls_id 组合在一起, 后面可有会用得上
                ground_truth.append([bnd_box, cls_id])
            
    return ground_truth

在返回的数据中, 包含的是目标框左上角和右下角的坐标, 还有目标类别序号, 接下来测试 get_ground_truth 函数

# 测试 get_ground_truth
test_idx = random.randint(0, len(train_set)) # 测试图像的序号
label_data = train_set[test_idx] # train_set 上面已经定义过了
gts = get_ground_truth(label_data[1], label_data[2], CATEGORIES)

image = cv.imread(label_data[0])
img_copy = image.copy()
print(img_copy.shape)

for gt in gts:    
    print(gt, "class:", CATEGORIES[gt[1]])
    cv.rectangle(img_copy, (gt[0][0], gt[0][1]), (gt[0][2], gt[0][3]),
                 (0, random.randint(128, 256), 0), 2)
    
plt.figure("label_box", figsize = (6, 3))
plt.imshow(img_copy[..., : : -1])
plt.show()
(334, 500, 3)
[[28, 44, 91, 113], 1] class: aeroplane
[[47, 151, 111, 212], 1] class: aeroplane
[[65, 239, 127, 299], 1] class: aeroplane
[[189, 143, 255, 205], 1] class: aeroplane
[[164, 29, 228, 96], 1] class: aeroplane
[[397, 15, 462, 83], 1] class: aeroplane

show_ground_truth

这样看是没有问题, 但是考虑到网络的输入尺寸是 416 × 416 416 \times 416 416×416, 所以需要对图像进行缩放, 那标注框也需要进行相应的缩放. 我的做法是保持图像比例, 将图像长边变成 416 416 416, 短边进行填充, 这样可以保证目标不会因为图像缩放而变形. 现修改 get_ground_truth 函数如下, 增加了对坐标的缩放和偏移, 还返回了图像的缩放系数与填充尺寸, 方便后面的函数操作

# 从 xml 或 json 文件中读出 ground_truth
# data_set: get_data_set 函数返回的列表
# categories: 类别列表
# file_type: 标注文件类型
# 返回 缩放系数, 填充尺寸, ground_truth 坐标与类别
def get_ground_truth(label_path, file_type, categories):
    ground_truth = []    
    scale = 1.0  # 缩放比例
    pad_size = 0 # 填充尺寸
    with open(label_path, 'r', encoding = "utf-8") as f:
        if "json" == file_type:
            jsn = f.read()
            js_dict = json.loads(jsn)        
            shapes = js_dict["shapes"] # 取出所有图形
            
            # 增加对图像尺寸的判断
            image_rows = js_dict["imageHeight"]
            image_cols = js_dict["imageWidth"]
            
            if image_rows < image_cols:
                scale = LONG_SIDE / image_cols
                pad_size = (LONG_SIDE - image_rows * scale) / 2
            else:
                scale = LONG_SIDE / image_rows
                pad_size = (LONG_SIDE - image_cols * scale) / 2
                
            for shape in shapes:
                if shape["label"] in categories:                
                    pts = shape["points"]
                    x1 = round(pts[0][0])
                    x2 = round(pts[1][0])
                    y1 = round(pts[0][1])
                    y2 = round(pts[1][1])

                    # 防止有些人标注的时候喜欢从右下角拉到左上角
                    if x1 > x2:
                        x1, x2 = x2, x1
                    if y1 > y2:
                        y1, y2 = y2, y1
                    
                    bnd_box = [x1, y1, x2, y2]
                    
                    if image_rows < image_cols:
                        bnd_box[0] = round(bnd_box[0] * scale)
                        bnd_box[2] = round(bnd_box[2] * scale)
                        bnd_box[1] = round(bnd_box[1] * scale + pad_size)
                        bnd_box[3] = round(bnd_box[3] * scale + pad_size)
                    else:
                        bnd_box[0] = round(bnd_box[0] * scale + pad_size)
                        bnd_box[2] = round(bnd_box[2] * scale + pad_size)
                        bnd_box[1] = round(bnd_box[1] * scale)
                        bnd_box[3] = round(bnd_box[3] * scale)
                        
                    cls_id = categories.index(shape["label"])

                    # 把 bnd_box 和 cls_id 组合在一起, 后面可有会用得上
                    ground_truth.append([bnd_box, cls_id])
        elif "xml" == file_type:
            tree = et.parse(f)
            root = tree.getroot()
            
            # 增加对图像尺寸的判断
            image_shape = root.find("size")
            image_rows = int(image_shape.find("height").text)
            image_cols = int(image_shape.find("width").text)
            
            if image_rows < image_cols:
                scale = LONG_SIDE / image_cols
                pad_size = (LONG_SIDE - image_rows * scale) / 2
            else:
                scale = LONG_SIDE / image_rows
                pad_size = (LONG_SIDE - image_cols * scale) / 2
            
            for obj in root.iter("object"):
                cls_id = obj.find("name").text
                cls_id = categories.index(cls_id) # 类别 id

                bnd_box = obj.find("bndbox")
                bnd_box = [int(bnd_box.find("xmin").text),
                           int(bnd_box.find("ymin").text),
                           int(bnd_box.find("xmax").text),
                           int(bnd_box.find("ymax").text)]

                if image_rows < image_cols:
                    bnd_box[0] = round(bnd_box[0] * scale)
                    bnd_box[2] = round(bnd_box[2] * scale)
                    bnd_box[1] = round(bnd_box[1] * scale + pad_size)
                    bnd_box[3] = round(bnd_box[3] * scale + pad_size)
                else:
                    bnd_box[0] = round(bnd_box[0] * scale + pad_size)
                    bnd_box[2] = round(bnd_box[2] * scale + pad_size)
                    bnd_box[1] = round(bnd_box[1] * scale)
                    bnd_box[3] = round(bnd_box[3] * scale)
                        
                # 把 bnd_box 和 cls_id 组合在一起, 后面可有会用得上
                ground_truth.append([bnd_box, cls_id])
            
    return scale, pad_size, ground_truth

测试函数也增加相应的缩放与图像填充

# 测试 get_ground_truth
test_idx = random.randint(0, len(train_set)) # 测试图像的序号
label_data = train_set[test_idx] # train_set 上面已经定义过了
# 增加了返回的缩放系数与填充尺寸
scale, pad_size, gts = get_ground_truth(label_data[1], label_data[2], CATEGORIES)

image = cv.imread(label_data[0])
img_copy = cv.resize(image, (round(image.shape[1] * scale), round(image.shape[0] * scale)),
                     interpolation = cv.INTER_LINEAR)

if img_copy.shape[0] < img_copy.shape[1]:
    img_copy = cv.copyMakeBorder(img_copy, round(pad_size), round(pad_size), 0, 0, cv.BORDER_CONSTANT, (0, 0, 0))
else:
    img_copy = cv.copyMakeBorder(img_copy, 0, 0, round(pad_size), round(pad_size), cv.BORDER_CONSTANT, (0, 0, 0))

print(img_copy.shape)

for gt in gts:
    print(gt, "class:", CATEGORIES[gt[1]])
    cv.rectangle(img_copy, (gt[0][0], gt[0][1]), (gt[0][2], gt[0][3]),
                 (0, random.randint(128, 256), 0), 2)
    
plt.figure("label_box", figsize = (6, 3))
plt.imshow(img_copy[..., : : -1])
plt.show()

效果如下, 图像的尺寸变成了 ( 416 × 416 ) (416 \times 416) (416×416)

(416, 416, 3)
[[23, 106, 76, 163], 0] class: aeroplane
[[39, 195, 92, 245], 0] class: aeroplane
[[54, 268, 106, 318], 0] class: aeroplane
[[157, 188, 212, 240], 0] class: aeroplane
[[136, 93, 190, 149], 0] class: aeroplane
[[330, 82, 384, 138], 0] class: aeroplane

resized_ground_truth

三. K-Means 计算 anchor box 聚类尺寸

上面已经可以读出标签文件中的各目标框的坐标, 那接下为就可以用这些坐标来计算我们想要的 k k k 种 anchor box 尺寸了, 这里 k = 9 k = 9 k=9, 所以聚类个数为 9 9 9

读出所有标注框尺寸

因为在聚类的时候, 距离公式是 1 − I o U 1 - IoU 1IoU, 而各标注框位置是随机的, 所以需要将标注框左上角移动到相同的位置, 这样才有计算的基准, 这个相同位置最简单的就是 ( 0 , 0 ) (0, 0) (0,0), 所以读出来的标注框就可在简化成为 ( w , h ) (w, h) (w,h)

# 读出所有标注框
all_boxes = []

for s in (train_set, valid_set, test_set):
    for each in s:
        _, __, gts = get_ground_truth(each[1], each[2], CATEGORIES)
        for box, _ in gts:
            all_boxes.append((box[2] - box[0], box[3] - box[1]))

print("box_num:", len(all_boxes))
print(all_boxes[: 4])
box_num: 15658
[(254, 228), (306, 383), (56, 45), (59, 110)]

K-Means 聚类

既然要用 I o U IoU IoU 计算距离, 那就要先定义计算 I o U IoU IoU 的函数

# 计算聚类 IoU
# box: 单个真实框 (w, h)
# clusters: 聚类中心的 (w, h)
# 返回标注框和所有聚类中心的 IoU 值
def cluster_iou(box, clusters):
    # 交集
    x = np.minimum(box[0], clusters[:, 0])
    y = np.minimum(box[1], clusters[:, 1])    
    intersection = x * y
    
    # 并集
    area_box = box[0] * box[1]
    area_cluster = clusters[:, 0] * clusters[:, 1]    
    union = area_box + area_cluster - intersection
    
    return intersection / union

现在就可以定义一个函数来聚类 anchor box 了

# 使用 k-means 聚类算法和 1-IoU 距离函数来确定 anchor box
# boxes: 标注框 (w, h)
# k: 聚类的数量
# 返回聚类中心
def kmeans_anchor(boxes, k):
    n = boxes.shape[0]
    distances = np.empty((n, k))
    last_clusters = np.zeros((n,))
    
    # 随机初始化聚类中心
    np.random.seed(0)
    clusters = boxes[np.random.choice(n, k, replace = False)]

    while True:
        for i, box in enumerate(boxes):
            distances[i] = 1 - cluster_iou(box, clusters)
        
        nearest_clusters = np.argmin(distances, axis = 1)
        
        if (last_clusters == nearest_clusters).all():
            break
        
        # 更新聚类中心
        for cluster in range(k):
            clusters[cluster] = np.median(boxes[nearest_clusters == cluster], axis = 0)
        
        last_clusters = nearest_clusters

    return clusters

上面的函数中, 更新聚类中心用的是中值(np.median), 也可以使用平均值 (np.mean), 只是平均值容易受距离较远点的影响, 接下来调用函数得到 k k k 个 anchor box 尺寸

# 聚类 k 个 anchor box 尺寸
cluster_anchors = kmeans_anchor(np.array(all_boxes), CLUSTER_K)
# 计算矩形面积从小到大排序
areas = cluster_anchors[:, 0] * cluster_anchors[:, 1]
sorted_indices = np.argsort(areas)
cluster_anchors = cluster_anchors[sorted_indices]
print(cluster_anchors)
[[ 16  22]
 [ 26  58]
 [ 48  34]
 [ 53  87]
 [118  85]
 [ 85 160]
 [245 134]
 [156 228]
 [310 263]]

上面的 9 9 9 个尺寸便是图像缩放后的聚类尺寸, 你运行的代码结果可能和我的不一样, 因为聚类算法会受初始值的影响, 不过也差不多

四. 代码下载

示例代码可下载 Jupyter Notebook 示例代码

上一篇: 保姆级 Keras 实现 YOLO v3 一
下一篇: 保姆级 Keras 实现 YOLO v3 三

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

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

相关文章

MySQL数据库 函数

目录 函数概述 字符串函数 数值函数 日期函数 流程函数 函数概述 函数是指一段可以直接被另一段程序调用的程序或代码。也就意味着&#xff0c;这一段程序或代码在MysQL中已经给我们提供了&#xff0c;我们要做的就是在合适的业务场景调用对应的函数完成对应的业务需求即…

前后端传参中遇见的问题

前后端传参经常容易出错&#xff0c;本文记录开发springBootMybatis-plusvuecli项目中出现的传参问题及解决办法 1.前后端没有跨域配置&#xff0c;报错 解决方法&#xff1a;后端进行跨域配置&#xff0c;拷贝CorsConfig类 package com.example.xxxx.config;import org.spr…

k8s-ingress 8

ExternalName类型 当集群外的资源往集群内迁移时&#xff0c;地址并不稳定&#xff0c;访问域名或者访问方式等会产生变化&#xff1b; 使用svc的方式来做可以保证不会改变&#xff1a;内部直接访问svc&#xff1b;外部会在dns上加上解析&#xff0c;以确保访问到外部地址。 …

2024年软件测试入坑指南,新人必看系列

本科非计算机专业&#xff0c;在深圳做了四年软件测试工作&#xff0c;从之前的一脸懵的点点点&#xff0c;到现在会点自动化测试&#xff0c;说一点点非计算机专业人员从事软件测试的心得体会&#xff0c;仅供参考交流。 如果你是非计算机专业&#xff0c;毕业不久&#xff0…

CMOS电源稳压器LDO

一、基本概述 TX6213是一款300mA Low Power LDO&#xff0c;输入电压2.5V~6.5V&#xff0c;输出范围1.0V~3.3V&#xff0c;输出电流300mA&#xff0c;PSRR为75dB 1KHz&#xff0c;压差为220mV IOUT200mA。 二、应用场景 MP3/MP4 Players Cellphones, radiophone, digital ca…

智能优化算法应用:基于适应度相关算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于适应度相关算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于适应度相关算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.适应度相关算法4.实验参数设定5.算法…

C++设计模式-Builder 构建器

通过“对象创建” 模式绕开new&#xff0c;来避免对象创建&#xff08;new&#xff09;过程中所导致的紧耦合&#xff08;依赖具体类&#xff09;&#xff0c;从而支持对象创建的稳定。它是接口抽象之后的第一步工作。 一、动机 在软件系统中&#xff0c;有时候面临着“一个复…

Python:(Sentinel-1)如何解析SNAP输出的HDF5文件并输出为GeoTIFF?

博客已同步微信公众号&#xff1a;GIS茄子&#xff1b;若博客出现纰漏或有更多问题交流欢迎关注GIS茄子&#xff0c;或者邮箱联系(推荐-见主页). Python&#xff1a;&#xff08;Sentinel-1&#xff09;如何解析SNAP输出的HDF5文件并输出为GeoTIFF&#xff1f; 01 前言 最近…

MySQL安装——备赛笔记——2024全国职业院校技能大赛“大数据应用开发”赛项——任务2:离线数据处理

MySQLhttps://www.mysql.com/ 将下发的ds_db01.sql数据库文件放置mysql中 12、编写Scala代码&#xff0c;使用Spark将MySQL的ds_db01库中表user_info的全量数据抽取到Hive的ods库中表user_info。字段名称、类型不变&#xff0c;同时添加静态分区&#xff0c;分区字段为etl_da…

TCP单人聊天

TCP和UDP两种通信方式它们都有着自己的优点和缺点 这两种通讯方式不通的地方就是TCP是一对一通信 UDP是一对多的通信方式 TCP通信 TCP通信方式呢 主要的通讯方式是一对一的通讯方式&#xff0c;也有着优点和缺点 它的优点对比于UDP来说就是可靠一点 因为它的通讯方式是需…

谈谈你知道的设计模式?请手动实现单例模式 , Spring 等框架中使用了哪些模式?

文章目录 谈谈你知道的设计模式请手动实现单例模式Spring等框架中使用哪些设计模式&#xff1f;设计模式分类 谈谈你知道的设计模式 我们知道 InputStream 是一个抽象类&#xff0c;标准类库中提供了 FileInputStream、ByteArrayInputStream 等各种不同的子类&#xff0c;分别…

8款AI写作神器,轻松创作高质量内容

随着AI技术的不断发展&#xff0c;AI生成文案平台也逐渐成为一种新型的写作工具。这些平台利用先进的算法和自然语言处理技术&#xff0c;能够快速生成高质量的文案内容。不仅可以提高写作效率&#xff0c;还可以帮助创作者更好地表达思想和创意。AIGCer介绍几款好用的AI写作工…

什么?Figma 的 fig 文件格式居然被破解出来了

大家好&#xff0c;我是前端西瓜哥。 上周图形编辑器交流群里有人问&#xff0c;对于 Figma 导出的 fig 文件&#xff0c;该如何解析其格式&#xff0c;拿到可读数据。 经过群友的一番讨论&#xff0c;这个问题最后算是解决了。 fig 文件 导出 Figma 的设计文件&#xff0c…

智能优化算法应用:基于人工电场算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于人工电场算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于人工电场算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.人工电场算法4.实验参数设定5.算法结果6.…

产品调研——AI平台

本文主要记录了对腾讯云-TIONE平台、华为云-ModelArt等主流AI平台的产品调研。 交互式建模 简单点说就是提供了带训练资源的云IDE&#xff0c;使用形态包括Notebook、VsCode等。 腾讯云-TI平台 TI平台将tensorflow、pytorch、spark环境等均集成到一个Notebook容器中&#xf…

深入理解强化学习——马尔可夫决策过程:价值迭代-[价值迭代算法]

分类目录&#xff1a;《深入理解强化学习》总目录 文章《深入理解强化学习——马尔可夫决策过程&#xff1a;价值迭代-[最优性原理]》和文章《深入理解强化学习——马尔可夫决策过程&#xff1a;价值迭代-[确认性价值迭代]》介绍了价值迭代的基础知识&#xff0c;本文将介绍价值…

AX7A200教程(9): ov5640摄像头输出显示720p视频

一&#xff0c;功能框图 ov5640摄像头视频通过ddr3缓存后&#xff0c;最后使用hdmi接口进行输出显示 二&#xff0c;摄像头硬件说明 2.1&#xff0c;像头硬件管脚 如下图所示&#xff0c;一共18个管脚 2.2&#xff0c;摄像头电源初始化时序 因这个ov5640摄像头是买的老摄像…

制造企业MES管理系统可以和AI结合应用吗

在当今的数字化时代&#xff0c;人工智能AI和MES生产管理系统的结合将成为制造企业发展的重要趋势。这种结合可以为制造企业带来许多优势&#xff0c;如提高生产效率、降低成本、优化资源利用等。本文将探讨MES管理系统和AI的结合以及它们在制造企业中的应用&#xff0c;并分析…

【JavaWeb学习笔记】11 - WEB工程路径专题

一、工程路径问题 1.引入该问题 通过这几个去访问很麻烦 二、工程路径解决方案 1.相对路径 1.说明:使用相对路径来解决&#xff0c;一 个非常重要的规则:页面所有的相对路径&#xff0c;在默认情况下&#xff0c;都会参考当前浏览器地址栏的路径http:/ /ip:port/工程名/来进…

BM61 矩阵最长递增路径

题目 矩阵最长递增路径 给定一个 n 行 m 列矩阵 matrix &#xff0c;矩阵内所有数均为非负整数。 你需要在矩阵中找到一条最长路径&#xff0c;使这条路径上的元素是递增的。并输出这条最长路径的长度。 这个路径必须满足以下条件&#xff1a; 1. 对于每个单元格&#xff0c;你…