基于OpenCV的cv2.Canny与cv2.findContours提取异质卫星图像有效区域
Hey there! Welcome to the platform—happy to help you tackle this satellite image processing task. Let’s break down how to locate that largest valid region, simplify its contour to a small set of points (max 15), and save them out. Here’s a practical, step-by-step approach tailored to your needs:
First, you need to define what counts as "valid data"—this usually means excluding black borders, cloud cover, or pixels marked as NoData (common in satellite imagery, often 0, NaN, or a specific value like 65535 for 16-bit files). We’ll create a binary mask to separate valid vs. invalid pixels:
import cv2 import numpy as np # Load your satellite image (adjust path/band as needed; use grayscale for simplicity) img = cv2.imread("satellite_image.tif", cv2.IMREAD_GRAYSCALE) # Define valid pixel range (tweak this based on your image's actual NoData values) valid_mask = (img > 0) & (img != 65535) # Example: exclude 0 and 65535 valid_mask = valid_mask.astype(np.uint8) * 255 # Convert to 8-bit mask for OpenCV compatibility
Next, we’ll detect all connected valid regions and pick the one with the largest area (since you want the biggest valid data region):
# Find outer contours only (avoids nested small regions inside larger ones) contours, _ = cv2.findContours(valid_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # Sort contours by area and grab the largest one largest_contour = max(contours, key=cv2.contourArea)
Use the Douglas-Peucker algorithm (via OpenCV’s approxPolyDP) to reduce the number of contour points. Adjust the epsilon parameter (a percentage of the contour perimeter) until you get a point count ≤15:
# Calculate the contour's perimeter perimeter = cv2.arcLength(largest_contour, closed=True) # Start with a small epsilon (2% of perimeter) and adjust incrementally epsilon = 0.02 * perimeter simplified_contour = cv2.approxPolyDP(largest_contour, epsilon, closed=True) # If we still have too many points, increase epsilon until we hit the 15-point limit while len(simplified_contour) > 15: epsilon += 0.005 * perimeter simplified_contour = cv2.approxPolyDP(largest_contour, epsilon, closed=True) # Convert contour points to clean (x,y) tuples simplified_points = [tuple(point[0]) for point in simplified_contour] print(f"Simplified contour to {len(simplified_points)} points: {simplified_points}")
Save the simplified points to a format that works for your workflow—text file, JSON, etc.:
# Save to a plain text file (one point per line) with open("contour_points.txt", "w") as f: for x, y in simplified_points: f.write(f"{x}, {y}\n") # Or save as JSON for easier parsing later import json with open("contour_points.json", "w") as f: json.dump(simplified_points, f)
- Adjust Valid Mask Logic: Since your images are diverse, you may need to tweak the valid pixel check for different image types (e.g., multi-band imagery might require all relevant bands to be non-NoData).
- Batch Processing: Wrap this code in a loop using
os.listdirorpathlibto process hundreds of images automatically. - Edge Cases: For perfect square regions,
approxPolyDPwill automatically reduce the contour to 4 points—exactly what you need for simple scenarios.
内容的提问来源于stack exchange,提问作者rudypl2

