You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

OpenCV(C++):如何平衡两幅灰度图像的亮度?

嘿,我完全懂你的处境——作为OpenCV新手,想让两幅灰度图亮度对齐,试了equalizeHist却发现它只能单图优化,根本不考虑另一幅图的情况。直方图匹配(也叫直方图规定化)正是你需要的方法,虽然OpenCV没有直接提供现成的函数,但咱们自己实现起来其实挺简单的,我给你一步步拆解思路,再附上可运行的代码示例。

核心实现思路

直方图匹配的本质是让目标图像的亮度分布(直方图)和参考图像的分布保持一致,具体步骤如下:

  • 计算两幅图像的累积分布函数(CDF):CDF是直方图的累积形式,能反映图像中像素值小于等于某个灰度级的比例,这是实现匹配的核心依据。
  • 建立灰度值映射表:遍历目标图像的每个灰度级,找到参考图像中CDF最接近该灰度级CDF值的灰度级,建立从目标灰度到参考灰度的映射关系。
  • 应用映射到目标图像:用建好的映射表替换目标图像的每个像素,就能得到亮度和参考图高度相似的结果。
完整Python代码示例

我用Python+OpenCV来写示例,新手友好度更高:

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

def histogram_matching(source, reference):
    # 计算源图像和参考图像的直方图
    src_hist, _ = np.histogram(source.flatten(), 256, [0, 256])
    ref_hist, _ = np.histogram(reference.flatten(), 256, [0, 256])
    
    # 计算累积分布函数(CDF)
    src_cdf = src_hist.cumsum()
    ref_cdf = ref_hist.cumsum()
    
    # 归一化CDF到[0,255]范围,确保映射的灰度值在合理区间
    src_cdf = (255 * src_cdf / src_cdf[-1]).astype(np.uint8)
    ref_cdf = (255 * ref_cdf / ref_cdf[-1]).astype(np.uint8)
    
    # 构建灰度映射表
    mapping = np.zeros(256, dtype=np.uint8)
    current_ref_idx = 0
    for src_gray in range(256):
        # 找到第一个CDF值大于等于当前源灰度CDF的参考灰度位置
        while current_ref_idx < 255 and ref_cdf[current_ref_idx] < src_cdf[src_gray]:
            current_ref_idx += 1
        mapping[src_gray] = current_ref_idx
    
    # 将映射应用到源图像
    matched_image = mapping[source]
    return matched_image

# 读取两幅灰度图像(替换成你自己的图片路径)
source_img = cv2.imread('source_gray.jpg', cv2.IMREAD_GRAYSCALE)
reference_img = cv2.imread('reference_gray.jpg', cv2.IMREAD_GRAYSCALE)

# 执行直方图匹配
matched_img = histogram_matching(source_img, reference_img)

# 可视化结果,方便对比
plt.figure(figsize=(12, 8))

# 源图像及直方图
plt.subplot(2, 3, 1)
plt.imshow(source_img, cmap='gray')
plt.title('Source Image')
plt.axis('off')

plt.subplot(2, 3, 4)
plt.hist(source_img.flatten(), 256, [0, 256], color='gray')
plt.title('Source Histogram')

# 参考图像及直方图
plt.subplot(2, 3, 2)
plt.imshow(reference_img, cmap='gray')
plt.title('Reference Image')
plt.axis('off')

plt.subplot(2, 3, 5)
plt.hist(reference_img.flatten(), 256, [0, 256], color='gray')
plt.title('Reference Histogram')

# 匹配后图像及直方图
plt.subplot(2, 3, 3)
plt.imshow(matched_img, cmap='gray')
plt.title('Matched Image')
plt.axis('off')

plt.subplot(2, 3, 6)
plt.hist(matched_img.flatten(), 256, [0, 256], color='gray')
plt.title('Matched Histogram')

plt.tight_layout()
plt.show()
额外说明
  • 这个示例针对的是8位灰度图(像素值0-255),如果是其他位深的图像,需要调整灰度级的范围(比如16位图就改成65536个灰度级)。
  • 如果用C++版本的OpenCV,思路完全一致:用cv::calcHist计算直方图,手动计算CDF,然后构建映射表,最后用cv::LUT函数应用映射(比Python的索引替换更高效)。
  • 运行代码时记得把图片路径换成你自己的图像路径,这样就能看到亮度对齐后的效果啦。

内容的提问来源于stack exchange,提问作者Miroslav Novák

火山引擎 最新活动