基于直方图算法实现通过qt实现匀光匀色的两种方法

第一步,在QT中配置opencv

Qt配置OpenCV(保姆级教程)_qt opencv安装-CSDN博客

参考这个写的非常详细。

方法一

基于opencv库的实现

OpenCV是一个开源的计算机视觉库,提供了丰富的图像处理函数和工具,能够方便地实现各种图像处理操作,包括直方图均衡化、像素操作等。

采用了直方图匹配算法:直方图匹配是一种常见的图像增强方法,通过调整像素的灰度分布,使得输出图像的直方图更接近于目标直方图,从而达到图像增强的目的。

代码

#include <opencv2/opencv.hpp>
#include <map>
#include <vector>


using namespace std;
using namespace cv;
void getSigBandImgHistogram(Mat SrcImage, map<int, double> &SrcImg_SigMap, int BandId);

int main(int argc, char* argv[])
{


    const char* SrcPath = "D:/image2/daichuli.png";
    const char* BaseMap = "D:/image2/1.png";
    const char* outPutPath = "D:/image2/outPut.png";
    int IsDislay = atoi(argv[4]);//是否显示匹配后影像效果,0为不显示,其他的都显示

    Mat SrcImage = imread(SrcPath);
    Mat BaseMapImg = imread(BaseMap);


    cout << SrcImage.channels() << endl;
    vector<uchar*>resultMultMap;//存储每个波段最终的映射关系
    for (int BandId = 0; BandId < SrcImage.channels(); BandId++)
    {//单独对影像每个波段进行直方图均衡,然后找到最终的映射关系
        printf("Start Get BandID:%d Histogram!\n", BandId);

        uchar* SigResultBandMap = new uchar[256];//首先定义一下单个波段的映射结果
        //1、获取单波段直方图
        map<int, double> SrcImg_SigMap;//
        map<int, double> BaseMapImg_SigMap;
        getSigBandImgHistogram(SrcImage, SrcImg_SigMap, BandId);
        getSigBandImgHistogram(BaseMapImg, BaseMapImg_SigMap, BandId);

        //计算累计直方图
        double BaseSum_SigMap = 0;
        double SrcSum_SigMap = 0;
        map<int, double> SrcImg_AccSigMap;//累计直方图
        map<int, double> BaseMapImg_AccSigMap;//累计直方图
        for (int i = 0; i < 256; i++)
        {
            SrcSum_SigMap += SrcImg_SigMap[i];
            SrcImg_AccSigMap[i] = SrcSum_SigMap;

            BaseSum_SigMap += BaseMapImg_SigMap[i];
            BaseMapImg_AccSigMap[i] = BaseSum_SigMap;
        }
        printf("Start Build resultMap :%d!\n", BandId);

        for (int i = 0; i < 256; i++)
        {
            double MinValue = 255;//最小值初始化
            for (int j = 0; j < 256; j++)
            {
                double SubVelue = fabs(SrcImg_AccSigMap[i] * 255 - BaseMapImg_AccSigMap[j] * 255);//取绝对值
                if (MinValue > SubVelue)
                {//找到最靠近直方图均衡化的值,作为映射值
                    MinValue = SubVelue;
                    SigResultBandMap[i] = j;
                }
            }
        }
        resultMultMap.push_back(SigResultBandMap);
    }
    //最后进行直方图像素替换
    Mat resultImg = SrcImage.clone();//均衡化的src图
    for (int row = 0; row < resultImg.rows; row++) {

        uchar* uc_pixel = resultImg.data + row * resultImg.step;
        for (int col = 0; col < resultImg.cols; col++)
        {
            for (int BID = 0; BID < SrcImage.channels(); BID++)
            {
                uc_pixel[BID] = resultMultMap[BID][uc_pixel[BID]];
            }
            //uc_pixel[0] = resultMap[uc_pixel[0]];
            //uc_pixel[2] = resultMap[uc_pixel[2]];
            //uc_pixel[1] = resultMap[uc_pixel[1]];
            //uc_pixel += 3;
            uc_pixel += SrcImage.channels();
        }
    }

    imwrite(outPutPath, resultImg);
    printf("Success save img:%s\n", outPutPath);

    if (IsDislay != 0)
    {//是否显示影像
        imshow("SrcImage", SrcImage);
        //imshow("SrcImage_Equ", SrcImage_Equ);
        imshow("BaseMapImg", BaseMapImg);
        //imshow("BaseMapImg_Equ", BaseMapImg_Equ);
        imshow("resultImg", resultImg);
        waitKey(0);
    }

    return 0;
}

void getSigBandImgHistogram(Mat SrcImage, map<int, double> &SrcImg_SigMap, int BandId)
{//获取指定波段的直方图信息
    int w = SrcImage.cols;
    int h = SrcImage.rows;
    double totalPixel = w * h;
    double rate = 1 / totalPixel;
    for (int i = 0; i < 256; i++) {
        //map.put(i, 0.0);// 通过循环,往集合里面填充0~255个位置,初始值都为0
        SrcImg_SigMap[i] = 0.0;
    }

    //分别统计图像上0~255上分布总数
    for (int row = 0; row < h; row++) {
        uchar* uc_pixel = SrcImage.data + row * SrcImage.step;
        for (int col = 0; col < w; col++) {
            SrcImg_SigMap[uc_pixel[BandId]] += rate;
            uc_pixel += 3;
        }
    }
    return;

}

在main函数路径中第一个路径是你需要匀光匀色处理的路径,第二个是你要参考的路径,第三个是你生成图片所放的路径。

方法二

基于qt自带的库的实现

头文件

#ifndef WIDGET_H
#define WIDGET_H
#include <QImage>
#include <QWidget>
#include <QLabel>
#include <QVBoxLayout>


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();


   // void processImage(const QImage& img1, QImage& img2);

    //void processImageWithReference(const QImage& referenceImg, const QImage& targetImg, QImage& processedImg);

QImage histogramMatching(const QImage& referenceImg, const QImage& targetImg);

private slots:
    //void on_pushButton_clicked();

    //void on_pushButton_2_clicked();
    void on_selectReferenceImage();
    void on_selectTargetImage();
    void on_processImage();
    QImage processImageWithReference(const QImage& referenceImg, const QImage& targetImg);
/*
private:
    QImage m_image1;
    QImage m_image2;

    QLabel *m_label1;
    QLabel *m_label2;
*/
private:
    QLabel *m_labelReference;
    QLabel *m_labelTarget;
    QImage m_referenceImage;
    QImage m_targetImage;
    QImage m_processedImage;

    QVector<QVector<double>> calculateCumulativeHistogram(const QImage& image);
    int findNearestValueIndex(const QVector<QVector<double>>& referenceHistograms, double value);
    QImage applyLookupTable(const QImage& image, const QVector<QVector<int>>& lookupTables);


private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

源文件,这里是wiget.cpp

#include "widget.h"
#include <QFileDialog>
#include <QHBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QPixmap>
#include <cmath>
#include <vector>
#include <algorithm>
#include <QImage>
#include <QStandardPaths>
Widget::Widget(QWidget *parent)
    : QWidget(parent)

{



    QVBoxLayout *mainLayout = new QVBoxLayout(this);
   m_labelReference = new QLabel(this);
    mainLayout->addWidget(m_labelReference);



QPushButton *buttonSelectReference = new QPushButton("选择参考图片", this);
    connect(buttonSelectReference, &QPushButton::clicked, this, &Widget::on_selectReferenceImage);
    mainLayout->addWidget(buttonSelectReference);

    m_labelTarget = new QLabel(this);
    mainLayout->addWidget(m_labelTarget);

    QPushButton *buttonSelectTarget = new QPushButton("选择待处理图片", this);
    connect(buttonSelectTarget, &QPushButton::clicked, this, &Widget::on_selectTargetImage);
    mainLayout->addWidget(buttonSelectTarget);

    QPushButton *buttonProcess = new QPushButton("处理图片", this);
    connect(buttonProcess, &QPushButton::clicked, this, &Widget::on_processImage);
    mainLayout->addWidget(buttonProcess);

    setLayout(mainLayout);



}



void Widget::on_selectReferenceImage()
{
    QString referenceImagePath = QFileDialog::getOpenFileName(this, tr("选择参考图片"), "", tr("Images (*.png *.jpg)"));
    if (!referenceImagePath.isEmpty()) {
        m_referenceImage = QImage(referenceImagePath);//选路径图片

        m_labelReference->setPixmap(QPixmap::fromImage(m_referenceImage).scaled(400, 400, Qt::KeepAspectRatio));
    }
}

void Widget::on_selectTargetImage()
{
    QString targetImagePath = QFileDialog::getOpenFileName(this, tr("选择待处理图片"), "", tr("Images (*.png *.jpg)"));
    if (!targetImagePath.isEmpty()) {
        m_targetImage = QImage(targetImagePath);
        m_labelTarget->setPixmap(QPixmap::fromImage(m_targetImage).scaled(400, 400, Qt::KeepAspectRatio));
    }
}

void Widget::on_processImage()
{
    if (m_referenceImage.isNull() || m_targetImage.isNull()) {
        return;
    }

    // 在这里使用选定的参考图像m_referenceImage对待处理的图像m_targetImage进行匀光匀色处理
    // 进行直方图匹配处理
    m_processedImage = histogramMatching(m_referenceImage, m_targetImage);


    // 将处理后的图像保存在m_processedImage中
   // m_processedImage = processImageWithReference(m_referenceImage, m_targetImage);

    // 显示处理后的图像
    QLabel *processedLabel = new QLabel(this);
    processedLabel->setPixmap(QPixmap::fromImage(m_processedImage).scaled(400, 400, Qt::KeepAspectRatio));
    QVBoxLayout *mainlayout = qobject_cast<QVBoxLayout*>(layout());
    mainlayout->addWidget(processedLabel);



    // 将处理后的图片保存为 PNG 格式文件到桌面
    QString desktopPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
    QString filePath = desktopPath + "/processed_image.png";
    m_processedImage.save(filePath, "PNG");


}



QImage Widget::processImageWithReference(const QImage& referenceImg, const QImage& targetImg)
{
    // 在这里实现匀光匀色处理算法,并返回处理后的图像
   QImage processedImg = histogramMatching(referenceImg, targetImg);  // 这里暂时将目标图像作为处理后的图像,需要替换成实际的处理算法

    return processedImg;
}




QImage Widget::histogramMatching(const QImage& referenceImg, const QImage& targetImg)//建立了一个查找表,其实就是根据差值表和累积直方图的关系生成的。
{
    // 计算模板影像和待处理影像的累积直方图

     QVector<QVector<double>> referenceHistograms = calculateCumulativeHistogram(referenceImg);
    QVector<QVector<double>> targetHistograms = calculateCumulativeHistogram(targetImg);

    // 生成查找表
    QVector<QVector<int>> lookupTables;
    for (int channel = 0; channel < 3; ++channel) {
        QVector<int> lookupTable;
        for (int i = 0; i < 256; ++i) {
            int nearestIndex = findNearestValueIndex(referenceHistograms, targetHistograms[channel][i]);
            lookupTable.append(nearestIndex);
        }
        lookupTables.append(lookupTable);
    }

    // 应用查找表对待处理图像进行处理
    QImage processedImg = applyLookupTable(targetImg, lookupTables);

    return processedImg;
}


   QVector<QVector<double>>Widget:: calculateCumulativeHistogram(const QImage& image) {
    QVector<QVector<double>> cumulativeHistograms(3, QVector<double>(256, 0.0));

    int width = image.width();
    int height = image.height();
    double totalPixel = width * height;
    double rate = 1.0 / (3.0 * totalPixel);


    for (int channel = 0; channel < 3; ++channel) {
       QVector<double> histogram(256, 0.0);
        // 统计每个通道的直方图
        for (int y = 0; y < height; ++y) {
            for (int x = 0; x < width; ++x) {
                QRgb pixelValue = image.pixel(x, y);

                int colorValue=0;
                if (channel == 0) {
                    colorValue = qGreen(pixelValue) ;
                    colorValue = qBound(0, colorValue, 255);
                } else if (channel == 1) {
                   colorValue = qRed(pixelValue); // 对绿色通道进行加权处理
                    colorValue = qBound(0, colorValue, 255);
                    if (colorValue > 255) colorValue = 255; // 确保不超过255
                } else if (channel == 2) {
                    colorValue = qBlue(pixelValue);
                    colorValue = qBound(0, colorValue, 255);
                }


                 Q_ASSERT_X(colorValue >= 0 && colorValue < histogram.size(), "QList::operator[]", "index out of range");

               // histogram[colorValue] += 1.0;
                  ++histogram[colorValue];
            }
        }


        // 计算累积直方图
        cumulativeHistograms[channel][0] = histogram[0];
        for (int i = 1; i < 256; ++i) {
            cumulativeHistograms[channel][i] = cumulativeHistograms[channel][i - 1] + histogram[i];
        }

        // 归一化到0~1之间
        for (int i = 0; i < 256; ++i) {
           // cumulativeHistograms[channel][i] /= totalPixel;
            cumulativeHistograms[channel][i] = std::round(cumulativeHistograms[channel][i] / totalPixel * 255.0);
        }
    }

    return cumulativeHistograms;
}



//int Widget::findNearestValueIndex(const QList<QList<double>>& referenceHistograms, double value)
int Widget::findNearestValueIndex(const QVector<QVector<double>>& referenceHistograms, double value)//找到差值表中的最小值索引
{
    int index = 0;
    double minDiff = qAbs(referenceHistograms[0][0] - value);

    // 找到最接近值的索引
    for (int i = 0; i < referenceHistograms.size(); ++i) {
        for (int j = 0; j < referenceHistograms[i].size(); ++j) {
            double diff = qAbs(referenceHistograms[i][j] - value);
            if (diff < minDiff) {
                minDiff = diff;
                index = j;
            }
        }
    }
    return index;
}


QImage Widget::applyLookupTable(const QImage& image, const QVector<QVector<int>>& lookupTables)
{
    QImage processedImage = image;

    // 应用查找表到红绿蓝三个通道
    for (int y = 0; y < processedImage.height(); ++y) {
        for (int x = 0; x < processedImage.width(); ++x) {
            QRgb pixelValue = processedImage.pixel(x, y);
            int r = qRed(pixelValue);
            int g = qGreen(pixelValue);
            int b = qBlue(pixelValue);
            int newR = lookupTables[0][r];
            int newG = lookupTables[1][g];
            int newB = lookupTables[2][b];
            processedImage.setPixel(x, y, qRgb(newR, newG, newB));
        }
    }

    return processedImage;
}




Widget::~Widget()
{
    QLayout *layout = this->layout();
    if (layout) {
        QLayoutItem *item;
        while ((item = layout->takeAt(0))) {
            delete item->widget();
            delete item;
        }
        delete layout;
    }
    delete m_labelReference;
    delete m_labelTarget;
}


运行后明显基于opencv的更清晰,不过可以根据你想要实现的颜色在代码中做调整

Gadl是一个基于Python的图像处理库,它提供了一系列功能强大的图像处理算法和工具。其中,直方图匹配是Gadl库中的一个功能,用于将一张图像的直方图转换为另一张图像的直方图,从而实现两张图像之间的颜色匹配。 直方图匹配的目标是使得两张图像的直方图尽可能接近,从而达到颜色匹配的效果。在匹配过程中,首先计算源图像和目标图像的直方图,并对其进行归一化处理。然后,通过将源图像的每个像素值映射到目标图像的对应像素值,来实现直方图的匹配。这样,源图像的颜色分布就会与目标图像的颜色分布相似。 匀光匀色直方图匹配中的一种常见需求,它可以用于调整图像的整体亮度和色彩平衡。通过匀光匀色,可以使得两张图像在亮度和色彩上更加一致,从而达到更好的视觉效果。 如果你想使用Gadl库进行直方图匹配和匀光匀色操作,可以按照以下步骤进行: 1. 导入Gadl库:`import gadl` 2. 读取源图像和目标图像:`source_image = gadl.imread('source.jpg')`,`target_image = gadl.imread('target.jpg')` 3. 计算源图像和目标图像的直方图:`source_hist = gadl.histogram(source_image)`,`target_hist = gadl.histogram(target_image)` 4. 对直方图进行归一化处理:`source_hist_norm = gadl.normalize_histogram(source_hist)`,`target_hist_norm = gadl.normalize_histogram(target_hist)` 5. 进行直方图匹配:`matched_image = gadl.match_histograms(source_image, target_image, source_hist_norm, target_hist_norm)` 6. 进行匀光匀色操作(可选):`balanced_image = gadl.balance_light_color(matched_image)` 以上是使用Gadl库进行直方图匹配和匀光匀色的简单介绍,你可以根据具体需求进行参数调整和功能扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值