文章目录
- 一、任务描述
- 二、指标分析
- 2.1 TP/FP/FN/TN
- 2.2 精准率
- 2.3 召回率
- 三、接口处理
- 四、数据集处理
- 五、开始计算指标
- 五、实用工具
- 5.1 移动文件
- 5.2 可视化JSON标签
- 5.3 可视化TXT标签
一、任务描述
通过给定的算法接口,对算法的输出(置信度、检测框、告警、类别等)进行数据处理,结合原标签完成TP、FP、FN、TN、准确率、召回率的指标测试。
二、指标分析
2.1 TP/FP/FN/TN
TP(True Positives,真正例):表示模型正确预测为正类的样本数量,也就是将正例正确分类为正例的数量。
FP(False Positives,假正例):表示模型错误预测为正类的样本数量,也就是将负例错误分类为正例的数量。
FN(False Negatives,假负例):表示模型错误预测为负类的样本数量,也就是将正例错误分类为负例的数量。
TN(True Negatives,真负例):表示模型正确预测为负类的样本数量,也就是将负例正确分类为负例的数量。
2.2 精准率
精准率是指模型在所有被预测为正例的样本中,真正为正例的比例。它的计算公式是:Precision = TP / (TP + FP)。精准率衡量了模型的预测中有多少是真正的正例,是一个关于假正例的指标。
2.3 召回率
召回率是指模型在所有实际正例中,成功预测为正例的比例。它的计算公式是:Recall = TP / (TP + FN)。召回率衡量了模型对于所有正例的识别能力,是一个关于假负例的指标。
三、接口处理
import os, base64, json, time, requestsdef get_all_file(path):result_list = []for root, dirs, files in os.walk(path):for file in files:if os.path.basename(file).__contains__(".jpg"):result_list.append(os.path.join(root, file))return result_listdef pic2base64(img_path):if os.path.exists(img_path):encoded_base64 = base64.b64encode(open(img_path, 'rb').read()).decode()return encoded_base64else:os.error("图片不存在,请检查图片路径:{}".format(img_path))def write_content_to_json(content, json_file_path):with open(json_file_path, 'w+', encoding='utf-8') as f:f.write(json.dumps(content, indent=4, ensure_ascii=False))if __name__ == "__main__":result = {}fish_pic_path = r"D:\datasets\test_mission\fishing_test_data"url_fish = "http://***.***.***.*:端口号/能力接口"pic_list = get_all_file(fish_pic_path)for pic in pic_list:image_base64 = pic2base64(pic)payload = {"seqid": "test1","timestamp": int(time.time()),"image": image_base64# "image": rf';base64,{image_base64}\"alt=\"\">'}res = requests.post(url_fish, json=payload)json_res = res.json()print(json_res)if 'code' in json_res and json_res['code'] == 6000:result[os.path.basename(pic)] = json_res['data']write_content_to_json(result, 'result/fish_result.json')
将所有的结果保存为JSON文档,如下:
四、数据集处理
数据集统一处理成:
数据集如果是JSON格式,通过下面的代码转换为TXT格式
import os
import numpy as np
import json
from glob import glob
import cv2
from sklearn.model_selection import train_test_split
from shutil import copyfile
import argparseobj_classes = []# Labelme坐标到YOLO V5坐标的转换
def convert(size, box):dw = 1. / (size[0])dh = 1. / (size[1])x = (box[0] + box[1]) / 2.0 - 1y = (box[2] + box[3]) / 2.0 - 1w = box[1] - box[0]h = box[3] - box[2]x = x * dww = w * dwy = y * dhh = h * dhreturn (x, y, w, h)def create(yolo_labels_dir):if not os.path.exists(yolo_labels_dir):os.makedirs(yolo_labels_dir)# 样本转换
def convertToYolo5(fileList, output_dir, labelImg_path, unify_path):# 创建指定样本的父目录if not os.path.exists(output_dir):os.makedirs(output_dir)# 创建指定样本的images和labels子目录yolo_images_dir = '{}/images/'.format(output_dir)yolo_labels_dir = '{}/labels/'.format(output_dir)create(yolo_images_dir)create(yolo_labels_dir)create(unify_path)# 一个样本图片一个样本图片地转换for num,json_file_ in enumerate(fileList):# print('fileList',fileList)# 1. 生成YOLO样本图片# 构建json图片文件的全路径名imagePath = labelImg_path + '/' + json_file_ + ".jpg"print('name',imagePath, json_file_)# print('labelme_path', labelme_path)# 构建Yolo图片文件的全路径名yolo_image_file_path = yolo_images_dir + "{}.jpg".format(json_file_)print('yolo_image_file_path', yolo_image_file_path)# copy样本图片copyfile(imagePath, yolo_image_file_path)# 2. 生成YOLO样本标签# 构建json标签文件的全路径名labelme_path_ = labelImg_path.split('image')[0]json_filename = labelme_path_ + 'json'+'\\' + json_file_ + ".json"# 构建Yolo标签文件的全路径名yolo_label_file_path = yolo_labels_dir + "{}.txt".format(json_file_)txt_label_file_path = unify_path + "/{}.txt".format(json_file_)# 创建新的Yolo标签文件yolo_label_file = open(yolo_label_file_path, 'w')txt_label_file = open(txt_label_file_path, 'w')# 获取当前图片的Json标签文件json_obj = json.load(open(json_filename, "r", encoding="utf-8"))# 获取当前图片的长度、宽度信息height = json_obj['imageHeight']width = json_obj['imageWidth']# 依次读取json文件中所有目标的shapes信息for shape in json_obj["shapes"]:# 获取shape中的物体分类信息label = shape["label"]if label not in ['car_red','car_orange','car_green','car_blue','car_black','car_white','car_purple','car_grey','car_silvery','car grey','car orange','car black','car','car blue','car purple','car white','car silvery','car green','car_yellow','car red']:if label == 'Fengtain' or label == 'FengTain' or label == 'FengtTian' or label == 'Fengtian':label = 'FengTian'if (label not in obj_classes):obj_classes.append(label)# 获取shape中的物体坐标信息if (shape["shape_type"] == 'rectangle'):points = np.array(shape["points"])xmin = min(points[:, 0]) if min(points[:, 0]) > 0 else 0xmax = max(points[:, 0]) if max(points[:, 0]) > 0 else 0ymin = min(points[:, 1]) if min(points[:, 1]) > 0 else 0ymax = max(points[:, 1]) if max(points[:, 1]) > 0 else 0# 对坐标信息进行合法性检查if xmax <= xmin:passelif ymax <= ymin:passelse:# Labelme坐标转换成YOLO V5坐标bbox_labelme_float = (float(xmin), float(xmax), float(ymin), float(ymax))bbox_yolo_normalized = convert((width, height), bbox_labelme_float)# 把分类标签转换成分类idclass_id = obj_classes.index(label)# 生成YOLO V5的标签文件yolo_label_file.write(str(class_id) + " " + " ".join([str(a) for a in bbox_yolo_normalized]) + '\n')# 保存为统一的位置txt_label_file.write(str(class_id) + " " + " ".join([str(a) for a in bbox_yolo_normalized]) + '\n')yolo_label_file.close()txt_label_file.close()def check_output_directory(output=""):# 创建保存输出图片的目录save_path = output + '/'is_exists = os.path.exists(save_path)if is_exists:print('Warning: path of %s already exist, please remove it firstly by manual' % save_path)# shutil.rmtree(save_path) # 避免误删除已有的文件return ""# print('create output path %s' % save_path)os.makedirs(save_path)return save_pathdef create_yolo_dataset_cfg(output_dir='', label_class=[]):# 创建文件data_cfg_file = open(output_dir + '/data.yaml', 'w')# 创建文件内容data_cfg_file.write('train: ../train/images\n')data_cfg_file.write("val: ../valid/images\n")data_cfg_file.write("test: ../test/images\n")data_cfg_file.write("\n")data_cfg_file.write("# Classes\n")data_cfg_file.write("nc: %s\n" % len(label_class))data_cfg_file.write('names: ')i = 0for label in label_class:if (i == 0):data_cfg_file.write("[")else:data_cfg_file.write(", ")if (i % 10 == 0):data_cfg_file.write("\n ")i += 1data_cfg_file.write("'" + label + "'")data_cfg_file.write('] # class names')data_cfg_file.close()# 关闭文件def labelImg2yolo(input='', output='', unify_path=""):outputdir_root = output + '/'labelImg_path = inputprint(labelImg_path)labelImg_path_imagepath = labelImg_path + '\\' + 'image'print(labelImg_path_imagepath)json_path = labelImg_path+'\\'+'json'print(json_path)print("*"*100)# 1.获取input目录中所有的json标签文件全路径名files = glob(json_path + "/*.json")print(files)# 2.获取所有标签文件的短文件名称files = [i.replace("\\", "/").split("/")[-1].split(".json")[0] for i in files]print(files)# 3. 按比例随机切分数据集,获取训练集样本train_files, valid_test_files = train_test_split(files, test_size=0.2, random_state=55)# 4. 按比例随机切分数据集,获取验证集和测试集样本valid_files, test_files = train_test_split(valid_test_files, test_size=0.1, random_state=55)# 5. 构建YOLO数据集目录train_path = outputdir_root + '/train'valid_path = outputdir_root + '/valid'test_path = outputdir_root + '/test'# 6. 生成YOLO 训练、验证、测试数据集:图片+标签convertToYolo5(train_files, train_path, labelImg_path_imagepath, unify_path)convertToYolo5(valid_files, valid_path, labelImg_path_imagepath, unify_path)convertToYolo5(test_files, test_path, labelImg_path_imagepath, unify_path)print("*"*100)# 7. 创建YOLO数据集配置文件create_yolo_dataset_cfg(output, obj_classes)labelme_path = inputprint("Classes:", obj_classes)print('Finished, output path =', outputdir_root)return 0def parse_opt():# define argparse objectparser = argparse.ArgumentParser()# input 包含两个文件夹, image和json,分别存放了对应的文件parser.add_argument('--input', type=str, default=r'F:\python\mission\fish_power_test\data\test',help='The input LabelImg directory')# output 存放保存的yolov5的训练数据,分为train、val、test三个文件,里面分别存放了对应的images和labels,在train目录下还存放了yolov5加载数据集的yaml文件(见data.yaml)parser.add_argument('--output', type=str,default=r'F:\python\mission\fish_power_test\data\test/yolo_txt', help='The output YOLO V5 directory')# 统一存放parser.add_argument('--unify_path', type=str,default=r'F:\python\mission\fish_power_test\data\test/txt', help='The output txt directory')# parse arges from command lineopt = parser.parse_args()print("input =", opt.input)print("output =", opt.output)print("unify_path =", opt.unify_path)# return optreturn optdef main(opt):labelImg2yolo(**vars(opt))if __name__ == '__main__':opt = parse_opt()main(opt)
然后通过下面的代码将源标签的txt文档进行总结
# 将txt标签对应的原始的label和boxs写入到txt文档中
import cv2
import os# 读取txt文件信息
def read_list(txt_path):pos = []with open(txt_path, 'r') as file_to_read:while True:lines = file_to_read.readline() # 整行读取数据if not lines:break# 将整行数据分割处理,如果分割符是空格,括号里就不用传入参数,如果是逗号, 则传入‘,'字符。p_tmp = [float(i) for i in lines.split(' ')]pos.append(p_tmp) # 添加新读取的数据passreturn posdef draw_box_in_single_image(image_path, txt_path, image_name):# 读取图像image = cv2.imread(image_path)pos = read_list(txt_path)reses = []reses.append(image_name)for i in range(len(pos)):label = classes[int(str(int(pos[i][0])))]print('label is '+label)# 将中心坐标、宽度和高度转换为 xywh 格式x_center = int(pos[i][1] * image.shape[1])y_center = int(pos[i][2] * image.shape[0])width = int(pos[i][3] * image.shape[1])height = int(pos[i][4] * image.shape[0])x_min = x_center - width // 2y_min = y_center - height // 2# 绘制矩形框cv2.rectangle(image, (x_min, y_min), (x_min + width, y_min + height), colores[int(str(int(pos[i][0])))], 2)cv2.putText(image, label, (x_min, y_min - 2), 0, 1, colores[int(str(int(pos[i][0])))], thickness=2, lineType=cv2.LINE_AA)res = [label, x_min, y_min, width, height]reses.append(res)# cv2.imshow("images", image)# cv2.waitKey(0)return resesif __name__ == '__main__':f = open('result/ori_data.txt', 'w+')img_folder = "data/test/image"img_list = [f for f in os.listdir(img_folder) if f.endswith('.jpg')]img_list.sort()label_folder = "data/test/txt"label_list = [f for f in os.listdir(label_folder) if f.endswith('.txt')]label_list.sort()classes = {0: "fishing", 1: "no_fishing"}colores = [(0, 0, 255), (255, 0, 255)]for i in range(len(img_list)):image_path = os.path.join(img_folder, img_list[i])txt_path = os.path.join(label_folder, label_list[i])reses = draw_box_in_single_image(image_path, txt_path, img_list[i])print(reses)f.write(str(reses))f.write("\n")f.close()
五、开始计算指标
处理之后就可以得到检测后的fish_result.json和ori_data.txt。然后根据这两个文件来计算即可。
部分重要代码放在这里面:链接
五、实用工具
5.1 移动文件
import os
import shutil# 源文件夹和目标文件夹的路径
source_folder = 'fishing_test_data'
destination_folder = 'test/image'# 确保目标文件夹存在,如果不存在就创建它
if not os.path.exists(destination_folder):os.makedirs(destination_folder)# 遍历源文件夹中的文件
for filename in os.listdir(source_folder):# 检查文件扩展名是否为图片格式,可以根据需要添加其他格式if filename.endswith(('.jpg', '.png', '.jpeg')):# 构建源文件的完整路径和目标文件的完整路径source_file_path = os.path.join(source_folder, filename)destination_file_path = os.path.join(destination_folder, filename)# 移动文件shutil.move(source_file_path, destination_file_path)print(f'Moved: {filename} to {destination_folder}')
5.2 可视化JSON标签
# -*- coding: utf-8 -*-
import cv2
import os
import json
import shutil
import numpy as np
from pathlib import Path
from glob import globid2cls = {0: 'fishing', 1: "no_fishing"}
cls2id = {'fishing': 0, "no_fishing": 1}
id2color = {"fishing": (0, 255, 0), "no_fishing": (0, 255, 255)}# 支持中文路径
def cv_imread(filePath):cv_img = cv2.imdecode(np.fromfile(filePath, dtype=np.uint8), flags=cv2.IMREAD_COLOR)return cv_imgdef get_labelme_info(label_file):anno = json.load(open(label_file, "r", encoding="utf-8"))shapes = anno['shapes']image_path = os.path.basename(anno['imagePath'])labels = []for s in shapes:pts = s['points']x1, y1 = pts[0]x2, y2 = pts[1]color = id2color[s["label"]]labels.append([color, x1, y1, x2, y2])return labels, image_pathdef vis_labelme(labelme_label_dir, save_dir='res/'):labelme_label_dir = str(Path(labelme_label_dir)) + '/'save_dir = str(Path(save_dir)) + '/'if not os.path.exists(save_dir):os.makedirs(save_dir)json_files = glob(labelme_label_dir + '*.json')for ijf, jf in enumerate(json_files):print(ijf + 1, '/', len(json_files), jf)filename = os.path.basename(jf).rsplit('.', 1)[0]labels, image_path = get_labelme_info(jf)image = cv_imread(labelme_label_dir + image_path)for label in labels:color = label[0]x1, y1, x2, y2 = label[1:]x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)cv2.rectangle(image, (x1, y1), (x2, y2), color, 3)# 显示图片# cv2.imshow(filename, image)# cv2.waitKey(0)# 支持中文路径,保存图片cv2.imencode(os.path.splitext(image_path)[-1], image)[1].tofile(save_dir + image_path)print('Completed!')if __name__ == '__main__':root_dir = r'D:\Python\money\data\test'save_dir = r'D:\Python\money\data\test/t'vis_labelme(root_dir, save_dir)
5.3 可视化TXT标签
import cv2
import os# 读取txt文件信息
def read_list(txt_path):pos = []with open(txt_path, 'r') as file_to_read:while True:lines = file_to_read.readline() # 整行读取数据if not lines:break# 将整行数据分割处理,如果分割符是空格,括号里就不用传入参数,如果是逗号, 则传入‘,'字符。p_tmp = [float(i) for i in lines.split(' ')]pos.append(p_tmp) # 添加新读取的数据# Efield.append(E_tmp)passreturn pos# txt转换为box
def convert(size, box):xmin = (box[1] - box[3] / 2.) * size[1]xmax = (box[1] + box[3] / 2.) * size[1]ymin = (box[2] - box[4] / 2.) * size[0]ymax = (box[2] + box[4] / 2.) * size[0]box = (int(xmin), int(ymin), int(xmax), int(ymax))return boxdef draw_box_in_single_image(image_path, txt_path):# 读取图像image = cv2.imread(image_path)pos = read_list(txt_path)for i in range(len(pos)):label = classes[int(str(int(pos[i][0])))]print('label is '+label)box = convert(image.shape, pos[i])image = cv2.rectangle(image,(box[0], box[1]),(box[2],box[3]),colores[int(str(int(pos[i][0])))],2)cv2.putText(image, label,(box[0],box[1]-2), 0, 1, colores[int(str(int(pos[i][0])))], thickness=2, lineType=cv2.LINE_AA)cv2.imshow("images", image)cv2.waitKey(0)if __name__ == '__main__':img_folder = r"F:\python\mission\fish_power_test\data\test\yolo_txt\train\images"img_list = os.listdir(img_folder)img_list.sort()label_folder = r"F:\python\mission\fish_power_test\data\test\yolo_txt\train/labels"label_list = os.listdir(label_folder)label_list.sort()classes = {0: "fishing", 1: "no_fishing"}colores = [(0,0,255),(255,0,255)]for i in range(len(img_list)):image_path = img_folder + "\\" + img_list[i]txt_path = label_folder + "\\" + label_list[i]draw_box_in_single_image(image_path, txt_path)