第一步,在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的更清晰,不过可以根据你想要实现的颜色在代码中做调整