大数跨境
0
0

掌握计算机视觉三大基础任务:图像分类、分割与检测

掌握计算机视觉三大基础任务:图像分类、分割与检测 知识代码AI
2025-11-28
0
导读:掌握计算机视觉三大基础任务:图像分类、分割与检测计算机视觉是深度学习最具影响力的应用领域之一。

掌握计算机视觉三大基础任务:图像分类、分割与检测

计算机视觉是深度学习最具影响力的应用领域之一。在入门阶段,我们需要重点掌握三项基本的计算机视觉任务,它们是构建更复杂应用的基础。

三大基础计算机视觉任务

1. 图像分类(Image Classification)

目的:为图像指定一个或多个标签。

  • 单标签分类:一张图像只能属于一个类别
  • 多标签分类:找出图像所属的所有类别

实际应用:谷歌照片应用中的图像搜索功能,使用了包含20,000+类别的多标签分类模型。

2. 图像分割(Image Segmentation)

目的:将图像"分割"成不同区域,每个区域对应一个类别。

实际应用:视频通话中的虚拟背景、自动驾驶中的道路识别等。

图像分割又分为两种:

  • 语义分割:将每个像素划分到语义类别(如"猫"),不区分个体实例
  • 实例分割:不仅按类别分类像素,还要区分单个对象实例
语义分割与实例分割对比
语义分割与实例分割对比

3. 目标检测(Object Detection)

目的:在图像中感兴趣的目标周围绘制边界框,并给出每个框对应的类别。

实际应用:自动驾驶汽车检测车辆、行人和交通标志。

图像分割实战:宠物图像分割

下面我们通过一个实际例子,学习如何使用深度学习进行语义分割。

数据集准备

我们使用Oxford-IIIT宠物数据集,包含7,390张猫狗图片及对应的分割掩码。

import os

input_dir = "images/"
target_dir = "annotations/trimaps/"

input_img_paths = sorted([
    os.path.join(input_dir, fname)
for fname in os.listdir(input_dir)
if fname.endswith(".jpg")
])

target_paths = sorted([
    os.path.join(target_dir, fname) 
for fname in os.listdir(target_dir)
if fname.endswith(".png"andnot fname.startswith(".")
])

理解分割掩码

分割掩码是图像分割任务的标签,它是与输入图像大小相同的单通道图像,其中每个像素值表示:

  • 1:前景
  • 2:背景
  • 3:轮廓

构建分割模型

图像分割模型通常采用编码器-解码器结构:

from tensorflow import keras
from tensorflow.keras import layers

defget_model(img_size, num_classes):
# 编码器(下采样)
    inputs = keras.Input(shape=img_size + (3,))
    x = layers.Conv2D(323, strides=2, activation="relu", padding="same")(inputs)
    x = layers.Conv2D(643, strides=2, activation="relu", padding="same")(x)
    x = layers.Conv2D(1283, strides=2, activation="relu", padding="same")(x)

# 解码器(上采样)
    x = layers.Conv2DTranspose(1283, activation="relu", padding="same")(x)
    x = layers.Conv2DTranspose(643, activation="relu", padding="same")(x)
    x = layers.Conv2DTranspose(323, activation="relu", padding="same")(x)

# 输出层
    outputs = layers.Conv2D(num_classes, 3, activation="softmax", padding="same")(x)

return keras.Model(inputs, outputs)

model = get_model(img_size=(200200), num_classes=3)

关键设计要点

  1. 使用步幅卷积而非最大池化:保留位置信息,对分割任务至关重要
  2. 编码器-解码器结构:先压缩提取特征,再上采样恢复分辨率
  3. Conv2DTranspose层:学习上采样操作,恢复原始图像尺寸

训练与评估

model.compile(optimizer="rmsprop", loss="sparse_categorical_crossentropy")

callbacks = [
    keras.callbacks.ModelCheckpoint("oxford_segmentation.keras"
                                    save_best_only=True)
]

history = model.fit(train_input_imgs, train_targets,
                    epochs=50,
                    callbacks=callbacks,
                    validation_data=(val_input_imgs, val_targets))

结果展示

训练完成后,模型能够准确地区分前景(宠物)和背景:

测试图像和预测的分割掩码
测试图像和预测的分割掩码

总结

  • 图像分类:识别图像内容,"这是什么?"
  • 图像分割:像素级分类,"每个像素属于什么?"
  • 目标检测:定位并识别物体,"物体在哪里?是什么?"

掌握这三项基础任务,你就具备了解决大多数计算机视觉问题的核心能力。图像分割技术在图像编辑、自动驾驶、医学影像等领域有着广泛应用,是计算机视觉工程师必备的技能之一。

完整代码已整理发布,欢迎在技术社区交流讨论!

import os
import numpy as np
import matplotlib

matplotlib.use('Agg')  # 使用非交互式后端
import matplotlib.pyplot as plt
import sys

# 设置TensorFlow日志级别
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

print("开始执行图像分割程序...")

# 检查必要的目录和文件
print("检查数据目录...")
current_dir = os.getcwd()
print(f"当前工作目录: {current_dir}")

# 根据项目规范,使用更可靠的方式定位项目根目录
# 使用当前文件的路径来确定项目根目录,而不是依赖于当前工作目录
script_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(script_dir)
print(f"脚本目录: {script_dir}")
print(f"项目根目录: {project_root}")

# 准备文件路径 - 使用相对于项目根目录的路径
input_dir = os.path.join(project_root, "images")
target_dir = os.path.join(project_root, "annotations""trimaps")

print(f"输入图像目录: {input_dir}")
print(f"目标掩码目录: {target_dir}")

# 检查目录是否存在
ifnot os.path.exists(input_dir):
    print(f"错误: 输入目录 {input_dir} 不存在")
    print("请确保已下载并解压 Oxford-IIIT Pet Dataset 数据集")
    print("数据集下载命令:")
    print("wget http://www.robots.ox.ac.uk/~vgg/data/pets/data/images.tar.gz")
    print("wget http://www.robots.ox.ac.uk/~vgg/data/pets/data/annotations.tar.gz")
    print("tar -xf images.tar.gz")
    print("tar -xf annotations.tar.gz")
    sys.exit(1)

ifnot os.path.exists(target_dir):
    print(f"错误: 目标目录 {target_dir} 不存在")
    sys.exit(1)

# 获取文件列表
try:
    input_img_paths = sorted([
        os.path.join(input_dir, fname)
for fname in os.listdir(input_dir)
if fname.endswith(".jpg")
    ])

    target_paths = sorted([
        os.path.join(target_dir, fname)
for fname in os.listdir(target_dir)
if fname.endswith(".png"andnot fname.startswith(".")
    ])

    print(f"找到 {len(input_img_paths)} 张输入图像")
    print(f"找到 {len(target_paths)} 个目标掩码")

if len(input_img_paths) == 0or len(target_paths) == 0:
        print("错误: 没有找到图像文件")
        sys.exit(1)

except Exception as e:
    print(f"获取文件列表时出错: {e}")
    sys.exit(1)

# 限制处理的图像数量以减少内存使用
max_samples = min(500, len(input_img_paths))  # 进一步减少样本数量
input_img_paths = input_img_paths[:max_samples]
target_paths = target_paths[:max_samples]
print(f"限制处理图像数量为 {max_samples} 张以减少内存使用")

# 跳过图像显示部分,直接进入数据处理
print("跳过图像显示,直接进入数据处理...")

# 延迟导入TensorFlow,避免可能的冲突
try:
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.utils import load_img

    print("TensorFlow模块导入成功")
except ImportError as e:
    print(f"导入TensorFlow模块时出错: {e}")
    sys.exit(1)


# 加载和处理数据的简化版本
defload_images_simple(input_img_paths, target_paths, size=(128128)):# 使用更小的图像尺寸
    print(f"开始加载 {len(input_img_paths)} 张图像...")
    input_imgs = []
    targets = []

for i in range(len(input_img_paths)):
if i % 50 == 0:
            print(f"已加载 {i}/{len(input_img_paths)} 张图像")

try:
# 加载输入图像
            img = load_img(input_img_paths[i], target_size=size)
            img_array = np.array(img, dtype="float32") / 255.0

# 加载目标掩码
            mask = load_img(target_paths[i], target_size=size, color_mode="grayscale")
            mask_array = np.array(mask, dtype="uint8")

# 处理标签值,确保它们在正确范围内
# 对于Oxford-IIIT Pet数据集,标签值是1,2,3,需要转换为0,1,2
            mask_array = mask_array - 1
            mask_array[mask_array == 255] = 2# 将边界像素设置为类别2

            mask_array = np.expand_dims(mask_array, 2)

            input_imgs.append(img_array)
            targets.append(mask_array)

except Exception as e:
            print(f"跳过图像 {input_img_paths[i]}{e}")
continue

    print(f"成功加载 {len(input_imgs)} 张图像")
return np.array(input_imgs), np.array(targets)


# 加载所有数据
try:
    input_imgs, targets = load_images_simple(input_img_paths, target_paths, size=(128128))

if len(input_imgs) == 0:
        print("错误: 没有成功加载任何图像")
        sys.exit(1)

except Exception as e:
    print(f"加载图像时出错: {e}")
    sys.exit(1)

# 分割训练集和验证集
num_val_samples = min(100, len(input_imgs) // 5)
train_input_imgs = input_imgs[:-num_val_samples]
train_targets = targets[:-num_val_samples]
val_input_imgs = input_imgs[-num_val_samples:]
val_targets = targets[-num_val_samples:]

print(f"训练集大小: {len(train_input_imgs)}")
print(f"验证集大小: {len(val_input_imgs)}")


# 构建简化模型
defget_simple_model(img_size, num_classes):
    print("正在构建简化模型...")
    inputs = keras.Input(shape=img_size + (3,))

# 编码器
    x = layers.Conv2D(323, activation='relu', padding='same')(inputs)
    x = layers.MaxPooling2D(2)(x)
    x = layers.Conv2D(643, activation='relu', padding='same')(x)
    x = layers.MaxPooling2D(2)(x)

# 解码器
    x = layers.Conv2DTranspose(643, activation='relu', padding='same')(x)
    x = layers.UpSampling2D(2)(x)
    x = layers.Conv2DTranspose(323, activation='relu', padding='same')(x)
    x = layers.UpSampling2D(2)(x)

# 输出层
    outputs = layers.Conv2D(num_classes, 1, activation='softmax')(x)

    model = keras.Model(inputs, outputs)
    print("简化模型构建完成")
return model


# 创建简化模型
model = get_simple_model(img_size=(128128), num_classes=3)
model.summary()

# 编译模型
print("正在编译模型...")
model.compile(
    optimizer="adam",  # 使用adam优化器,通常更稳定
    loss="sparse_categorical_crossentropy",
    metrics=['accuracy']
)

# 确保目标数据类型正确
train_targets = train_targets.astype(np.int32)
val_targets = val_targets.astype(np.int32)

# 设置回调函数
callbacks = [
    keras.callbacks.ModelCheckpoint(
"simple_oxford_segmentation.keras",
        save_best_only=True
    ),
    keras.callbacks.EarlyStopping(patience=3)  # 添加早停
]

# 训练模型
print("开始训练模型...")
try:
    history = model.fit(
        train_input_imgs, train_targets,
        epochs=5,  # 少量epochs进行测试
        callbacks=callbacks,
        batch_size=8,  # 更小的batch_size
        validation_data=(val_input_imgs, val_targets),
        verbose=1
    )

    print("模型训练完成")

# 绘制训练和验证损失
if len(history.history["loss"]) > 0:
        epochs = range(1, len(history.history["loss"]) + 1)
        loss = history.history["loss"]
        val_loss = history.history["val_loss"]

        plt.figure()
        plt.plot(epochs, loss, "bo-", label="Training loss")
        plt.plot(epochs, val_loss, "ro-", label="Validation loss")
        plt.title("Training and validation loss")
        plt.legend()
        plt.savefig('simple_training_loss.png', dpi=150, bbox_inches='tight')
        plt.close()
        print("训练损失图表已保存为 simple_training_loss.png")

# 进行预测
    print("进行预测...")
if os.path.exists("simple_oxford_segmentation.keras"):
        model = keras.models.load_model("simple_oxford_segmentation.keras")

# 在验证集上测试模型
    test_idx = min(5, len(val_input_imgs) - 1)
    test_image = val_input_imgs[test_idx]

# 预测
    mask_pred = model.predict(np.expand_dims(test_image, 0), verbose=0)[0]
    predicted_mask = np.argmax(mask_pred, axis=-1)

# 创建结果图表
    fig, axes = plt.subplots(13, figsize=(124))

# 原始图像
    axes[0].imshow(test_image)
    axes[0].set_title("Test Image")
    axes[0].axis('off')

# 预测掩码
    axes[1].imshow(predicted_mask, cmap='viridis')
    axes[1].set_title("Predicted Mask")
    axes[1].axis('off')

# 真实掩码
    axes[2].imshow(val_targets[test_idx][:, :, 0], cmap='viridis')
    axes[2].set_title("Ground Truth")
    axes[2].axis('off')

    plt.tight_layout()
    plt.savefig('simple_prediction_result.png', dpi=150, bbox_inches='tight')
    plt.close()
    print("预测结果图表已保存为 simple_prediction_result.png")

    print("程序执行成功完成!")

except Exception as e:
    print(f"训练过程中发生错误: {e}")
import traceback

    traceback.print_exc()
    print("程序执行遇到问题,但已尽力完成")

【声明】内容源于网络
0
0
知识代码AI
技术基底 机器视觉全栈 × 光学成像 × 图像处理算法 编程栈 C++/C#工业开发 | Python智能建模 工具链 Halcon/VisionPro工业部署 | PyTorch/TensorFlow模型炼金术 | 模型压缩&嵌入式移植
内容 366
粉丝 0
知识代码AI 技术基底 机器视觉全栈 × 光学成像 × 图像处理算法 编程栈 C++/C#工业开发 | Python智能建模 工具链 Halcon/VisionPro工业部署 | PyTorch/TensorFlow模型炼金术 | 模型压缩&嵌入式移植
总阅读108
粉丝0
内容366