【食物识别】数据集

数据集

数据集介绍

数据集我们选择UEC FOOD 100数据集。

数据集“UEC FOOD 100”包含100种食物照片。每张食物照片都有一个边框,指示照片中食物的位置。

该数据集中的大多数食品类别都是日本流行的食品。因此,有些目录可能不为日本人以外的其他人所熟悉。

[1-100]:目录名称对应食物ID。

[1-100]/*.jpg :食物照片文件(某些照片在两个或更多目录中重复,因为它们包含两个或更多食物。)

[1-100]/bb_info.txt:各目录下照片文件的边界框信息

category.txt :食物列表,包括食物ID和食物英文名称的对应关系

category_ja.txt :食物列表,包括食物ID和食物名称的日文对应关系

multiple_food.txt:表示包含两个或多个食品的食品照片的列表

以上是官网的介绍,实际上我下载下来解压后,是一个UECFOOD100文件夹:

查看内部结构:

是1-100文件夹:

还有几个index和类别的映射文件:

category.txt记录了1到100对应着什么食物:

multiple_food.txt记录了表示包含两个或多个食品的食品照片的列表:

每个文件夹下存放了一类食物的图片,还有一个bb_info.txt文件,是每张图片的标注框的坐标信息。其中img一列表示的是UECFOOD100/1/文件夹下图片的名称,x1y1x2y2分别表示该类食物(1类食物)在该图片中的位置的框的左上坐标和右下坐标。

数据集格式转换

显然,这个数据集的格式不是我们想要的,于是我写了一个python脚本,将其变成符合yolo格式的数据集

yolo格式数据集要求如下:

由两个文件夹构成:images和labels

images文件夹下是图片,labels文件夹中有每张图片对应的同名的txt文件,该txt文件中内容格式如下:

1 0.6245612541289654 0.8125469325469415 0.0794512586321524 0.0808451256325814
3 0.3245614232849654 0.4125469348669415 0.0394512584621724 0.0408454686325544

第一个数字表示这张图片中的每个目标对象的类别编号(即是什么食物),剩下四个为xywh,其中xy表示目标对象框的中心在该图片中的相对位置(归一化),即将该图片左上角设为(0,0),向右为x正半轴,图片右上角(1,0),向下为y正半轴,图片左下角(0,1)。w和h代表归一化后框的相对宽度和高度。

脚本如下:

import os
import shutil
from PIL import Image

# 读取bb_info.txt获取边界框信息
def read_bounding_boxes(input_folder):
    bounding_boxes = {}
    if os.path.isdir(input_folder):
        bb_info_file = os.path.join(input_folder, "bb_info.txt")
        if os.path.isfile(bb_info_file):
            with open(bb_info_file, 'r') as file:
                next(file)  # 跳过文件的第一行(标题行)
                for line in file:
                    parts = line.strip().split()
                    img_id = int(parts[0])
                    boxes = [(int(parts[i]), int(parts[i + 1]), int(parts[i + 2]), int(parts[i + 3])) for i in
                             range(1, len(parts), 4)]
                    bounding_boxes[img_id] = boxes
    return bounding_boxes

# 生成YOLO格式标签
def generate_yolo_labels(input_folder, output_folder):
    for folder_name in os.listdir(input_folder):
        folder_path = os.path.join(input_folder, folder_name)
        if os.path.isdir(folder_path):
            if not folder_name.isdigit() or int(folder_name) < 1 or int(folder_name) > 100:
                raise ValueError(f"Invalid folder name: {folder_name}")
            img_sizes = read_image_sizes(folder_path)
            bounding_boxes = read_bounding_boxes(folder_path)
            for img_name in os.listdir(folder_path):
                img_path = os.path.join(folder_path, img_name)
                if os.path.isfile(img_path) and img_name.endswith(".jpg"):
                    img_id = int(img_name[:-4])
                    img_width, img_height = img_sizes[img_id]
                    bounding_box = bounding_boxes[img_id]
                    yolo_label_path = os.path.join(output_folder, "labels", f"{img_id}.txt")
                    with open(yolo_label_path, 'a') as label_file:
                        for box in bounding_box:
                            category_id = folder_name
                            # 计算框的中心坐标
                            x_center = (box[0] + box[2]) / (2.0 * img_width)
                            y_center = (box[1] + box[3]) / (2.0 * img_height)
                            # 计算归一化后的框的宽度和高度
                            box_width = (box[2] - box[0]) / (1.0 * img_width)
                            box_height = (box[3] - box[1]) / (1.0 * img_height)
                            # 将计算结果保留16位小数
                            label_file.write(f"{category_id} {x_center:.16f} {y_center:.16f} {box_width:.16f} {box_height:.16f}\n")

# 复制图片文件
def copy_images(input_folder, output_folder):
    image_output_folder = os.path.join(output_folder, "images")
    os.makedirs(image_output_folder, exist_ok=True)
    for folder_name in os.listdir(input_folder):
        folder_path = os.path.join(input_folder, folder_name)
        if os.path.isdir(folder_path):
            for file_name in os.listdir(folder_path):
                if file_name.endswith(".jpg"):
                    shutil.copy(os.path.join(folder_path, file_name), image_output_folder)

# 读取图片尺寸信息
def read_image_sizes(input_folder):
    img_sizes = {}
    for file_name in os.listdir(input_folder):
        if file_name.endswith(".jpg"):
            img_id = int(file_name[:-4])
            img = Image.open(os.path.join(input_folder, file_name))
            img_sizes[img_id] = img.size
    return img_sizes


# 主函数
def main():
    input_folder = "UECFOOD100"
    output_folder = "UECFOOD_YOLO"
    os.makedirs(os.path.join(output_folder, "images"), exist_ok=True)
    os.makedirs(os.path.join(output_folder, "labels"), exist_ok=True)

    # 生成YOLO格式标签
    generate_yolo_labels(input_folder, output_folder)

    copy_images(input_folder, output_folder)

if __name__ == "__main__":
    main()

然后得到了UECFOOD_YOLO文件夹:

但是还没有划分训练集和测试集。

划分数据集

于是再写一脚本进行划分。

import os
import random
import shutil

def split_dataset(input_folder, output_folder, train_ratio=0.7):
    # 创建输出文件夹
    os.makedirs(output_folder, exist_ok=True)
    os.makedirs(os.path.join(output_folder, "images", "train"), exist_ok=True)
    os.makedirs(os.path.join(output_folder, "images", "val"), exist_ok=True)
    os.makedirs(os.path.join(output_folder, "labels", "train"), exist_ok=True)
    os.makedirs(os.path.join(output_folder, "labels", "val"), exist_ok=True)

    # 遍历images文件夹中的图片
    image_folder = os.path.join(input_folder, "images")
    label_folder = os.path.join(input_folder, "labels")
    filenames = os.listdir(image_folder)
    random.shuffle(filenames)  # 随机打乱文件顺序
    num_train = int(len(filenames) * train_ratio)
    
    for i, filename in enumerate(filenames):
        # 决定该图片应该放入训练集还是测试集
        if i < num_train:
            shutil.copy(os.path.join(image_folder, filename), os.path.join(output_folder, "images", "train"))
            shutil.copy(os.path.join(label_folder, filename[:-4] + ".txt"), os.path.join(output_folder, "labels", "train"))
        else:
            shutil.copy(os.path.join(image_folder, filename), os.path.join(output_folder, "images", "val"))
            shutil.copy(os.path.join(label_folder, filename[:-4] + ".txt"), os.path.join(output_folder, "labels", "val"))

# 调用函数
input_folder = "UECFOOD_YOLO"  # 修改为你的输入文件夹路径
output_folder = "datasets"  # 修改为你的输出文件夹路径
split_dataset(input_folder, output_folder)

更新标签index

这时我们发现,此时的数据集的labels是从1到100,但是我们需要的是0-99,因此进行更新。

import os

def update_label_file(file_path):
    with open(file_path, 'r') as file:
        lines = file.readlines()

    updated_lines = []
    for line in lines:
        parts = line.strip().split()
        parts[0] = str(int(parts[0]) - 1)  # 减小第一个数字
        updated_lines.append(' '.join(parts) + '\n')

    with open(file_path, 'w') as file:
        file.writelines(updated_lines)

def process_label_folder(folder_path):
    for root, dirs, files in os.walk(folder_path):
        for file_name in files:
            if file_name.endswith('.txt'):
                file_path = os.path.join(root, file_name)
                update_label_file(file_path)

def main():
    labels_folder = "labels"
    train_folder = os.path.join(labels_folder, "train")
    val_folder = os.path.join(labels_folder, "val")

    process_label_folder(train_folder)
    process_label_folder(val_folder)

if __name__ == "__main__":
    main()

最终得到数据集datasets

制作yaml文件

我们从数据集自带的category.txt中取出类别名,并将其翻译成中文,然后写一个小脚本,将其前面添加0-99的index:

input_file = "classes.txt"
output_file = "classes_with_index.txt"

with open(input_file, 'r', encoding='utf-8') as f:
    lines = f.readlines()

# 在每一行前添加序号
numbered_lines = [f"{i}: {line.strip()}\n" for i, line in enumerate(lines)]

# 将带有序号的行写入新的文件
with open(output_file, 'w', encoding='utf-8') as f:
    f.writelines(numbered_lines)

  • 25
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值