Выравнивание гистограммы не работает с цветным изображением - OpenCV

Я пытаюсь выполнить выравнивание гистограммы с помощью 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

Что пропустило?

Ответ 1

Выравнивание гистограммы является нелинейным процессом. Разделение каналов и выравнивание каждого канала в отдельности не является подходящим способом выравнивания контраста. Выравнивание включает в себя значения интенсивности изображения, а не цветовые компоненты. Таким образом, для простого цветного изображения RGB HE не следует применять отдельно для каждого канала. Скорее, его следует применять таким образом, чтобы значения интенсивности были выровнены, не нарушая цветовой баланс изображения. Итак, первым шагом является преобразование цветового пространства изображения из RGB в одно из цветовых пространств, которое отделяет значения интенсивности от цветовых компонентов. Вот некоторые из них:

Преобразуйте изображение из RGB в одно из вышеупомянутых цветовых пространств. YCbCr является предпочтительным, поскольку он предназначен для цифровых изображений. Выполните HE плоскости интенсивности Y. Преобразуйте изображение обратно в RGB.

В вашей текущей ситуации вы не наблюдаете каких-либо существенных изменений, поскольку на изображении присутствуют только два ярких цвета. Если на изображении много цветов, метод разделения вызовет цветовой дисбаланс.

В качестве примера рассмотрим следующие изображения:

Входное изображение

Input Image

Выравнивание интенсивности изображения

Intensity Equalized

Выравнивание индивидуального канала

(Обратите внимание на ложные цвета)

Split Equalized

Вот код OpenCV для выравнивания гистограммы цветного изображения с использованием YCbCr цветового пространства.

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();
}

Ответ 2

И версия 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

Ответ 3

Я реализовал выравнивание гистограммы для изображения BGRA. Я думаю, что эта функция полезна для вашей цели (но вы должны игнорировать альфа-канал).

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;
}