【PyTorch】基于YOLO的多目标检测项目(二)

news2024/11/17 12:32:55

【PyTorch】基于YOLO的多目标检测项目(一)

【PyTorch】基于YOLO的多目标检测项目(二)

YOLO-v3网络由跨距为2的卷积层、跳跃连接层和上采样层组成,没有池化层。网络接收一幅416 * 416的图像作为输入,并提供三个YOLO输出。

目录

准备配置文件

搭建YOLO模型 

搭建PyTorch模块

搭建DarkNet模型

定义损失函数

训练模型

部署模型


准备配置文件

新建一个py文件导入以下代码,命名为myutils.py作为配置文件,辅助构建模型。

import torch
from torch import nn


device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

def parse_model_config(path2file):
    cfg_file = open(path2file, 'r')
    lines = cfg_file.read().split('\n')

    lines = [x for x in lines if x and not x.startswith('#')]
    lines = [x.rstrip().lstrip() for x in lines] 
    
    blocks_list = []
    for line in lines:
        if line.startswith('['): 
            blocks_list.append({})
            blocks_list[-1]['type'] = line[1:-1].rstrip()
        else:
            key, value = line.split("=")
            value = value.strip()
            blocks_list[-1][key.rstrip()] = value.strip()

    return blocks_list


def create_layers(blocks_list):
    hyperparams = blocks_list[0]
    channels_list = [int(hyperparams["channels"])]
    module_list = nn.ModuleList()
    
    for layer_ind, layer_dict in enumerate(blocks_list[1:]):
        modules = nn.Sequential()
        
        if layer_dict["type"] == "convolutional":
            filters = int(layer_dict["filters"])
            kernel_size = int(layer_dict["size"])
            pad = (kernel_size - 1) // 2
            bn=layer_dict.get("batch_normalize",0)    
            
            
            conv2d= nn.Conv2d(
                        in_channels=channels_list[-1],
                        out_channels=filters,
                        kernel_size=kernel_size,
                        stride=int(layer_dict["stride"]),
                        padding=pad,
                        bias=not bn)
            modules.add_module("conv_{0}".format(layer_ind), conv2d)
            
            if bn:
                bn_layer = nn.BatchNorm2d(filters,momentum=0.9, eps=1e-5)
                modules.add_module("batch_norm_{0}".format(layer_ind), bn_layer)
                
                
            if layer_dict["activation"] == "leaky":
                activn = nn.LeakyReLU(0.1)
                modules.add_module("leaky_{0}".format(layer_ind), activn)
                
        elif layer_dict["type"] == "upsample":
            stride = int(layer_dict["stride"])
            upsample = nn.Upsample(scale_factor = stride)
            modules.add_module("upsample_{}".format(layer_ind), upsample) 
            

        elif layer_dict["type"] == "shortcut":
            backwards=int(layer_dict["from"])
            filters = channels_list[1:][backwards]
            modules.add_module("shortcut_{}".format(layer_ind), EmptyLayer())
            
        elif layer_dict["type"] == "route":
            layers = [int(x) for x in layer_dict["layers"].split(",")]
            filters = sum([channels_list[1:][l] for l in layers])
            modules.add_module("route_{}".format(layer_ind), EmptyLayer())
            
        elif layer_dict["type"] == "yolo":
            anchors = [int(a) for a in layer_dict["anchors"].split(",")]
            anchors = [(anchors[i], anchors[i + 1]) for i in range(0, len(anchors), 2)]

            mask = [int(m) for m in layer_dict["mask"].split(",")]
            
            anchors = [anchors[i] for i in mask]
            
            num_classes = int(layer_dict["classes"])
            img_size = int(hyperparams["height"])
            
            yolo_layer = YOLOLayer(anchors, num_classes, img_size)
            modules.add_module("yolo_{}".format(layer_ind), yolo_layer)
            
        module_list.append(modules)       
        channels_list.append(filters)

    return hyperparams, module_list        



class EmptyLayer(nn.Module):
    def __init__(self):
        super(EmptyLayer, self).__init__()
        
        
class YOLOLayer(nn.Module):

    def __init__(self, anchors, num_classes, img_dim=416):
        super(YOLOLayer, self).__init__()
        self.anchors = anchors
        self.num_anchors = len(anchors)
        self.num_classes = num_classes
        self.img_dim = img_dim
        self.grid_size = 0 
        
        
    def forward(self, x_in):
        batch_size = x_in.size(0)
        grid_size = x_in.size(2)
        devide=x_in.device
        
        prediction=x_in.view(batch_size, self.num_anchors, 
                             self.num_classes + 5, grid_size, grid_size)
        prediction=prediction.permute(0, 1, 3, 4, 2)
        prediction=prediction.contiguous()
        
        obj_score = torch.sigmoid(prediction[..., 4]) 
        pred_cls = torch.sigmoid(prediction[..., 5:]) 
        
        if grid_size != self.grid_size:
            self.compute_grid_offsets(grid_size, cuda=x_in.is_cuda)
            
        pred_boxes=self.transform_outputs(prediction) 
        
        output = torch.cat(
            (
                pred_boxes.view(batch_size, -1, 4),
                obj_score.view(batch_size, -1, 1),
                pred_cls.view(batch_size, -1, self.num_classes),
            ), -1,)
        return output        
    
    
        
    def compute_grid_offsets(self, grid_size, cuda=True):
        self.grid_size = grid_size
        self.stride = self.img_dim / self.grid_size
        
        self.grid_x = torch.arange(grid_size, device=device).repeat(1, 1, grid_size, 1 ).type(torch.float32)
        self.grid_y = torch.arange(grid_size, device=device).repeat(1, 1, grid_size, 1).transpose(3, 2).type(torch.float32)
        
        scaled_anchors=[(a_w / self.stride, a_h / self.stride) for a_w, a_h in self.anchors]
        self.scaled_anchors=torch.tensor(scaled_anchors,device=device)
        
        self.anchor_w = self.scaled_anchors[:, 0:1].view((1, self.num_anchors, 1, 1))
        self.anchor_h = self.scaled_anchors[:, 1:2].view((1, self.num_anchors, 1, 1))
        
        
        
    def transform_outputs(self,prediction):
        device=prediction.device
        x = torch.sigmoid(prediction[..., 0]) # Center x
        y = torch.sigmoid(prediction[..., 1]) # Center y
        w = prediction[..., 2] # Width
        h = prediction[..., 3] # Height

        pred_boxes = torch.zeros_like(prediction[..., :4]).to(device)
        pred_boxes[..., 0] = x.data + self.grid_x
        pred_boxes[..., 1] = y.data + self.grid_y
        pred_boxes[..., 2] = torch.exp(w.data) * self.anchor_w
        pred_boxes[..., 3] = torch.exp(h.data) * self.anchor_h
        
        return pred_boxes * self.stride             

搭建YOLO模型 

解析配置文件,使用parse_model_config助手读取并打印

from myutils import parse_model_config

path2config="./config/yolov3.cfg"
blocks_list = parse_model_config(path2config)
blocks_list[:2]

搭建PyTorch模块

基于解析的配置文件创建PyTorch模块,调用 create_layers 辅助函数进行转换并获取 PyTorch 模块的列表

from myutils import create_layers

hy_pa, m_l= create_layers(blocks_list)
print(m_l)
print(hy_pa)

搭建DarkNet模型

from torch import nn

class Darknet(nn.Module):
    def __init__(self, config_path, img_size=416):
        super(Darknet, self).__init__()
        self.blocks_list = parse_model_config(config_path)
        self.hyperparams, self.module_list = create_layers(self.blocks_list)
        self.img_size = img_size
        
    def forward(self, x):
        img_dim = x.shape[2]
        layer_outputs, yolo_outputs = [], []
        
        for block, module in zip(self.blocks_list[1:], self.module_list):
            if block["type"] in ["convolutional", "upsample", "maxpool"]:
                x = module(x)        
                
                
            elif block["type"] == "shortcut":
                layer_ind = int(block["from"])
                x = layer_outputs[-1] + layer_outputs[layer_ind]
            elif block["type"] == "yolo":
                x= module[0](x)
                yolo_outputs.append(x)
            elif block["type"] == "route":
                x = torch.cat([layer_outputs[int(l_i)] 
                               for l_i in block["layers"].split(",")], 1)
            layer_outputs.append(x)
        yolo_out_cat = torch.cat(yolo_outputs, 1)
        return yolo_out_cat, yolo_outputs        
    
model = Darknet(path2config).to(device)
print(model)

# 创建一个随机的dummy_img,大小为1x3x416x416,并将其移动到指定的设备上
dummy_img=torch.rand(1,3,416,416).to(device)
# 在不计算梯度的情况下,执行模型的前向传播
with torch.no_grad():
    # 获取模型的前向传播结果
    dummy_out_cat, dummy_out=model.forward(dummy_img)
    # 打印dummy_out_cat的形状
    print(dummy_out_cat.shape)
    # 打印dummy_out中每个元素的形状
    print(dummy_out[0].shape,dummy_out[1].shape,dummy_out[2].shape)

定义损失函数

YOLO通常使用组合损失函数

def get_loss_batch(output,targets, params_loss, opt=None):
    # 获取损失函数的参数
    ignore_thres=params_loss["ignore_thres"]
    scaled_anchors= params_loss["scaled_anchors"]    
    mse_loss= params_loss["mse_loss"]
    bce_loss= params_loss["bce_loss"]
    
    # 获取yolo的参数
    num_yolos=params_loss["num_yolos"]
    num_anchors= params_loss["num_anchors"]
    obj_scale= params_loss["obj_scale"]
    noobj_scale= params_loss["noobj_scale"]
    
    # 初始化损失
    loss=0.0
    for yolo_ind in range(num_yolos):
        # 获取yolo的输出
        yolo_out=output[yolo_ind]
        batch_size, num_bbxs, _=yolo_out.shape
        
        # 获取网格大小
        gz_2=num_bbxs/num_anchors
        grid_size=int(np.sqrt(gz_2))
        
        # 将yolo的输出reshape为(batch_size,num_anchors,grid_size,grid_size,-1)
        yolo_out=yolo_out.view(batch_size,num_anchors,grid_size,grid_size,-1)
        
        # 获取预测的边界框
        pred_boxes=yolo_out[:,:,:,:,:4]
        x,y,w,h= transform_bbox(pred_boxes, scaled_anchors[yolo_ind])
        # 获取预测的置信度
        pred_conf=yolo_out[:,:,:,:,4]
        # 获取预测的类别概率
        pred_cls_prob=yolo_out[:,:,:,:,5:]
        
        # 获取yolo的目标
        yolo_targets = get_yolo_targets({
                                            "pred_cls_prob": pred_cls_prob,
                                            "pred_boxes":pred_boxes,    
                                            "targets": targets,    
                                            "anchors": scaled_anchors[yolo_ind],    
                                            "ignore_thres": ignore_thres,
                                        }) 
        
        # 获取目标掩码
        obj_mask=yolo_targets["obj_mask"]        
        noobj_mask=yolo_targets["noobj_mask"]            
        # 获取目标的x,y,w,h
        tx=yolo_targets["tx"]                
        ty=yolo_targets["ty"]                    
        tw=yolo_targets["tw"]                        
        th=yolo_targets["th"]                            
        # 获取目标的类别
        tcls=yolo_targets["tcls"]                                
        # 获取目标的置信度
        t_conf=yolo_targets["t_conf"]
        
        # 计算x,y,w,h的损失
        loss_x = mse_loss(x[obj_mask], tx[obj_mask])
        loss_y = mse_loss(y[obj_mask], ty[obj_mask])
        loss_w = mse_loss(w[obj_mask], tw[obj_mask])
        loss_h = mse_loss(h[obj_mask], th[obj_mask])
        
        # 计算置信度的损失
        loss_conf_obj = bce_loss(pred_conf[obj_mask], t_conf[obj_mask])
        loss_conf_noobj = bce_loss(pred_conf[noobj_mask], t_conf[noobj_mask])
        loss_conf = obj_scale * loss_conf_obj + noobj_scale * loss_conf_noobj
        # 计算类别的损失
        loss_cls = bce_loss(pred_cls_prob[obj_mask], tcls[obj_mask])
        # 累加损失
        loss += loss_x + loss_y + loss_w + loss_h + loss_conf + loss_cls
        
    # 如果有优化器,则进行反向传播和优化
    if opt is not None:
        opt.zero_grad()
        loss.backward()
        opt.step()
        
    # 返回损失
    return loss.item()        
def transform_bbox(bbox, anchors):
    # 将bbox的x、y、w、h分别赋值给x、y、w、h
    x=bbox[:,:,:,:,0]
    y=bbox[:,:,:,:,1]
    w=bbox[:,:,:,:,2]
    h=bbox[:,:,:,:,3]
    # 将anchors的w、h分别赋值给anchor_w、anchor_h
    anchor_w = anchors[:, 0].view((1, 3, 1, 1))
    anchor_h = anchors[:, 1].view((1, 3, 1, 1))       
    
    # 将x、y分别减去其向下取整的值
    x=x-x.floor()
    y=y-y.floor()
    # 将w、h分别除以anchor_w、anchor_h,并取对数
    w= torch.log(w / anchor_w + 1e-16)
    h= torch.log(h / anchor_h + 1e-16)
    return x, y, w, h

def get_yolo_targets(params):
    # 获取预测框、预测类别概率、目标、锚点、忽略阈值
    pred_boxes=params["pred_boxes"]
    pred_cls_prob=params["pred_cls_prob"]
    target=params["targets"]
    anchors=params["anchors"] 
    ignore_thres=params["ignore_thres"] 

    # 获取批量大小、锚点数量、网格大小、类别数量
    batch_size = pred_boxes.size(0)
    num_anchors = pred_boxes.size(1)
    grid_size = pred_boxes.size(2)
    num_cls = pred_cls_prob.size(-1)
    
    
    # 定义目标张量的形状
    sizeT=batch_size, num_anchors, grid_size, grid_size
    # 定义目标张量,用于存储目标框的掩码
    obj_mask = torch.zeros(sizeT,device=device,dtype=torch.uint8)
    # 定义目标张量,用于存储非目标框的掩码
    noobj_mask = torch.ones(sizeT,device=device,dtype=torch.uint8)
    # 定义目标张量,用于存储目标框的x坐标
    tx = torch.zeros(sizeT, device=device, dtype=torch.float32)
    # 定义目标张量,用于存储目标框的y坐标
    ty= torch.zeros(sizeT, device=device, dtype=torch.float32)
    # 定义目标张量,用于存储目标框的宽度
    tw= torch.zeros(sizeT, device=device, dtype=torch.float32)
    # 定义目标张量,用于存储目标框的高度
    th= torch.zeros(sizeT, device=device, dtype=torch.float32)
    
    # 定义目标张量的形状
    sizeT=batch_size, num_anchors, grid_size, grid_size, num_cls
    # 定义目标张量,用于存储目标类别
    tcls= torch.zeros(sizeT, device=device, dtype=torch.float32)
    
    # 将目标框的坐标乘以网格大小
    target_bboxes = target[:, 2:] * grid_size
    # 获取目标框的xy坐标
    t_xy = target_bboxes[:, :2]
    # 获取目标框的wh坐标
    t_wh = target_bboxes[:, 2:]
    # 获取目标框的x坐标
    t_x, t_y = t_xy.t()
    # 获取目标框的宽度
    t_w, t_h = t_wh.t()

    # 获取目标框的网格坐标
    grid_i, grid_j = t_xy.long().t()
    
    # 计算每个锚点与目标框的iou
    iou_with_anchors=[get_iou_WH(anchor, t_wh) for anchor in anchors]
    # 将iou转换为张量
    iou_with_anchors = torch.stack(iou_with_anchors)
    # 获取iou最大的锚点索引
    best_iou_wa, best_anchor_ind = iou_with_anchors.max(0)
    
    # 获取目标框的batch索引和类别标签
    batch_inds, target_labels = target[:, :2].long().t()
    # 将目标框的掩码设置为1
    obj_mask[batch_inds, best_anchor_ind, grid_j, grid_i] = 1
    # 将非目标框的掩码设置为0
    noobj_mask[batch_inds, best_anchor_ind, grid_j, grid_i] = 0

    # 将大于忽略阈值的iou对应的非目标框掩码设置为0
    for ind, iou_wa in enumerate(iou_with_anchors.t()):
        noobj_mask[batch_inds[ind], iou_wa > ignore_thres, grid_j[ind], grid_i[ind]] = 0
        
        
    # 将目标框的x坐标减去网格的整数部分
    tx[batch_inds, best_anchor_ind, grid_j, grid_i] = t_x - t_x.floor()
    # 将目标框的y坐标减去网格的整数部分
    ty[batch_inds, best_anchor_ind, grid_j, grid_i] = t_y - t_y.floor()
    

    # 获取最佳锚点的宽度
    anchor_w=anchors[best_anchor_ind][:, 0]
    # 将目标框的宽度除以锚点的宽度,并取对数
    tw[batch_inds, best_anchor_ind, grid_j, grid_i] = torch.log(t_w / anchor_w + 1e-16)
    
    # 获取最佳锚点的高度
    anchor_h=anchors[best_anchor_ind][:, 1]
    # 将目标框的高度除以锚点的高度,并取对数
    th[batch_inds, best_anchor_ind, grid_j, grid_i] = torch.log(t_h / anchor_h + 1e-16)
    
    # 将目标类别设置为1
    tcls[batch_inds, best_anchor_ind, grid_j, grid_i, target_labels] = 1
    
    # 返回目标张量
    output={
        "obj_mask" : obj_mask,
        "noobj_mask" : noobj_mask,
        "tx": tx,
        "ty": ty,
        "tw": tw,
        "th": th,
        "tcls": tcls,
        "t_conf": obj_mask.float(),
    }
    return output    

def get_iou_WH(wh1, wh2):
    # 将wh2转置
    wh2 = wh2.t()
    # 获取wh1的宽度和高度
    w1, h1 = wh1[0], wh1[1]
    # 获取wh2的宽度和高度
    w2, h2 = wh2[0], wh2[1]
    # 计算交集面积
    inter_area = torch.min(w1, w2) * torch.min(h1, h2)
    # 计算并集面积
    union_area = (w1 * h1 + 1e-16) + w2 * h2 - inter_area
    # 返回交集面积与并集面积的比值
    return inter_area / union_area
        

训练模型

在训练数据上训练模型,并在验证数据上对其进行评估,训练过程遵循标准的随机梯度下降(SGD)。

def loss_epoch(model,params_loss,dataset_dl,sanity_check=False,opt=None):
    running_loss=0.0
    len_data=len(dataset_dl.dataset)
    running_metrics= {}
    
    for xb, yb,_ in dataset_dl:
        yb=yb.to(device)
        _,output=model(xb.to(device))
        loss_b=get_loss_batch(output,yb, params_loss,opt)
        running_loss+=loss_b
        if sanity_check is True:
            break 
    loss=running_loss/float(len_data)
    return loss

import copy
def train_val(model, params):
    num_epochs=params["num_epochs"]
    params_loss=params["params_loss"]
    opt=params["optimizer"]
    train_dl=params["train_dl"]
    val_dl=params["val_dl"]
    sanity_check=params["sanity_check"]
    lr_scheduler=params["lr_scheduler"]
    path2weights=params["path2weights"]
    
    
    loss_history={
        "train": [],
        "val": [],
    }
    best_model_wts = copy.deepcopy(model.state_dict())
    best_loss=float('inf') 
    
    for epoch in range(num_epochs):
        current_lr=get_lr(opt)
        print('Epoch {}/{}, current lr={}'.format(epoch, num_epochs - 1, current_lr)) 
        model.train()
        train_loss=loss_epoch(model,params_loss,train_dl,sanity_check,opt)
        loss_history["train"].append(train_loss)
        print("train loss: %.6f" %(train_loss))    
        
        model.eval()
        with torch.no_grad():
            val_loss=loss_epoch(model,params_loss,val_dl,sanity_check)
        loss_history["val"].append(val_loss)
        print("val loss: %.6f" %(val_loss))
        
        
        if val_loss < best_loss:
            best_loss = val_loss
            best_model_wts = copy.deepcopy(model.state_dict())
            torch.save(model.state_dict(), path2weights)
            print("Copied best model weights!")
            
        lr_scheduler.step(val_loss)
        if current_lr != get_lr(opt):
            print("Loading best model weights!")
            model.load_state_dict(best_model_wts) 
        print("-"*10) 
    model.load_state_dict(best_model_wts)
    return model, loss_history            

def get_lr(opt):
    for param_group in opt.param_groups:
        return param_group['lr']
from torch import optim
from torch.optim.lr_scheduler import ReduceLROnPlateau

opt = optim.Adam(model.parameters(), lr=1e-3)
lr_scheduler = ReduceLROnPlateau(opt, mode='min',factor=0.5, patience=20,verbose=1)

path2models= "./models/"
if not os.path.exists(path2models):
        os.mkdir(path2models)
        
scaled_anchors=[model.module_list[82][0].scaled_anchors,
                model.module_list[94][0].scaled_anchors,
                model.module_list[106][0].scaled_anchors]        

mse_loss = nn.MSELoss(reduction="sum")
bce_loss = nn.BCELoss(reduction="sum")
params_loss={
    "scaled_anchors" : scaled_anchors,
    "ignore_thres": 0.5,
    "mse_loss": mse_loss,
    "bce_loss": bce_loss,
    "num_yolos": 3,
    "num_anchors": 3,
    "obj_scale": 1,
    "noobj_scale": 100,
} 

params_train={
    "num_epochs": 5,
    "optimizer": opt,
    "params_loss": params_loss,
    "train_dl": train_dl,
    "val_dl": val_dl,
    "sanity_check": True,
    "lr_scheduler": lr_scheduler,
    "path2weights": path2models+"weights.pt",
}
model,loss_hist=train_val(model,params_train)

部署模型

将训练后的权重加载到模型中 

path2weights="./models/weights.pt"
model.load_state_dict(torch.load(path2weights))

img,tg,_=coco_val[11]
print(img.shape)
print(tg.shape)
show_img_bbox(img,tg)

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

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

相关文章

华为网络模拟器eNSP安装部署教程

eNSP是图形化网络仿真平台&#xff0c;该平台通过对真实网络设备的仿真模拟&#xff0c;帮助广大ICT从业者和客户快速熟悉华为数通系列产品&#xff0c;了解并掌握相关产品的操作和配置、提升对企业ICT网络的规划、建设、运维能力&#xff0c;从而帮助企业构建更高效&#xff0…

数据结构 | LinkedList与链表

前言 ArrayList底层使用连续的空间,任意位置(尤其是0位置下标)插入或删除元素时,需要将该位置后序元素 整体 往前或往后搬移,故时间复杂度为O(N). 优点(给定一个下标,可以快速查找到对应的元素,时间复杂度为O(1))增容需要申请新空间,拷贝数据,释放旧空间,会有不小的消耗.增容一…

Linux进程——环境变量之二

文章目录 环境变量查看环境变量获取环境变量main()的第三个参数本地变量全局环境变量内建命令与常规命令 环境变量 查看环境变量 在上一篇文章中我们只说了查看某个环境变量的值&#xff0c;那么如何查看所有的环境变量呢 使用指令env即可 例如 这里我们也不需要全部记住&a…

FastAPI(七十四)实战开发《在线课程学习系统》接口开发-- 删除留言

源码见&#xff1a;"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 之前文章FastAPI&#xff08;七十三&#xff09;实战开发《在线课程学习系统》接口开发-- 回复留言&#xff0c;那么我们这次分享删除留言接口的开发…

从0开始的STM32HAL库学习9

定时器输入捕获测频率 生成待测信号 配置环境 选择如上图所示 代码修改 在main函数中加入 HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1); 测量信号频率 配置环境 如图所示打开TIM3定时器 1. 设置TI1FP1为ResetMode,即清空计数 2. 使用内部时钟 3. 通道 1 设置为输…

手机怎么设置不同的ip地址

在数字化日益深入的今天&#xff0c;智能手机已成为我们生活、工作和学习中不可或缺的设备。然而&#xff0c;随着网络应用的广泛和深入&#xff0c;我们有时需要为手机设置不同的IP地址来满足特定需求。比如&#xff0c;避免网络限制、提高网络安全、或者进行网络测试等。本文…

tarojs项目启动篇

TaroJS 是一个开放式跨端开发解决方案&#xff0c;使用 React 语法规范来开发多端应用&#xff08;包括小程序、H5、React Native 等&#xff09;。它可以帮助开发者高效地构建出在不同端上运行一致的应用。以下是启动 TaroJS 项目&#xff08;本来就有的旧项目&#xff09;的步…

经典文献阅读之--LIV-GaussMap(实时3D辐射场地图渲染的LiDAR惯性视觉融合算法)

Tip: 如果你在进行深度学习、自动驾驶、模型推理、微调或AI绘画出图等任务&#xff0c;并且需要GPU资源&#xff0c;可以考虑使用UCloud云计算旗下的Compshare的GPU算力云平台。他们提供高性价比的4090 GPU&#xff0c;按时收费每卡2.6元&#xff0c;月卡只需要1.7元每小时&…

一文总结代理:代理模式、代理服务器

概述 代理在计算机编程领域&#xff0c;是一个很通用的概念&#xff0c;包括&#xff1a;代理设计模式&#xff0c;代理服务器等。 代理类持有具体实现类的实例&#xff0c;将在代理类上的操作转化为实例上方法的调用。为某个对象提供一个代理&#xff0c;以控制对这个对象的…

Haproxy 下载、编译部署、使用

文章目录 前言Haproxy 下载、编译部署、使用1. 下载2. 编译部署3. 使用3.1. 验证配置文件3.2. 启动 HAProxy 并指定配置文件路径3.3. 关闭HAProxy3.4. 重载HAProxy 3. 测试 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&am…

go-kratos 学习笔记(7) 服务发现服务间通信grpc调用

服务发现 Registry 接口分为两个&#xff0c;Registrar 为实例注册和反注册&#xff0c;Discovery 为服务实例列表获取 创建一个 Discoverer 服务间的通信使用的grpc&#xff0c;放到data层&#xff0c;实现的是从uses服务调用orders服务 app/users/internal/data.go 加入 New…

可见性::

目录 定义&#xff1a; 解决方法&#xff1a; ①使用synchronized实现缓存和内存的同步 修改一&#xff1a; 加入语句&#xff1a; 代码&#xff1a; 修改2&#xff1a; 在代码块中加入&#xff1a; 代码&#xff1a; 执行结果&#xff1a; 原因&#xff1a; ②使用…

数字经济赋能爱疆事业:同疆共创,打造疆品出疆新通道

在数字化浪潮的推动下&#xff0c;新疆吐鲁番地区正迎来一场前所未有的变革。近日&#xff0c;由同疆共创公司承办的“品牌新力量&#xff0c;助农电商直播行&#xff0c;音乐嘉年华吐鲁番活动”在吐鲁番市火热开展&#xff0c;这一创新举措不仅彰显了同疆共创积极履行社会责任…

【redis】一致性hash算法和hash槽

普通hash取模 直接hash(key)%N , N为机器的数量&#xff0c;但不利于集器扩容或者缩容 一致性hash算法和hash槽 一致性hash算法是在redis 分片中使用&#xff0c;hash槽在redis cluster&#xff08;集群&#xff09;中使用 Redis一致性hash&#xff1a;Redis一致性hash是为…

AvaloniaUI的学习

相关网站 github:https://github.com/AvaloniaUI/Avalonia 官方中文文档&#xff1a;https://docs.avaloniaui.net/zh-Hans/docs/welcome IDE选择 VS2022VSCodeRider 以上三种我都尝试过&#xff0c;体验Rider最好。VS2022的提示功能不好&#xff0c;VSCode太慢&#xff0c…

Typora笔记上传到CSDN

1.Typora 安装 Typora链接&#xff1a;百度网盘 提取码&#xff1a;b6d1 旧版本是不需要破解的 后来的版本比如1.5.9把放在typora的根目录下就可以了 2.上传到CSDN 步骤 csdn 写文章-使用MD编辑器-导入本地md文件即可 问题 图片没法显示 原因 图片的链接是本地的 当然没法…

PySide(PyQt)使用QPropertyAnimation制作动态界面

主脚本&#xff1a; # encoding: utf-8 import os import sysfrom PySide6.QtCore import QPropertyAnimation, QEasingCurvefrom UIS import *# 主画面类 class MainWindow(QMainWindow, animationButton_ui.Ui_MainWindow):def __init__(self):super().__init__()self.setup…

【OpenCV C++20 学习笔记】图片处理基础

OpenCV C20 图片处理基础 VS 2022 C20 标准库导入的问题头文件包含以及命名空间声明main函数读取图片读取检查显式图片写入图片 完整代码bug VS 2022 C20 标准库导入的问题 VS还没有完全兼容C20。C20的import语句不一定能正确导入标准库&#xff0c;所以必须要新建一个头文件专…

基站光伏直流叠光能效管理方案

安科瑞 华楠 基站现状和趋势 5G基站是专门提供5G网络服务的公用移动通信基站。5G基站主要用于提供5G空口协议功能&#xff0c;支持与用户设备、核心网之间的通信。按照逻辑功能划分&#xff0c;5G基站可分为5G基带单元与5G射频单元&#xff0c;二者之间可通过CPRI或eCPRI接口…

Flink 技术与应用(一)

Flink技术与应用&#xff08;初级篇&#xff09; 起源 Apache Flink 是一个开源的大数据处理框架&#xff0c;其起源可以追溯到一个名为 Stratosphere 的研究项目&#xff0c;旨在建立下一代大数据分析引擎&#xff0c;2010 年&#xff0c;从 Stratosphere 项目中分化出了 Fl…