用OpenCV和Numpy在Python中实现“全局对比度因子”

zvms9eto  于 2024-01-08  发布在  Python
关注(0)|答案(2)|浏览(148)

我想使用Numpy和OpenCV在Python中实现Krešimir Matkovic,László Neumann,Attila Neumann,托马斯Psik和Werner Purgatharn的论文 Global Contrast Factor - a New Approach to Image Contrast 中详细描述的过程。然而,有一个步骤我很难做到。
这个过程本质上涉及到灰度图像像素的一些基本乘法和加法,这可以通过Numpy矢量化操作相当容易地完成。然而,这个过程需要在越来越小的分辨率下重复图像,其中较小图像中的超像素根据论文中提供的特定公式进行组合。
从原始图像开始,每次尺寸减半,并且“超像素值被计算为平均线性亮度,然后将其转换为感知亮度”,由公式给出:
x1c 0d1x的数据
其中gamma = 2.2,k是原始像素值[0,255]。我假设这意味着每次缩小图像时,我需要一次计算四个像素(排列成正方形)的第一个公式,对它们进行平均,然后应用第二个公式作为较小图像的灰度值。
我的问题是,我不知道如何有效地缩小图像,以便其像素值以对应于这些公式的方式计算。OpenCV已经有一些内置的缩放算法,但老实说,我不确定哪种算法(如果有的话)会提供与论文中详细描述的结果相似的结果。

如何有效地调整灰度图像的大小,其中较小图像中的像素值由这些公式确定?

kx5bkwkv

kx5bkwkv1#

OpenCV resize不执行这些操作。您必须显式计算图像中每个像素的线性亮度值,然后使用线性插值(INTER_LINEAR)应用resize。线性插值将计算平均值。请参阅以下示例。它计算非重叠2x2块的平均值。

>>> im = np.random.randint(0, 255, (8, 8)).astype(np.float32)
>>> im
array([[ 74.,  73., 109.,  41., 165.,  24., 165., 189.],
       [143.,  15., 212., 139.,  63., 123., 222., 231.],
       [ 58., 150., 168., 234.,  64., 139., 239., 179.],
       [227.,  11., 142.,  42.,  28., 127., 213.,  52.],
       [187., 232.,  26.,  34.,  63., 104., 197., 155.],
       [ 95., 154.,  69.,  56., 163.,  93.,  37., 136.],
       [ 14., 249., 204.,  15., 182., 226.,  20., 114.],
       [231.,  61., 155., 184., 211., 162.,  62.,  26.]], dtype=float32)
>>> cv2.resize(im, dsize=None, fx=0.5, fy=0.5, interpolation=cv2.INTER_LINEAR)
array([[ 76.25, 125.25,  93.75, 201.75],
       [111.5 , 146.5 ,  89.5 , 170.75],
       [167.  ,  46.25, 105.75, 131.25],
       [138.75, 139.5 , 195.25,  55.5 ]], dtype=float32)
>>> (168+234+142+42)/4
146.5

字符串
据我所知,你只计算原始图像的线性亮度(l),然后依次降低分辨率(如果我错了,请纠正我)。
您可以使用查找表(LUT)加速线性亮度计算,因为如果图像是8位(8U),则最多只有256个不同的像素值。

bvjveswy

bvjveswy2#

我根据原始论文为任何需要的人实现了GCF测量。
请注意,权重生成函数对于不同的分辨率可能不是理想的,而不是在论文中进行的实验。

import numpy as np
import skimage.measure
import cv2

class GlobalContrastFactorContrastMeasure:
    """
    Global Contrast Factor is a global measure of the overall image contrast
    as perceived by the HVS.

    It is calculated as a weighted average of the local contrast computed
    at different resolution levels.

    See:
    Original paper: https://www.researchgate.net/profile/Werner-Purgathofer-2/publication/220795239_Global_Contrast_Factor_-_a_New_Approach_to_Image_Contrast/links/0912f506162e773787000000/Global-Contrast-Factor-a-New-Approach-to-Image-Contrast.pdf
    """

    def __init__(self):
        self.gamma = 2.2

    def assess(self, img: np.ndarray):
        # Use Value from HSV, which represents the perceived brightness
        img = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)[..., -1]

        l = self._correct_lumuninance_with_gamma(img)
        C = [self._calculate_average_contrast(l)]

        li = l
        num_resolution_decreases = self._get_num_resolution_decreases(img)
        for i in range(num_resolution_decreases):
            # Decrease resolution of l by half using average pooling 2x2
            li = self._average_pooling(li)
            # Calculate the average contrast
            Ci = self._calculate_average_contrast(li)
            C.append(Ci)

        weights = self._calculate_weights(len(C))
        gcf = np.sum(weights * C)
        return gcf

    def _correct_lumuninance_with_gamma(self, img: np.ndarray):
        return np.power(img / 255, self.gamma)

    def _get_num_resolution_decreases(self, img: np.ndarray):
        height, width = img.shape[:2]
        # Average pooling uses 2x2 kernel -> use log2
        # Do not decrease the resolution below 3 pixels in size
        min_resolution_pixels = 3
        min_resolution_log = np.log2(min_resolution_pixels)
        h_resolutions = int(np.log2(height) - min_resolution_log)
        w_resolutions = int(np.log2(width) - min_resolution_log)
        num_resolution_decreases = min(h_resolutions, w_resolutions)
        return num_resolution_decreases

    def _average_pooling(self, l: np.ndarray):
        return skimage.measure.block_reduce(l, (2, 2), np.mean)

    def _calculate_average_contrast(self, l: np.ndarray):
        # perceptual luminance
        eps = 0.0001  # for numerical stability
        L = 100 * np.sqrt(l + eps)

        # local contrast as the avarage of the four neigboring pixels of perceptual luminance
        lc = self._average_difference(L)

        # average contrast
        C = np.mean(lc)
        return C

    def _average_difference(self, image):
        """
        Calculates the average difference of a pixel and its neighbors.
        """
        # Pad the array with zeros to handle boundary cases
        padded_image = np.pad(image, ((1, 1), (1, 1)), mode='edge')

        # Calculate the differences between each pixel and its four neighbors
        differences = np.abs(image - padded_image[:-2, 1:-1])  # Top neighbor
        differences += np.abs(image - padded_image[2:, 1:-1])  # Bottom neighbor
        differences += np.abs(image - padded_image[1:-1, :-2])  # Left neighbor
        differences += np.abs(image - padded_image[1:-1, 2:])  # Right neighbor

        # Count the number of valid neighbors for each pixel
        # Eges have only three neighbors and corners just two.
        valid_neighbors = np.zeros_like(image)
        valid_neighbors[:-1, :] += 1  # Top neighbor
        valid_neighbors[1:, :] += 1  # Bottom neighbor
        valid_neighbors[:, :-1] += 1  # Left neighbor
        valid_neighbors[:, 1:] += 1  # Right neighbor

        # Calculate the average difference for each pixel
        average_diff = differences / valid_neighbors

        return average_diff

    def _calculate_weights(self, num_resolutions):
        """
        Calculates weights using a function proposed in the paper.

        Note that this function was determined in the original paper for a specific resolution
        of images, which may not be correct for different resolutions.
        """
        i = np.arange(num_resolutions)
        weights = (-0.406385 * i / num_resolutions + 0.334573) * i / num_resolutions + 0.0877526
        return weights

字符串

相关问题