mmdetection 中 Mask Rcnn检测结果可视化(DICE计算、PR曲线绘制等)

news2024/10/7 20:24:50

mmdetection中的Mask Rcnn是一个很不错的检测网络,既可以实现目标检测,也可以实现语义分割。官方也有很详细的doc指导,但是对新手来说并不友好,刚好之前笔者写的mmlab系列里面关于可视化都还没有一个详细的文档,也在此一并介绍。

具体怎么制作自己的数据集和训练自己的模型教程如下:
mmdetect2d训练自己的数据集(一)—— labelme数据处理
mmdetect2d训练自己的数据集(二)—— 模型训练

通过上述两个教程,可以训练得到自己的config文件和checkpoints文件以及训练日志 xxx.log.json文件。接下来的可视化就是要用到这三个东西。

注:运行mmdetection的时候,我是用pycharm,这里有一个比较奇葩的bug,就是有时候我直接在terminal里面运行程序,会出现bug比如mmcv版本不对等,但是在RUN里面跑又可以了(别问怎么知道的,问就是卡了一周…)。所以出现bug的时候不要急着否定自己,多研究一下。

模型检测部署(检测结果可视化)

模型检测结果可视化有多种方式,这里介绍两种方法:1.调用api直接检测;2.用test.py进行检测。

1.使用test.py

运行test.py 必须要有config文件和checkpoint文件,其他为可选参数,具体如下:

python tools/test.py configs/myconfig.py checkpoints/last.pth \
--show --out result_file.pkl --show-dir result/test_result --eval segm

# --show 决定是否现实图片
# --out 将结果输出为pkl格式的文件
# --show-dir 将测试得到的文件存到目标文件夹下
# --eval 选择需要评估的指标,比如segm是分割的情况,这是mask rcnn网络会有这个结果,还有bbox等

2.调用API

在mmdet里面集成了很多现成的api也可以直接用来查看检测结果,这里写一个简单的调用方法。

from mmdet.apis import init_detector, inference_detector, show_result_pyplot
import cv2
import numpy as np

# config文件地址
config_file = 'others_ct/ct_head_mask_rcnn_r50_rpn_100_coco/mask_rcnn_r50_fpn_1x_coco.py'

# checkpoint文件地址
checkpoint_file = 'others_ct/ct_head_mask_rcnn_r50_rpn_100_coco/latest.pth'

# 选择使用的显卡
device = 'cuda:0'

# 模型载入
model = init_detector(config_file, checkpoint_file, device=device)

# 待检测的图片地址
img = 'data/coco_ct/val2017/Snipaste_2022-07-28_15-48-49.jpg'

# 检测结果输出
result = inference_detector(model, img)
#bbox_result, mask_result = result

# 现实检测结果
show_result_pyplot(model, img, result, score_thr=0.3)

3.对比自己的验证集标注

我之前都是用labelme直接看的,当然就显得b格不太够,mmdetection里面也可以直接看,用的browse_dataset.py,这个可以看训练集的,当然如果要看测试集的话,可以将config文件里面的data这个dict里面的train的dict中的ann_file和img_profix路径改成和val那个dict中的一样就好了在这里插入图片描述
运行的时候,需要输入参数有config文件,以及--show # 如果你要现实每一张的话 --output-dir ../mydir # 浏览的图片可以保存到该地址下
这种现实办法有一个好处就是可以得到比较干净的对比图片,就是test.py得到预测的图片,这个得到标注的图片。但是如果想要将结果放在同一张图上进行展示,及标注和预测放在同一张图上的话,mmdetection也提供了对应的代码。使用tools/analyze_results.py即可。使用方法如下:

python tools/analysis_tools/analyze_results.py \
      ${CONFIG} \  
      ${PREDICTION_PATH} \
      ${SHOW_DIR} \
      [--show] \
      [--wait-time ${WAIT_TIME}] \
      [--topk ${TOPK}] \
      [--show-score-thr ${SHOW_SCORE_THR}] \
      [--cfg-options ${CFG_OPTIONS}]

{CONFIG}: 是config文件路径
{PREDICTION_PATH}: 是test.py得到的pkl文件路径
{SHOW_DIR}: 是绘制的到的图片存放的目录
--show: 决定是否显示,不指定的话,默认为不显示
--wait-time时间的间隔,若为 0 表示持续显示
--topk: 根据最高或最低 topk 概率排序保存的图片数量,若不指定,默认设置为 20
--show-score-thr: 能够展示的概率阈值,默认为 0
--cfg-options: 如果指定,可根据指定键值对覆盖更新配置文件的对应选项

DICE(DSC)计算

我使用mask-rcnn这个网络的,想要计算语义分割结果理论上是可以直接用mmdetection里面的dice loss计算,但是我找了好几天没找到,主要是不知道怎么输出,所以就手动写了一个。主要原理是挨个计算标注的mask的面积、预测的mask的面积,以及二者的iou。
语义分割里 Dice = 2 * (A∩B) /(|A| + |B|)
但是这里有一点需要注意,我做的是单类别检测,且每张图最多会存在一个检测结果,如果有多类别的话,可能就需要对代码进行修改了,后续如果我用到的话也会更新的。
所以我是将mmdetection里面mask部分的numpy数组输出保存了。具体在image.py里面找到def imshow_det_bboxes函数,在判定segms的部分加上输出的指令:


if segms is not None:
	##############在原代码中的if语句里的开头部分添加该部分代码#################
	# 获取输出的的图片名称,这里一定要在test.py 和 browse_dataset.py的时候,选择保存文件,即必须有--show-dir和路径
	file_tmp = str(out_file)
	# 设置npy文件的名称
	file_name = file_tmp[:-3] + 'npy'
	# 将segms的numpy数组保存为npy文件
	np.save(file_name, segms)
	####################################################################

然后在运行上述的test.py文件和browse_dataset.py时,会在–show-dir下面,生成与图片同名的npy文件。计算dice就是要用到这部分文件。

因此具体代码如下:

import numpy as np
import os
# 存放预测结果的路径(前面test.py结果的--show-dir)
pred_root_path = '/home/kevin/mmlab/mmdetection/tools/result004'
# 存放标签的路径(前面dataset.py结果的--show-dir)
label_root_path = '/home/kevin/mmlab/mmdetection/tools/misc/ct_results'

# 读取预测结果路径下的文件
file_list_tmp = os.listdir(pred_root_path)
file_list = []
dice_list = []

for i in file_list_tmp:
	# 判定是否为npy文件
    if i[-3:] == 'npy':  
    	# 获取预测的npy文件路径
        pred_path = pred_root_path + '/' + i
        # 获取同名的标签文件路径
        label_path = label_root_path + '/' + i
        print(pred_path)
        print(label_path)

        # 导入ndarray文件
        pred = np.load(pred_path)
        label = np.load(label_path)

        # 标签文件中的数组是Ture和False组成,需要变成1、0
        pred_1 = pred + 0
        # 因为browse得到的标签文件会有0.5的比例Flip来达到数据增强的目的,即图像会水平翻转
        # 所以产生的mask文件也有一半的概率是水平翻转过的,需要将其翻转回来
        pred_2 = pred_1[:,:,::-1]
        # 有时候检测为空但还是会有pred的npy文件,原因我也不清楚,所以这里是加了个判定,如果输出为空则直接跳过。
        # 预测的mask文件形状都是(n, 512, 512)形状的,n取决于预测的个数,我是单目标,所以最多只有一个。
        if pred_1.shape == (0, 512, 512):
            continue
        # 同上
        label = label + 0


        # 将 (1, 512, 512) 展平为 (262144,),目的是方便计算
        b1_1 = pred_1.flatten()
        b1_2 = pred_2.flatten()
        b2 = label.flatten()

        # 分别计算预测的和标签的两个mask部分的面积,因为翻转对面积不影响,这里只计算一次
        pred_area = np.sum(b1_1)
        label_area = np.sum(b2)

        # 计算IoU
        iou_pred_label_1 = b1_1 * b2
        iou_pred_label_2 = b1_2 * b2

        iou_area_1 = np.sum(iou_pred_label_1)
        iou_area_2 = np.sum(iou_pred_label_2)
        # print(iou_area_1, iou_area_2)
		
		# IoU小说明该图被翻转过了,因此选大的那个
        iou_area = max(iou_area_1, iou_area_2)
        print(iou_area)

		# 防止为空文件
        if (pred_area + label_area) > 0:
            dice = (2 * iou_area) / (pred_area + label_area)
        else:
            dice = 0
		# 将得到的dice加入list
        dice_list.append(dice)

# print("dice_list is:", dice_list)

# 求和前将list转为numpy数组
dice_np = np.array(dice_list)
dice_sum = np.sum(dice_np)

# 计算总共有多少张图片,因为一些检测为空,所以用的是browse的结果,由于结果包含图片和npy,所以除以2
pic_num = len(os.listdir(label_root_path)) / 2
print('num is:', pic_num)
# 计算平均Dice
Dice = dice_sum / pic_num
print('Dice is:', Dice)

训练日志可视化

这一部分主要是将mmdetection训练得到的json文件可视化,代码主要源于github,具体哪一个忘记了(readme里面没有原址…)是专门做的mmdetection 结果可视化的,非常强!!。使用时如果出现keyerror的话,将json文件中第一行的env_info删掉就可以了。

import json
import matplotlib.pyplot as plt
import sys
import os
from collections import OrderedDict

class visualize_mmdetection():
    def __init__(self, path):
        self.log = open(path)
        self.dict_list = list()
        self.loss_rpn_bbox = list()
        self.loss_rpn_cls = list()
        self.loss_bbox = list()
        self.loss_cls = list()
        self.loss = list()
        self.acc = list()

    def load_data(self):
        for line in self.log:
            info = json.loads(line)
            # print('info:', info)
            if info['mode'] == 'train':
                self.dict_list.append(info)

        for i in range(1, len(self.dict_list)):
            for value, key in dict(self.dict_list[i]).items():
                # 读取每一行的信息
                loss_rpn_cls_value = dict(self.dict_list[i])['loss_rpn_cls']
                loss_rpn_bbox_value = dict(self.dict_list[i])['loss_rpn_bbox']
                loss_bbox_value = dict(self.dict_list[i])['loss_bbox']
                loss_cls_value = dict(self.dict_list[i])['loss_cls']
                loss_value = dict(self.dict_list[i])['loss']
                acc_value = dict(self.dict_list[i])['acc']
                # 将其保存至对应列表中
                self.loss_rpn_cls.append(loss_rpn_cls_value)
                self.loss_rpn_bbox.append(loss_rpn_bbox_value)
                self.loss_bbox.append(loss_bbox_value)
                self.loss_cls.append(loss_cls_value)
                self.loss.append(loss_value)
                self.acc.append(acc_value)
        # 清除list中的重复项 
        self.loss_rpn_cls = list(OrderedDict.fromkeys(self.loss_rpn_cls))
        self.loss_rpn_bbox = list(OrderedDict.fromkeys(self.loss_rpn_bbox))
        self.loss_bbox = list(OrderedDict.fromkeys(self.loss_bbox))
        self.loss_cls = list(OrderedDict.fromkeys(self.loss_cls))
        self.loss = list(OrderedDict.fromkeys(self.loss))
        self.acc = list(OrderedDict.fromkeys(self.acc))

    def show_chart(self):
        plt.rcParams.update({'font.size': 15})

        plt.figure(figsize=(20, 20))

        plt.subplot(321, title='loss_rpn_cls', ylabel='loss')
        plt.plot(self.loss_rpn_cls)
        plt.subplot(322, title='loss_rpn_bbox', ylabel='loss')
        plt.plot(self.loss_rpn_bbox)

        plt.subplot(323, title='loss_cls', ylabel='loss')
        plt.plot(self.loss_cls)
        plt.subplot(324, title='loss_bbox', ylabel='loss')
        plt.plot(self.loss_bbox)
        plt.subplot(325, title='total loss', ylabel='loss')
        plt.plot(self.loss)
        plt.subplot(326, title='accuracy', ylabel='accuracy')
        plt.plot(self.acc)
        plt.suptitle((sys.argv[1][5:] + "\n training result"), fontsize=30)
        plt.savefig(('output/' + sys.argv[1][5:] + '_result.png'))


if __name__ == '__main__':
    x = visualize_mmdetection(sys.argv[1])
    x.load_data()
    x.show_chart()

使用时直接是:

python visualize.py xxxx.json

xxxx.json是生成的json文件,结果如下:

在这里插入图片描述

PR曲线绘制

不太确定mmdetection里面有没有内置的绘制PR曲线的代码,这是参考其他一些博主写的代码

import os
import mmcv
import numpy as np
import matplotlib.pyplot as plt

from pycocotools.coco import COCO
from pycocotools.cocoeval import COCOeval

from mmcv import Config
from mmdet.datasets import build_dataset

# config文件路径
CONFIG_FILE = '/home/kevin/mmlab/mmdetection/tools/others_ct/r18/mask_rcnn_r18_fpn_1x_coco.py'
# test.py得到的pkl文件路径
RESULT_FILE = '/home/kevin/mmlab/mmdetection/tools/r18_result.pkl'


## 对比不同网络之间的结果
#CONFIG_FILE_01 = '/home/kevin/mmlab/mmdetection/tools/others_ct/r18/mask_rcnn_r18_fpn_1x_coco.py'
#RESULT_FILE_01 = '/home/kevin/mmlab/mmdetection/tools/r18_result.pkl'

#CONFIG_FILE_02 = '/home/kevin/mmlab/mmdetection/others_ct/ct_head_mask_rcnn_r50_rpn_100_coco/mask_rcnn_r50_fpn_1x_coco.py'
#RESULT_FILE_02 = '/home/kevin/mmlab/mmdetection/tools/r50_result.pkl'

#CONFIG_FILE_03 = '/home/kevin/mmlab/mmdetection/tools/others_ct/ct_head_mask_rcnn_r101_rpn_100_coco/mask_rcnn_r101_fpn_1x_coco.py'
#RESULT_FILE_03 = '/home/kevin/mmlab/mmdetection/tools/r101_result.pkl'

# 绘制曲线
def plot_pr_curve(config_file, result_file, metric="bbox"):
    """plot precison-recall curve based on testing results of pkl file.

        Args:
            config_file (list[list | tuple]): config file path.
            result_file (str): pkl file of testing results path.
            metric (str): Metrics to be evaluated. Options are
                'bbox', 'segm'.
    """

    cfg = Config.fromfile(config_file)
    # turn on test mode of dataset
    if isinstance(cfg.data.test, dict):
        cfg.data.test.test_mode = True
    elif isinstance(cfg.data.test, list):
        for ds_cfg in cfg.data.test:
            ds_cfg.test_mode = True

    # build dataset
    dataset = build_dataset(cfg.data.test)
    # load result file in pkl format
    pkl_results = mmcv.load(result_file)
    # convert pkl file (list[list | tuple | ndarray]) to json
    json_results, _ = dataset.format_results(pkl_results)
    # initialize COCO instance
    coco = COCO(annotation_file=cfg.data.test.ann_file)
    coco_gt = coco
    coco_dt = coco_gt.loadRes(json_results[metric])
    # initialize COCOeval instance
    coco_eval = COCOeval(coco_gt, coco_dt, metric)
    coco_eval.evaluate()
    coco_eval.accumulate()
    coco_eval.summarize()
    # extract eval data
    precisions = coco_eval.eval["precision"]
    '''
    precisions[T, R, K, A, M]
    T: 是IOU的阈值,值为0-9,依次对应,[0.5 : 0.05 : 0.95],
    R: 召回率的阈值,[0 : 0.01 : 1], idx from 0 to 100
    K: 检测类别的索引,我只有1类,所以为0
    A: 检测的大小,对应 (all, small, medium, large), 索引值0-3 
    M: 最大检测数量, 对应(1, 10, 100), 索引值0-2
    '''
    pr_array1 = precisions[0, :, 0, 0, 2]
    pr_array2 = precisions[1, :, 0, 0, 2]
    pr_array3 = precisions[2, :, 0, 0, 2]
    pr_array4 = precisions[3, :, 0, 0, 2]
    pr_array5 = precisions[4, :, 0, 0, 2]
    pr_array6 = precisions[5, :, 0, 0, 2]
    pr_array7 = precisions[6, :, 0, 0, 2]
    pr_array8 = precisions[7, :, 0, 0, 2]
    pr_array9 = precisions[8, :, 0, 0, 2]
    pr_array10 = precisions[9, :, 0, 0, 2]

	# 计算平均值 iou@0.5:0.95
    pr_array = pr_array1 + pr_array2 +pr_array3 +pr_array4 + pr_array5 + \
        pr_array6 + pr_array7 + pr_array8 + pr_array9 +pr_array10
    print(pr_array/10)

    x = np.arange(0.0, 1.01, 0.01)
    # 绘制PR曲线
    plt.plot(x, pr_array1, label="iou=0.5")
    plt.plot(x, pr_array2, label="iou=0.55")
    plt.plot(x, pr_array3, label="iou=0.6")
    plt.plot(x, pr_array4, label="iou=0.65")
    plt.plot(x, pr_array5, label="iou=0.7")
    plt.plot(x, pr_array6, label="iou=0.75")
    plt.plot(x, pr_array7, label="iou=0.8")
    plt.plot(x, pr_array8, label="iou=0.85")
    plt.plot(x, pr_array9, label="iou=0.9")
    plt.plot(x, pr_array10, label="iou=0.95")

    plt.xlabel("recall")
    plt.ylabel("precison")
    plt.xlim(0, 1.0)
    plt.ylim(0, 1.01)
    plt.grid(True)
    plt.legend(loc="lower left")
    # 保存图像
    plt.savefig('PR_r18.png')
    plt.show()
	
	# 只绘制ap @ 0.5:0.95
    # return pr_array/10



if __name__ == "__main__":
    plot_pr_curve(config_file=CONFIG_FILE, result_file=RESULT_FILE, metric="bbox")
    
    # pr_array_1 = plot_pr_curve(config_file=CONFIG_FILE_01, result_file=RESULT_FILE_01, metric="bbox")
    # pr_array_2 = plot_pr_curve(config_file=CONFIG_FILE_02, result_file=RESULT_FILE_02, metric="bbox")
    # pr_array_3 = plot_pr_curve(config_file=CONFIG_FILE_03, result_file=RESULT_FILE_03, metric="bbox")

    # x = np.arange(0.0, 1.01, 0.01)
    # plot PR curve
    # plt.plot(x, pr_array_1, label="r18")
    # plt.plot(x, pr_array_2, label="r50")
    # plt.plot(x, pr_array_3, label="r100")

    # plt.xlabel("recall")
    # plt.ylabel("precison")
    # plt.xlim(0, 1.0)
    # plt.ylim(0, 1.01)
    # plt.grid(True)
    # plt.legend(loc="lower left")
    # plt.savefig('PR_r18_r50_r101.png')
    # plt.show()

这边遇到了一个问题,就是使用plt保存图片时,必须要在ply.show前面,不然会保存为空白图像,因为它默认show完图像就被输出了,即没有可以保存的图像了。
得到结果图如下:
在这里插入图片描述

模型复杂度计算

这个官方文档就有python tools/analysis_tools/get_flops.py ${CONFIG_FILE} [--shape ${INPUT_SHAPE}]
得到结果如下:

==============================
Input shape: (3, 1280, 800)
Flops: 239.32 GFLOPs
Params: 37.74 M
==============================

对输出图像的处理

mmdetection检测或者预览的图像都是在image.py里面实现的,比如如果只是用mask rcnn来达到语义分割的效果,那么其实bbox和label输出就有点多余。同样是在def imshow_det_bboxes里面,把draw_bboxes和draw_labels注释掉就可以了

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

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

相关文章

JAVA常用API - Runtime和System

文章目录 前言 大家好,我是最爱吃兽奶,今天给大家带来JAVA常用API中的Runtime类和System类 那么就让我们一起去看看吧! 一、Rubtime 1.Rubtime是什么? 2.Runtime常用方法 Runtime提供了很多方法,在这里演示两个 public static Runtime getRuntime(): 返回当前运行时环境的…

ChatSQL - 文本生成SQL【LLM】

ChatSQL将用户提供的纯文本转换为 mysql 查询,基于ChatGPT实现。 推荐:用 NSDT设计器 快速搭建可编程3D场景。 1、ChatSQL简介 我们需要从一开始就指定一些关于我们数据库的信息,以便 Chatgpt 了解我们的数据库。 info.json 文件可用于此过程…

什么是胆汁酸,其与肠道微生物互作如何影响人体健康

谷禾健康 胆汁酸是一种代谢胆固醇的物质,它具有两个亲和性不同的区域,即一部分能够与水分子相互作用(亲水),而另一部分则不能与水分子相互作用(疏水)。 由于拥有这种两亲性质,胆汁酸…

开悟和悟道的一点看法

今天看到一个老师的在讲何为开悟,就做了一下笔记: 开悟就是明心见性,去除了见惑,见解方面,思想方面的迷惑,包含:身见(我和身体关系,我是谁),边见…

Vue3-黑马(十二)

目录: (1)vue3-router-图标的二次封装-jxl组件 (2)vue3-进阶-动态路由 (3)vue3-进阶-动态路由2 (1)vue3-router-图标的二次封装-jxl组件 以.vue结尾结尾的单文件组件…

谈谈OPCUA 聚合服务器(aggregation-server)

OPC UA 作为一种以信息模型为基础的工业自动化通信协议,如同它的名字一样正在朝着“统一架构”的方向飞速地发展,从传感器,PLC ,传感器到云端应用都正在向OPCUA 迁移。它将成为开放型系统,数字孪生,仿真系统…

Ubuntu20.04 -- 小白系列4 之小笔记

1、clash for linux Releases Dreamacro/clash GitHub 下载,建议这圈中的第一个。 解压后可执行文件改为clash 打开终端(打开你的suying,并复制自己的) wget -O config.yml https://s&log-levelinfo config.yml 复制并修改文件名为…

百度地图JavaScript API添加自定义Marker

百度地图JavaScript API添加自定义Marker 官网指导添加自定义Marker 实际使用中发现无法显示图标,找了一些博客 百度地图开发自定义图标无法显示的问题 百度地图自定义图标不显示问题解决方案 关于百度地图开放平台api覆盖物“自定义Marker图标”不能正常显示的解决…

bgp路由策略

* - valid 有效的, > - best 最佳的 上图中,有*和>,是有效最佳的。而没有*和没有>,是无效的,下一跳不可达 1--64511是公有AS 64512-65534为私有AS //属于哪个大的联盟 AS200 //连着一个子类AS 65002 //和子…

如何在桌面手动创建Windows安全中心的应用和浏览器快捷方式

Windows 10包括 Windows Security,它提供了最新的防病毒保护。从你启动Windows 10的那一刻起,你的设备将受到主动保护。 Windows Security不断扫描恶意软件(恶意软件)、病毒和安全威胁。除了这种实时保护外,还会自动下载更新,以帮助保护你的设备安全并保护其免受威胁。 …

centos安装SNB服务

Samba 是一种开源软件,它提供了一种让 Linux 和 Unix 系统与 Windows 操作系统相互通信的标准协议。Samba 允许 Linux 和 Unix 系统作为文件服务器和打印服务器,提供 Windows 客户端所需的服务。 具体来说,Samba 通过实现 SMB/CIFS 协议来实现…

Systick定时器

一、SysTick定时器介绍 SysTick定时器也叫SysTick滴答定时器,它是Cortex-M3内核的一个外设,所有使用Cortex-M3内核的单片机都具有这个定时器。Systick被内嵌在NVIC寄存器中,是一个24位的向下递减计数的定时器,在stm32中它的时钟通…

Xcode真机运行报错:Failed to prepare the device for development解决方法

一、遇到的问题 运行Xcode然后打包APP,结果Xcode报错,如下: Failed to prepare the device for development. This operation can fail if the version of the OS on the device is incompatible with the installed version of Xcode. You …

9个刚入门产品经理必备的原型工具

每一个产品的诞生都离不开一个优秀的产品经理,每一个优秀的产品经理都离不开一个原型图软件。 一款好用的产品原型图软件,可以让产品经理在绘制原型图时事半功倍。 本文总结了9款产品经理常用的产品原型图软件。如果你是产品经理小白,赶紧收…

路径规划算法:基于正余弦算法的路径规划算法- 附代码

路径规划算法:基于正余弦优化的路径规划算法- 附代码 文章目录 路径规划算法:基于正余弦优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要:本文主要介绍利用智能优化算法…

Java【网络编程2】详解基于 TCP 协议的 Socket API, 逐行代码解析如何服务器客户端通信(附代码)

文章目录 前言一、认识 Socket(套接字), TCP 协议和 UDP 协议1, 什么是 Socket(套接字)2, 浅谈 TCP 协议和 UDP 协议的区别和特点 二、基于 TCP 协议的 Socket API1, ServerSocket 类2, Socket 类 三、逐行代码解析网络编程1, 逐行解析客户端1.1, 核心成员方法 start() 2, 逐行…

三维空间与位姿变换

在使用相机或者机械臂的过程中,经常会用到三维空间变换,大部分时候我们可以使用TF去获取空间中的位姿关系,但是也存在一些时候无法使用现有TF表示的情况,例如:使用相机识别工件时,已知通过图像确定了相机到…

C++ - RBTree

前面的文章中我们讲述了以二叉搜索树为基础的AVL树,本文中我们将继续讲一种二叉搜索树为基础的红黑树。 红黑树的概念 红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或 Black。 通过对任何一条…

冲量在线出席2023鲲鹏昇腾开发者峰会,联合鲲鹏打造可信AIGC一体机,共筑产业数字根基

近日,以“创未来 享非凡”为主题的2023鲲鹏昇腾开发者峰会在东莞松山湖举办。鲲鹏昇腾开发者峰会是面向ICT领域开发者的技术盛会,旨在打造生态伙伴、开发者学习、交流的平台,帮助开发者深入了解鲲鹏、昇腾全栈技术,加速行业技术、…

超聚变携手冲量在线打造可信AIGC计算联合解决方案:软硬件高效协同之跃

金融行业作为全球经济的核心引擎,不断变革和创新是其发展的常态,在算力这一日趋成为数字经济时代的新型生产力的趋势下,围绕金融业数字化,业界展开了新一轮探索。 近日,2023中国国际金融展(简称&#xff1…