PySODEvalToolkit 使用笔记

1. 克隆仓库

首先,克隆PySODEvalToolkit仓库到你的本地机器:

git clone https://github.com/lartpang/PySODEvalToolkit.git

2. 创建虚拟环境

cd PySODEvalToolkit
conda create -n pysodeval python=3.7

3. 安装依赖

pip install -r requirements.txt

4. 放置要参与评估的数据

在 PySODEvalToolkit 目录下创建 Data 文件夹,如下图所示,在其中创建要评估测试的数据集文件夹,这里我创建了 2 个 CHAMELEON 和 COD10K,然后 Test/GT/ 下面放的是 Ground Truth 二值图,Test/Mask/ 下面放的是模型预测的 mask 二值图

PySODEvalToolkit/
├── Data/
│   ├── CHAMELEON/
│   │   ├── Test/
│   │   │   ├── GT/
│   │   │   └── Mask/
│   ├── COD10K/
│   │   ├── Test/
│   │   │   ├── GT/
│   │   │   └── Mask/
│   
├── config_dataset_json_example.json
├── config_method_json_example.json
├── eval.py

5. 修改数据配置文件

config_dataset_json_example.jsonconfig_method_json_example.json 这两个文件,

config_dataset_json_example.json 里对不同数据集的ground truth图像进行配置
config_method_json_example.json 里是模型在不同数据集上预测的mask图像进行配置

修改 config_dataset_json_example.json

以 CHAMELEON 为例,在配置文件中添加如下内容,实际只读取 “mask” 部分的内容,“prefix” 是 Data/CHAMELEON/Test/GT/ 中所有图像文件名的共同前缀,“suffix” 则是共同后缀

  "CHAMELEON": {"image": {"path": "Data/CHAMELEON/Test/GT", "prefix": "animal-","suffix": ".png"},"mask": {"path": "Data/CHAMELEON/Test/GT","prefix": "animal-","suffix": ".png"}}

修改 config_method_json_example.json

只评估 CHAMELEON 数据集的话,修改后的 config_method_json_example.json 文件内容如下,删除了 Method1,Method2,Method3,因为这里我只评估 SINetV2 这种方法在 CHAMELEON 数据集上的预测结果,如果要评估该方法在其他数据集上的预测结果,在该方法对应的方括号内按照 “CHAMELEON” 找猫画虎添加配置信息即可。
这里注意,新建"Method4",里面的名字替换成你要测试的模型方法,例如我要测SINetV2_2022,需要把 “Method4” 改成 “SINetV2_2022”,不然后面会报错。

{"Method4": {"CHAMELEON": {"path": "Data/CHAMELEON/Test/Mask","prefix": "animal-","suffix": ".png"}}
}

正确的:

{"SINetV2_2022": {"CHAMELEON": {"path": "Data/CHAMELEON/Test/Mask","prefix": "animal-","suffix": ".png"}}
}

6. 修改 yaml 文件 和 alias_for_plotting.json

alias_for_plotting.json 照猫画虎修改,添加 dataset 和 method

{"dataset": {"Name_In_Json": "Name_In_SubFigure","NJUD": "NJUD","NLPR": "NLPR","DUTRGBD": "DUTRGBD","STEREO1000": "SETERE","RGBD135": "RGBD135","SSD": "SSD","SIP": "SIP","CHAMELEON": "CHAMELEON"},"method": {"Name_In_Json": "Name_In_Legend","GateNet_2020": "GateNet","MINet_R50_2020": "MINet","SINetV2_2022": "SINetV2"}
}

converter_config.yaml 照猫画虎添加

dataset_names: ['NJUD','NLPR','SIP','STEREO1000','SSD','LFSD','RGBD135','DUTRGBD','COD10K','CHAMELEON' // 添加测试数据集
]# 使用单引号保证不被转义
method_names: {'2020-ECCV-DANetV19': 'DANet$_{20}$','2020-ECCV-HDFNetR50': 'HDFNet$_{20}$','2022-AAAI-SSLSOD-ImageNet': 'SSLSOD$_{22}$','2022-TPAMI-SINetV2': 'SINetV2$_{22}$', // 添加了这一行
}# 使用单引号保证不被转义
metric_names: {'sm': '$S_{m}~\uparrow$','wfm': '$F^{\omega}_{\beta}~\uparrow$','mae': '$MAE~\downarrow$','adpf': '$F^{adp}_{\beta}~\uparrow$','avgf': '$F^{avg}_{\beta}~\uparrow$','maxf': '$F^{max}_{\beta}~\uparrow$','adpe': '$E^{adp}_{m}~\uparrow$','avge': '$E^{avg}_{m}~\uparrow$','maxe': '$E^{max}_{m}~\uparrow$',
}

rgbd_aliases.yaml 照猫画虎添加

dataset: {"NJUD": "NJUD","NLPR": "NLPR","SIP": "SIP","STEREO1000": "STEREO1000","RGBD135": "RGBD135","SSD": "SSD","LFSD": "LFSD","DUTRGBD": "DUTRGBD","CHAMELEON": "CHAMELEON"
}method: {'2020-ECCV-DANetV19': 'DANet$_{20}$','2020-ECCV-HDFNetR50': 'HDFNet$_{20}$','2022-AAAI-SSLSOD-ImageNet': 'SSLSOD$_{22}$','2022-TPAMI-SINetV2': 'SINetV2$_{22}$'
}

7. 检测修改后的配置文件是否有问题

在修改完数据集配置文件和方法配置文件后,执行这个命令检查

python tools/check_path.py -m examples/config_method_json_example.json -d examples/config_dataset_json_example.json

如果正确配置的话会打印

examples/config_method_json_example.json &examples/config_dataset_json_example.json 基本正常

如果未正确配置的话,会打印错误信息,
根据错误信息调试,如果提示 Data/CHAMELEON/Test/GT/ 和 Data/CHAMELEON/Test/Mask 文件名不匹配的话,首先检查这两个路径下的文件数量是否相同,前缀名和后缀名是否全部相同且json文件中是否正确配置。

如果确保Data/CHAMELEON/Test/GT/ 和 Data/CHAMELEON/Test/Mask 文件名是匹配的且json文件中也正确配置了,此时需要检查 check_path.py 文件中解析文件名的逻辑是否有问题,大概率是这里出错了,修改 check_path.py 添加调试打印信息根据输出进行排查。

8. 运行评估

运行评估脚本以计算评估指标和生成可视化图表:

python eval.py \--dataset-json config_dataset_json_example.json \--method-json config_method_json_example.json \--metric-npy output/metrics.npy \--curves-npy output/curves.npy \--record-txt output/results.txt \--record-xlsx output/results.xlsx \--num-workers 4 \--num-bits 3 \--metric-names sm wfm mae fmeasure em \--include-datasets datasettobeevaled \--include-methods yourmethod

参数解释

  • --dataset-json:数据集配置文件的路径。
  • --method-json:方法配置文件的路径,可以指定多个文件。
  • --metric-npy:用于保存评估指标结果的.npy文件路径。
  • --curves-npy:用于保存曲线结果的.npy文件路径。
  • --record-txt:用于保存评估结果的.txt文件路径。
  • --record-xlsx:用于保存评估结果的.xlsx文件路径。
  • --num-workers:用于多线程或多进程的工作线程数,默认值为4。
  • --num-bits:结果显示的小数位数,默认值为3。
  • --metric-names:要计算的评估指标的名称列表。
  • --include-datasets:要评估的特定数据集的名称。
  • --include-methods:要评估的特定方法的名称。

执行命令时 --num-workers--num-bits 可以省略,

然后我实际执行的eval命令语句是:

python eval.py --dataset-json examples/config_dataset_json_example.json 
--method-json examples/config_method_json_example.json 
--metric-npy output/metrics.npy 
--curves-npy output/curves.npy 
--record-txt output/results.txt 
--record-xlsx output/results.xlsx 
--metric-names sm wfm mae fmeasure em precision recall 
--include-datasets CHAMELEON 
--include-methods SINetV2_2022

执行该命令后评估的结果会自动创建并保存到这个目录下 PySODEvalToolkit/output ,可以看到,命令在 metric-names 中添加了 precision recall 指标,对SINetV2_2022 模型方法在 CHAMELEON 数据集的结果进行评估,如果要对多种模型方法在多个数据集的结果进行评估,空格并列添加就行,这里 --include-methods 中的方法名来自于 alias_for_plotting.json 中。

解释输出结果

运行脚本后,你将在指定的输出路径中找到结果文件:

  • metrics.npy:包含计算的评估指标结果的numpy数组。
  • curves.npy:包含生成的曲线结果的numpy数组。
  • results.txt:包含评估结果的文本文件。
  • results.xlsx:包含评估结果的Excel文件。

这些文件将详细记录你的模型在 CHAMELEON 数据集上的性能,包括各种评估指标和可视化曲线。

9. 绘制曲线图

plot.py 脚本用于绘制评估曲线,例如 Fm 曲线和 PR 曲线。该脚本通过读取保存的 .npy 文件中的曲线数据,并使用指定的样式配置文件生成图像。

使用方法

参数说明
  • --alias-yaml:数据集和方法别名的 YAML 文件。
  • --style-cfg:用于绘制曲线的 YAML 配置文件。
  • --curves-npys:保存曲线结果的 .npy 文件。
  • --our-methods:我们的方法的名称,用于突出显示。
  • --num-rows:子图的行数。
  • --num-col-legend:图例的列数。
  • --mode:绘图模式,可以是 prfmem
  • --separated-legend:使用分离的图例。
  • --sharey:使用共享的 y 轴。
  • --save-name:导出文件路径。
示例用法

绘制的命令

python plot.py --style-cfg examples/single_row_style.yml 
--num-rows 1 
--curves-npys output/curves.npy
--mode em 
--save-name output/simple_curve_em 
--alias-yaml examples/rgbd_aliases.yaml

例如 output/simple_curve_em 会在 output 目录下生成 simple_curve_em.pdf 文件,该文件打开就是绘制的曲线图

参数详细说明

  • --curves-npys:指定一个或多个保存曲线结果的 .npy 文件,这些文件包含了曲线的数据。
  • --style-cfg:指定用于绘图的样式配置文件,包含了 matplotlib 的配置,例如图例位置、字体大小等。
  • --num-rows:指定生成图像的行数。
  • --num-col-legend:指定图例的列数。
  • --mode:指定绘图的模式,有三种选择:
    • pr:绘制 Precision-Recall 曲线。
    • fm:绘制 F-measure 曲线。
    • em:绘制 E-measure 曲线。
  • --separated-legend:如果设置了这个选项,将使用分离的图例。
  • --sharey:如果设置了这个选项,将使用共享的 y 轴。
  • --save-name:指定保存图像的文件路径。

draw_curves 的使用

脚本的核心功能由 draw_curves.draw_curves 实现。以下是该函数的参数:

  • mode:绘图模式 (prfmem)。
  • axes_setting:不同曲线的绘图配置。
  • curves_npy_path:包含曲线数据的 .npy 文件路径。
  • row_num:子图的行数。
  • method_aliases:方法的别名。
  • dataset_aliases:数据集的别名。
  • style_cfg:样式配置文件路径。
  • ncol_of_legend:图例的列数。
  • separated_legend:是否使用分离的图例。
  • sharey:是否使用共享的 y 轴。
  • our_methods:需要高亮显示的方法。
  • save_name:保存图像的文件路径。

配置文件示例

single_row_style.yml 配置文件可能如下:

savefig.format: "png"
savefig.dpi: 300
savefig.bbox: "tight"
savefig.pad_inches: 0.1
axes.labelsize: 12
axes.titlesize: 14
xtick.labelsize: 10
ytick.labelsize: 10
legend.fontsize: 10

绘制图像的步骤

  1. 确保已经生成了包含曲线数据的 .npy 文件。
  2. 准备好样式配置文件,例如 single_row_style.yml
  3. 运行 plot.py,指定必要的参数。

总结

plot.py 脚本通过读取 .npy 文件中的曲线数据,并使用指定的样式配置文件生成图像。用户可以通过不同的参数控制生成图像的样式、子图的布局以及图例的配置。使用示例命令可以帮助用户快速上手绘制评估曲线。

10. 查看结果

评估结果和可视化图表将保存在配置文件中指定的save_dir目录中。

出现的问题

在修改完数据集配置文件和方法配置文件后,执行这个命令

python tools/check_path.py -m examples/config_method_json_example.json -d examples/config_dataset_json_example.json

出错提示:

(pysodeval) root@autodl-container-d9084da129-76991c4d:~/PySODEvalToolkit# python tools/check_path.py -m examples/config_method_json_example.json -d examples/config_dataset_json_example.json
Checking for Method1 ...
Traceback (most recent call last):File "tools/check_path.py", line 30, in <module>dataset_mask_info = datasets_info[dataset_name]["mask"]
KeyError: 'PASCAL-S'

解决方法:
在 examples/config_dataset_json_example.json 中添加

  "PASCAL-S": {"image": {"path": "Path_Of_RGBDSOD_Datasets/PASCAL-S/Image","suffix": ".jpg"},"mask": {"path": "Path_Of_RGBDSOD_Datasets/PASCAL-S/Mask","suffix": ".png"}},"ECSSD": {"image": {"path": "Path_Of_RGBDSOD_Datasets/ECSSD/Image","suffix": ".jpg"},"mask": {"path": "Path_Of_RGBDSOD_Datasets/ECSSD/Mask","suffix": ".png"}},"HKU-IS": {"image": {"path": "Path_Of_RGBDSOD_Datasets/HKU-IS/Image","suffix": ".jpg"},"mask": {"path": "Path_Of_RGBDSOD_Datasets/HKU-IS/Mask","suffix": ".png"}},"DUT-OMRON": {"image": {"path": "Path_Of_RGBDSOD_Datasets/DUT-OMRON/Image","suffix": ".jpg"},"mask": {"path": "Path_Of_RGBDSOD_Datasets/DUT-OMRON/Mask","suffix": ".png"}},"DUTS-TE": {"image": {"path": "Path_Of_RGBDSOD_Datasets/DUTS-TE/Image","suffix": ".jpg"},"mask": {"path": "Path_Of_RGBDSOD_Datasets/DUTS-TE/Mask","suffix": ".png"}}

之后重新执行,又出错:

Checking for Method1 ...
Checking for Method2 ...
Checking for Method3 ...
Checking for Method4 ...
Path_Of_Method1/PASCAL-S/DGRL 不存在
Path_Of_Method1/ECSSD/DGRL 不存在
Path_Of_Method1/HKU-IS/DGRL 不存在
Path_Of_Method1/DUT-OMRON/DGRL 不存在
Path_Of_Method1/DUTS-TE/DGRL 不存在
Path_Of_Method2/pascal 不存在
Path_Of_Method2/ecssd 不存在
Path_Of_Method2/hku 不存在
Path_Of_Method2/duto 不存在
Path_Of_Method2/dut_te 不存在
Path_Of_Method3/pascal 不存在
Path_Of_Method3/ecssd 不存在
Path_Of_Method3/hku 不存在
Path_Of_Method3/duto 不存在
Path_Of_Method3/dut_te 不存在
Data/COD10K/Test/Mask 中数据名字与真值 Data/COD10K/Test/GT 不匹配

解决办法:

把config_method_json_example.json下面这段代码删除掉可以解决掉不存在的报错,但是仍然存在数据名字不匹配的错误,

  "Method1": {"PASCAL-S": {"path": "Path_Of_Method1/PASCAL-S/DGRL","prefix": "some_method_prefix","suffix": ".png"},"ECSSD": {"path": "Path_Of_Method1/ECSSD/DGRL","prefix": "some_method_prefix","suffix": ".png"},"HKU-IS": {"path": "Path_Of_Method1/HKU-IS/DGRL","prefix": "some_method_prefix","suffix": ".png"},"DUT-OMRON": {"path": "Path_Of_Method1/DUT-OMRON/DGRL","prefix": "some_method_prefix","suffix": ".png"},"DUTS-TE": {"path": "Path_Of_Method1/DUTS-TE/DGRL","suffix": ".png"}},"Method2": {"PASCAL-S": {"path": "Path_Of_Method2/pascal","prefix": "pascal_","suffix": ".png"},"ECSSD": {"path": "Path_Of_Method2/ecssd","prefix": "ecssd_","suffix": ".png"},"HKU-IS": {"path": "Path_Of_Method2/hku","prefix": "hku_","suffix": ".png"},"DUT-OMRON": {"path": "Path_Of_Method2/duto","prefix": "duto_","suffix": ".png"},"DUTS-TE": {"path": "Path_Of_Method2/dut_te","prefix": "dut_te_","suffix": ".png"}},"Method3": {"PASCAL-S": {"path": "Path_Of_Method3/pascal","prefix": "pascal_","suffix": "_fused_sod.png"},"ECSSD": {"path": "Path_Of_Method3/ecssd","prefix": "ecssd_","suffix": "_fused_sod.png"},"HKU-IS": {"path": "Path_Of_Method3/hku","prefix": "hku_","suffix": "_fused_sod.png"},"DUT-OMRON": {"path": "Path_Of_Method3/duto","prefix": "duto_","suffix": "_fused_sod.png"},"DUTS-TE": {"path": "Path_Of_Method3/dut_te","prefix": "dut_te_","suffix": "_fused_sod.png"}},

数据名字不匹配的错误原因是 check_path.py 文件中解析文件名的逻辑有问题:
修改后增加了打印调试信息的 check_path.py 代码:

# -*- coding: utf-8 -*-import argparse
import json
import os
from collections import OrderedDictparser = argparse.ArgumentParser(description="A simple tool for checking your json config file.")
parser.add_argument("-m", "--method-jsons", nargs="+", required=True, help="The json file about all methods."
)
parser.add_argument("-d", "--dataset-jsons", nargs="+", required=True, help="The json file about all datasets."
)
args = parser.parse_args()for method_json, dataset_json in zip(args.method_jsons, args.dataset_jsons):with open(method_json, encoding="utf-8", mode="r") as f:methods_info = json.load(f, object_hook=OrderedDict)  # 有序载入with open(dataset_json, encoding="utf-8", mode="r") as f:datasets_info = json.load(f, object_hook=OrderedDict)  # 有序载入total_msgs = []for method_name, method_info in methods_info.items():print(f"Checking for {method_name} ...")for dataset_name, results_info in method_info.items():if results_info is None:continuedataset_mask_info = datasets_info[dataset_name]["mask"]mask_path = dataset_mask_info["path"]mask_suffix = dataset_mask_info["suffix"]dir_path = results_info["path"]file_prefix = results_info.get("prefix", "")file_suffix = results_info["suffix"]if not os.path.exists(dir_path):total_msgs.append(f"{dir_path} 不存在")continueelif not os.path.isdir(dir_path):total_msgs.append(f"{dir_path} 不是正常的文件夹路径")continueelse:pred_names = [name[len(file_prefix):-len(file_suffix)]for name in os.listdir(dir_path)if name.startswith(file_prefix) and name.endswith(file_suffix)]if len(pred_names) == 0:total_msgs.append(f"{dir_path} 中不包含前缀为 {file_prefix} 且后缀为 {file_suffix} 的文件")continuemask_names = [name[len(file_prefix):-len(mask_suffix)]for name in os.listdir(mask_path)if name.endswith(mask_suffix)]# 调试输出print(f"Prefix: {file_prefix}")print(f"Suffix: {file_suffix}")print(f"Prediction names in {dir_path}: {pred_names}")print(f"Ground truth mask names in {mask_path}: {mask_names}")pred_names_set = set(pred_names)mask_names_set = set(mask_names)intersection_names = pred_names_set.intersection(mask_names_set)if len(intersection_names) == 0:total_msgs.append(f"{dir_path} 中数据名字与真值 {mask_path} 不匹配")elif len(intersection_names) != len(mask_names):difference_names = mask_names_set.difference(pred_names_set)total_msgs.append(f"{dir_path} 中数据({len(pred_names_set)})与真值({len(mask_names_set)})不一致: {difference_names}")if total_msgs:print(*total_msgs, sep="\n")else:print(f"{method_json} & {dataset_json} 基本正常")

重新检查配置文件后输出如下:

Checking for Method4 ...
Prefix: animal-
Suffix: .png
Prediction names in Data/CHAMELEON/Test/Mask: ['1', '10', '11', '12', 
'13', '14', '15', '16', '17', '18', '19', '2', '20', '21', '22', '23', 
'24', '25', '26', '27', '28', '29', '3', '30', '31', '32', '33', '34', 
'35', '36', '37', '38', '39', '4', '40', '41', '42', '43', '44', '45','46', '47', '48', '49', '5', '50', '51', '52', '53', '54', '55','56', '57', '58', '59', '6', '60', '61', '62', '63', '64', '65','66', '67', '68', '69', '7', '70', '71', '72', '73', '74', '75','76', '8', '9']
Ground truth mask names in Data/CHAMELEON/Test/GT: ['1', '10', '11','12', '13', '14', '15', '16', '17', '18', '19', '2', '20', '21','22', '23', '24', '25', '26', '27', '28', '29', '3', '30', '31','32', '33', '34', '35', '36', '37', '38', '39', '4', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '5', '50', '51','52', '53', '54', '55', '56', '57', '58', '59', '6', '60', '61','62', '63', '64', '65', '66', '67', '68', '69', '7', '70', '71','72', '73', '74', '75', '76', '8', '9']
examples/config_method_json_example.json &examples/config_dataset_json_example.json 基本正常

可以看到此时对比两个路径下文件名信息是匹配的。

绘制pr曲线时

Traceback (most recent call last):File "plot.py", line 139, in <module>main(args)File "plot.py", line 133, in mainsave_name=args.save_name,File "/root/PySODEvalToolkit/metrics/draw_curves.py", line 71, in draw_curvesraise ValueError(f"{x} must be contained in\n{dataset_names_from_npy}")
ValueError: NJUD must be contained in
['CHAMELEON', 'CAMO', 'COD10K']

原因是 rgbd_aliases.yaml 中的数据集需要删掉这样部分,因为 plot.py 脚本尝试绘制的曲线数据集包含 NJUD,但实际提供的 .npy 文件中只有 CHAMELEON、CAMO 和 COD10K。删除掉 rgbd_aliases.yaml 中下面这些数据集

    "NJUD": "NJUD","NLPR": "NLPR","SIP": "SIP","STEREO1000": "STEREO1000","RGBD135": "RGBD135","SSD": "SSD","LFSD": "LFSD","DUTRGBD": "DUTRGBD",

绘制的曲线是空白

首先确保 output/curves.npy 文件中确实包含了正确的数据。可以使用以下代码来查看 .npy 文件的内容:

在 PySODEvalToolkit 目录下创建一个 check_nyp.py 文件然后执行该脚本查看输出的数据

import numpy as npdata = np.load('output/curves.npy', allow_pickle=True).item()
print(data)

排查了半天才发现是 alias_for_plotting.json 和 rgbd_aliases.yaml 中 method 配置不匹配导致 提供的 method_aliases 中的方法别名与 curves.npy 文件中的方法名称不匹配。因为别名不匹配,target_unique_method_names 为空,所以没有绘制任何曲线。为了解决这个问题,首先确保 method_aliases 中的方法别名与 curves.npy 中的方法名称一致。修改如下

alias_for_plotting.json

{"dataset": {"Name_In_Json": "Name_In_SubFigure","NJUD": "NJUD","NLPR": "NLPR","DUTRGBD": "DUTRGBD","STEREO1000": "SETERE","RGBD135": "RGBD135","SSD": "SSD","SIP": "SIP","CAMO": "CAMO","CHAMELEON": "CHAMELEON","COD10K": "COD10K"},"method": {"Name_In_Json": "Name_In_Legend","GateNet_2020": "GateNet","MINet_R50_2020": "MINet","SINetV2_2022": "SINetV2"}
}

rgbd_aliases.yaml

dataset: {"CAMO": "CAMO","CHAMELEON": "CHAMELEON","COD10K": "COD10K"
}method: {'2020-ECCV-DANetV19': 'DANet$_{20}$','2020-ECCV-HDFNetR50': 'HDFNet$_{20}$','2022-AAAI-SSLSOD-ImageNet': 'SSLSOD$_{22}$','SINetV2_2022': 'SINetV2'
}

然后执行 plot 就可以得到绘制的非空白曲线了!

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

[算法] 优先算法(一): 双指针算法(上)

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏:&#x1f355; Collection与数据结构 (91平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm1001.2014.3001.5482 &#x1f9c0;Java …

SpringBoot2.0.x旧版集成Swagger UI报错Unable to infer base url...解决办法

一、问题描述 1.1项目背景 SpringBoot2.0.9的旧版项目维护开发&#xff0c;集成Swagger-ui2.9.2无法访问的问题。不用想啊&#xff0c;这种老项目是各种过滤器拦截器的配置&#xff0c;访问不到&#xff0c;肯定是它们在作妖。懂得都懂啊&#xff0c;这里交给大家一个排错的办…

Flutter设计模式全面解析:单例模式

谈到设计模式这个“古老”的话题&#xff0c;大家先别急着划走哈&#xff0c;虽然对它再熟悉不过&#xff0c;几乎是最初开始学习编程到现在伴随着我们整个编程生涯&#xff0c;最早 Java、C 语言实现的各种设计模式到现在还会经常有所接触&#xff0c;面试中也是必问的环节&am…

Adobe Camera Raw 11 for Mac/win:摄影后期处理的革命性飞跃

在数字摄影的世界里&#xff0c;RAW格式以其未压缩的原始数据特性&#xff0c;为摄影师提供了更大的后期处理空间。而Adobe Camera Raw 11&#xff0c;作为这一领域的翘楚&#xff0c;以其卓越的性能和创新的功能&#xff0c;为摄影师们带来了前所未有的创作体验。 Adobe Came…

LeetCode450删除二叉搜索树中的节点

题目描述 给定一个二叉搜索树的根节点 root 和一个值 key&#xff0c;删除二叉搜索树中的 key 对应的节点&#xff0c;并保证二叉搜索树的性质不变。返回二叉搜索树&#xff08;有可能被更新&#xff09;的根节点的引用。一般来说&#xff0c;删除节点可分为两个步骤&#xff1…

Linux环境中部署docker私有仓库Registry与远程访问详细流程

目录 前言 1. 部署Docker Registry 2. 本地测试推送镜像 3. Linux 安装cpolar 4. 配置Docker Registry公网访问地址 5. 公网远程推送Docker Registry 6. 固定Docker Registry公网地址 前言 作者简介&#xff1a; 懒大王敲代码&#xff0c;计算机专业应届生 今天给大家聊…

网络安全知识核心20要点

1、什么是SQL注入攻击 概述 攻击者在 HTTP 请求中注入恶意的 SQL 代码&#xff0c;服务器使用参数构建数据库 SQL 命令时&#xff0c;恶意SQL 被一起构造&#xff0c;并在数据库中执行。 注入方法 用户登录&#xff0c;输入用户名 lianggzone&#xff0c;密码‘ or ‘1’’…

揭秘Python的魔法:装饰器的超能力大揭秘 ‍♂️✨

文章目录 Python进阶之装饰器详解1. 引言装饰器的概念与意义装饰器在Python编程中的作用 2. 背景介绍2.1 函数作为对象2.2 高阶函数 3. 装饰器基础3.1 理解装饰器3.2 装饰器的工作原理 4. 带参数的装饰器4.1 为什么需要带参数4.2 实现带参数的装饰器使用函数包裹装饰器使用类实…

TypeScript-泛型

泛型(Generics) 指在定义接口&#xff0c;函数等类型的时候&#xff0c;不预先指定具体的类型&#xff0c;而在使用的时候再指定类型的一种特性&#xff0c;使用泛型可以复用类型并且让类型更加灵活 泛型接口-interface 语法&#xff1a;在 interface 接口类型的名称后面使用…

web前端的路径和Servlet注解开发

目录 在web前端的两种路径 绝对路径的两种写法 相对路径 相对路径进阶 使用注解开发Servlet 使用注解开发Servlet的注意事项 使用idea创建servlet模板 在web前端的两种路径 绝对路径的两种写法 1.带网络三要素 http://ip地址:端口号/资源路径 2.不带网络三要素 /资源路…

『哈哥赠书 - 53期』-『深入浅出 Spring Boot 3.x』

⭐️ 《深入浅出 Spring Boot 3.x》 ⭐️ 学习Spring Boot的必读之书 在 Java 后端开发领域&#xff0c;功能强大的 Spring 开源框架不仅是首选&#xff0c;也是事实上的标准。但由于 Spring 存在配置烦琐、部署不易、依赖管理困难等问题&#xff0c;因此基于 Spring 的快速开…

告别传统,拥抱未来——上门回收小程序引领变革

随着科技的飞速发展&#xff0c;我们生活的方方面面都在经历着前所未有的变革。在环保和可持续发展的背景下&#xff0c;传统的废品回收方式已经难以满足现代社会的需求。而上门回收小程序的出现&#xff0c;正以其便捷、高效的特点&#xff0c;引领着废品回收行业的变革。 一、…

HCIP-Datacom-ARST自选题库__OSPF单选【80道题】

1.OSPFV2是运行在IPV4网络的IGP&#xff0c;OSPFV3是运行在IPV6网络的ICP&#xff0c;OSPFV3与OSPFv2的报文类型相同&#xff0c;包括Hello报文、DD报文、LSR报文、LSU报文和LSAck报文。关于OSPFv3报文&#xff0c;以下哪个说法是正确的 OSPFv3使用报文头部的认证字段完成报文…

抖音跳转微信卡片制作教程 小白也能搞

实测可以正常跳转&#xff0c;很牛逼&#xff0c;给大家分享一下~ 这是我做出来抖音发出去的效果&#xff0c;大家会制作了可以去卖钱&#xff0c;市场上一个这个卡片都要卖50-200&#xff0c;很不错的&#xff01;&#xff01; https://pan.baidu.com/s/1xPmGAWPcbAp7eXg7Dc…

温故而知新-秒杀项目篇【面试复习】

温故而知新-秒杀项目篇【面试复习】 前言版权推荐温故而知新-论坛项目篇【面试】秒杀项目中注册模块怎么实现的&#xff1f;秒杀项目中登录模块怎么实现的&#xff1f;秒杀项目中显示登录用户信息怎么实现的&#xff1f;SessionStorage是什么?为什么不用session而用token什么是…

骨位深间距小模具镶件如何走水路?3D打印让一切简单

在模具制造领域&#xff0c;骨位深且间距小的模具镶件由于结构复杂&#xff0c;传统加工方法难以制造出符合要求的冷却水路&#xff0c;导致模具在注塑过程中容易产生热量积聚&#xff0c;进而引发烫伤、缩孔等不良。然而&#xff0c;随着3D打印技术的飞速发展&#xff0c;这些…

0基础学习Mybatis系列数据库操作框架——Mysql的Geometry数据处理之WKT方案

大纲 几何结构构建点点集合线线集合面面集合几何信息集合 TypeHandlerSQL操作写入操作读取操作完整XML Mapper测试代码建表SQL总结代码参考资料 WKT全称是Well-Known Text。它是一种表达几何信息的字符串内容。比如点可以用WKT表示为POINT (3 3)&#xff1b;线可以用WKT表示为L…

Playwright教程

Playwright简介 支持多数浏览器 在Chromium&#xff0c;Firefox和WebKit上进行测试。Playwright拥有适用于所有现代浏览器的完整API覆盖&#xff0c;包括Google Chrome和Microsoft Edge&#xff08;带有Chromium&#xff09;&#xff0c;Apple Safari&#xff08;带有WebKit&a…

【哈希映射 字符串 乘法原理】2227. 加密解密字符串

本文涉及知识点 哈希映射 字符串 乘法原理 LeetCode 2227. 加密解密字符串 给你一个字符数组 keys &#xff0c;由若干 互不相同 的字符组成。还有一个字符串数组 values &#xff0c;内含若干长度为 2 的字符串。另给你一个字符串数组 dictionary &#xff0c;包含解密后所…

SpringCloud微服务03-微服务保护-分布式事务-MQ基础-MQ高级

一、微服务保护 1.雪崩问题 如何做好后备方案就是后续&#xff1a; 2.雪崩解决方案 某一个服务的线程是固定的&#xff0c;出现故障线程占满后&#xff0c;就不会让取调用这个服务&#xff0c;对其他服务就没有影响。 3.Sentinel ①初识Sentinel 配置过程&#xff1a;day05-服…