云图-地基云图
云图-地基云图
- 地基云图介绍
- 云图预处理
- 云图障碍物清除
- 云图圆心及边界处理
- 云图色度增强
- 云图太阳位置
- 云图云层识别
- 云层识别方法
- 云团矢量外推
地基云图介绍
云图预处理
云图障碍物清除
- 手动标记障碍物掩码
import cv2
import numpy as np
# ===== 初始化数据 =====
img = cv2.imread(img_path)
if img is None:raise FileNotFoundError(f"无法读取文件 {img_path}")
fixed_window_size = (1000, 1000) # 窗口大小为800x600
img = cv2.resize(img, fixed_window_size)
clone = img.copy() # 原图备份
mask = np.zeros(img.shape[:2], dtype=np.uint8) # 单通道掩码
points = [] # 存储多边形点# 鼠标回调:点击添加点
def click_event(event, x, y, flags, param):global points, imgif event == cv2.EVENT_LBUTTONDOWN:points.append((x, y))# 在图上画一个小圆点标记点击位置cv2.circle(img, (x, y), 3, (0, 0, 255), -1)# 如果有两个及以上的点,就画折线if len(points) > 1:cv2.line(img, points[-2], points[-1], (0, 255, 0), 1)# 右键:闭合多边形elif event == cv2.EVENT_RBUTTONDOWN:if len(points) > 2:cv2.polylines(img, [np.array(points)], True, (255, 0, 0), 1)cv2.fillPoly(mask, [np.array(points)], 255)points.clear()# 创建窗口
cv2.namedWindow('image')
cv2.setMouseCallback('image', click_event)print("操作说明:\n"" 左键点击:添加多边形顶点\n"" 右键:闭合多边形并填充掩码\n"" R 键:重置所有标注\n"" S 键:保存掩码并退出\n"" Esc 键:退出不保存\n")while True:cv2.imshow('image', img)# cv2.imshow('mask', mask)key = cv2.waitKey(1) & 0xFFif key == ord('r'):img = clone.copy()mask[:] = 0points.clear()print("标注已重置。")elif key == ord('s'):cv2.imwrite(mask_save_path, mask)print(f"掩码已保存到 {mask_save_path}")breakelif key == 27: # ESCbreakcv2.destroyAllWindows()
云图圆心及边界处理
- 使用行列扫描法
def CloudCircularBboundary(img, T=40):"""云图半径圆心坐标及有效区域裁剪"""rows, cols = img.shape[:2]img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)top, bottom, left, right = 0, 0, 0, 0# 从上向下扫描for i in range(0, rows, 1):for j in range(0, cols, 1):if img_gray[i, j] >= T:if img_gray[i + 1, j] >= T:top = ibreakelse:continuebreak# 从下向上扫描for i in range(rows - 1, -1, -1):for j in range(0, cols, 1):if img_gray[i, j] >= T:if img_gray[i - 1, j] >= T:bottom = ibreakelse:continuebreak# 从左向右扫描for j in range(0, cols, 1):for i in range(top, bottom, 1):if img_gray[i, j] >= T:if img_gray[i, j + 1] >= T:left = jbreakelse:continuebreak# 从右向左扫描for j in range(cols - 1, -1, -1):for i in range(top, bottom, 1):if img_gray[i, j] >= T:if img_gray[i, j - 1] >= T:right = jbreakelse:continuebreak# 计算有效区域半径R = int(max((bottom - top) / 2, (right - left) / 2))img_valid = img[top:int(top + 2 * R), left:int(left + 2 * R)]# 创建掩膜(单通道)h, w = img_valid.shape[:2]x, y = w // 2, h // 2 # 中心mask = np.zeros((h, w), dtype=np.uint8)cv2.circle(mask, (x, y), R, 255, -1)white_bg = np.ones_like(img_valid, dtype=np.uint8) * 255# 把圆形区域的像素从原图复制到白背景上result = white_bg.copy()result[mask == 255] = img_valid[mask == 255]return result, R, (x, y)
- 基于霍夫变换的云图边界检测
def CloudCircularBboundary(img):"""云图圆形边界检测"""gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)edges = cv2.Canny(gray, 250, 255)circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 1, 20,param1=50, param2=30, minRadius=0, maxRadius=0)if circles is not None:max_radius = 0masked_img,selected_circle = None,Nonecircles = np.round(circles[0, :]).astype("int")for (x, y, r) in circles:if r > max_radius:max_radius = rselected_circle = (x, y, r)print("最大圆形:", selected_circle)if selected_circle is not None:# x, y, r = selected_circlex, y, r = 300,300,250mask = np.zeros(img.shape[:2], dtype="uint8")cv2.circle(mask, (x, y), r, 255, -1)masked_img = cv2.bitwise_and(img, img, mask=mask)return masked_img,selected_circle,""else:return None,None,"未检测到圆形区域"
云图色度增强
def CloudChroma(img, B_coff=0.5, R_coff=0.5):# 云图色度增强R = img[:, :, 0].astype(np.float32)G = img[:, :, 1].astype(np.float32)B = img[:, :, 2].astype(np.float32)BR_ratio = B - RBR_mean = np.mean(BR_ratio)diff = BR_ratio - BR_meanR_enhanced = R.copy()B_enhanced = B.copy()B_enhanced += diff * B_coffR_enhanced -= diff * R_coffB_enhanced = np.clip(B_enhanced, 0, 255)R_enhanced = np.clip(R_enhanced, 0, 255)img_enhanced = np.stack([R_enhanced, G, B_enhanced], axis=2).astype(np.uint8)return img_enhanced
云图太阳位置
def SolarPosition(img, dt, lat, lon, R: float, camera_angle: float = 0) -> Tuple[int, int, float, float]:img_h, img_w = img.shape[:2]z, a = SolarZenithAzimuth(lat, lon, dt)r = (z / 90) * Rtheta_rad = math.radians(a - camera_angle)x0, y0 = img_w / 2, img_h / 2x = x0 - r * math.sin(theta_rad)y = y0 - r * math.cos(theta_rad)return int(round(x)), int(round(y)), z, a
云图云层识别
云层识别方法
- RB
def RBR(img, R1, center, threshold=1.2):B = img[:, :, 0]R = img[:, :, 2]BR_ratio = B / (R + 1e-6)cloud_mask = (BR_ratio > threshold).astype(np.uint8) * 255 # 云为1,天空为0mask = np.zeros_like(BR_ratio, dtype=np.uint8)cv2.circle(mask, center, R1, 255, -1)pixel_count = np.count_nonzero(mask) # 圆内全部像素点个数cloud_count = np.count_nonzero(cloud_mask)cloud_coverage = 1 - cloud_count / pixel_countgo_img = 255 - cloud_maskgo_img = cv2.bitwise_and(go_img, go_img, mask=mask)return round(cloud_coverage, 2), go_imgdef BRD(img, R1, center, threshold=1.2):B = img[:, :, 0]R = img[:, :, 2]BR_ratio = B - Rcloud_mask = (BR_ratio > threshold).astype(np.uint8) * 255 # 云为1,天空为0mask = np.zeros_like(BR_ratio, dtype=np.uint8)cv2.circle(mask, center, R1, 255, -1)pixel_count = np.count_nonzero(mask) # 圆内全部像素点个数cloud_count = np.count_nonzero(cloud_mask)cloud_coverage = 1 - cloud_count / pixel_countgo_img = cloud_maskgo_img = cv2.bitwise_and(go_img, go_img, mask=mask)return round(cloud_coverage, 2), go_imgdef NRBR(img, threshold=0.05):img_float = img.astype(np.float32)B = img_float[:, :, 0]G = img_float[:, :, 1]R = img_float[:, :, 2]denominator = (R + B)denominator[denominator == 0] = 1e-6nrbr = abs(R - B) / denominatorcloud_mask = (nrbr < threshold).astype(np.uint8)cloud_fraction = np.sum(cloud_mask) / cloud_mask.size * 100return cloud_fraction, cloud_mask * 255def CloudSegment_BRD_Diff(image, threshold=5):image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)b, g, r = cv2.split(image)diff = cv2.subtract(b, cv2.max(r, g))_, mask = cv2.threshold(diff, threshold, 255, cv2.THRESH_BINARY)mask = 255 - maskcloud_coverage = np.sum((mask > 0).astype(np.uint8)) / mask.size * 100return round(cloud_coverage,2),mask
- RB Kmean
def CloudSegment_BRD_Kmeans(img,alpha=0.5):# 云图色度增强R = img[:, :, 0].astype(np.float32)G = img[:, :, 1].astype(np.float32)B = img[:, :, 2].astype(np.float32)BR_ratio = B - RBR_mean = np.mean(BR_ratio)diff = BR_ratio - BR_meanR_enhanced = R.copy()B_enhanced = B.copy()B_enhanced += diff * alphaR_enhanced -= diff * alphaB_enhanced = np.clip(B_enhanced, 0, 255)R_enhanced = np.clip(R_enhanced, 0, 255)img_enhanced = np.stack([R_enhanced, G, B_enhanced], axis=2).astype(np.uint8)# K-Means云层检测features = np.stack([R_enhanced, G, B_enhanced], axis=-1)h_img, w_img, c_img = features.shapefeatures_2d = features.reshape(-1, 3) # 转为二维 (N, 3)kmeans = KMeans(n_clusters=2, n_init=10, max_iter=1000)labels = kmeans.fit_predict(features_2d)labels_img = labels.reshape(h_img, w_img)# 根据聚类中心亮度判断哪个是云(亮度大)cloud_cluster = np.argmax(kmeans.cluster_centers_[:, 0])cloud_mask = (labels_img == cloud_cluster).astype(np.uint8) * 255cloud_coverage = 1return cloud_coverage,cloud_mask
- RB GMM
def CloudSegment_GMM(img):# 2. 转 HSVhsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV).astype(np.float32)# 4. 取特征 这里选 S,V 作为聚类特征features = hsv[..., 1:3] # shape = (H, W, 2)h, w, _ = features.shapefeatures_2d = features.reshape(-1, 2)# 5. 下采样加速sample_idx = np.random.choice(features_2d.shape[0], size=5000, replace=False)sample_features = features_2d[sample_idx]# 6. 拟合高斯混合模型gmm = GaussianMixture(n_components=2, covariance_type='full',tol=1e-3, # 收敛阈值reg_covar=1e-6, # 正则化参数max_iter=200, # 最大迭代次数n_init=5, # 运行次数取最好init_params='kmeans', # 初始化方式random_state=42)gmm.fit(sample_features)# 7. 对全图预测labels = gmm.predict(features_2d)labels_img = labels.reshape(h, w)# 通常云像素亮度高,所以可以判断哪个聚类均值的V值更高cluster_means = []for k in range(2):cluster_means.append(np.mean(features_2d[labels == k, 1])) # 取V均值cloud_label = np.argmax(cluster_means)mask_cloud = (labels_img == cloud_label).astype(np.uint8) * 255cloud_coverage = np.sum((mask_cloud > 0).astype(np.uint8)) / mask_cloud.size * 100return round(cloud_coverage,2),mask_cloud
- Gray
def CloudGray(img, threshold=130):gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)ret, binary = cv2.threshold(gray, threshold, 255, cv2.THRESH_BINARY)cloud_coverage = 1return cloud_coverage,binary
云团矢量外推
仅个人笔记使用,感谢点赞关注
