AI-Profiles: Cloud Detection in Ceilometer Measurements
image segmentation, CNN, clustering
A. Mortier (augustinm@met.no), FoU-KL, MET Norway
📌 context
🫡 motivations
I could add more “if”s but.. I’ve seen some ML techniques used to distinguish cats 🐱 from dogs 🐶! Could I use this instead??
objective: detect clouds ☁️ in ceilometer measurements.
constraints: single wavelength measurements. distinction with dense aerosol layers. noise
k-means
concept
The k-means algorithm is one of the most widely recognized and implemented clustering techniques in machine learning. Its core principle revolves around partitioning a dataset into k distinct, non-overlapping clusters.
To understand the mechanics, consider a dataset X. The objective of k-means is to determine k centroids and assign each data point to the nearest centroid. These centroids, which represent the center of the clusters, are initialized randomly or based on specific heuristics.
The algorithm iteratively performs two primary steps:
This iterative process continues until the centroids no longer change significantly or a set number of iterations is reached.
k-means
landscape
4 clusters
——————————
img (1440, 1440, 3)
input img (2073600, 3)
labels (2073600,)
centers (4, 3)
——————————
k-means
attenuated backscatter profile
4 clusters
——————————
img (462, 1240, 1)
input img (1718640, 1)
labels (1718640,)
centers (4, 1)
——————————
🤔 nice but…
this “works” for a specific day… but we have various atmospheric conditions:
what would give 4 clusters in a “clear” day?
→ let’s just look at the previous day
k-means
attenuated backscatter profile
4 clusters
——————————
img (462, 1240, 1)
input img (1718640, 1)
labels (1718640,)
centers (4, 1)
——————————
💭 I want a model that
→ DEC (Deep Embedded Clustering)
→ feature extraction with CNN (Convolutional Neural Networks)
convolution
image x kernel
-1 | 0 | 1 |
-2 | 0 | 2 |
-1 | 0 | 1 |
-1 | 0 | 1 |
-1 | 0 | 1 |
-1 | 0 | 1 |
1 | 1 | 1 |
1 | -8 | 1 |
1 | 1 | 1 |
1 | 0 |
0 | -1 |
0 | 1 |
-1 | 0 |
[...]
CNN-based autoencoder architecture
⚙️ workflow
↪️ general idea
📖 model training
🔍 cloud detection
autoencoder
# Encoder
input_img = Input(shape=(256, 512, 1)) # 256, 512, 1
x = Conv2D(32, (3, 3), activation='relu', padding='same')(input_img) # 256, 512, 32
x = MaxPooling2D((2, 2), padding='same')(x) # 128, 256, 32
x = Conv2D(64, (3, 3), activation='relu', padding='same')(x) # 128, 256, 64
x = MaxPooling2D((2, 2), padding='same')(x) # 64, 128, 64
# Latent Space
x = Conv2D(128, (3, 3), activation='relu', padding='same')(x) # 64, 128, 128
encoded = MaxPooling2D((2, 2), padding='same')(x) # 32, 64, 128
# Decoder
x = Conv2D(128, (3, 3), activation='relu', padding='same')(encoded) # 32, 64, 128
x = UpSampling2D((2, 2))(x) # 64, 128, 128
x = Conv2D(64, (3, 3), activation='relu', padding='same')(x) # 64, 128, 64
x = UpSampling2D((2, 2))(x) # 128, 256, 64
x = Conv2D(32, (3, 3), activation='relu', padding='same')(x) # 128, 256, 32
x = UpSampling2D((2, 2))(x) # 256, 512, 32
decoded = Conv2D(1, (3, 3), activation='sigmoid', padding='same')(x) # 256, 512, 1
# Model definition
autoencoder = Model(input_img, decoded)
autoencoder.compile(optimizer='adam', loss='mse')
# Train the autoencoder
autoencoder.fit(training_images, training_images, �validation_data=(validation_images, validation_images), �epochs=10, batch_size=16, shuffle=True)
encoder = Model(inputs=autoencoder.input, outputs=encoded)
encoder.save('dec/encoder.keras')
⏳25 minutes
k-means
# Path to the images
image_dir = 'images/rcs'
image_size = (256, 512) # Resize images to a consistent size
# Step 1: Load and Preprocess the Dataset
images = []
for filename in os.listdir(image_dir):
img_path = os.path.join(image_dir, filename)
img = load_img(img_path, target_size=image_size, color_mode='grayscale') # Load as grayscale
img_array = img_to_array(img) / 255.0 # Normalize to [0, 1]
images.append(img_array)
images = np.array(images)
# 1. Load encoder
encoder_path = 'unsupervised/cnn/encoder.keras'
encoder = load_model(encoder_path)
# 2. Encode each image to get pixel-level features
encoded_images = encoder.predict(images)
# 3. Flatten spatial dimensions but keep feature channels intact
# This will give (num_images * height * width, num_features)
num_images, enc_height, enc_width, num_features = encoded_images.shape
encoded_images_flat = encoded_images.reshape((num_images * enc_height * enc_width, num_features))
# 4. Apply KMeans to pixel features
kmeans = KMeans(n_clusters=8)
pixel_labels = kmeans.fit_predict(encoded_images_flat) # Clusters all pixels independently
joblib.dump(kmeans, 'dec/kmeans.pkl')
🚀 5ish seconds
results
results
results
results
results
results
results
results
what’s next
Mini-MPL
Oslo, this morning
some resources