[opencv] 直方图匹配

[opencv] 直方图匹配




1. 定义

  • 直方图匹配又称为直方图规定化,是指将一幅图像的直方图变成规定形状的直方图而进行的图像增强方法。 即将某幅影像或某一区域的直方图匹配到另一幅影像上。使两幅影像的色调保持一致。
  • 直方图规定化的实现步骤如下:
  1. 计算原图像的累积直方图
  2. 计算规定直方图的累积直方图
  3. 计算两累积直方图的差值的绝对值
  4. 根据累积直方图最小差值建立灰度级的映射

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


2.1. 单通道匹配

1. 完整代码

""""----------------------------------------
需求:
实现直方图匹配

流程:
1. 计算原图的累积直方图
2. 计算目标图的累积直方图
3. 比较两个直方图每一个灰度值的概率差(的绝对值)
4. 找出最小的绝对差,完成灰度值的映射
----------------------------------------"""

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


# 1. 获取图像宽高
# 2. 计算并绘制直方图
# 3. 计算并绘制直方图概率图
# 4. 计算并绘制累计概率直方图
def get_accumulative_hist(image, plot_enable=False):
    """
    计算累积直方图概率图矩阵
    :param image: 输入的图像矩阵
    :param plot_enable: 是否开启绘制功能
    :return: 累积概率直方图矩阵
    """
    height, width = image.shape[0], image.shape[1]
    hist = cv.calcHist([image], [0], None, [256], [0, 256])
    ratio_hist = hist / (height * width)
    accumulative_hist = np.zeros(256, np.float)
    ratio_sum = 0
    for i in range(256):
        ratio_sum += ratio_hist[i]
        accumulative_hist[i] = ratio_sum

    if plot_enable:
        plot_hist(accumulative_hist, hist, ratio_hist)

    return accumulative_hist


# 1. 创建图表对象
# 2. 绘制直方图
# 3. 绘制直方概率图
# 4. 绘制累计直方概率图
def plot_hist(accumulative_hist, hist, ratio_hist):
    """
    绘制图像直方图,直方概率图,累积直方概率图
    :param accumulative_hist: 累积概率直方图矩阵
    :param hist: 直方图矩阵
    :param ratio_hist: 概率直方图矩阵
    :return: None
    """
    fig = plt.figure()
    grid_rows = 1
    grid_cols = 3

    plt.subplot(grid_rows, grid_cols, 1)
    plt.plot(hist)
    plt.subplot(grid_rows, grid_cols, 2)
    plt.plot(ratio_hist)
    plt.subplot(grid_rows, grid_cols, 3)
    plt.plot(accumulative_hist)


"""----------------------------------------
1. 读取图像 
----------------------------------------"""
src_img = cv.imread("../img/yunduo.jpg", cv.IMREAD_COLOR)
src_gray = cv.cvtColor(src_img, cv.COLOR_BGR2GRAY)

ref_img = cv.imread("../img/fl4.jpg", cv.IMREAD_COLOR)
ref_gray = cv.cvtColor(ref_img, cv.COLOR_BGR2GRAY)

"""----------------------------------------
2. 计算累计累计直方概率图, src, ref
----------------------------------------"""
src_accumulative_hist = get_accumulative_hist(src_gray, True)
ref_accumulative_hist = get_accumulative_hist(ref_gray, True)

"""----------------------------------------
3. 比较两个直方概率图的差 
----------------------------------------"""
# 创建一个0-255映射数组
# 遍历src的每一个灰度的累积概率(i:灰度值,src_hist_bin:累积概率)
# 预先定义一个最小绝对差, 最小绝对差对应的ref的索引
# 遍历ref的每一个灰度的累积概率(j:灰度值,ref_hist_bin:累积概率)
# 重置最小绝对差, 通过这个值找到其索引
# 重置最小绝对差索引,目标是 inde(abs(src_i - ref_j),并记录
# 找出当前灰度值的映射关系 src 的灰度值 匹配 ref 的灰度值

color_map = np.zeros(256, src_gray.dtype)

for i, src_hist_bin in enumerate(src_accumulative_hist):
    min_diff_abs = 1000
    min_diff_abs_index = 0
    for j, ref_hist_bin in enumerate(ref_accumulative_hist):
        diff_abs = np.abs(ref_hist_bin - src_hist_bin)

        if (diff_abs < min_diff_abs):
            min_diff_abs = diff_abs
            min_diff_abs_index = j

    color_map[i] = min_diff_abs_index

"""----------------------------------------
4. 修改原图的灰度值,通过直方图匹配
----------------------------------------"""
src_height = src_gray.shape[0]
src_width = src_gray.shape[1]
src_map = np.zeros(src_gray.shape, src_gray.dtype)
print(src_map.shape)

# 遍历src,通过 gray_map_array,用 ref 的灰度分布修改 src
# 获取 src 当前像素的灰度值
# 通过当前灰度值,得出映射灰度值
# 将 src 像素赋值映射灰度值

for row in range(src_height):
    for col in range(src_width):
        src_color = src_gray[row, col]
        map_color = color_map[src_color]
        src_map[row, col] = map_color

src_map_accumulative_hist = get_accumulative_hist(src_map, True)

"""----------------------------------------
5. 显示图像
----------------------------------------"""
cv.imshow("src", src_gray)
cv.imshow("ref", ref_gray)
cv.imshow("map", src_map)

cv.waitKey(1)

"""---------------------------------------- 
显示所有绘制得图像 
----------------------------------------"""
plt.show()

2. 调试验证

原图 src 与 ref 图进行直方图匹配,得到结果图 map
在这里插入图片描述

src 的直方图,直方概率图,累计概率图
在这里插入图片描述

ref 的直方图,直方概率图,累计概率图
在这里插入图片描述

map 的直方图,直方概率图,累计概率图
在这里插入图片描述


2.2. 三通道匹配

1. 完整代码

""""----------------------------------------
需求:
实现直方图匹配_彩图

流程:
1. 计算原图的累积直方图
2. 计算目标图的累积直方图
3. 比较两个直方图每一个灰度值的概率差(的绝对值)
4. 找出最小的绝对差,完成灰度值的映射
----------------------------------------"""

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


def plot_hist(accumulative_hist, hist, ratio_hist):
    fig = plt.figure()
    grid_rows = 1
    grid_cols = 3
    plt.subplot(grid_rows, grid_cols, 1)
    plt.plot(hist)
    plt.subplot(grid_rows, grid_cols, 2)
    plt.plot(ratio_hist)
    plt.subplot(grid_rows, grid_cols, 3)
    plt.plot(accumulative_hist)


def get_accumulative_hist(image, plot_enable=False):
    height, width = image.shape[0], image.shape[1]
    hist = cv.calcHist([image], [0], None, [256], [0, 256])
    ratio_hist = hist / (height * width)
    accumulative_hist = np.zeros(256, np.float)
    ratio_sum = 0
    for i in range(256):
        ratio_sum += ratio_hist[i]
        accumulative_hist[i] = ratio_sum

    if plot_enable:
        plot_hist(accumulative_hist, hist, ratio_hist)

    return accumulative_hist


def hist_match_channel_one(src, ref):
    mapped_img = np.zeros(src.shape, src.dtype)

    src_accumulative_hist = get_accumulative_hist(src, True)
    ref_accumulative_hist = get_accumulative_hist(ref, True)

    map_array_c1 = np.zeros(256, src.dtype)
    for i, src_hist_bin in enumerate(src_accumulative_hist):
        min_diff_abs = 1000
        min_diff_abs_index = 0
        for j, ref_hist_bin in enumerate(ref_accumulative_hist):
            diff_abs = np.abs(ref_hist_bin - src_hist_bin)

            if diff_abs < min_diff_abs:
                min_diff_abs = diff_abs
                min_diff_abs_index = j

        map_array_c1[i] = min_diff_abs_index

    src_height = src.shape[0]
    src_width = src.shape[1]

    for row in range(src_height):
        for col in range(src_width):
            src_color = src[row, col]
            map_color = map_array_c1[src_color]
            mapped_img[row, col] = map_color

    return mapped_img


"""----------------------------------------
1. 读取图像 
----------------------------------------"""
src_img = cv.imread("../img/yunduo.jpg", cv.IMREAD_COLOR)
ref_img = cv.imread("../img/fl4.jpg", cv.IMREAD_COLOR)


"""----------------------------------------
2. 三通道直方图匹配
----------------------------------------"""
# 拆分彩图,每一个通道进行直方图匹配,再融合
src_channels = cv.split(src_img)
ref_channels = cv.split(ref_img)

src_mapped_channels = []
for i in range(3):
    src_mapped_channel = hist_match_channel_one(src_channels[i], ref_channels[i])
    src_mapped_channels.append(src_mapped_channel)

src_mapped = cv.merge(src_mapped_channels)

# src_mapped = hist_match_channel_one(cv.cvtColor(src_img, cv.COLOR_BGR2GRAY),
#                                     cv.cvtColor(ref_img, cv.COLOR_BGR2GRAY))

"""----------------------------------------
5. 显示图像
----------------------------------------"""
cv.imshow("src", src_img)
cv.imshow("ref", ref_img)
cv.imshow("map", src_mapped)

cv.waitKey(0)

for i in range(3):
    plt.figure()
    plt.hist(src_mapped_channels[i].ravel(), bins=256)

"""---------------------------------------- 
显示所有绘制得图像 
----------------------------------------"""
plt.show()

2. 调试验证

原图 src,ref,与 src_mapped
在这里插入图片描述

src 直方图,直方概率图,累积直方图~~(RGB)~~(BGR)
在这里插入图片描述

ref 直方图,直方概率图,累积直方图~~(RGB)~~(BGR)
在这里插入图片描述

src_mapped 三通道直方图~~(RGB)~~ (BGR)
在这里插入图片描述


*. 参考


*. 问题解决

1. matplotlib 绘制多个图形,如何同时独立显示?

链接:matplotlib 绘制多个图形,如何同时独立显示?(解决)


*. rough

  1. 定义

  2. 完整代码

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenCV是一个开源的计算机视觉库,它提供了丰富的图像处理和计算机视觉算法。直方图匹配是一种图像处理技术,用于将一幅图像的直方图与另一幅图像的直方图进行匹配,从而实现颜色或亮度的调整。 在OpenCV中,可以使用`cv2.calcHist()`函数计算图像的直方图,使用`cv2.normalize()`函数对直方图进行归一化。然后,可以使用`cv2.compareHist()`函数来比较两个直方图的相似性。 下面是一个简单的示例代码,演示了如何使用OpenCV实现直方图匹配: ```python import cv2 import numpy as np # 读取原始图像和目标图像 src_img = cv2.imread('source_image.jpg', 0) target_img = cv2.imread('target_image.jpg', 0) # 计算原始图像和目标图像的直方图 src_hist = cv2.calcHist([src_img], [0], None, [256], [0, 256]) target_hist = cv2.calcHist([target_img], [0], None, [256], [0, 256]) # 归一化直方图 cv2.normalize(src_hist, src_hist, 0, 255, cv2.NORM_MINMAX) cv2.normalize(target_hist, target_hist, 0, 255, cv2.NORM_MINMAX) # 使用相关性匹配方法比较两个直方图 match_value = cv2.compareHist(src_hist, target_hist, cv2.HISTCMP_CORREL) print("直方图匹配结果:", match_value) ``` 在上面的代码中,我们首先使用`cv2.imread()`函数读取原始图像和目标图像,并将它们转换为灰度图像(参数为0)。然后,使用`cv2.calcHist()`函数计算原始图像和目标图像的直方图。接下来,使用`cv2.normalize()`函数对直方图进行归一化,以便进行比较。最后,使用`cv2.compareHist()`函数比较两个直方图的相似性,返回一个匹配值。 需要注意的是,直方图匹配只能调整图像的颜色或亮度,无法改变图像的形状或结构。如果需要更复杂的图像处理任务,可能需要使用其他技术或算法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值