Skip to main content

3 posts tagged with "Python"

View All Tags

YOLO(You Only Look Once)是一种广泛使用的目标检测模型,近年来也逐渐应用于图像分割和姿态估计任务。本篇文章将详细讲解YOLO模型在目标检测、图像分割及姿态估计中的应用,通过代码和预测结果分析帮助您更好地理解和使用YOLO模型。

Ultralytics库的所有预测结果都放在Result对象中,适用于目标检测、图像分割和姿态估计等任务,本文也将详细介绍如何处理不同任务的预测结果。

任务概述与对比

YOLO支持三种主要视觉任务,每个任务都有其独特的输出结构和应用场景:

  1. 目标检测(Object Detection)

    • 输出:边界框(boxes)和类别标签
    • 特点:定位物体位置并进行分类
    • 应用场景:物体识别、车辆检测、人脸检测等
  2. 图像分割(Image Segmentation)

    • 输出:像素级别掩码(masks)和类别标签
    • 特点:提供物体精确的轮廓信息
    • 应用场景:医学图像分析、场景理解等
  3. 姿态估计(Pose Estimation)

    • 输出:人体关键点坐标(keypoints)和骨架连接
    • 特点:识别人体姿态和动作
    • 应用场景:运动分析、姿态追踪、行为监控等

YOLO模型的预测结果对象结构

所有任务的预测结果都封装在Results对象中,Results对象包含以下通用属性:

- orig_img: 原始图像数据
- orig_shape: 原始图像尺寸(,)
- path: 输入图像路径
- save_dir: 结果保存路径
- speed: 预测耗时信息

这些属性帮助我们在不同任务中标准化处理预测结果。

目标检测

目标检测的代码实现

下面的代码演示了如何使用YOLO进行目标检测,识别图像中的物体,并将检测结果(包括边界框和类别标签)绘制在原始图像上。

import os
from ultralytics import YOLO
import cv2
import os
import glob
import shutil

OBJECT_DETECTION_MODEL_PATH = './models/object_detection.onnx'
TASK_NAME = 'detect'

def generate_colors(names):
colors = {}
for name in names:
hash_object = hashlib.md5(name.encode())
hash_int = int(hash_object.hexdigest(), 16)
b = (hash_int & 0xFF0000) >> 16
g = (hash_int & 0x00FF00) >> 8
r = hash_int & 0x0000FF
colors[name] = (b, g, r) # OpenCV 使用 BGR 顺序
return colors

# 单张图像目标检测预测
def predict_single_image_by_detect(image_path, out_image_file):
# 获取输出文件`out_image_path`文件所在的目录
out_dir = os.path.dirname(out_image_path)
os.makedirs(out_dir, exist_ok=True)

image_list = [image_path]
results = model(image_list)

for result in results:
boxes = result.boxes
if boxes is None:
cv2.imwrite(out_image_file, result.orig_img)
continue
boxes_data = boxes.data.cpu().numpy()
names = result.names
class_names = list(names.values())

color_map = generate_colors(class_names)

img = result.orig_img

for box in boxes_data:
x1, y1, x2, y2, score, class_id = box
x1, y1, x2, y2 = map(int, [x1, y1, x2, y2])
class_name = names[int(class_id)]
color = color_map[class_name]
cv2.rectangle(img, (x1, y1), (x2, y2), color, 2)
label = f'{class_name} {score:.2f}'
cv2.putText(img, label, (x1, max(y1 - 10, 0)),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, color, 2)
print(f"图像写入路径: {out_image_file}")
cv2.imwrite(out_image_file, img)

if __name__ == '__main__':
# 预测单张图像
image_path = 'bus.jpg'
out_image_path = image_path + '_predicted.jpg'
predict_single_image_by_detect(image_path, out_image_path)

目标检测结果分析

在目标检测任务中,Results对象中最重要的字段是:

  • boxes:包含边界框的坐标、置信度和类别ID。
  • names:类别标签映射。
  • orig_img:原始图像数据。

每个边界框包含以下六个值:

[x1, y1, x2, y2, score, class_id]
# x1, y1: 左上角坐标
# x2, y2: 右下角坐标
# score: 检测置信度
# class_id: 类别ID

图像分割

图像分割的代码实现

图像分割任务比目标检测更加精细,它不仅需要识别物体的类别,还要提取每个物体的准确轮廓。

import os
import hashlib
import cv2
import numpy as np
from ultralytics import YOLO
import glob
import shutil

SEGMENT_MODEL_PATH = "./models/segmentation.onnx"
TASK_NAME = 'segment'
model = YOLO(SEGMENT_MODEL_PATH, task=TASK_NAME)

# 单张图像的分割模型预测函数
def predict_single_image_by_segment(image_path, out_image_path):
out_dir = os.path.dirname(out_image_path)
os.makedirs(out_dir, exist_ok=True)

results = model.predict(source=image_path)

for result in results:
if result.masks is None:
cv2.imwrite(out_image_path, result.orig_img)
continue
masks = result.masks.data.cpu().numpy()
boxes = result.boxes.data.cpu().numpy()
label_map = result.names
color_map = generate_colors(label_map.values())

img_with_masks = result.orig_img.copy()

for i, mask in enumerate(masks):
mask = mask.astype(np.uint8)
mask = cv2.resize(mask, (result.orig_shape[1], result.orig_shape[0]))

color = np.random.randint(0, 255, (3,), dtype=np.uint8)
colored_mask = np.zeros_like(result.orig_img, dtype=np.uint8)
colored_mask[mask > 0] = color

img_with_masks = cv2.addWeighted(img_with_masks, 1, colored_mask, 0.5, 0)

box_data = boxes[i]
x1, y1, x2, y2 = map(int, box_data[:4])
class_name = label_map[int(box_data[5])]
score = box_data[4]
cv2.rectangle(img_with_masks, (x1, y1), (x2, y2), color_map[class_name], 2)
label = f"{class_name}: {score:.4f}"
cv2.putText(img_with_masks, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)

cv2.imwrite(out_image_path, img_with_masks)
print(f"Prediction saved to {out_image_path}")

if __name__ == '__main__':
image_path = 'bus.jpg'
out_image_path = image_path + '_segmented.jpg'
predict_single_image_by_segment(image_path, out_image_path)

图像分割结果分析

Results对象的特有字段:

  • masks:实例分割掩码数据。
  • boxes:边界框信息。
  • names:类别标签映射。

掩码数据为二值化图像,需调整到与原图相同的尺寸,并与原图叠加进行可视化。

姿态估计

姿态估计的代码实现

姿态估计的目标是检测人体的关键点,并根据关键点绘制出人体骨架。

import cv2
from ultralytics import YOLO
import os

POSE_MODEL_PATH = './models/pose.onnx'
TASK_NAME = 'pose'
model = YOLO(POSE_MODEL_PATH, task=TASK_NAME)

def predict_single_image_by_pose(image_path, out_image_path):
out_dir = os.path.dirname(out_image_path)
os.makedirs(out_dir, exist_ok=True)

results = model.predict(source=image_path)

for result in results:
if result.keypoints is None:
continue
if result.boxes is None:
continue

orig_img = result.orig_img
keypoints = result.keypoints.data.cpu().numpy()
boxes = result.boxes.data.cpu().numpy()

for box_data, kpts in zip(boxes, keypoints):
for keypoint in kpts:
x, y, score = keypoint
cv2.circle(orig_img, (int(x), int(y)), 3, (255, 0, 0), -1)

for connection in skeleton:
part_a, part_b = connection
if kpts[part_a][2] > 0.5 and kpts[part_b][2] > 0.5:
x1, y1 = int(kpts[part_a][0]), int(kpts[part_a][1])
x2, y2 = int(kpts[part_b][0]), int(kpts[part_b][1])
cv2.line(orig_img, (x1, y1), (x2, y2), (0, 255, 255), 1)

cv2.imwrite(out_image_path, orig_img)

if __name__ == '__main__':
image_path = 'bus.jpg'
out_image_path = image_path + '_posed.jpg'
predict_single_image_by_pose(image_path, out_image_path)

姿态估计结果分析

  • keypoints:包含人体关键点坐标和置信度。
  • boxes:人体检测框。
  • names:通常为'person'类别。

每个关键点包含以下数据结构:

[x, y, confidence]  # 每个关键点包含坐标和置信度

实践建议

  1. 数据预处理

    • 确保输入图像尺寸适合模型。
    • 检查图像格式(OpenCV通常使用BGR格式)。
    • 视需要进行图像增强。
  2. 结果处理注意事项

    • 始终进行空值检查。
    • 将tensor数据转换为numpy格式。
    • 坐标值转换为整数,确保OpenCV兼容性。
  3. 性能优化

    • 尽量批量处理图像以提高效率。
    • 使用GPU加速推理过程。
    • 根据实际需求选择合适的模型大小。
  4. 可视化建议

    • 为不同类别分配固定颜色,以便更好区分。
    • 调整线条的粗细和标签字体大小,保持预测结果可读性。

总结

YOLO在目标检测、图像分割和姿态估计三大任务中的表现令人印象深刻,模型的高度通用性使其成为计算机视觉领域中的热门选择。

  1. 数据结构差异

    • 目标检测:处理boxes数据。
    • 图像分割:同时处理masks和boxes。
    • 姿态估计:处理关键点(keypoints)和骨架结构。
  2. 应用场景

    • 目标检测:适用于物体定位和分类。
    • 图像分割:适用于精确轮廓分析。
    • 姿态估计:适用于人体动作追踪与行为分析。
  3. 通用处理流程

    • 模型加载与初始化。
    • 数据预处理。
    • 结果处理与可视化。
    • 错误与异常检查。
鱼雪

Rye 是一个现代化的 Python 包管理工具,旨在简化 Python 项目的管理和构建流程。 它提供了更快的依赖解析和更简单的项目配置方式。 本文将带您了解如何使用 Rye 来管理您的 Python 项目。

在我们深入介绍安装和基本使用指南之前,重要的是让您了解Rye实际上是什么。 Rye是一个一站式工具。其理念是,作为Python开发人员,您需要了解的只有Rye, 因为Rye是您进入体验的起点。

作为Rye用户,您甚至不需要自己安装Python,因为Rye会替您完成这一切。 这意味着要使用Rye,您只需要安装Rye,其他工作由Rye自己完成。

一旦Rye安装在您的系统上,它可以为您自动安装Python解释器,可以指定版本, 从软件包索引安装软件包,管理虚拟环境并进行幕后管理等。

工具对比

工具优势劣势
Rye- 自动管理虚拟环境
- 依赖解析速度快
- 简化项目初始化和配置
- 相对较新
- 社区支持和生态系统尚未完全成熟
Pip- 简单
- 广泛支持
- 与 PyPI 的直接集成
- 缺乏内置的虚拟环境管理
Conda- 强大的环境管理
- 支持多语言
- 适合科学计算
- 较大的安装包
- 可能引入不必要的复杂性
Poetry- 强大的依赖管理
- 简化的项目配置
- 在大型项目中速度较慢
Pipenv- 自动管理虚拟环境
- 简化的依赖管理
- 速度较慢
- 与其他工具集成性较差

安装 Rye

Rye安装有一个优点,那就是:不需要提前安装Python环境。 不像pippipenvpoetry等工具,需要提前安装Python环境。

虽然Anaconda/MiniConda也不需要提前安装环境,有一些包做了优化加速(比如Numpy之类的), 但是Anaconda系列的包管理工具比较大,不适合所有场景。尤其是Anaconda有一个缺点就是: Anaconda不好升级,导致不能指定高于某个版本的Python环境。

当然Rye也有缺点,就是比较新,可能不像pip那样稳定和广泛支持。 但是Rye的一大好处就是可以严格管理Python依赖包的版本,并且可以将开发依赖和生产依赖分开。 脚本启动等功能,也是Rye的一大特色。(由于Rye是Rust编写,所以,可能是受到Rust的Cargo工具的启发,我猜是这样)

Linux/MacOS安装方式

curl -sSf https://rye.astral.sh/get | bash

Windows上安装Rye

安装连接

安装完成后,您可以通过运行 rye --version 来验证安装是否成功。

创建新项目

使用 Rye 创建新项目非常简单。使用init子命令即可,可以指定init更多的选项,比如指定Python版本等。

只需在命令行中运行以下命令:

rye init your_project_name
# 或
rye init your_project_name --py=3.11 # 指定 Python 版本
# 或
rye init your_project_name -p=3.11 # 简写,指定Python版本

这将创建一个名为 your_project_name 的新目录,并在其中初始化一个新的 Python 项目。

添加依赖

Rye 使得添加和管理项目依赖变得非常简单。要向项目添加依赖,可以使用以下命令:

rye add diffusers
# 或
rye add diffusers --dev # 添加开发依赖
# 或
rye add diffusers==0.30.0 # 指定版本
# 或
rye add "diffusers[torch]" # 指定依赖包的额外功能

这将把 diffusers 包添加到项目的依赖中,并自动更新项目的依赖文件。

移除依赖

如果您需要移除某个依赖,只需运行:

rye remove diffusers

这将从项目中移除 diffusers 包,并更新依赖文件。

管理虚拟环境

Rye 自动为每个项目创建和管理虚拟环境。 您无需手动激活或管理虚拟环境,Rye 会在需要时自动为您处理只要进入到创建的项目目录中,Rye会自动激活虚拟环境。可以执行python -V检查Python版本。

Rye虚拟环境

构建和发布

Rye 还提供了简单的构建和发布工具。要构建项目,可以使用:

rye build

构建完成后,您可以通过以下命令发布项目:

rye publish

确保您已配置好发布所需的凭据。

运行项目脚本

要运行脚本,需要做以下几步:

  1. 需要配置pyproject.toml文件中的scripts字段。
[project.scripts]
my-hello-script = 'hello:main'
  1. 创建脚本文件hello.py,并在其中定义main函数。
def main():
print("Run Rye main")
tip

这里需要注意,要把hello.py文件放在项目的根目录下的src的目录下。

  1. 运行项目脚本
rye run my-hello-script

这里的my-hello-script就是pyproject.toml文件中的scripts字段中的my-hello-script

在项目中使用Python直接运行脚本

由于进入了rye init创建的项目后,就会默认激活虚拟环境,所以,可以直接使用Python运行脚本。

比如,创建一个demo.py文件,内容如下:

def main():
print("Hello Rye")

运行方式:

python demo.py
tip

只要*.py放在项目根目录下,或者项目的其他目录下,都可以直接使用Python运行脚本。

Rye环境中使用python运行脚本

总结

Python 的包管理器一直都是一个痛点问题。Rye 是一个新的工具,它试图解决这个问题。

Rye 是一个强大的工具,为 Python 开发者提供了简洁高效的包管理体验。 通过简化依赖管理、自动化虚拟环境以及提供便捷的构建和发布功能, Rye 帮助开发者更专注于编写代码,而不是管理项目配置。

希望这篇指南能帮助您更好地使用 Rye 来管理您的 Python 项目!如果您有任何问题或建议,欢迎在评论区留言。

鱼雪

使用Poetry做Python的项目管理工具,集成到Github Actions,包含以下内容:

  • Check out 代码仓库
  • 启动Python环境,可以限制版本,也可以使用多个版本
  • 安装Poetry
  • 设置虚拟环境缓存
  • 安装依赖
  • 代码格式化(yapf)
  • 代码静态类型检查(pytype)
  • 代码测试,以及覆盖率测试报告

Github Action workflow 配置如下:

name: CI
on: push

jobs:
ci:
strategy:
fail-fast: false
matrix:
python-version: ["3.8.5"]
poetry-version: ["1.2.2"]
os: [ubuntu-18.04]
runs-on: ${{ matrix.os }}
steps:
- name: Check out repository
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install Poetry
uses: snok/install-poetry@v1
with:
virtualenvs-create: true
virtualenvs-in-project: true
installer-parallel: true
- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v3
with:
path: .venv
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}

- name: Install Dependencies
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
run: poetry install --no-interaction --no-root

- name: Code Format
run: poetry run yapf

- name: Type Check
run: poetry run pytype --config=pytype.cfg

- name: Testing and coverage
run: poetry run pytest --cov
鱼雪