Python+OpenCV解决彩色图亮度不均衡问题

目录
  • 前言
  • 处理
    • 对比度拉伸
    • log变换
    • Gamma校正
    • 直方图均衡化
    • 对比度自适应直方图均衡化(CLAHE)
  • 处理结果展示
  • 附源码
    • opencv版本
    • skimage版本

前言

CSDN博客好久没有换过头像了,想换个新头像,在相册里面翻来翻去,然后就找到以前养的小宠物的一些照片,有一张特别有意思

惊恐到站起来的金丝熊:这家伙不会要吃我吧

没见过仓鼠的小猫:这啥玩意儿?

好,就决定把这张图当自己的头像了

一顿操作之后,把头像换成了这张照片

此时我:啥玩意儿?

。。。。感觉黑乎乎的,啥也看不清

这时候我想起来我学过图像处理,这用亮度变换搞一下不就可以了吗,搞起来!

注意:一般对灰度图进行亮度变换的多一点,但是我这张图是RGB图(准确来说是RGBA,但我们只取前三个通道),对于RGB图,我这里对其每个通道分别进行处理然后拼接处理

处理

对比度拉伸

也就是把图像重新缩放到指定的范围内

# 对比度拉伸
p1, p2 = np.percentile(img, (0, 70))  # numpy计算多维数组的任意百分比分位数
rescale_img = np.uint8((np.clip(img, p1, p2) - p1) / (p2 - p1) * 255)

其中,numpy的percentile函数可以计算多维数组的任意百分比分位数,因为我的图片中整体偏暗,我就把原图灰度值的0% ~ 70%缩放到0 ~255

log变换

使用以下公式进行映射:

# 对数变换
log_img = np.zeros_like(img)
scale, gain = 255, 1.5
for i in range(3):
    log_img[:, :, i] = np.log(img[:, :, i] / scale + 1) * scale * gain

Gamma校正

使用以下公式进行映射:

# gamma变换
gamma, gain, scale = 0.7, 1, 255
gamma_img = np.zeros_like(img)
for i in range(3):
    gamma_img[:, :, i] = ((img[:, :, i] / scale) ** gamma) * scale * gain

直方图均衡化

使用直方图均衡后的图像具有大致线性的累积分布函数,其优点是不需要参数。

其原理为,考虑这样一个图像,它的像素值被限制在某个特定的值范围内,即灰度范围不均匀。所以我们需要将其直方图缩放遍布整个灰度范围(如下图所示,来自维基百科),这就是直方图均衡化所做的(简单来说)。这通常会提高图像的对比度。

这里使用OpenCV来演示。

# 直方图均衡化
equa_img = np.zeros_like(img)
for i in range(3):
    equa_img[:, :, i] = cv.equalizeHist(img[:, :, i])

对比度自适应直方图均衡化(CLAHE)

这是一种自适应直方图均衡化方法

OpenCV提供了该方法。

# 对比度自适应直方图均衡化
clahe_img = np.zeros_like(img)
clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
for i in range(3):
    clahe_img[:, :, i] = clahe.apply(img[:, :, i])

处理结果展示

使用Matplotlib显示上述几种方法的结果:

可以看到,前四种方法效果都差不多,都有一个问题亮的地方过于亮,这是因为他们考虑的是全局对比度,而且因为我们使用的彩色图像原因,使用log变换的结果图中有部分区域色彩失真。最后一种CLAHE方法考虑的是局部对比度,所以效果会好一点。

因为图像是彩色的,这里我只绘制了R通道的直方图(红色线)及其累积分布函数(黑色线)

可以看到均衡后的图像具有大致线性的累积分布函数。

总之,经过以上的探索,我最终决定使用CLAHE均衡后的结果

感觉是比之前的好了点

附源码

opencv版本

import cv2.cv2 as cv
import matplotlib.pyplot as plt
import numpy as np

def plot_img_and_hist(image, axes, bins=256):
    """Plot an image along with its histogram and cumulative histogram.

    """
    ax_img, ax_hist = axes
    ax_cdf = ax_hist.twinx()

    # Display image
    ax_img.imshow(image, cmap=plt.cm.gray)
    ax_img.set_axis_off()

    # Display histogram
    colors = ['red', 'green', 'blue']
    for i in range(1):
        ax_hist.hist(image[:, :, i].ravel(), bins=bins, histtype='step', color=colors[i])

    ax_hist.ticklabel_format(axis='y', style='scientific', scilimits=(0, 0))

    ax_hist.set_xlabel('Pixel intensity')
    ax_hist.set_xlim(0, 255)    # 这里范围为0~255 如果使用img_as_float,则这里为0~1
    ax_hist.set_yticks([])

    # Display cumulative distribution
    for i in range(1):
        hist, bins = np.histogram(image[:, :, i].flatten(), 256, [0, 256])
        cdf = hist.cumsum()
        cdf = cdf * float(hist.max()) / cdf.max()
        ax_cdf.plot(bins[1:], cdf, 'k')
    ax_cdf.set_yticks([])

    return ax_img, ax_hist, ax_cdf

def plot_all(images, titles, cols):
    """
    输入titles、images、以及每一行多少列,自动计算行数、并绘制图像和其直方图
    :param images:
    :param titles:
    :param cols: 每一行多少列
    :return:
    """
    fig = plt.figure(figsize=(12, 8))
    img_num = len(images)  # 图片的个数
    rows = int(np.ceil(img_num / cols) * 2)  # 上图下直方图 所以一共显示img_num*2个子图
    axes = np.zeros((rows, cols), dtype=object)
    axes = axes.ravel()
    axes[0] = fig.add_subplot(rows, cols, 1)  # 先定义第一个img 单独拿出来定义它是为了下面的sharex
    # 开始创建所有的子窗口
    for i in range(1, img_num):  #
        axes[i + i // cols * cols] = fig.add_subplot(rows, cols, i + i // cols * cols + 1, sharex=axes[0],
                                                     sharey=axes[0])
    for i in range(0, img_num):
        axes[i + i // cols * cols + cols] = fig.add_subplot(rows, cols, i + i // cols * cols + cols + 1)

    for i in range(0, img_num):  # 这里从1开始,因为第一个在上面已经绘制过了
        ax_img, ax_hist, ax_cdf = plot_img_and_hist(images[i],
                                                    (axes[i + i // cols * cols], axes[i + i // cols * cols + cols]))
        ax_img.set_title(titles[i])
        y_min, y_max = ax_hist.get_ylim()
        ax_hist.set_ylabel('Number of pixels')
        ax_hist.set_yticks(np.linspace(0, y_max, 5))

        ax_cdf.set_ylabel('Fraction of total intensity')
        ax_cdf.set_yticks(np.linspace(0, 1, 5))

    # prevent overlap of y-axis labels
    fig.tight_layout()
    plt.show()
    plt.close(fig)

if __name__ == '__main__':
    img = cv.imread('catandmouse.png', cv.IMREAD_UNCHANGED)[:, :, :3]
    img = cv.cvtColor(img, cv.COLOR_BGR2RGB)

    gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    # 对比度拉伸
    p1, p2 = np.percentile(img, (0, 70))  # numpy计算多维数组的任意百分比分位数
    rescale_img = np.uint8((np.clip(img, p1, p2) - p1) / (p2 - p1) * 255)

    # 对数变换
    log_img = np.zeros_like(img)
    scale, gain = 255, 1.5
    for i in range(3):
        log_img[:, :, i] = np.log(img[:, :, i] / scale + 1) * scale * gain

    # gamma变换
    gamma, gain, scale = 0.7, 1, 255
    gamma_img = np.zeros_like(img)
    for i in range(3):
        gamma_img[:, :, i] = ((img[:, :, i] / scale) ** gamma) * scale * gain

    # 彩色图直方图均衡化
    # 直方图均衡化
    equa_img = np.zeros_like(img)
    for i in range(3):
        equa_img[:, :, i] = cv.equalizeHist(img[:, :, i])
    # 对比度自适应直方图均衡化
    clahe_img = np.zeros_like(img)
    clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
    for i in range(3):
        clahe_img[:, :, i] = clahe.apply(img[:, :, i])

    titles = ['img', 'rescale', 'log', 'gamma', 'equalizeHist', 'CLAHE']
    images = [img, rescale_img, log_img, gamma_img, equa_img, clahe_img]
    plot_all(images, titles, 3)

skimage版本

from skimage import exposure, util, io, color, filters, morphology
import matplotlib.pyplot as plt
import numpy as np

def plot_img_and_hist(image, axes, bins=256):
    """Plot an image along with its histogram and cumulative histogram.

    """
    image = util.img_as_float(image)
    ax_img, ax_hist = axes
    ax_cdf = ax_hist.twinx()

    # Display image
    ax_img.imshow(image, cmap=plt.cm.gray)
    ax_img.set_axis_off()

    # Display histogram
    colors = ['red', 'green', 'blue']
    for i in range(1):
        ax_hist.hist(image[:, :, i].ravel(), bins=bins, histtype='step', color=colors[i])

    ax_hist.ticklabel_format(axis='y', style='scientific', scilimits=(0, 0))
    ax_hist.set_xlabel('Pixel intensity')
    ax_hist.set_xlim(0, 1)
    ax_hist.set_yticks([])

    # Display cumulative distribution
    for i in range(1):
        img_cdf, bins = exposure.cumulative_distribution(image[:, :, i], bins)
        ax_cdf.plot(bins, img_cdf, 'k')
    ax_cdf.set_yticks([])

    return ax_img, ax_hist, ax_cdf

def plot_all(images, titles, cols):
    """
    输入titles、images、以及每一行多少列,自动计算行数、并绘制图像和其直方图
    :param images:
    :param titles:
    :param cols: 每一行多少列
    :return:
    """
    fig = plt.figure(figsize=(12, 8))
    img_num = len(images)  # 图片的个数
    rows = int(np.ceil(img_num / cols) * 2)  # 上图下直方图 所以一共显示img_num*2个子图
    axes = np.zeros((rows, cols), dtype=object)
    axes = axes.ravel()
    axes[0] = fig.add_subplot(rows, cols, 1)  # 先定义第一个img 单独拿出来定义它是为了下面的sharex
    # 开始创建所有的子窗口
    for i in range(1, img_num):  #
        axes[i + i // cols * cols] = fig.add_subplot(rows, cols, i + i // cols * cols + 1, sharex=axes[0],
                                                     sharey=axes[0])
    for i in range(0, img_num):
        axes[i + i // cols * cols + cols] = fig.add_subplot(rows, cols, i + i // cols * cols + cols + 1)

    for i in range(0, img_num):  # 这里从1开始,因为第一个在上面已经绘制过了
        ax_img, ax_hist, ax_cdf = plot_img_and_hist(images[i],
                                                    (axes[i + i // cols * cols], axes[i + i // cols * cols + cols]))
        ax_img.set_title(titles[i])
        y_min, y_max = ax_hist.get_ylim()
        ax_hist.set_ylabel('Number of pixels')
        ax_hist.set_yticks(np.linspace(0, y_max, 5))

        ax_cdf.set_ylabel('Fraction of total intensity')
        ax_cdf.set_yticks(np.linspace(0, 1, 5))

    # prevent overlap of y-axis labels
    fig.tight_layout()
    plt.show()
    plt.close(fig)

if __name__ == '__main__':
    img = io.imread('catandmouse.png')[:, :, :3]

    gray = color.rgb2gray(img)
    # 对比度拉伸
    p1, p2 = np.percentile(img, (0, 70))  # numpy计算多维数组的任意百分比分位数
    rescale_img = exposure.rescale_intensity(img, in_range=(p1, p2))

    # 对数变换
    # img = util.img_as_float(img)
    log_img = np.zeros_like(img)
    for i in range(3):
        log_img[:, :, i] = exposure.adjust_log(img[:, :, i], 1.2, False)

    # gamma变换
    gamma_img = np.zeros_like(img)
    for i in range(3):
        gamma_img[:, :, i] = exposure.adjust_gamma(img[:, :, i], 0.7, 2)

    # 彩色图直方图均衡化
    equa_img = np.zeros_like(img, dtype=np.float64)  # 注意直方图均衡化输出值为float类型的
    for i in range(3):
        equa_img[:, :, i] = exposure.equalize_hist(img[:, :, i])

    # 对比度自适应直方图均衡化
    clahe_img = np.zeros_like(img, dtype=np.float64)
    for i in range(3):
        clahe_img[:, :, i] = exposure.equalize_adapthist(img[:, :, i])

    # 局部直方图均衡化 效果不好就不放了
    selem = morphology.rectangle(50, 50)
    loc_img = np.zeros_like(img)
    for i in range(3):
        loc_img[:, :, i] = filters.rank.equalize(util.img_as_ubyte(img[:, :, i]), footprint=selem)

    # Display results
    titles = ['img', 'rescale', 'log', 'gamma', 'equalizeHist', 'CLAHE']
    images = [img, rescale_img, log_img, gamma_img, equa_img, clahe_img]

    plot_all(images, titles, 3)

以上就是Python+OpenCV解决彩色图亮度不均衡问题的详细内容,更多关于Python OpenCV彩色图亮度不均衡的资料请关注我们其它相关文章!

(0)

相关推荐

  • Python实现PS图像明亮度调整效果示例

    本文实例讲述了Python实现PS图像明亮度调整效果.分享给大家供大家参考,具体如下: 这里用 Python 实现 PS 图像调整中的明度调整: 我们知道,一般的非线性RGB亮度调整只是在原有R.G.B值基础上增加和减少一定量来实现的,而PS的明度调整原理还得从前面那个公式上去找.我们将正向明度调整公式: RGB = RGB + (255 - RGB) * value / 255 转换为 RGB = (RGB * (255 - value) + 255 * value) / 255, 如果val

  • Python实现 PS 图像调整中的亮度调整

    本文用 Python 实现 PS 图像调整中的亮度调整,具体的算法原理和效果可以参考之前的博客: https://www.jb51.net/article/164191.htm import matplotlib.pyplot as plt from skimage import io file_name='D:/Image Processing/PS Algorithm/4.jpg'; img=io.imread(file_name) Increment = -10.0 img = img *

  • python 调整图片亮度的示例

    实现效果 实现代码 import matplotlib.pyplot as plt from skimage import io file_name='D:/2020121173119242.png' img=io.imread(file_name) Increment = -10.0 img = img * 1.0 I = (img[:, :, 0] + img[:, :, 1] + img[:, :, 2])/3.0 + 0.001 mask_1 = I > 128.0 r = img [:

  • python中PS 图像调整算法原理之亮度调整

    亮度调整 非线性亮度调整: 对于R,G,B三个通道,每个通道增加相同的增量. 线性亮度调整: 利用HSL颜色空间,通过只对其L(亮度)部分调整,可达到图像亮度的线性调整.但是,RGB和HSL颜色空间的转换很繁琐,一般还需要浮点数的运算,不仅增加了代码的复杂度,更重要的是要逐点将RGB转换为HSL,然后确定新的L值,再将HSL转换为RGB,运行速度可想而知是很慢的.要想提高图像亮度线性调整的速度,应该从三方面考虑,一是变浮点运算为整数运算,二是只提取HSL的L部分进行调整,三是采用汇编代码,在De

  • Python+OpenCV解决彩色图亮度不均衡问题

    目录 前言 处理 对比度拉伸 log变换 Gamma校正 直方图均衡化 对比度自适应直方图均衡化(CLAHE) 处理结果展示 附源码 opencv版本 skimage版本 前言 CSDN博客好久没有换过头像了,想换个新头像,在相册里面翻来翻去,然后就找到以前养的小宠物的一些照片,有一张特别有意思 惊恐到站起来的金丝熊:这家伙不会要吃我吧 没见过仓鼠的小猫:这啥玩意儿? 好,就决定把这张图当自己的头像了 一顿操作之后,把头像换成了这张照片 此时我:啥玩意儿? ....感觉黑乎乎的,啥也看不清 这时

  • python opencv实现灰度图和彩色图的互相转换

    目录 opencv灰度图和彩色图互相转换 注意: 附:python将灰度图转换为RGB彩色图 总结 opencv灰度图和彩色图互相转换 如果摄像头本来就得到3维度红外图那就不用处理直接可以用: import cv2 cap = cv2.VideoCapture(0) ret, image_np = cap.read() 直接转成单通道的灰度图看看能不能用: #如果后面不写0,那就是默认彩色的 # 第一种方式 image = cv2.imread('***/timg4.jpg',0) #第二种方式

  • python opencv将多个图放在一个窗口的实例详解

    编程需求:使用python+opencv对图像进行处理,把两张图放在一起显示 方法:使用numpy模块中的水平堆叠或竖直堆叠完成所有图像的堆叠,最后当成一整张图显示. 注意:把不同的尺寸(宽和高)的图片或不同的颜色通道在一起来展示,会发生黑屏,所以要么把两张图的高度设置相同,要么宽度相同.这样才能正常显示. 举个栗子: 1.只设置两张图的高相同,拼接两图,显示:只设置两张图的宽相同,拼接两图,显示. import cv2 import numpy as np #读图1和图2 img1 = cv2

  • python实现彩色图转换成灰度图

    本文实例为大家分享了python实现彩色图转换成灰度图的具体代码,供大家参考,具体内容如下 from PIL import Image import os # 图像组成:红绿蓝 (RGB)三原色组成 亮度(255,255,255) image = "Annie1.jpg" img = Image.open(image) img_all = "素描" + image new = Image.new("L", img.size, 255) width

  • Python OpenCV 彩色与灰度图像的转换实现

    彩色图像转换为灰度图像 第一种方式通过 imread 读取图像的时候直接设置参数为 0 ,自动转换彩色图像为灰度图像 第二种方式,可以通过 split 进行通道分离,或者叫做读取单个通道,也可以将一个彩色图像分离成 3 个单通道的灰度图像 今天要学习的方法,是通过一个叫做 cvtColor 的方法实现该操作. cv2.cvtColor() 方法用于将图像从一种颜色空间转换为另一种颜色空间. OpenCV 提供了 150 多种 color-space 转换方法.多到用不过来~ 该方法的语法格式为:

  • python+opencv+selenium自动化登录邮箱并解决滑动验证的问题

    前言 大家做自动化登录时可能都遇到过滑块验证码需要手动验证的问题,这次我们就来解决他 如下:    在我们做自动化登录时,总会遇到各种奇奇怪怪的验证码,滑块验证码就是其中最常见的一种.若我们的程序自动输入账号密码之后,还需要我们人工去滑动验证码那还能称得上是自动化吗? 那么先给大家说一下我的'解题步骤'. 1.使用selenium打开邮箱首页. 2.定位到账号密码框,键入账号密码. 3.获取验证图片,使用opencv处理返回滑块应拖动的距离. 4.创建鼠标事件,模拟拖动滑块完成验证.   需要解

  • Opencv中cv2.cvtColor彩色图转灰度图的其他6种方法

    目录 1.公式集成: 2.代码实现: 3.实验结果: 4.参考文章: 1.公式集成: 2.代码实现: import os import cv2 import queue import threading import numpy as np #用户存取函数的返回值 q=queue.Queue() def rgb2gray(image,method): h,w,c=image.shape gray=np.zeros((h,w),dtype=np.uint8) y=0 for row in rang

  • python opencv将图片转为灰度图的方法示例

    使用opencv将图片转为灰度图主要有两种方法,第一种是将彩色图转为灰度图,第二种是在使用OpenCV读取图片的时候直接读取为灰度图. 将彩色图转为灰度图 import cv2 import numpy as np if __name__ == "__main__": img_path = "timg.jpg" img = cv2.imread(img_path) #获取图片的宽和高 width,height = img.shape[:2][::-1] #将图片缩小

  • 解决python opencv无法显示图片的问题

    结合网上解决方法,总结了一下 注意三点: 1.文件名或路径名开头如果会引起转义,则\要替换为\\ 2.文件不能放在桌面,因为读取时按中文路径 3.运行后未响应,原因还没有查明,在下一行 cv.waitKey(0)解决 import cv2 as cv img = cv.imread("D:\\python_file\ae.jpg") cv.imshow("image",img) #cv.waitKey(0) 以上这篇解决python opencv无法显示图片的问题就

  • python matplotlib画盒图、子图解决坐标轴标签重叠的问题

    在使用matplotlib画图的时候将常会出现坐标轴的标签太长而出现重叠的现象,本文主要通过自身测过好用的解决办法进行展示,希望也能帮到大家,原图出现重叠现象例如图1: 代码为: data1=[[0.3765,0.3765,0.3765,0.3765,0.3765],[0.3765,0.3765,0.3765,0.3765,0.3765],[0.3765,0.3765,0.3765,0.3765,0.3765],[0.3765,0.3765,0.3765,0.3765,0.3765]] data

随机推荐