使用分割 Mask 和 K-means 聚类获取天空的颜色

引言

在计算机视觉领域,获取天空的颜色是一个常见任务,广泛应用于天气分析、环境感知和图像增强等场景。本篇博客将介绍如何通过已知的天空区域 Mask 提取天空像素,并使用 K-means 聚类分析天空颜色,最终根据颜色占比查表得到主导颜色。

一、流程概述

本文的流程分为以下几个步骤:

1. 使用分割 Mask 提取天空区域像素。
2. 通过 K-means 聚类对天空像素进行颜色聚类。
3. 统计每个聚类的面积占比,筛选面积占比超过 30% 的颜色。
4. 根据筛选结果查表获取对应的颜色名称。

接下来我们将详细讲解每个步骤。

二、提取天空区域像素

分割 Mask 是一个二值化的图像,其中 1 表示天空区域,0 表示非天空区域。通过 Mask,可以从原图中提取出天空区域的像素。

python 复制代码
from typing import Tuple
import numpy as np
from PIL import Image

def extract_sky_pixels(image_path: str, sky_mask: np.ndarray) -> np.ndarray:
    """
    根据天空区域的 Mask 提取像素。
    
    :param image_path: 输入图像的路径。
    :param sky_mask: 天空区域的二值化 Mask,1 表示天空,0 表示非天空。
    :return: 天空区域的像素数组,形状为 (N, 3),每行表示一个像素的 [R, G, B] 值。
    """
    image = Image.open(image_path).convert("RGB")
    image_np = np.array(image)  # 转为 NumPy 数组
    return image_np[sky_mask == 1]  # 仅保留天空区域的像素

三、K-means 聚类分析天空颜色

提取天空像素后,我们使用 K-means 聚类将颜色分为若干类(默认设定为 k=3),并统计每类颜色的面积占比。

以下是 K-means 聚类的代码实现:

python 复制代码
from sklearn.cluster import KMeans
from typing import Tuple

def kmeans_clustering(sky_pixels: np.ndarray, k: int = 3) -> Tuple[np.ndarray, np.ndarray]:
    """
    使用 K-means 对天空像素聚类。
    
    :param sky_pixels: 天空区域的像素数组,形状为 (N, 3),每行表示 [R, G, B] 值。
    :param k: 聚类的数量,默认值为 3。
    :return: 
        - cluster_centers: 每个聚类的中心颜色,形状为 (k, 3)。
        - area_ratios: 每个聚类的面积占比,形状为 (k,)。
    """
    # 归一化像素值(0-1 范围)
    sky_pixels_normalized = sky_pixels / 255.0
    # 使用 K-means 聚类
    kmeans = KMeans(n_clusters=k, random_state=0).fit(sky_pixels_normalized)
    # 获取每个像素的聚类标签
    labels = kmeans.labels_
    # 每个聚类的中心颜色
    cluster_centers = kmeans.cluster_centers_ * 255.0
    # 统计每个聚类的像素数量
    label_counts = np.bincount(labels)
    # 计算每个聚类的面积占比
    area_ratios = label_counts / len(sky_pixels)
    return cluster_centers, area_ratios

四、筛选主导颜色并查表

我们关心面积占比超过 30% 的颜色,并通过查表将 RGB 值转换为对应的颜色名称。

以下是颜色查表和筛选的代码:

python 复制代码
from typing import List, Tuple

def rgb_to_color_name(rgb: np.ndarray) -> str:
    """
    根据 RGB 值查表获取颜色名称。
    
    :param rgb: RGB 颜色值,形状为 (3,)。
    :return: 颜色名称。
    """
    # 定义常见颜色的 RGB 值和名称
    color_table = {
        "Sky Blue": (135, 206, 235),
        "Deep Sky Blue": (0, 191, 255),
        "Light Blue": (173, 216, 230),
        "Azure": (240, 255, 255),
        "Navy": (0, 0, 128)
    }
    # 计算每个颜色的欧几里得距离
    min_dist = float('inf')
    color_name = "Unknown"
    for name, value in color_table.items():
        dist = np.linalg.norm(np.array(rgb) - np.array(value))
        if dist < min_dist:
            min_dist = dist
            color_name = name
    return color_name

def filter_and_get_color_names(
    cluster_centers: np.ndarray, 
    area_ratios: np.ndarray, 
    threshold: float = 0.3
) -> List[Tuple[str, float]]:
    """
    筛选面积占比超过阈值的颜色,并查表获取颜色名称。
    
    :param cluster_centers: 每个聚类的中心颜色,形状为 (k, 3)。
    :param area_ratios: 每个聚类的面积占比,形状为 (k,)。
    :param threshold: 面积占比的筛选阈值,默认为 0.3。
    :return: 主导颜色及其面积占比的列表,每个元素为 (颜色名称, 面积占比)。
    """
    colors = []
    for center, ratio in zip(cluster_centers, area_ratios):
        if ratio > threshold:
            color_name = rgb_to_color_name(center)
            colors.append((color_name, ratio))
    return colors

五、完整代码示例

以下是完整的流程代码,实现从分割 Mask 提取天空颜色的过程:

python 复制代码
from typing import List, Tuple
import numpy as np

def get_sky_colors(image_path: str, sky_mask: np.ndarray, k: int = 3, threshold: float = 0.3) -> List[Tuple[str, float]]:
    """
    获取天空区域的主导颜色。
    
    :param image_path: 输入图像的路径。
    :param sky_mask: 天空区域的二值化 Mask,1 表示天空,0 表示非天空。
    :param k: 聚类的数量,默认值为 3。
    :param threshold: 面积占比的筛选阈值,默认为 0.3。
    :return: 主导颜色及其面积占比的列表,每个元素为 (颜色名称, 面积占比)。
    """
    # Step 1: 提取天空像素
    sky_pixels = extract_sky_pixels(image_path, sky_mask)
    # Step 2: K-means 聚类
    cluster_centers, area_ratios = kmeans_clustering(sky_pixels, k)
    # Step 3: 筛选并查表获取颜色名称
    dominant_colors = filter_and_get_color_names(cluster_centers, area_ratios, threshold)
    return dominant_colors

# 测试
if __name__ == "__main__":
    # 假设已知分割 Mask
    sample_mask = np.load("sky_mask.npy")  # 载入二值化 Mask
    image_path = "example.jpg"  # 输入图像路径
    sky_colors = get_sky_colors(image_path, sample_mask)
    print("天空颜色:", sky_colors)

六、总结

通过分割 Mask 和 K-means 聚类,我们可以高效提取天空区域的颜色特征,并获取主导颜色。这种方法简单易用,适合多种场景需求。

希望这篇博客能帮助你快速上手天空颜色提取的实现!如有疑问,欢迎在评论区交流!

相关推荐
siy23333 小时前
[c语言日寄]精英怪:三子棋(tic-tac-toe)3命慢通[附免费源码]
c语言·开发语言·笔记·学习·算法
arnold664 小时前
华为OD E卷(100分)53-TLV解码
java·算法·华为od
_周游4 小时前
【C语言】_sizeof与strlen的相关示例
c语言·开发语言·算法
冠位观测者6 小时前
【Leetcode 热题 100】215. 数组中的第K个最大元素
数据结构·算法·leetcode
忆源6 小时前
C -- 结构体内存对齐的原理
c语言·开发语言·算法
朔北之忘 Clancy6 小时前
2024 年 3 月青少年软编等考 C 语言二级真题解析
c语言·开发语言·c++·学习·算法·青少年编程·题解
躺不平的理查德7 小时前
C 语言中二维数组的退化
c语言·开发语言·数据结构·算法
hkj88087 小时前
SM3在线哈希运行
算法
工业甲酰苯胺8 小时前
.NET8.0多线程编码结合异步编码示例
java·算法·.net