测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)

简介: 这篇文章是关于如何通过算法接口返回的目标检测结果来计算性能指标的笔记。它涵盖了任务描述、指标分析(包括TP、FP、FN、TN、精准率和召回率),接口处理,数据集处理,以及如何使用实用工具进行文件操作和数据可视化。文章还提供了一些Python代码示例,用于处理图像文件、转换数据格式以及计算目标检测的性能指标。

一、任务描述

通过给定的算法接口,对算法的输出(置信度、检测框、告警、类别等)进行数据处理,结合原标签完成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, requests

def 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_list

def pic2base64(img_path):
    if os.path.exists(img_path):
        encoded_base64 = base64.b64encode(open(img_path, 'rb').read()).decode()
        return encoded_base64
    else:
        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 argparse

obj_classes = []

# Labelme坐标到YOLO V5坐标的转换
def convert(size, box):
    dw = 1. / (size[0])
    dh = 1. / (size[1])
    x = (box[0] + box[1]) / 2.0 - 1
    y = (box[2] + box[3]) / 2.0 - 1
    w = box[1] - box[0]
    h = box[3] - box[2]
    x = x * dw
    w = w * dw
    y = y * dh
    h = h * dh
    return (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 0
                    xmax = max(points[:, 0]) if max(points[:, 0]) > 0 else 0
                    ymin = min(points[:, 1]) if min(points[:, 1]) > 0 else 0
                    ymax = max(points[:, 1]) if max(points[:, 1]) > 0 else 0

                    # 对坐标信息进行合法性检查
                    if xmax <= xmin:
                        pass
                    elif ymax <= ymin:
                        pass
                    else:
                        # Labelme坐标转换成YOLO V5坐标
                        bbox_labelme_float = (float(xmin), float(xmax), float(ymin), float(ymax))
                        bbox_yolo_normalized = convert((width, height), bbox_labelme_float)

                        # 把分类标签转换成分类id
                        class_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_path

def 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 = 0
    for 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 += 1
        data_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 = input
    print(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 = input

    print("Classes:", obj_classes)
    print('Finished, output path =', outputdir_root)

    return 0

def parse_opt():
    # define argparse object
    parser = 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 line
    opt = parser.parse_args()
    print("input  =", opt.input)
    print("output =", opt.output)
    print("unify_path =", opt.unify_path)
    # return opt
    return opt

def 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)  # 添加新读取的数据
            pass
    return pos

def 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 // 2
        y_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 reses

if __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 glob

id2cls = {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_img

def 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_path

def 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)
            pass
    return 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 box

def 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)
目录
相关文章
|
17天前
|
存储 分布式计算 算法
大数据-106 Spark Graph X 计算学习 案例:1图的基本计算、2连通图算法、3寻找相同的用户
大数据-106 Spark Graph X 计算学习 案例:1图的基本计算、2连通图算法、3寻找相同的用户
42 0
|
9天前
|
存储 算法 Java
Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定数据结构和算法确保元素唯一性
Java Set因其“无重复”特性在集合框架中独树一帜。本文解析了Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定数据结构和算法确保元素唯一性,并提供了最佳实践建议,包括选择合适的Set实现类和正确实现自定义对象的hashCode()与equals()方法。
24 4
|
8天前
|
监控 算法 数据安全/隐私保护
基于三帧差算法的运动目标检测系统FPGA实现,包含testbench和MATLAB辅助验证程序
本项目展示了基于FPGA与MATLAB实现的三帧差算法运动目标检测。使用Vivado 2019.2和MATLAB 2022a开发环境,通过对比连续三帧图像的像素值变化,有效识别运动区域。项目包括完整无水印的运行效果预览、详细中文注释的代码及操作步骤视频,适合学习和研究。
|
2月前
|
移动开发 JSON Java
Jmeter实现WebSocket协议的接口测试方法
WebSocket协议是HTML5的一种新协议,实现了浏览器与服务器之间的全双工通信。通过简单的握手动作,双方可直接传输数据。其优势包括极小的头部开销和服务器推送功能。使用JMeter进行WebSocket接口和性能测试时,需安装特定插件并配置相关参数,如服务器地址、端口号等,还可通过CSV文件实现参数化,以满足不同测试需求。
185 7
Jmeter实现WebSocket协议的接口测试方法
|
22天前
|
JavaScript 前端开发 API
vue尚品汇商城项目-day02【9.Home组件拆分+10.postman测试接口】
vue尚品汇商城项目-day02【9.Home组件拆分+10.postman测试接口】
32 0
|
23天前
|
算法 Java 测试技术
数据结构 —— Java自定义代码实现顺序表,包含测试用例以及ArrayList的使用以及相关算法题
文章详细介绍了如何用Java自定义实现一个顺序表类,包括插入、删除、获取数据元素、求数据个数等功能,并对顺序表进行了测试,最后还提及了Java中自带的顺序表实现类ArrayList。
14 0
|
2月前
|
JavaScript 前端开发 测试技术
ChatGPT与接口测试
ChatGPT与接口测试,测试通过
39 5
|
2月前
|
算法 C++
如何精确计算出一个算法的CPU运行时间?
如何精确计算出一个算法的CPU运行时间?
|
2月前
|
JSON 移动开发 监控
快速上手|HTTP 接口功能自动化测试
HTTP接口功能测试对于确保Web应用和H5应用的数据正确性至关重要。这类测试主要针对后台HTTP接口,通过构造不同参数输入值并获取JSON格式的输出结果来进行验证。HTTP协议基于TCP连接,包括请求与响应模式。请求由请求行、消息报头和请求正文组成,响应则包含状态行、消息报头及响应正文。常用的请求方法有GET、POST等,而响应状态码如2xx代表成功。测试过程使用Python语言和pycurl模块调用接口,并通过断言机制比对实际与预期结果,确保功能正确性。
187 3
快速上手|HTTP 接口功能自动化测试
|
3月前
|
网络协议 测试技术 网络安全
Python进行Socket接口测试的实现
在现代软件开发中,网络通信是不可或缺的一部分。无论是传输数据、获取信息还是实现实时通讯,都离不开可靠的网络连接和有效的数据交换机制。而在网络编程的基础中,Socket(套接字)技术扮演了重要角色。 Socket 允许计算机上的程序通过网络进行通信,它是网络通信的基础。Python 提供了强大且易于使用的 socket 模块,使开发者能够轻松地创建客户端和服务器应用,实现数据传输和交互。 本文将深入探讨如何利用 Python 编程语言来进行 Socket 接口测试。我们将从基础概念开始介绍,逐步引导大家掌握创建、测试和优化 socket 接口的关键技能。希望本文可以给大家的工作带来一些帮助~