【问题标题】:How to obtain smooth histogram after scaling image?缩放图像后如何获得平滑的直方图?
【发布时间】:2021-12-13 18:18:48
【问题描述】:

我正在尝试对图像进行线性缩放,以便使用整个灰度范围。这是为了改善镜头的照明。但是,在绘制直方图时,我不知道如何获得缩放直方图,使其更平滑,因此它是一条渴望离散箱的曲线。任何提示或要点将不胜感激。

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

产生的直方图是:

来自这张照片:

【问题讨论】:

  • 我猜你想要一个漂亮的钟形曲线? (在这种情况下)
  • 为什么需要找到直方图才能缩放图像。您可以使用 skimage.exposure.rescale_intensity(image, in_range='image', out_range=(0,255))。这将自动找到您的输入范围并缩放到范围 0,255 或您想要指定的任何值。
  • 官方文档在histogram equalization上有一个不错的python教程

标签: python opencv histogram


【解决方案1】:

我认为您想到的是一条通过您的点的样条曲线。操作方法如下:

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')

下面,结果:

最好的问候, 斯蒂芬

【讨论】:

    【解决方案2】:

    如果您对图像进行线性缩放,我不确定这是否可行。不过,你可以试试 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()
    

    您可以使用clipLimittileGridSize 来获得您想要的图像外观。 default values40.0(8, 8)

    【讨论】:

      【解决方案3】:

      你应该阅读gamma correction

      使用来自answer 的代码,它使用自动方式计算 gamma 值,您会得到以下结果:

      方法一:

      方法二:

      校正后图像的直方图(方法二)如下所示:

      **编辑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])
      

      【讨论】:

        【解决方案4】:

        我正在尝试对图像进行线性缩放,以便使用整个灰度范围

        为了增加图像的动态性,有一种常见的图像处理方法,称为“直方图均衡化”(参见:cv.equalizeHist()doc

        为了获得更好的结果,您应该删除图像的极端值,以消除曝光不足/过度曝光。

        为此,在均衡图像上:

        • 计算直方图的累积和
        • 找到第一个低于rate的强度和第一个高于1-rate的强度
        • 将所有图像强度修剪为这 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。

        【讨论】:

          猜你喜欢
          • 2010-11-25
          • 2012-11-17
          • 2010-12-20
          • 2019-01-15
          • 2016-11-15
          • 2011-02-01
          相关资源
          最近更新 更多