Một trong những ứng dụng quan trọng của công nghệ thị giác máy tính là phân đoạn hình ảnh thực phẩm, giúp xác định và tách các mục thực phẩm khác nhau từ hình ảnh phức tạp. YOLOv8, với tốc độ và độ chính xác cao, đã trở thành một lựa chọn phổ biến cho nhiệm vụ này. Tuy nhiên, khi xử lý hình ảnh thực phẩm đa dạng về loại, điều kiện ánh sáng và nền, vẫn còn nhiều thách thức.
Bài nghiên cứu sử dụng tập dữ liệu gồm 1500 hình ảnh thuộc 76 loại thực phẩm khác nhau như chuối, cải xoăn, cà chua, cà rốt. Sự đa dạng của tập dữ liệu giúp cải thiện khả năng tổng quát hóa của mô hình. Tập dữ liệu cũng phản ánh được sự thay đổi của thực phẩm trong môi trường thực tế, từ đó nâng cao độ tin cậy và độ chính xác của mô hình.
Tập dữ liệu
Tập dữ liệu "Food Items" được sử dụng để huấn luyện và tối ưu hóa mô hình YOLOv8-seg. Tập dữ liệu này bao gồm sáu loại thực phẩm: táo, chuối, cải xoăn, cà rốt, cà rốt (trùng lặp) và cam. Các loại này được chọn nhằm phản ánh những thực phẩm phổ biến và lành mạnh trong chế độ ăn uống hàng ngày.
Tập dữ liệu được xây dựng kỹ lưỡng để đảm bảo sự đa dạng và đại diện của từng loại thực phẩm. Nó chứa hình ảnh thực phẩm ở nhiều góc nhìn, điều kiện ánh sáng và nền khác nhau, giúp tăng cường khả năng chống chịu của mô hình.
Mã nguồn
import torch
import numpy as np
from typing import List, Tuple
def is_box_close_to_crop_edge(boxes: torch.Tensor, crop_area: List[int], original_area: List[int], tolerance: float = 20.0) -> torch.Tensor:
"""
Kiểm tra xem các hộp giới hạn có gần cạnh cắt hay không.
Tham số:
boxes: Hộp giới hạn cần kiểm tra, định dạng (x1, y1, x2, y2).
crop_area: Vùng cắt hiện tại [x0, y0, x1, y1].
original_area: Vùng gốc [x0, y0, x1, y1].
tolerance: Sai số tuyệt đối cho phép.
Trả về:
Một tensor boolean chỉ ra các hộp giới hạn gần cạnh cắt.
"""
crop_area_tensor = torch.tensor(crop_area, dtype=torch.float, device=boxes.device)
original_area_tensor = torch.tensor(original_area, dtype=torch.float, device=boxes.device)
boxes = convert_boxes_to_original(boxes, crop_area).float()
near_crop_edge = torch.isclose(boxes, crop_area_tensor[None, :], atol=tolerance, rtol=0)
near_original_edge = torch.isclose(boxes, original_area_tensor[None, :], atol=tolerance, rtol=0)
near_crop_edge = torch.logical_and(near_crop_edge, ~near_original_edge)
return torch.any(near_crop_edge, dim=1)
def convert_boxes_to_original(boxes: torch.Tensor, crop_area: List[int]) -> torch.Tensor:
"""
Chuyển đổi các hộp giới hạn từ vùng cắt sang vùng gốc.
Tham số:
boxes: Hộp giới hạn trong vùng cắt, định dạng (x1, y1, x2, y2).
crop_area: Vùng cắt hiện tại [x0, y0, x1, y1].
Trả về:
Hộp giới hạn được chuyển đổi, định dạng (x1, y1, x2, y2).
"""
x0, y0, _, _ = crop_area
offset = torch.tensor([[x0, y0, x0, y0]], device=boxes.device)
if len(boxes.shape) == 3:
offset = offset.unsqueeze(1)
return boxes + offset
def create_crop_areas(image_size: Tuple[int, ...], layers: int, overlap_factor: float) -> Tuple[List[List[int]], List[int]]:
"""
Tạo các vùng cắt khác nhau.
Tham số:
image_size: Kích thước hình ảnh (chiều cao, chiều rộng).
layers: Số lớp cắt.
overlap_factor: Hệ số giao nhau.
Trả về:
Danh sách các vùng cắt và chỉ số lớp tương ứng.
"""
crop_areas, layer_indices = [], []
height, width = image_size
shortest_side = min(height, width)
crop_areas.append([0, 0, width, height])
layer_indices.append(0)
def calculate_crop_length(original_length, num_crops, overlap):
return int(np.ceil((overlap * (num_crops - 1) + original_length) / num_crops))
for layer_index in range(layers):
crops_per_side = 2 ** (layer_index + 1)
overlap = int(overlap_factor * shortest_side * (2 / crops_per_side))
crop_width = calculate_crop_length(width, crops_per_side, overlap)
crop_height = calculate_crop_length(height, crops_per_side, overlap)
start_x = [int((crop_width - overlap) * i) for i in range(crops_per_side)]
start_y = [int((crop_height - overlap) * i) for i in range(crops_per_side)]
for x, y in np.product([start_x, start_y]):
area = [x, y, min(x + crop_width, width), min(y + crop_height, height)]
crop_areas.append(area)
layer_indices.append(layer_index + 1)
return crop_areas, layer_indices
def mask_to_bounding_box(masks: torch.Tensor) -> torch.Tensor:
"""
Tính toán hộp giới hạn xung quanh mặt nạ, định dạng XYXY.
Tham số:
masks: Mặt nạ nhị phân, định dạng CxHxW.
Trả về:
Hộp giới hạn, định dạng Cx4.
"""
if torch.numel(masks) == 0:
return torch.zeros(*masks.shape[:-2], 4, device=masks.device)
shape = masks.shape
h, w = shape[-2:]
masks = masks.flatten(0, -3) if len(shape) > 2 else masks.unsqueeze(0)
top, _ = torch.max(masks, dim=-1)
top_coords = top * torch.arange(h, device=top.device)[None, :]
bottom, _ = torch.max(top_coords, dim=-1)
top_coords = top_coords + h * (~top)
top_edge, _ = torch.min(top_coords, dim=-1)
left, _ = torch.max(masks, dim=-2)
left_coords = left * torch.arange(w, device=left.device)[None, :]
right, _ = torch.max(left_coords, dim=-1)
left_coords = left_coords + w * (~left)
left_edge, _ = torch.min(left_coords, dim=-1)
empty_filter = (right < left_edge) | (bottom < top_edge)
bounding_boxes = torch.stack([left_edge, top_edge, right, bottom], dim=-1)
bounding_boxes = bounding_boxes * (~empty_filter).unsqueeze(-1)
return bounding_boxes.reshape(*shape[:-2], 4) if len(shape) > 2 else bounding_boxes[0]
Mô tả chức năng chính:
- Xử lý hộp giới hạn: Kiểm tra xem các hộp giới hạn có gần cạnh cắt hay không và chuyển đổi tọa độ.
- Tạo vùng cắt: Tạo các vùng cắt khác nhau dựa trên kích thước hình ảnh, số lớp và hệ số giao nhau.
- Chuyển đổi từ mặt nạ đến hộp giới hạn: Tính toán hộp giới hạn từ mặt nạ nhị phân để hỗ trợ xử lý tiếp theo.
Mã nguồn trên là phần quan trọng của mô hình YOLO xử lý cắt hình ảnh và mặt nạ, đã được tinh chỉnh và chú thích để dễ hiểu và bảo trì.
import sys
import subprocess
def run_script(script_path):
"""
Chạy một kịch bản cụ thể bằng môi trường Python hiện tại.
Tham số:
script_path (str): Đường dẫn đến kịch bản cần chạy
Trả về:
None
"""
python_executable = sys.executable
command = f'"{python_executable}" -m streamlit run "{script_path}"'
result = subprocess.run(command, shell=True)
if result.returncode != 0:
print("Có lỗi xảy ra khi chạy kịch bản.")
if __name__ == "__main__":
script_path = "web_interface.py"
run_script(script_path)
Mô tả mã nguồn:
- Import module:
- `sys`: Truy cập các biến và hàm liên quan đến trình thông dịch Python.
- `subprocess`: Tạo tiến trình mới, kết nối đến đầu vào/đầu ra/lỗi của chúng và nhận mã trả về.
- Hàm `run_script`:
- Nhận đường dẫn đến kịch bản và chạy nó bằng môi trường Python hiện tại.
- Sử dụng `sys.executable` để lấy đường dẫn đến trình thông dịch Python.
- Xây dựng lệnh chạy kịch bản bằng `streamlit` và thực thi bằng `subprocess.run`.
- Kiểm tra mã trả về, nếu khác 0 thì in ra thông báo lỗi.
- Điểm nhập chính:
- Sử dụng `if __name__ == "__main__":` để đảm bảo chỉ chạy khi tệp được thực thi trực tiếp.
- Gọi hàm `run_script` với đường dẫn đến kịch bản `web_interface.py`.
Tệp `ui.py` này cung cấp một cách tiện lợi để chạy một kịch bản cụ thể bằng môi trường Python hiện tại và xử lý các lỗi có thể xảy ra.