直方图均衡化在彩色图像上不起作用 - OpenCV

32
我正在尝试使用OpenCV执行直方图均衡化,使用以下函数:
Mat Histogram::Equalization(const Mat& inputImage)
{
    if(inputImage.channels() >= 3)
    {
        vector<Mat> channels;
        split(inputImage,channels);
        Mat B,G,R;

        equalizeHist( channels[0], B );
        equalizeHist( channels[1], G );
        equalizeHist( channels[2], R );
        vector<Mat> combined;
        combined.push_back(B);
        combined.push_back(G);
        combined.push_back(R);
        Mat result;
        merge(combined,result);
        return result;
    }
    return Mat();
}

但是当我得到结果时,输入图像和输出图像似乎没有任何区别,我做错了什么吗?
抱歉图片质量不好,“预处理”(左)是直方图均衡化的,您可以看到它与输入图像(右)相同。

enter image description here

什么错过了?

4
“分离”->“均衡化”->“合并”不是正确的彩色图像直方图均衡化方法。这种方法会严重影响图像的色彩平衡。当有多种颜色的物体在图像中时,你将看到它会造成的色彩失衡。 - sgarizvi
3个回答

100
直方图均衡化是一种非线性过程。通道分离并单独均衡每个通道不是对比度均衡的正确方法。均衡涉及图像的强度值而不是颜色成分。因此,对于简单的RGB彩色图像,不应该在每个通道上单独应用HE。相反,应该应用HE,使强度值均衡,而不干扰图像的色彩平衡。因此,第一步是将图像的颜色空间从RGB转换为其中一个颜色空间,该颜色空间将强度值与颜色成分分离开来。其中一些是:

将图像从RGB转换为上述其中一种颜色空间。首选YCbCr,因为它专为数字图像设计。对强度平面Y执行HE。将图像转换回RGB。

在您当前的情况下,由于图像中只有两种主要颜色,因此您没有观察到任何显着的变化。当图像中有很多颜色时,分割方法会导致颜色不平衡。
例如,考虑以下图像:

输入图像

Input Image

强度图像均衡化

Intensity Equalized

个别通道均衡

(注意假色)

Split Equalized

这里是使用YCbCr颜色空间进行彩色图像直方图均衡化的OpenCV代码。
Mat equalizeIntensity(const Mat& inputImage)
{
    if(inputImage.channels() >= 3)
    {
        Mat ycrcb;

        cvtColor(inputImage,ycrcb,CV_BGR2YCrCb);

        vector<Mat> channels;
        split(ycrcb,channels);

        equalizeHist(channels[0], channels[0]);

        Mat result;
        merge(channels,ycrcb);

        cvtColor(ycrcb,result,CV_YCrCb2BGR);

        return result;
    }
    return Mat();
}

3
美丽 - 这是最好的解决方案。我看到的其他方法都涉及操作栅格数组。++++1 - Jeshua Lacock

18

Python版本,@sga提供:

import cv2
import os

def hisEqulColor(img):
    ycrcb=cv2.cvtColor(img,cv2.COLOR_BGR2YCR_CB)
    channels=cv2.split(ycrcb)
    print len(channels)
    cv2.equalizeHist(channels[0],channels[0])
    cv2.merge(channels,ycrcb)
    cv2.cvtColor(ycrcb,cv2.COLOR_YCR_CB2BGR,img)
    return img


fname='./your.jpg'
img=cv2.imread(fname)

cv2.imshow('img', img)
img2=hisEqulColor(img)
cv2.imshow('img2',img2)

然而,这会在图像中产生噪点(例如下面左边的图像)enter image description here


1
直方图均衡化用于调整对比度,应仅用于亮度(Y或类似)而非每个色彩通道。这是正确的。 - coderforlife

1
我为BGRA图像实现了直方图均衡化。我认为这个函数对你的目标很有用(但你应该忽略alpha通道)。
Mat equalizeBGRA(const Mat& img)
{
Mat res(img.size(), img.type());
Mat imgB(img.size(), CV_8UC1);
Mat imgG(img.size(), CV_8UC1);
Mat imgR(img.size(), CV_8UC1);
Vec4b pixel;

if (img.channels() != 4)
{
    cout << "ERROR: image input is not a BGRA image!" << endl;
    return Mat();
}

for (int r = 0; r < img.rows; r++)
{
    for (int c = 0; c < img.cols; c++)
    {
        pixel = img.at<Vec4b>(r, c);
        imgB.at<uchar>(r, c) = pixel[0];
        imgG.at<uchar>(r, c) = pixel[1];
        imgR.at<uchar>(r, c) = pixel[2];
    }
}

equalizeHist(imgB, imgB);
equalizeHist(imgG, imgG);
equalizeHist(imgR, imgR);

for (int r = 0; r < img.rows; r++)
{
    for (int c = 0; c < img.cols; c++)
    {
        pixel = Vec4b(imgB.at<uchar>(r, c), imgG.at<uchar>(r, c), imgR.at<uchar>(r, c), img.at<Vec4b>(r, c)[3]);
        res.at<Vec4b>(r, c) = pixel;
    }
}

return res;
}

这种方法还可以,但仍然遵循不推荐的通道分离方法。此外,您可以使用 cv::mixChannels 来从 4 通道图像中提取 R、G 和 B,而无需循环。 - sgarizvi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接