数字图像学笔记——4. 直方图计算、线性变换、对数变换、Gamma变换

灰度直方图(Gray Histogram)

在数字图像处理中,灰度直方图是一种计算代价非常小但却很有用的工具,它概括了一幅图像的灰度级信息,它用来描述每个灰度级在图像矩阵中的像素个数或者占有率,或者出现频率。

通过直方图,我们可以很快知道一个灰度图片中有效数据的大概范围,可以通过一些简单的线性计算,将我们感兴趣的数据重新编辑到新的图片中,也可以调节一张照片的曝光量,某些教科书上称这种做法叫对比度进行调节。

当然,如果配合神经元网络,我们也可以对一些黑白照片重新上色。当然这些都是比较高阶的技巧了。

直方图调整有几种常用的方法,如线性变换、分段线性变换、伽马变换、直方图正规化、直方图均衡化、局部自适应直方图均衡化、图像翻转等,这些方法的计算代价较小,但是却产生了较为理想的效果。

直方图的计算方法

在这里插入图片描述
我又把这张图拿了出来,直方图的计算方式,其实就是从上到下,从左到右一个一个像素进行统计。所以假设我们对林肯的照片进行分析,绘制这张照片的直方图,那么在X轴的方向,就是强度信息,也就是灰度值 [ 0 , 255 ] [0, 255] [0,255],而它的Y轴方向,就是频率信息,或者说是各像素点值的出现率。

def calculate_hist(img):
    # create a matrix to hold histogram information
    histogram = np.zeros(256, dtype=np.uint64)

    # iterate each pixel
    row, col = img.shape
    for r in range(row):
        for c in range(col):
            histogram[img[r, c]] = histogram[img[r, c]] + 1

    # return to caller
    return histogram

我们对结果进行比对,看看是否一致:

    # load data from file
    img = cv2.imread("Data/lincoln.jpg", cv2.IMREAD_GRAYSCALE)
    my_hist = calculate_hist(img)

    # show
    show_images(img.reshape(-1), my_hist)

输出的效果:

在这里插入图片描述
左侧是我们的计算结果,而右侧是通过matplotlib进行统计的结果,我们可以看到结果是一样的。通过直方图,我们知道我们感兴趣的数据,主要集中在 [ 0 , 100 ] [0, 100] [0,100] [ 160 , 255 ] [160, 255] [160,255]这两个范围区间内,那么这两个区间内都包含哪些有趣的图像信息呢?

在这里插入图片描述

简单的图像转换方法

线性变换 / 图像翻转(Image Nagatives)

线性变换,又或者叫图像翻转,通常被应用于X光照片这类图像的处理。由于我们兴趣区域的数据多处于低密度区域,而这部分数据如果直接显示通常较难观察到(low intensity),例如X光来说,低密度的细节通常很难进行观察,而通过线性转换,则可以得到一些用肉眼不太容易注意到的细节。

在这里插入图片描述
比如这张对乳腺的X光照片,转换后的右侧图片中黑色斑块的位置,可能预示患者已经有了肿块,可能需要进一步的细致检查。

假设图像的灰阶为 [ 0 , 255 ] [0, 255] [0,255]这个范围,那么它的线性转换计算方式可以简单的表示为:

I c = 255 − I o I_c = 255 - I_o Ic=255Io

其中 I c I_c Ic表示为转换后的图像灰阶, I o I_o Io表示为原始图像灰阶。在冈萨雷斯的教材上,这个公式表示为: s = L − 1 − r s = L - 1 - r s=L1r,本质上是一样的。算法代码如果使用到 numpy 的独有特性,那么可以更简单的写成这样:

def image_negative(file: str):
    image = cv2.imread(file, cv2.IMREAD_GRAYSCALE)

    negative_img = 255 - image.flatten()
    negative_img = negative_img.reshape(image.shape)
    show_images(image, negative_img, "Negative Image")

对数变换(Log Transformation)

由于线性变化,正如其名字线性的,所以如果很多重要的数据都挤压在低密度数据区间时,直接进行线性变化也很难看出图像的特征,那么就通过对数的方式,不成比例的“放大”图像的细节。

I c = c ⋅ l o g ( 1 + I o ) I_c = c \cdot log(1 + I_o) Ic=clog(1+Io)

其中 c c c 是曲线放大系数, ( 1 + I o ) (1+I_o) (1+Io) 的目的是把 log 曲线向左移1个单位。
在这里插入图片描述
放大系数并无固定要求,通常根据自己实际工作的需要来调试确定。
在这里插入图片描述

def log_transform(file: str):
    image = cv2.imread(file, cv2.IMREAD_GRAYSCALE)

    log_trans = 10 * np.log(1 + image)
    log_trans = np.uint8(log_trans)
    show_images(image, log_trans, "Log Transform")

Gamma变换(Power-Law Transformation)

Gamma函数实际上就是幂函数,其公式为:
I c = c ⋅ I o γ I_c = c \cdot I{_o}^\gamma Ic=cIoγ
不同的gamma值,会输出不同的映射曲线,由于我们通常只使用其第一象限内的映射,且把 [ 0 , 255 ] [0,255] [0,255]的灰阶强度,归一化后控制在 [ 0 , 1 ] [0,1] [0,1]这个区间

在这里插入图片描述
所以实际上的转换效果是这样:

在这里插入图片描述
通过gamma变换,可以有效的纠正一张过曝或欠光的图片。从图中的曲线可以得知,如果要将一张欠光的照片调整到正常的样子,可以让 γ < 1 \gamma < 1 γ<1。而对于一张过曝的照片,则可以让 γ > 1 \gamma > 1 γ>1,从而获得满意的效果。

c c c 通常为放大、缩小倍数,一般情况下,我们令其为1。

对于欠光的图片:
在这里插入图片描述

而对于过曝照片:
在这里插入图片描述


def gamma_transform(file: str, c, gamma):
    image = cv2.imread(file, cv2.IMREAD_GRAYSCALE)

    # # normalization the pixels
    float_image = image / 255.0

    gamma_trans = c * np.power(float_image, gamma)
    gamma_trans = np.uint8(gamma_trans  * 255.0)

    show_images(image, gamma_trans , "Gamma Transform")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值