好的,我写了一个函数,它可以对图像的每个波段进行标准差对比度拉伸。
对于正态分布,68% 的观测值位于平均值的一倍标准差内,95.4% 的观测值位于两倍标准差内,99.73% 的观测值位于三倍标准差内。
这基本上是一个最小-最大拉伸,但最大值为 mean+sigma*std
,最小值为 mean-sigma*std
。
def stretch(img,sigma =3,plot_hist=False):
stretched = np.zeros(img.shape)
for i in range(img.shape[2]):
band = img[:,:,i]
if np.min(band)<0:
band = band + np.abs(np.min(band))
band = band / np.max(band)
band = band * 255
if plot_hist:
plt.hist(band.ravel(), bins=256)
plt.show()
std = np.std(band)
mean = np.mean(band)
max = mean+(sigma*std)
min = mean-(sigma*std)
band = (band-min)/(max-min)
band = band * 255
band[band>255]=255
band[band<0]=0
print('band',i,np.min(band),np.mean(band),np.std(band),np.max(band))
if plot_hist:
plt.hist(band.ravel(), bins=256)
plt.show()
stretched[:,:,i] = band
stretched = stretched.astype('int')
return stretched
在上述情况下,我不需要保持波段比率不变,但对于RGB图像的最佳实践应该是这样的:
https://docs.opencv.org/4.x/d5/daf/tutorial_py_histogram_equalization.html
不幸的是,这种CLAHE拉伸不适用于多波段图像,因此应分别应用于每个波段 - 这会导致对比度丢失并且图像倾向于灰色。我们需要做的是:将图像转换为HSV颜色空间并拉伸V(值 - 强度)并保留其余部分。这就是我们如何获得良好的拉伸效果(双关语)。
关于cv.COLOR_HSV2RGB的问题在于它实际上返回BGR而不是RGB,因此在HSV2RGB之后,我们需要反转波段。
以下是我编写的函数:
def stack_3_channel(r,g,b , clipLimit = 20 , tileGridSize=(16,16) ):
img = np.stack([r,g,b], axis=2)
img = cv.normalize(img, None, 0, 255, cv.NORM_MINMAX, dtype=cv.CV_8U)
hsv_img = cv.cvtColor(img, cv.COLOR_BGR2HSV)
h, s, v = hsv_img[:,:,0], hsv_img[:,:,1], hsv_img[:,:,2]
clahe = cv.createCLAHE(clipLimit, tileGridSize)
v = clahe.apply(v)
hsv_img = np.dstack((h,s,v))
bgr_stretched = cv.cvtColor(hsv_img, cv.COLOR_HSV2RGB)
rgb_stretched = np.zeros(bgr_stretched.shape)
rgb_stretched[:,:,0] = bgr_stretched[:,:,2]
rgb_stretched[:,:,1] = bgr_stretched[:,:,1]
rgb_stretched[:,:,2] = bgr_stretched[:,:,0]
rgb_stretched = rgb_stretched.astype('uint8')
return img , rgb_stretched
![image after histogram stretching](https://istack.dev59.com/GVTPw.webp)