def mask_ledaps_qa(rast, mask, nodata=-9999):
'''
Applies a given LEDAPS QA mask to a raster. It's unclear how these
bit-packed QA values ought to be converted back into 16-bit binary numbers:
"{0:b}".format(42).zfill(16) # Convert binary to decimal padded left?
"{0:b}".format(42).ljust(16, '0') # Or convert ... padded right?
The temporary solution is to use the most common (modal) value as the
"clear" pixel classification and discard everything else. We'd like to
just discard pixels above a certain value knowing that everything above
this threshold has a certain bit-packed QA meanining. For example, mask
pixels with QA values greater than or equal to 12287:
int("1000000000000000", 2) == 32768 # Maybe clouds
int("0010000000000000", 2) == 12287 # Maybe cirrus
Similarly, we'd like to discard pixels at or below 4, as these small binary
numbers correspond to dropped frames, desginated fill values, and/or
terrain occlusion. Arguments:
rast A gdal.Dataset or a NumPy array
mask A gdal.Dataset or a NumPy array
'''
# Can accept either a gdal.Dataset or numpy.array instance
if not isinstance(rast, np.ndarray):
rast = rast.ReadAsArray()
else:
rastr = rast.copy()
if not isinstance(mask, np.ndarray):
maskr = mask.ReadAsArray()
else:
maskr = mask.copy()
# Since the QA output is so unreliable (e.g., clouds are called water),
# we take the most common QA bit-packed value and assume it refers to
# the "okay" pixels
mode = np.argmax(np.bincount(maskr.ravel()))
assert mode > 4 and mode < 12287, "The modal value corresponds to a known error value"
maskr[np.isnan(maskr)] = 0
maskr[maskr != mode] = 0
maskr[maskr == mode] = 1
# Transform into a "1-band" array and apply the mask
maskr = maskr.reshape((1, maskr.shape[0], maskr.shape[1]))\
.repeat(rastr.shape[0], axis=0) # Copy the mask across the "bands"
rastr[maskr == 0] = nodata
return rastr
评论列表
文章目录