How to Estimate Visibility Using Atmospheric Tower Photos
This article explains a step‑by‑step method for measuring atmospheric visibility by annotating landmarks in fixed‑angle tower photos, extracting their pixel regions, scoring recognizability with a standard‑deviation‑based metric, and computing the farthest visible object using Python and OpenCV.
Fixed‑angle photos from the Chinese Academy of Atmospheric Physics 325 m meteorological tower are used to estimate visibility.
Atmospheric Tower and Camera Setup
The tower is located at 39°58′N, 116°22′E, 49 m above sea level. Two 24‑hour HD cameras are mounted at 280 m height, each capturing an image every half hour. The northeast‑facing camera is selected because the southwest camera often suffers from over‑exposure.
Landmark Distance Measurement
Landmarks visible in the tower photos are located in Google Earth using the tower’s known coordinates. Satellite imagery is manually aligned with the tower photo, and the Google Earth distance tool measures the distance from the tower to each landmark. The pixel coordinates of the landmark bounding boxes and the measured distances are stored in a config.json file, for example:
{
"Huating Jiayuan": {"x1": 896, "y1": 2285, "x2": 3154, "y2": 3246, "distance": 1.27},
"Water Cube": {"x1": 2420, "y1": 1886, "x2": 3524, "y2": 2191, "distance": 2.18},
"Pangu Grand View": {"x1": 1691, "y1": 1079, "x2": 2385, "y2": 2104, "distance": 1.71}
// ... additional entries omitted for brevity
}Cropping Landmark Regions
Using the coordinates from config.json, each landmark region is cropped from the original photo:
import cv2
def crop_image(x1, y1, x2, y2, image_path, output_path):
img = cv2.imread(image_path)
cropped_img = img[y1:y2, x1:x2]
cv2.imwrite(output_path, cropped_img)Recognizability Scoring
Visibility is inferred from the recognizability of each landmark. The recognizability score is defined as the product of the image’s standard deviation (SD) and its spectral width (max − min) after converting the image to grayscale. Higher scores indicate clearer, more recognizable scenes.
import matplotlib.pyplot as plt
def is_clear(image_fp, threshold=250):
image = (plt.imread(image_fp) * 255).astype("uint8")
std = image.std()
span = image.max() - image.min()
score = std * span
return score > thresholdEmpirical observation shows scores below 200 correspond to indistinguishable landmarks; a threshold of 250 is used to classify a landmark as “visible”.
Computing Full‑Image Visibility
After classifying each landmark, the farthest “visible” landmark determines the overall visibility distance. The implementation loads the configuration, crops each landmark, applies the recognizability test, collects distances of visible landmarks, and returns the maximum distance rounded to the nearest kilometre.
import os, json, shutil
CONFIG_FP = os.path.join(os.path.dirname(__file__), "config.json")
def get_config(config_fp=CONFIG_FP):
with open(config_fp) as f:
return json.load(f)
def analysis_visibility(image_fp, threshold=250, tmpdir="./tmp", keep_tmp=False):
config = get_config()
visibles = []
for key, value in config.items():
x1, y1, x2, y2 = value["x1"], value["y1"], value["x2"], value["y2"]
distance = value["distance"]
savefp = os.path.join(tmpdir, f"{key}.jpg")
os.makedirs(tmpdir, exist_ok=True)
crop_image(x1, y1, x2, y2, image_fp, savefp)
if is_clear(savefp, threshold):
visibles.append(distance)
if not keep_tmp:
shutil.rmtree(tmpdir)
return round(max(visibles), 0)The function analysis_visibility thus provides a one‑click visibility estimate for any tower photo.
Demo Results
Applying the pipeline to several real‑world tower images yields visibility distances that match visual expectations.
Source code is open‑sourced at https://github.com/caiyunapp/tower-eye/tree/main.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
