如何在缩放图像后获得平滑的直方图?
How to obtain smooth histogram after scaling image?
我正在尝试对图像进行线性缩放,以便使用整个灰度范围。这是为了改善镜头的照明。然而,在绘制直方图时,我不知道如何获得缩放直方图,使其更平滑,因此它是一条曲线,就像离散箱所期望的那样。任何提示或要点将不胜感激。
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img = cv.imread(r'/Users/harold/Documents/Academia/Nottingham Uni/Year 4/ImageProcessing/Imaging_Task_Sheet/PointImage.jpeg', cv.IMREAD_GRAYSCALE)
img_s = img/255
img_s = img_s / np.max(img_s)
img_s = img_s*255
histogram = cv.calcHist([img], [0], None, [256], [0, 256])
histogram1 = cv.calcHist([img_s.astype('uint8')], [0], None, [256], [0, 256])
plt.figure()
plt.title("Grayscale Histogram")
plt.xlabel("grayscale value")
plt.ylabel("pixels")
plt.plot(histogram, label='Original Image') # <- or here
plt.plot(histogram1, label='Equalised Image') # <- or here
生成的直方图是:
出自这张图片:
如果您线性缩放图像,我不确定这是否可行。但是,您可以试试 OpenCV 的 Contrast Limited Adaptive Histogram Equalization:
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img = cv.imread('3NKTJ.jpg', cv.IMREAD_GRAYSCALE)
img_clahe = img.copy()
img_clahe = img_clahe/np.max(img_clahe)
img_clahe = (255*img_clahe).astype(np.uint8)
clahe = cv.createCLAHE(clipLimit=5, tileGridSize=(3,3))
img_clahe = clahe.apply(img_clahe)
img_s = img/255
img_s = img_s / np.max(img_s)
img_s = img_s*255
histogram = cv.calcHist([img], [0], None, [256], [0, 256])
histogram1 = cv.calcHist([img_s.astype('uint8')], [0], None, [256], [0, 256])
histogram2 = cv.calcHist([img_clahe.astype('uint8')], [0], None, [256], [0, 256])
plt.figure(dpi=100)
plt.title("Grayscale Histogram")
plt.xlabel("grayscale value")
plt.ylabel("pixels")
plt.plot(histogram, label='Original Image') # <- or here
plt.plot(histogram1, label='Equalised Image') # <- or here
plt.plot(histogram2, label='CLAHE Image')
plt.legend()
plt.show()
您可以使用 clipLimit
和 tileGridSize
来获得您想要的图像。 default values 是 40.0
和 (8, 8)
。
您应该阅读有关 gamma correction 的内容:
使用此 中的代码,它使用自动方式计算 gamma 值,您会得到以下结果:
方法一:
方法二:
校正后的图像直方图(方法 2)如下所示:
**编辑2:**
或者您可以使用线性方法在 0 到 255 之间重新调整每个通道。使用此代码:
def apply_white_balance_single_channel(img, low_ratio=0.001, high_ratio=0.001):
hist_size = 256
hist = cv2.calcHist([img], [0], None, [hist_size], [0,hist_size])
acc = np.cumsum(hist)
low_limit = low_ratio * acc[-1]
high_limit = high_ratio * acc[-1]
min_gray = 0
while acc[min_gray] < low_limit and min_gray + 1 < hist_size:
min_gray += 1
max_gray = hist_size - 1
while acc[max_gray] >= acc[-1] - high_limit and max_gray > min_gray:
max_gray -= 1
input_range = max_gray - min_gray
alpha = (hist_size - 1) / input_range
beta = -min_gray * alpha
return (alpha * img + beta).clip(0,255).astype(np.uint8)
def apply_white_balance_multi_channel(img, low_ratio=0.001, high_ratio=0.001):
channels = cv2.split(img)
return cv2.merge([apply_white_balance_single_channel(ch, low_ratio, high_ratio) for ch in channels])
结果如下:
重新缩放的图像:
直方图:
编辑3
或者您可以使用更简单的版本,使用简单的最小-最大归一化来进行线性重新缩放:
def apply_white_balance_multi_channel_min_max(img):
channels = cv2.split(img)
return cv2.merge([cv2.normalize(img, None, 0, 255, cv2.NORM_MINMAX) for ch in channels])
我想你的想法是通过你的点的样条曲线。方法如下:
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
from scipy import interpolate
img = cv.imread(r'3NKTJ.jpg', cv.IMREAD_GRAYSCALE)
img_s = img/255
img_s = img_s / np.max(img_s)
img_s = img_s*255
histogram = cv.calcHist([img], [0], None, [256], [0, 256])
histogram1 = cv.calcHist([img_s.astype('uint8')], [0], None, [256], [0, 256])
x=np.linspace(0,len(histogram1),len(histogram1)) # x: 0 --> 255 with step=1
X=np.where(histogram1>0)[0] # extract bins with non-zero histogram1 values
Y=histogram1[X] # the corresponding Y values
F=interpolate.splrep(X, Y) # spline representation of (X,Y)
Ynew = interpolate.splev(x, F) # calculate interpolated Ynew
plt.figure()
plt.title("Grayscale Histogram")
plt.xlabel("grayscale value")
plt.ylabel("pixels")
plt.plot(histogram, label='Original Image') # <- or here
plt.plot(histogram1, label='Equalised Image') # <- or here
plt.plot(x,Ynew, label='spline interpolation of Equalised Image')
下面,结果:
此致,
斯蒂芬
I am trying to linearly scale an image so the whole greyscale range is used
为了增加图像的动态,有一种常用的图像处理方法叫做“直方图均衡化”(参见:cv.equalizeHist()
、doc)
为了获得更好的结果,您应该移除图像的极值,以移除 under/over 曝光。
为此,在均衡图像上:
- 计算直方图的累积和
- 找到第一个强度低于
rate
和第一个强度高于 1-rate
- trim 所有图像强度为这 2 个值。
这是一个示例代码:
def equalize_hist(data: np.ndarray, rate: float) -> np.ndarray:
data = cv.equalizeHist(data.astype(np.uint8))
hist = np.cumsum(np.histogram(data, 255)[0])
lowest_value = np.where((rate * hist[-1]) <= hist)[0][0]
highest_value = np.where(((1 - rate) * hist[-1]) >= hist)[0][-1]
sdata[self.data < lowest_value] = lowest_value
data[self.data > highest_value] = highest_value
return data
rate
的常用值为 0.05。当您增加速率时,您会增加图像的动态,但会丢失信息。您不能将 rate
提高到 0.5 以上。
我正在尝试对图像进行线性缩放,以便使用整个灰度范围。这是为了改善镜头的照明。然而,在绘制直方图时,我不知道如何获得缩放直方图,使其更平滑,因此它是一条曲线,就像离散箱所期望的那样。任何提示或要点将不胜感激。
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img = cv.imread(r'/Users/harold/Documents/Academia/Nottingham Uni/Year 4/ImageProcessing/Imaging_Task_Sheet/PointImage.jpeg', cv.IMREAD_GRAYSCALE)
img_s = img/255
img_s = img_s / np.max(img_s)
img_s = img_s*255
histogram = cv.calcHist([img], [0], None, [256], [0, 256])
histogram1 = cv.calcHist([img_s.astype('uint8')], [0], None, [256], [0, 256])
plt.figure()
plt.title("Grayscale Histogram")
plt.xlabel("grayscale value")
plt.ylabel("pixels")
plt.plot(histogram, label='Original Image') # <- or here
plt.plot(histogram1, label='Equalised Image') # <- or here
生成的直方图是:
出自这张图片:
如果您线性缩放图像,我不确定这是否可行。但是,您可以试试 OpenCV 的 Contrast Limited Adaptive Histogram Equalization:
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img = cv.imread('3NKTJ.jpg', cv.IMREAD_GRAYSCALE)
img_clahe = img.copy()
img_clahe = img_clahe/np.max(img_clahe)
img_clahe = (255*img_clahe).astype(np.uint8)
clahe = cv.createCLAHE(clipLimit=5, tileGridSize=(3,3))
img_clahe = clahe.apply(img_clahe)
img_s = img/255
img_s = img_s / np.max(img_s)
img_s = img_s*255
histogram = cv.calcHist([img], [0], None, [256], [0, 256])
histogram1 = cv.calcHist([img_s.astype('uint8')], [0], None, [256], [0, 256])
histogram2 = cv.calcHist([img_clahe.astype('uint8')], [0], None, [256], [0, 256])
plt.figure(dpi=100)
plt.title("Grayscale Histogram")
plt.xlabel("grayscale value")
plt.ylabel("pixels")
plt.plot(histogram, label='Original Image') # <- or here
plt.plot(histogram1, label='Equalised Image') # <- or here
plt.plot(histogram2, label='CLAHE Image')
plt.legend()
plt.show()
您可以使用 clipLimit
和 tileGridSize
来获得您想要的图像。 default values 是 40.0
和 (8, 8)
。
您应该阅读有关 gamma correction 的内容:
使用此
方法一:
方法二:
校正后的图像直方图(方法 2)如下所示:
**编辑2:** 或者您可以使用线性方法在 0 到 255 之间重新调整每个通道。使用此代码:
def apply_white_balance_single_channel(img, low_ratio=0.001, high_ratio=0.001):
hist_size = 256
hist = cv2.calcHist([img], [0], None, [hist_size], [0,hist_size])
acc = np.cumsum(hist)
low_limit = low_ratio * acc[-1]
high_limit = high_ratio * acc[-1]
min_gray = 0
while acc[min_gray] < low_limit and min_gray + 1 < hist_size:
min_gray += 1
max_gray = hist_size - 1
while acc[max_gray] >= acc[-1] - high_limit and max_gray > min_gray:
max_gray -= 1
input_range = max_gray - min_gray
alpha = (hist_size - 1) / input_range
beta = -min_gray * alpha
return (alpha * img + beta).clip(0,255).astype(np.uint8)
def apply_white_balance_multi_channel(img, low_ratio=0.001, high_ratio=0.001):
channels = cv2.split(img)
return cv2.merge([apply_white_balance_single_channel(ch, low_ratio, high_ratio) for ch in channels])
结果如下:
重新缩放的图像:
直方图:
编辑3
或者您可以使用更简单的版本,使用简单的最小-最大归一化来进行线性重新缩放:
def apply_white_balance_multi_channel_min_max(img):
channels = cv2.split(img)
return cv2.merge([cv2.normalize(img, None, 0, 255, cv2.NORM_MINMAX) for ch in channels])
我想你的想法是通过你的点的样条曲线。方法如下:
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
from scipy import interpolate
img = cv.imread(r'3NKTJ.jpg', cv.IMREAD_GRAYSCALE)
img_s = img/255
img_s = img_s / np.max(img_s)
img_s = img_s*255
histogram = cv.calcHist([img], [0], None, [256], [0, 256])
histogram1 = cv.calcHist([img_s.astype('uint8')], [0], None, [256], [0, 256])
x=np.linspace(0,len(histogram1),len(histogram1)) # x: 0 --> 255 with step=1
X=np.where(histogram1>0)[0] # extract bins with non-zero histogram1 values
Y=histogram1[X] # the corresponding Y values
F=interpolate.splrep(X, Y) # spline representation of (X,Y)
Ynew = interpolate.splev(x, F) # calculate interpolated Ynew
plt.figure()
plt.title("Grayscale Histogram")
plt.xlabel("grayscale value")
plt.ylabel("pixels")
plt.plot(histogram, label='Original Image') # <- or here
plt.plot(histogram1, label='Equalised Image') # <- or here
plt.plot(x,Ynew, label='spline interpolation of Equalised Image')
下面,结果:
此致, 斯蒂芬
I am trying to linearly scale an image so the whole greyscale range is used
为了增加图像的动态,有一种常用的图像处理方法叫做“直方图均衡化”(参见:cv.equalizeHist()
、doc)
为了获得更好的结果,您应该移除图像的极值,以移除 under/over 曝光。
为此,在均衡图像上:
- 计算直方图的累积和
- 找到第一个强度低于
rate
和第一个强度高于1-rate
- trim 所有图像强度为这 2 个值。
这是一个示例代码:
def equalize_hist(data: np.ndarray, rate: float) -> np.ndarray:
data = cv.equalizeHist(data.astype(np.uint8))
hist = np.cumsum(np.histogram(data, 255)[0])
lowest_value = np.where((rate * hist[-1]) <= hist)[0][0]
highest_value = np.where(((1 - rate) * hist[-1]) >= hist)[0][-1]
sdata[self.data < lowest_value] = lowest_value
data[self.data > highest_value] = highest_value
return data
rate
的常用值为 0.05。当您增加速率时,您会增加图像的动态,但会丢失信息。您不能将 rate
提高到 0.5 以上。