[opencv] 直方图匹配

[opencv] 直方图匹配


文章目录

  • [opencv] 直方图匹配
    • 1. 定义
    • 2.1. 单通道匹配
      • 1. 完整代码
      • 2. 调试验证
    • 2.2. 三通道匹配
      • 1. 完整代码
      • 2. 调试验证
    • *. 参考
    • *. 问题解决
      • 1. matplotlib 绘制多个图形,如何同时独立显示?
    • *. rough



1. 定义

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

[opencv] 直方图匹配_第1张图片
[opencv] 直方图匹配_第2张图片


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 的直方图,直方概率图,累计概率图
[opencv] 直方图匹配_第3张图片

ref 的直方图,直方概率图,累计概率图
[opencv] 直方图匹配_第4张图片

map 的直方图,直方概率图,累计概率图
[opencv] 直方图匹配_第5张图片


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)
[opencv] 直方图匹配_第6张图片

ref 直方图,直方概率图,累积直方图~~(RGB)~~(BGR)
[opencv] 直方图匹配_第7张图片

src_mapped 三通道直方图~~(RGB)~~ (BGR)
[opencv] 直方图匹配_第8张图片


*. 参考


*. 问题解决

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

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


*. rough

  1. 定义

  2. 完整代码

你可能感兴趣的:(opencv)