如何在Matplotlib中基于百分比累积计数实现栅格对比度增强?
Hey there! I’ve dealt with this exact problem when building tools for aligned raster visualization, so let’s break down the steps to get you what you need:
1. First, Load Your Raster Data
You’ll need to read your single-band raster into a NumPy array (since Matplotlib plays seamlessly with NumPy, and we’ll use NumPy’s built-in functions for percentile calculations). I’ll use rasterio here (a go-to library for raster handling), but you can use other tools like gdal if you prefer:
import rasterio import numpy as np import matplotlib.pyplot as plt # Load first raster with rasterio.open("first_raster.tif") as src1: raster1 = src1.read(1) # Read band 1 nodata1 = src1.nodata # Load second raster with rasterio.open("second_raster.tif") as src2: raster2 = src2.read(1) nodata2 = src2.nodata
2. Filter Out NoData Values
Rasters almost always have invalid NoData pixels (like -9999), which will skew your percentile calculations if you leave them in. Let’s filter those out:
# Get valid pixels for each raster valid_raster1 = raster1[raster1 != nodata1] valid_raster2 = raster2[raster2 != nodata2]
3. Calculate Shared 2% and 98% Percentiles
Since you want matching color scales between the two images (so the same raster value maps to the same color), we’ll calculate percentiles across both datasets combined. If you wanted each image to use its own 2/98% range (but still aligned), you could calculate separately, but combining ensures a unified scale:
# Combine valid data from both rasters combined_valid = np.concatenate([valid_raster1, valid_raster2]) # Compute 2nd and 98th percentiles vmin = np.percentile(combined_valid, 2) vmax = np.percentile(combined_valid, 98)
Note: np.percentile() is exactly the function you were missing! It calculates the value at the specified cumulative percentage of your dataset, just like QGIS’s cumulative count crop tool.
4. Plot the Images with Unified Scale
Now pass these vmin and vmax values to Matplotlib’s imshow() for both images, and add a shared colorbar to make alignment clear:
# Create side-by-side plot fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 7)) # Plot first raster with shared scale im1 = ax1.imshow(raster1, vmin=vmin, vmax=vmax, cmap="viridis") ax1.set_title("First Aligned Image") ax1.axis("off") # Plot second raster with the SAME scale im2 = ax2.imshow(raster2, vmin=vmin, vmax=vmax, cmap="viridis") ax2.set_title("Second Aligned Image") ax2.axis("off") # Add a single shared colorbar fig.colorbar(im1, ax=[ax1, ax2], orientation="horizontal", pad=0.05, label="Raster Value") plt.tight_layout() plt.show()
Quick Notes
- If you don’t want a shared scale (just each image using its own 2/98% range), skip combining the datasets and calculate
vmin/vmaxseparately for each raster. - Double-check your NoData value: some rasters use
np.naninstead of a numeric value, so you’d filter with~np.isnan(raster_data)instead.
内容的提问来源于stack exchange,提问作者Jan-Pieter

