用OpenCV图像处理技巧之巧用直方图

news2024/11/17 6:30:50

1. 引言

欢迎回到我的Python图像处理系列!在这一节中,我们将更深入地研究图像分析领域中图像直方图的应用,事实上通过对直方图进行相应操作,我们可以来调整图像的对比度和亮度,这可以极大地改善图像的视觉效果。

闲话少说,我们直接开始吧!

2. 前置条件

一般来说,通过合理利用一些直方图的技巧,可以用于提高低光照拍摄图像中物体的可见性,改善图像的细节,以及校正曝光过度或曝光不足的图像。

在介绍了枯燥的理论知识后,我们来看个具体的例子。首先还是导入我们所需要的基础库,代码如下:

import numpy as np
import matplotlib.pyplot as plt
from skimage.color import rgb2gray
from skimage.exposure import histogram, cumulative_distribution
from skimage import filters
from skimage.color import rgb2hsv, rgb2gray, rgb2yuv
from skimage import color, exposure, transform
from skimage.exposure import histogram, cumulative_distribution

接着使用以下代码加载我们的测试用例,如下:

# Load the image & remove the alpha or opacity channel (transparency) 
dark_image = imread('plasma_ball.png')[:,:,:3]

# Visualize the image
plt.figure(figsize=(10, 10))
plt.title('Original Image: Plasma Ball')
plt.imshow(dark_image)
plt.show()

显示结果如下:
在这里插入图片描述
可以看到,上述图像是在光照不足下进行拍摄的,接着我们就来通过控制直方图,来改善我们的视觉效果。

3. 统计数据分析

接着我们使用以下代码,进行相应的统计数据分析,主要是计算三个颜色通道下不同百分位下的亮度均值和方差,代码如下:

def calc_color_overcast(image):
    # Calculate color overcast for each channel
    red_channel = image[:, :, 0]
    green_channel = image[:, :, 1]
    blue_channel = image[:, :, 2]

    # Create a dataframe to store the results
    channel_stats = pd.DataFrame(columns=['Mean', 'Std', 'Min', 'Median', 
                                          'P_80', 'P_90', 'P_99', 'Max'])

    # Compute and store the statistics for each color channel
    for channel, name in zip([red_channel, green_channel, blue_channel], 
                             ['Red', 'Green', 'Blue']):
        mean = np.mean(channel)
        std = np.std(channel)
        minimum = np.min(channel)
        median = np.median(channel)
        p_80 = np.percentile(channel, 80)
        p_90 = np.percentile(channel, 90)
        p_99 = np.percentile(channel, 99)
        maximum = np.max(channel)

        channel_stats.loc[name] = [mean, std, minimum, median, p_80, p_90, p_99, maximum]

    return channel_stats

调用上述函数后,得到结果如下:
在这里插入图片描述

进而我们可以使用以下代码,来生成上述图像的直方图分布:

# Histogram plot
dark_image_intensity = img_as_ubyte(rgb2gray(dark_image))
freq, bins = histogram(dark_image_intensity)
plt.step(bins, freq*1.0/freq.sum())
plt.xlabel('intensity value')
plt.ylabel('fraction of pixels');

得到直方图可视化效果如下:
在这里插入图片描述
通过直方图可以看到,像素的平均强度似乎极低,这证实了图像的黑暗和曝光不足。直方图显示大多数像素具有低强度值,这是有道理的,因为低像素强度值意味着图像中的大多数像素非常暗或呈现黑色。

4. 直方图均衡化原理

我们可以将各种直方图处理技术应用于图像以提高其对比度。首先我们来介绍直方图均衡化,直方图均衡化是一种重新调整图像中像素强度分布以使直方图更加均匀的技术。不均匀的像素强度分布可能导致图像的对比度和细节较低,从而难以区分图像中的对象或特征。通过使像素强度分布更加均匀,提高了图像的对比度,使其更容易感知细节和特征。

实现直方图均衡化最为常用的一种方法是使图像的累积分布函数(CDF)线性化。这是因为线性CDF意味着每个像素强度值在图像中出现的可能性相等。另一方面,非线性CDF意味着某些像素强度值比其他像素强度值更频繁地出现,导致像素强度分布不均匀。通过使CDF线性化,可以使像素强度分布更加均匀,提高图像对比度。

通过以下代码实现CDF线性化:

def plot_cdf(image):
    """
    Plot the cumulative distribution function of an image.
    
    Parameters:
    image (ndarray): Input image.
    """
    
    # Convert the image to grayscale if needed
    if len(image.shape) == 3:
        image = rgb2gray(image[:,:,:3])
    
    # Compute the cumulative distribution function
    intensity = np.round(image * 255).astype(np.uint8)
    freq, bins = cumulative_distribution(intensity)
    
    # Plot the actual and target CDFs
    target_bins = np.arange(256)
    target_freq = np.linspace(0, 1, len(target_bins))
    plt.step(bins, freq, c='b', label='Actual CDF')
    plt.plot(target_bins, target_freq, c='r', label='Target CDF')
    
    # Plot an example lookup
    example_intensity = 50
    example_target = np.interp(freq[example_intensity], target_freq, target_bins)
    plt.plot([example_intensity, example_intensity, target_bins[-11], target_bins[-11]],
             [0, freq[example_intensity], freq[example_intensity], 0], 
             'k--', 
             label=f'Example lookup ({example_intensity} -> {example_target:.0f})')
    
    # Customize the plot
    plt.legend()
    plt.xlim(0, 255)
    plt.ylim(0, 1)
    plt.xlabel('Intensity Values')
    plt.ylabel('Cumulative Fraction of Pixels')
    plt.title('Cumulative Distribution Function')
    
    return freq, bins, target_freq, target_bins

调用上述函数,代码如下:

dark_image = imread('plasma_ball.png')
freq, bins, target_freq, target_bins = plot_cdf(dark_image);

得到结果如下:
在这里插入图片描述

5. 直方图均衡化实现

上述代码计算了dark_image的累积分布函数(CDF),然后基于线性分布定义目标CDF。然后,它用蓝色绘制dark_image的实际CDF,用红色绘制目标CDF(线性)。还绘制了强度值的示例查找,显示示例中实际CDF为50,我们希望其目标为230。

在获得目标CDF之后,下一步是计算要用于替换原始像素强度的强度值。这是通过使用插值来创建查找表来完成的,代码如下

# Display the result after replacing all actual values to target values
new_vals = np.interp(freq, target_freq, target_bins)
dark_image_eq = img_as_ubyte(new_vals[img_as_ubyte(rgb2gray(dark_image[:,:,:3]))].astype(int))
plt.figure(figsize=(10,10))
plt.imshow(dark_image_eq, cmap='gray');

得到结果如下:
在这里插入图片描述

可以看到,使用直方图均衡化后,原来偏暗的灰度图,整体对比度和亮度都得到了改善。

6. 扩展

上面展示了最基本的直方图操作类型,接着让我们尝试不同类型的CDF技术,看看哪种技术适合给定的图像,代码如下:

# Linear
target_bins = np.arange(256)


# Sigmoid
def sigmoid_cdf(x, a=1):
    return (1 + np.tanh(a * x)) / 2


# Exponential
def exponential_cdf(x, alpha=1):
    return 1 - np.exp(-alpha * x)


# Power
def power_law_cdf(x, alpha=1):
    return x ** alpha


# Other techniques:
def adaptive_histogram_equalization(image, clip_limit=0.03, tile_size=(8, 8)):
    clahe = exposure.equalize_adapthist(
        image, clip_limit=clip_limit, nbins=256, kernel_size=(tile_size[0], tile_size[1]))
    return clahe


def gamma_correction(image, gamma=1.0):
    corrected_image = exposure.adjust_gamma(image, gamma)
    return corrected_image


def contrast_stretching_percentile(image, lower_percentile=5, upper_percentile=95):
    in_range = tuple(np.percentile(image, (lower_percentile, upper_percentile)))
    stretched_image = exposure.rescale_intensity(image, in_range)
    return stretched_image


def unsharp_masking(image, radius=5, amount=1.0):
    blurred_image = filters.gaussian(image, sigma=radius, multichannel=True)
    sharpened_image = (image + (image - blurred_image) * amount).clip(0, 1)
    return sharpened_image


def equalize_hist_rgb(image):
    equalized_image = exposure.equalize_hist(image)
    return equalized_image


def equalize_hist_hsv(image):
    hsv_image = color.rgb2hsv(image[:,:,:3])
    hsv_image[:, :, 2] = exposure.equalize_hist(hsv_image[:, :, 2])
    hsv_adjusted = color.hsv2rgb(hsv_image)
    return hsv_adjusted


def equalize_hist_yuv(image):
    yuv_image = color.rgb2yuv(image[:,:,:3])
    yuv_image[:, :, 0] = exposure.equalize_hist(yuv_image[:, :, 0])
    yuv_adjusted = color.yuv2rgb(yuv_image)
    return yuv_adjusted

调用代码如下:

# Save each technique to a variable
linear = custom_rgb_adjustment(dark_image, np.linspace(0, 1, len(target_bins)))
sigmoid = custom_rgb_adjustment(dark_image, sigmoid_cdf((target_bins - 128) / 64, a=1))
exponential = custom_rgb_adjustment(dark_image, exponential_cdf(target_bins / 255, alpha=3))
power = custom_rgb_adjustment(dark_image, power_law_cdf(target_bins / 255, alpha=2))

clahe_image = adaptive_histogram_equalization(
    dark_image, clip_limit=0.09, tile_size=(50, 50))
gamma_corrected_image = gamma_correction(dark_image, gamma=0.4)
sharpened_image = unsharp_masking(dark_image, radius=10, amount=-0.98)
cs_image = contrast_stretching_percentile(dark_image, 0, 70)

equalized_rgb = equalize_hist_rgb(dark_image)
equalized_hsv = equalize_hist_hsv(dark_image)
equalized_yuv = equalize_hist_yuv(dark_image)

最后得到的不同展示效果如下:
在这里插入图片描述
有很多方法/技术可以用于改善RGB图像的可视化效果,但其中大多数都需要手动调整参数。上述输出显示了使用各种直方图操作生成的亮度校正后的效果。通过观察,HSV调整、指数、对比度拉伸和unsharp masking的效果都令人满意。

请注意,不同方法的结果会因使用的原始图像而异。针对具体的图像,大家可以尝试使用不同的参数值来改善相应图像的质量。直方图处理技术可以极大地增强图像的对比度和整体外观。然而,谨慎使用它们是很重要的,因为如果过度使用,它们也会引入伪影,并导致不自然的外观,我们观察自适应直方图均衡化的输出可以明显看出该方法由于过度强调边缘而显得有些不自然。

7. 其他例子

与上面使用的亮度偏暗的图像相反,我们尝试在亮度良好的图像上使用相同的参数值执行代码。让我们观察一下相应的效果:
在这里插入图片描述
正如大家可能已经注意到的,大多数对亮度偏暗的图像有效的技术对偏亮的图像无效。这可能是由于这些技术可能会增强或放大图像中现有的亮度,导致过度曝光或添加伪影或噪声。

8. 总结

在这一节中,我们深入探讨了图像处理的世界,探索了各种图像增强技术。重点介绍了利用直方图技术来实现各种图像增强的效果,并给出了相应的代码示例。

您学废了嘛?

参考链接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/789464.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue脚手架文件说明

vue脚手架文件说明 1、文件介绍2、脚手架里面主要文件和作用 1、文件介绍 2、脚手架里面主要文件和作用 node_modules 都是下载的第三方包public/index.html 浏览器运行的网页src/main.js webpack打包的入口src/APP.vue Vue页面入口package.json 依赖包列表文件

Tangible Software Solutions Crack

Tangible Software Solutions Crack 有形软件解决方案-最准确可靠的源代码转换器,在VB.NET、C#、Java、C和Python之间进行转换,同时节省了无数小时的艰苦工作和宝贵的时间。 主要优点: 节省宝贵时间 准确全面 安全-您的代码永远不会离开您的机…

阿里云容器镜像仓库(ACR)的创建和使用

天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。…

(一)RabbitMQ概念-优势、劣势、应用场景 、AMQP、工作原理

Lison <dreamlison163.com>, v1.0.0, 2023.06.22 RabbitMQ概念-优势、劣势、应用场景 、AMQP、工作原理 文章目录 RabbitMQ概念-优势、劣势、应用场景 、AMQP、工作原理RabbitMQ概念RabbitMQ的优势RabbitMQ劣势RabbitMQ应用的场景RabbitMQ_AMQPRabbitMQ工作原理 RabbitM…

如何在3ds max中创建可用于真人场景的巨型机器人:第 1部分

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 创建主体 步骤 1 打开 3ds Max。 打开 3ds Max 步骤 2 在左侧视口中&#xff0c;按键盘上的 Alt-B 键。它 打开视口配置窗口。 打开“锁定缩放/平移”和“匹配位图”选项。单击“文件”并转到参考 …

从Vue2到Vue3【五】——新的组件(Fragment、Teleport、Suspense)

系列文章目录 内容链接从Vue2到Vue3【零】Vue3简介从Vue2到Vue3【一】Composition API&#xff08;第一章&#xff09;从Vue2到Vue3【二】Composition API&#xff08;第二章&#xff09;从Vue2到Vue3【三】Composition API&#xff08;第三章&#xff09;从Vue2到Vue3【四】C…

STM32CUBUMX配置FLASH(W25Q128)--保姆级教程

———————————————————————————————————— ⏩ 大家好哇&#xff01;我是小光&#xff0c;嵌入式爱好者&#xff0c;一个想要成为系统架构师的大三学生。 ⏩最近在开发一个STM32H723ZGT6的板子&#xff0c;使用STM32CUBEMX做了很多驱动&#x…

服务网格技术对比:深入比较Istio、Linkerd和Envoy等服务网格解决方案的优缺点

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

Github上方导航栏介绍

Code Watch&#xff1a;相当于关注&#xff0c;到时候这个项目又有什么操作&#xff0c;就会以通知的形式提醒你。 Fork&#xff1a;也就是把这个项目拉到你的仓库里&#xff0c;之后你可以对该代码进行修改&#xff0c;之后你可以发起Pull Request&#xff0c;简称PR&#xf…

SpringBoot集成kafka全面实战

本文是SpringBootKafka的实战讲解&#xff0c;如果对kafka的架构原理还不了解的读者&#xff0c;建议先看一下《大白话kafka架构原理》、《秒懂kafka HA&#xff08;高可用&#xff09;》两篇文章。 一、生产者实践 普通生产者 带回调的生产者 自定义分区器 kafka事务提交…

Windows下基于VSCode搭建C++开发环境(包含整合MinGW64、CMake的详细流程)

最近想写写C&#xff0c;装了VisualStudio 2022&#xff0c;折腾半天。对于一个用惯VSCode的人来说&#xff0c;总感觉IDE太笨重。于是自己网上各种查资料&#xff0c;自己琢磨&#xff0c;搭建了一套Windows下基于VSCode和CMake的C轻量级开发环境。 具体搭建步骤 1. 下载并安…

golang 冒号等于号 := 的注意事项和全局和局部变量的使用

全局变量错误用法&#xff1a; 全局变量正确用法&#xff1a;

ChatGPT应用探索:自动文本生成的无限可能

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

基于一致性引导的元学习bootstraping半监督医学图像分割

文章目录 Consistency-guided Meta-Learning for Bootstrapping Semi-Supervised Medical Image Segmentation摘要本文方法实验结果 Consistency-guided Meta-Learning for Bootstrapping Semi-Supervised Medical Image Segmentation 摘要 医学成像取得了显著的进步&#xf…

3.Java面试题—JVM基础、内存管理、垃圾回收

七、JVM 一、JVM 基础 一篇文章掌握整个JVM&#xff0c;JVM超详细解析&#xff01;&#xff01;&#xff01; 什么是Java虚拟机? Java虚拟机(JVM) 是用来 解析和运行Java程序 的一种 虚拟机。编译器 将 Java ⽂件 编译成 平台无关 的 Java 字节码文件 (.class)&#xff0c;…

嵌入式:QT Day1

一、手动实现登录框 源码&#xff1a; widge.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QDebug> //用于打印输出 #include <QIcon> //图标头文件 #include <QPushButton> //按钮类头文件 #includ…

Hadoop 之 Spark 配置与使用(五)

Hadoop 之 Spark 配置与使用 一.Spark 配置1.Spark 下载2.单机测试环境配置3.集群配置 二.Java 访问 Spark1.Pom 依赖2.测试代码1.计算 π 三.Spark 配置 Hadoop1.配置 Hadoop2.测试代码1.统计字符数 一.Spark 配置 环境说明环境版本AnolisAnolis OS release 8.6Jdkjava versi…

【node.js】01-fs读写文件内容

目录 一、fs.readFile() 读取文件内容 二、fs.writeFile() 向指定的文件中写入内容 案例&#xff1a;整理txt 需求&#xff1a; 代码&#xff1a; 一、fs.readFile() 读取文件内容 代码&#xff1a; //导入fs模块&#xff0c;从来操作文件 const fs require(fs)// 2.调…

ClickHouse(一):ClickHouse介绍及OLAP场景特征

目录 1. ClickHouse与其特性 ​​​​​​​2. 什么是ClickHouse ​​​​​​​3. OLAP场景的特征 进入正文前&#xff0c;感谢宝子们订阅专题、点赞、评论、收藏&#xff01;关注IT贫道&#xff0c;获取高质量博客内容&#xff01; ​​​​​​​1. ClickHouse与其特性 …

刷题小总结

数组 数组是存放在连续内存空间上的相同类型数据的集合。 经典题目&#xff1a; 二分查找 双指针法 滑动窗口 模拟行为 链表 链表的种类主要为&#xff1a;单链表&#xff0c;双链表&#xff0c;循环链表链表的存储方式&#xff1a;链表的节点在内存中是分散存储的&…