图像处理-RGB域图像对比度增强方法(python)

对比度概念

对比度指的是一幅图像中明暗区域最亮的白和最暗的黑之间不同亮度层级的测量,差异范围越大代表对比越大,差异范围越小代表对比越小。

对比度越低的图像,图像越发灰。例如下图。

传统方法与代码

传统的对比度增强方法可分为两大类:基于直方图统计方法与基于像素点的方法

基于直方图:直方图均衡化(HE)、局部直方图均衡化(AHE)、限制对比度的局部直方图均衡化(CLAHE)

基于像素点:线性拉伸、GAMMA、ACE、PS对比度增强

HE

在这里插入图片描述

推理过程:参考以下链接https://blog.csdn.net/weixin_45930877/article/details/119581282

from headers import *

def historgram(config):
    for image in config.image_paths:
        name = image.split('/')[-1][:-4]
        print(name)
        image = cv2.imread(image)
        new_image = np.zeros_like(image)
        if image.shape[-1] == 3:
            new_image[:,:,0] = cv2.equalizeHist(image[:,:,0])
            new_image[:,:,1] = cv2.equalizeHist(image[:,:,1])
            new_image[:,:,2] = cv2.equalizeHist(image[:,:,2])
        else:
            new_image = cv2.equalizeHist(image)
        cv2.imwrite(config.target_dir + name + '_hist.jpg', new_image)

if __name__ == '__main__':
    image_paths = [
        './test.jpg'
    ]
    target_dir = './equzalizeHist/'
    os.makedirs(target_dir, exist_ok=True)
    parser = argparse.ArgumentParser()
    parser.add_argument('--image_paths', type=str, default=image_paths)
    parser.add_argument('--target_dir', type=str, default=target_dir)
    config = parser.parse_args()
    historgram(config)

在这里插入图片描述

AHE

在这里插入图片描述
在这里插入图片描述

from headers import *

def equalize_adapthist(config):
    for image in config.image_paths:
        name = image.split('/')[-1][:-4]
        print(name)
        image = cv2.imread(image)
        image = image/255
        new_image = np.zeros_like(image)
        if image.shape[-1] == 3:
            new_image[:,:,0] = exposure.equalize_adapthist(image[:,:,0])
            new_image[:,:,1] = exposure.equalize_adapthist(image[:,:,1])
            new_image[:,:,2] = exposure.equalize_adapthist(image[:,:,2])
        else:
            new_image = exposure.equalize_adapthist(image)
        new_image = np.uint8(np.clip(new_image*255., 0, 255))
        cv2.imwrite(config.target_dir + name + '_adphist.jpg', new_image)

if __name__ == '__main__':
    image_paths = [
         './test.jpg'
    ]
    target_dir = './equalize_adapthist/'
    os.makedirs(target_dir, exist_ok=True)
    parser = argparse.ArgumentParser()
    parser.add_argument('--image_paths', type=str, default=image_paths)
    parser.add_argument('--target_dir', type=str, default=target_dir)
    config = parser.parse_args()
    equalize_adapthist(config)

CLAHE

在这里插入图片描述

实现步骤:参考以下链接
http://www.skcircle.com/?id=1762

from headers import *

def clahe(config):
    clipLimit = 2.5
    tileGridSize = (8,8)
    for image in config.image_paths:
        name = image.split('/')[-1][:-4]
        print(name)
        image = cv2.imread(image)
        new_image = np.zeros_like(image)
        clahe_trans = cv2.createCLAHE(clipLimit, tileGridSize)
        if image.shape[-1] == 3:
            new_image[:,:,0] = clahe_trans.apply(image[:,:,0])
            new_image[:,:,1] = clahe_trans.apply(image[:,:,1])
            new_image[:,:,2] = clahe_trans.apply(image[:,:,2])
        else:
            new_image = clahe_trans.apply(image)
        new_image = np.uint8(np.clip(new_image, 0, 255))
        cv2.imwrite(config.target_dir + name + '_clahe_' + str(clipLimit) + '_' + str(tileGridSize[0]) + '.jpg', new_image)

if __name__ == '__main__':
    image_paths = [
        './test.jpg'
    ]
    target_dir = './clahe/'
    os.makedirs(target_dir, exist_ok=True)
    parser = argparse.ArgumentParser()
    parser.add_argument('--image_paths', type=str, default=image_paths)
    parser.add_argument('--target_dir', type=str, default=target_dir)
    config = parser.parse_args()
    clahe(config)

1_1.5

在这里插入图片描述2_1.5
在这里插入图片描述4_1.5

在这里插入图片描述8_1.5

线性拉伸

本质:归一化,然后放缩到指定大小范围。有多种计算方式,直接线性拉升、裁剪线性拉伸、分段式拉伸。

from headers import *

def linear(config):
    maxout = 250
    minout = 20
    for image in config.image_paths:
        name = image.split('/')[-1][:-4]
        print(name)
        image = cv2.imread(image)
        new_image = np.zeros_like(image)
        if image.shape[-1] == 3:
            for c in range(3):
                tmp = (image[:,:,c] - image[:,:,c].min()) / (image[:,:,c].max() - image[:,:,c].min())
                tmp_n = tmp * (maxout-minout)
                new_image[:,:,c] = tmp_n
        else:
            tmp = (image - image.min()) / (image.max() - image.min())
            new_image = tmp * (maxout-minout)
        new_image = np.uint8(new_image)
        cv2.imwrite(config.target_dir + name + '_linear.jpg', new_image)

def linear_crop(config):
    a = 2.5
    b = 99
    for image in config.image_paths:
        name = image.split('/')[-1][:-4]
        print(name)
        image = cv2.imread(image)
        a, b = np.percentile(image, (a, b))
        c = a - 0.1 * (b - a)
        d = b + 0.5 * (b - a)
        new_image = (image - c) / (d - c) * 255
        new_image = np.clip(new_image, 0, 255)
        new_image = np.uint8(new_image)
        cv2.imwrite(config.target_dir + name + '_linear_crop.jpg', new_image)

def linear_segmentation(config):
    for image in config.image_paths:
        name = image.split('/')[-1][:-4]
        print(name)
        image = cv2.imread(image)

        r1, s1 = 80, 10
        r2, s2 = 140, 200
        k1 = s1 / r1
        k2 = (s2 - s1) / (r2 - r1)
        k3 = (255 - s2) / (255 - r2)

        new_image = np.zeros_like(image)
        if image.shape[-1] == 3:
            for c in range(3):
                h, w = image.shape[:2]
                for i in range(h):
                    for j in range(w):
                        if image[i,j,c] < r1:
                            new_image[i,j,c] = k1 * image[i,j,c]
                        elif r1 <= image[i,j,c] <= r2:
                            new_image[i,j,c] = k2 * (image[i,j,c] - r1) + s1
                        else:
                            new_image[i,j,c] = k3 * (image[i,j,c] - r2) + s2
        else:
            for i in range(h):
                for j in range(w):
                    if image[i,j] < r1:
                        new_image[i,j] = k1 * image[i,j]
                    elif r1 <= image[i,j] <= r2:
                        new_image[i,j] = k2 * (image[i,j] - r1) + s1
                    else:
                        new_image[i,j] = k3 * (image[i,j] - r2) + s2
        new_image = np.clip(new_image, 0, 255)
        new_image = np.uint8(new_image)
        cv2.imwrite(config.target_dir + name + '_linear_segmentation.jpg', new_image)

if __name__ == '__main__':
    image_paths = [
        './test.jpg'
    ]
    target_dir = './linear/'
    os.makedirs(target_dir, exist_ok=True)
    parser = argparse.ArgumentParser()
    parser.add_argument('--image_paths', type=str, default=image_paths)
    parser.add_argument('--target_dir', type=str, default=target_dir)
    config = parser.parse_args()
    linear(config)
    linear_crop(config)
    linear_segmentation(config)

在这里插入图片描述(50, 20),(140,200)
在这里插入图片描述(80, 20),(140,200)

GAMMA

普通的Gamma曲线是一条向上凸的指数曲线。
一般的理解,认为Gamma校正能够提高亮度,其实这样理解并不完全对。也就是说Gamma校正并不能提高图像的整体亮度。它的作用就是将Sensor送过来的原始数据进行非线性调整为另外一组数据。因为在低值部分的Gamma曲线的斜率超过1,那么校正后的数据,在低值部分的差距会被拉大,而在高值部分的斜率小于1,那么高值部分的数据之间的差距会变小。
这样的图像数据,处于低值的细节部分会被提升的更明显,而高值部分的由于人眼感觉本来就不会很明显,因此也不会造成图像细节的损失。
通。
对比度的调整在一定程度上说,其实也就是对gamma曲线的调整,增大对比度就是提Gamma 值。对于图像处理来说,也有在硬件 gamma 校正后,单独由软件再进行一次类的幂函数变换来调整对比度。
(在整理资料的时候学习了一个优化的gamma,针对原本饱和度对比度还可以的图像增强效果还不错)

from headers import *

def gamma(config):

    for image in config.image_paths:
        name = image.split('/')[-1][:-4]
        print(name)
        image = cv2.imread(image)
        new_image = np.zeros_like(image)
        g_v = 2.0
        def gamma_trans_auto(img):
            mean = np.mean(img)
            g_v = math.log10(0.5)/math.log10(mean/255)
            table = [np.power(x / 255., g_v) * 255 for x in range(256)]
            table = np.round(np.array(table)).astype(np.uint8)
            new_img = cv2.LUT(img, table)
            return new_img

        def gamma_trans(img, g_v):
            img = img / 255
            new_img = exposure.adjust_gamma(img, g_v)
            new_img = np.uint8(np.clip(new_img * 255, 0, 255))
            return new_img

        if image.shape[-1] == 3:
            new_image[:,:,0] = gamma_trans(image[:,:,0], g_v)
            new_image[:,:,1] = gamma_trans(image[:,:,1], g_v)
            new_image[:,:,2] = gamma_trans(image[:,:,2], g_v)
        else:
            new_image = gamma_trans(image)
        new_image = np.uint8(np.clip(new_image, 0, 255))
        cv2.imwrite(config.target_dir + name + '_gamma_'  + str(g_v) + '.jpg', new_image)

if __name__ == '__main__':
    image_paths = [
        './test.jpg'
    ]
    target_dir = './gamma/'
    os.makedirs(target_dir, exist_ok=True)
    parser = argparse.ArgumentParser()
    parser.add_argument('--image_paths', type=str, default=image_paths)
    parser.add_argument('--target_dir', type=str, default=target_dir)
    config = parser.parse_args()
    gamma(config)

在这里插入图片描述1.2
在这里插入图片描述0.7

ACE

在这里插入图片描述

from headers import *

def getVarianceMean(scr, winSize):
    if scr is None or winSize is None:
        print("The input parameters of getVarianceMean Function error")
        return -1

    if winSize % 2 == 0:
        print("The window size should be singular")
        return -1

    copyBorder_map = cv2.copyMakeBorder(scr, winSize // 2, winSize // 2, winSize // 2, winSize // 2,
                                        cv2.BORDER_REPLICATE)
    shape = np.shape(scr)

    local_mean = np.zeros_like(scr)
    local_std = np.zeros_like(scr)

    for i in range(shape[0]):
        for j in range(shape[1]):
            temp = copyBorder_map[i:i + winSize, j:j + winSize]
            local_mean[i, j], local_std[i, j] = cv2.meanStdDev(temp)
            if local_std[i, j] <= 0:
                local_std[i, j] = 1e-8

    return local_mean, local_std

def adaptContrastEnhancement(scr, winSize, maxCg):
    if scr is None or winSize is None or maxCg is None:
        print("The input parameters of ACE Function error")
        return -1

    YUV_img = cv2.cvtColor(scr, cv2.COLOR_BGR2YUV)  ##转换通道
    Y_Channel = YUV_img[:, :, 0]
    shape = np.shape(Y_Channel)

    meansGlobal = cv2.mean(Y_Channel)[0]
    localMean_map, localStd_map = getVarianceMean(Y_Channel, winSize)

    for i in range(shape[0]):
        for j in range(shape[1]):

            cg = 0.2 * meansGlobal / localStd_map[i, j];
            if cg > maxCg:
                cg = maxCg
            elif cg < 1:
                cg = 1

            temp = Y_Channel[i, j].astype(float)
            temp = max(0, min(localMean_map[i, j] + cg * (temp - localMean_map[i, j]), 255))

            #            Y_Channel[i,j]=max(0,min(localMean_map[i,j]+cg*(Y_Channel[i,j]-localMean_map[i,j]),255))
            Y_Channel[i, j] = temp

    YUV_img[:, :, 0] = Y_Channel

    dst = cv2.cvtColor(YUV_img, cv2.COLOR_YUV2BGR)

    return dst

def ace(config):
    winSize = 15
    maxCg = 10
    for image in config.image_paths:
        name = image.split('/')[-1][:-4]
        print(name)
        image = cv2.imread(image)
        new_image = adaptContrastEnhancement(image, winSize, maxCg)
        new_image = np.uint8(np.clip(new_image, 0, 255))
        cv2.imwrite(config.target_dir + 'ace_'+ str(winSize) + '_' + str(maxCg) + '.jpg', new_image)

if __name__ == '__main__':
    image_paths = [
         './test.jpg'
    ]
    target_dir = './ace/'
    os.makedirs(target_dir, exist_ok=True)
    parser = argparse.ArgumentParser()
    parser.add_argument('--image_paths', type=str, default=image_paths)
    parser.add_argument('--target_dir', type=str, default=target_dir)
    config = parser.parse_args()
    ace(config)

在这里插入图片描述ws=15,mg=15
在这里插入图片描述ws=5,mg=5

PS对比度增强

nRGB = RGB + (RGB - Threshold) * Contrast / 255
公式中,nRGB表示图像像素新的R、G、B分量,RGB表示图像像素R、G、B分量,Threshold为给定的阈值,Contrast为处理过的对比度增量。

Photoshop对于对比度增量,是按给定值的正负分别处理的:

当增量等于-255时,是图像对比度的下端极限,此时,图像RGB各分量都等于阈值,图像呈全灰色,灰度图上只有1条线,即阈值灰度;

当增量大于-255且小于0时,直接用上面的公式计算图像像素各分量;

当增量等于255时,是图像对比度的上端极限,实际等于设置图像阈值

from headers import *

def ps_contrast(config):
    contrast = 125
    for image_path in config.image_paths:
        image = cv2.imread(image_path)
        img_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        name = image_path.split('/')[-1][:-4]
        print(name)
        img = img_rgb * 1.0
        thre = np.mean(img)
        # thre = np.full(img.shape, thre)

        img_out = img * 1.0
        if contrast <= -255.0:
            img_out = (img_out >= 0) + thre - 1
        elif contrast > -255.0 and contrast < 0:
            a = (img - thre)
            b = a * contrast / 255.0
            img_out = img + b
        elif contrast < 255.0 and contrast > 0:
            new_con = 255.0 * 255.0 / (256.0 - contrast) - 255.0
            img_out = img + (img - thre) * new_con / 255.0
        else:
            mask_1 = img > thre
            img_out = mask_1 * 255.0

        img_out = img_out / 255.0

        mask_1 = img_out < 0
        mask_2 = img_out > 1

        img_out = img_out * (1 - mask_1)
        img_out = img_out * (1 - mask_2) + mask_2

        img_out = np.uint8(np.clip(img_out*255, 0, 255))
        img_out = cv2.cvtColor(img_out, cv2.COLOR_RGB2BGR)

        cv2.imwrite(config.target_dir +  name + '_' + str(contrast) + '.jpg', img_out)

if __name__ == '__main__':
    image_paths = [
         './test.jpg'
    ]
    target_dir = './ps_contrast/'
    os.makedirs(target_dir, exist_ok=True)
    parser = argparse.ArgumentParser()
    parser.add_argument('--image_paths', type=str, default=image_paths)
    parser.add_argument('--target_dir', type=str, default=target_dir)
    config = parser.parse_args()
    ps_contrast(config)

在这里插入图片描述20
在这里插入图片描述50
100100

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值