mi-task/utils/gray_sky_analyzer.py

143 lines
4.9 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import cv2
import numpy as np
import os
import argparse
import matplotlib.pyplot as plt
def analyze_gray_sky_ratio(image_path, debug=False, save_result=False):
"""
专门针对灰色天空图片的分析算法
参数:
image_path: 图片路径
debug: 是否显示处理过程中的图像用于调试
save_result: 是否保存处理结果图像
返回:
sky_ratio: 天空占比0-1之间的浮点数
"""
# 读取图片
img = cv2.imread(image_path)
if img is None:
raise ValueError(f"无法读取图片: {image_path}")
# 获取图片文件名(不带路径和扩展名)
filename = os.path.splitext(os.path.basename(image_path))[0]
# 转换为HSV色彩空间更适合颜色分割
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# 提取图像的各个通道
h, s, v = cv2.split(hsv)
# 灰色天空特征:饱和度低(灰色是低饱和度的颜色)
# 亮度适中到较高(相对于黑色路面)
low_saturation_mask = s < 30 # 低饱和度阈值
medium_high_value_mask = v > 120 # 中高亮度阈值
# 组合掩码:低饱和度且中高亮度的区域可能是灰色天空
sky_mask = np.logical_and(low_saturation_mask, medium_high_value_mask).astype(np.uint8) * 255
# 应用形态学操作
kernel = np.ones((5, 5), np.uint8)
sky_mask = cv2.morphologyEx(sky_mask, cv2.MORPH_OPEN, kernel)
sky_mask = cv2.morphologyEx(sky_mask, cv2.MORPH_CLOSE, kernel)
# 假设天空通常位于图像上部
height, width = sky_mask.shape
sky_region_height = int(height * 0.6) # 假设天空在上部60%的区域
# 保留上部区域的掩码
upper_mask = np.zeros_like(sky_mask)
upper_mask[:sky_region_height, :] = 255
# 结合上部区域限制
sky_mask = cv2.bitwise_and(sky_mask, upper_mask)
# 使用连通区域分析,去除小的噪点区域
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(sky_mask, connectivity=8)
# 过滤小的连通区域
min_size = 1000 # 最小连通区域大小
filtered_sky_mask = np.zeros_like(sky_mask)
# 从索引1开始因为0是背景
for i in range(1, num_labels):
if stats[i, cv2.CC_STAT_AREA] >= min_size:
filtered_sky_mask[labels == i] = 255
# 计算天空区域占比
total_pixels = height * width
sky_pixels = np.sum(filtered_sky_mask == 255)
sky_ratio = sky_pixels / total_pixels
# 在原图上标记天空区域
result = img.copy()
overlay = img.copy()
overlay[filtered_sky_mask > 0] = [0, 255, 255] # 用黄色标记天空区域
cv2.addWeighted(overlay, 0.4, img, 0.6, 0, result) # 半透明效果
# 显示检测结果信息
cv2.putText(result, f"Sky Ratio: {sky_ratio:.2%}", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 2)
# 调试模式:显示处理过程图像
if debug:
plt.figure(figsize=(15, 10))
plt.subplot(231)
plt.title("Original Image")
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.subplot(232)
plt.title("Saturation Channel")
plt.imshow(s, cmap='gray')
plt.subplot(233)
plt.title("Value Channel")
plt.imshow(v, cmap='gray')
plt.subplot(234)
plt.title("Initial Sky Mask")
plt.imshow(sky_mask, cmap='gray')
plt.subplot(235)
plt.title("Filtered Sky Mask")
plt.imshow(filtered_sky_mask, cmap='gray')
plt.subplot(236)
plt.title("Sky Detection Result")
plt.imshow(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))
plt.tight_layout()
plt.show()
# 保存结果
if save_result:
result_dir = "results"
os.makedirs(result_dir, exist_ok=True)
output_path = os.path.join(result_dir, f"{filename}_gray_sky_result.jpg")
cv2.imwrite(output_path, result)
print(f"结果已保存至: {output_path}")
return sky_ratio
def main():
parser = argparse.ArgumentParser(description='分析图片中灰色天空区域占比')
# parser.add_argument('--image_path', default='./res/bar/image_20250525_085235.png', type=str, help='图片路径')
parser.add_argument('--image_path', default='./res/bar/image_20250525_085240.png', type=str, help='图片路径')
parser.add_argument('--debug', default=False, action='store_true', help='显示处理过程图像')
parser.add_argument('--save', action='store_true', help='保存处理结果图像')
args = parser.parse_args()
try:
sky_ratio = analyze_gray_sky_ratio(args.image_path, args.debug, args.save)
print(f"灰色天空区域占比: {sky_ratio:.2%}")
except Exception as e:
print(f"错误: {e}")
if __name__ == "__main__":
main()