820 lines
24 KiB
C++
820 lines
24 KiB
C++
/****************************************************************************
|
|
** Copyright (c) 2025 Evgeny Teterin (nayk) <nayk@nxt.ru>
|
|
** All right reserved.
|
|
**
|
|
** Permission is hereby granted, free of charge, to any person obtaining
|
|
** a copy of this software and associated documentation files (the
|
|
** "Software"), to deal in the Software without restriction, including
|
|
** without limitation the rights to use, copy, modify, merge, publish,
|
|
** distribute, sublicense, and/or sell copies of the Software, and to
|
|
** permit persons to whom the Software is furnished to do so, subject to
|
|
** the following conditions:
|
|
**
|
|
** The above copyright notice and this permission notice shall be
|
|
** included in all copies or substantial portions of the Software.
|
|
**
|
|
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
**
|
|
****************************************************************************/
|
|
#include "main_window.h"
|
|
#include "./ui_main_window.h"
|
|
|
|
#include <QtWidgets/QScrollBar>
|
|
#include <QtWidgets/QLabel>
|
|
#include <QtCore/QDebug>
|
|
#include <QtGui/QPixmap>
|
|
#include <QtWidgets/QVBoxLayout>
|
|
#include <QtWidgets/QHBoxLayout>
|
|
#include <QtWidgets/QPushButton>
|
|
#include <QtWidgets/QFileDialog>
|
|
#include <QtGui/QResizeEvent>
|
|
#include <QtWidgets/QMessageBox>
|
|
|
|
#include <math.h>
|
|
#include <float.h>
|
|
#include <opencv2/opencv.hpp>
|
|
#include <opencv2/imgproc/types_c.h>
|
|
#include <opencv2/core/types.hpp>
|
|
|
|
//==============================================================================
|
|
// Функции обработки изображений OpenCV
|
|
//==============================================================================
|
|
cv::Mat qImageToMat(const QImage &image)
|
|
{
|
|
qDebug() << "qImageToMat";
|
|
|
|
cv::Mat result;
|
|
|
|
if (image.isNull()) return result;
|
|
|
|
QImage img = image.convertToFormat( QImage::Format_RGB888 );
|
|
cv::Mat tmp(img.height(), img.width(), CV_8UC3,
|
|
const_cast<uchar*>(img.bits()),
|
|
static_cast<size_t>(img.bytesPerLine()));
|
|
|
|
try {
|
|
|
|
cv::cvtColor(tmp, result, CV_BGR2RGB);
|
|
|
|
} catch (...) {
|
|
|
|
return cv::Mat();
|
|
}
|
|
|
|
return result.clone();
|
|
}
|
|
//==============================================================================
|
|
QImage matToQImage(const cv::Mat &mat)
|
|
{
|
|
qDebug() << "matToQImage";
|
|
|
|
if(mat.empty()) {
|
|
|
|
return QImage();
|
|
}
|
|
|
|
cv::Mat temp;
|
|
|
|
try {
|
|
|
|
cv::cvtColor(mat, temp, CV_BGR2RGB);
|
|
|
|
} catch (...) {
|
|
|
|
return QImage();
|
|
}
|
|
|
|
if(temp.empty()) {
|
|
|
|
return QImage();
|
|
}
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
QImage dest( const_cast<uchar *>(temp.data),
|
|
temp.cols, temp.rows,
|
|
static_cast<qsizetype>(temp.step), QImage::Format_RGB888);
|
|
#else
|
|
QImage dest( const_cast<uchar *>(temp.data),
|
|
temp.cols, temp.rows,
|
|
static_cast<int>(temp.step), QImage::Format_RGB888);
|
|
#endif
|
|
|
|
if(dest.isNull()) return QImage();
|
|
return dest.copy();
|
|
}
|
|
//==============================================================================
|
|
cv::Mat applyGain(const cv::Mat &inputImage, double gain)
|
|
{
|
|
qDebug() << "applyGain";
|
|
|
|
cv::Mat outputImage;
|
|
|
|
if (inputImage.empty()) return outputImage;
|
|
|
|
try {
|
|
// Умножаем каждый пиксель на коэффициент усиления
|
|
inputImage.convertTo(outputImage, -1, gain, 0);
|
|
cv::threshold(outputImage, outputImage, 255, 255, cv::THRESH_TRUNC);
|
|
}
|
|
catch (...) {
|
|
|
|
outputImage = cv::Mat();
|
|
}
|
|
|
|
return outputImage;
|
|
}
|
|
//==============================================================================
|
|
cv::Mat adjustBrightnessContrast(const cv::Mat &img, double alpha, int beta)
|
|
{
|
|
qDebug() << "adjustBrightnessContrast";
|
|
cv::Mat adjusted;
|
|
|
|
if (img.empty()) return adjusted;
|
|
|
|
try {
|
|
|
|
img.convertTo(adjusted, -1, alpha, beta); // alpha - контраст, beta - яркость
|
|
cv::threshold(adjusted, adjusted, 255, 255, cv::THRESH_TRUNC);
|
|
}
|
|
catch (...) {
|
|
|
|
adjusted = cv::Mat();
|
|
}
|
|
|
|
return adjusted;
|
|
}
|
|
//==============================================================================
|
|
cv::Mat equalizeColorHist(const cv::Mat &bgrImg)
|
|
{
|
|
qDebug() << "equalizeColorHist";
|
|
|
|
cv::Mat result;
|
|
|
|
if (bgrImg.empty()) return result;
|
|
|
|
try {
|
|
|
|
cv::Mat ycrcb;
|
|
cv::cvtColor(bgrImg, ycrcb, cv::COLOR_BGR2YCrCb);
|
|
std::vector<cv::Mat> channels;
|
|
cv::split(ycrcb, channels);
|
|
cv::equalizeHist(channels[0], channels[0]); // Эквализация только по Y (яркость)
|
|
cv::merge(channels, ycrcb);
|
|
cv::cvtColor(ycrcb, result, cv::COLOR_YCrCb2BGR);
|
|
}
|
|
catch (...) {
|
|
|
|
result = cv::Mat();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
//==============================================================================
|
|
cv::Mat sharpenLaplacian(const cv::Mat &img)
|
|
{
|
|
qDebug() << "sharpenLaplacian";
|
|
cv::Mat result;
|
|
|
|
if (img.empty()) return result;
|
|
|
|
try {
|
|
|
|
cv::Mat sharpened;
|
|
cv::Laplacian(img, sharpened, CV_8U, 3);
|
|
result = img - 0.5 * sharpened; // Можно регулировать силу эффекта
|
|
}
|
|
catch (...) {
|
|
|
|
result = cv::Mat();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
//==============================================================================
|
|
cv::Mat unsharpMask(const cv::Mat &img, double sigma, double amount)
|
|
{
|
|
qDebug() << "unsharpMask";
|
|
cv::Mat result;
|
|
|
|
if (img.empty()) return result;
|
|
|
|
try {
|
|
|
|
cv::Mat blurred;
|
|
cv::GaussianBlur(img, blurred, cv::Size(0, 0), sigma);
|
|
result = img * (1 + amount) - blurred * amount;
|
|
}
|
|
catch (...) {
|
|
|
|
result = cv::Mat();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
//==============================================================================
|
|
cv::Mat denoiseMedian(const cv::Mat &img, int kernelSize = 3)
|
|
{
|
|
qDebug() << "denoiseMedian";
|
|
cv::Mat denoised;
|
|
|
|
if (img.empty()) return denoised;
|
|
|
|
try {
|
|
|
|
cv::medianBlur(img, denoised, (kernelSize % 2 == 0) ? kernelSize + 1 : kernelSize);
|
|
}
|
|
catch (...) {
|
|
|
|
denoised = cv::Mat();
|
|
}
|
|
|
|
return denoised;
|
|
}
|
|
//==============================================================================
|
|
cv::Mat denoiseNlm(const cv::Mat &img, float h = 10)
|
|
{
|
|
qDebug() << "denoiseNLM";
|
|
cv::Mat denoised;
|
|
|
|
if (img.empty()) return denoised;
|
|
|
|
try {
|
|
|
|
cv::fastNlMeansDenoisingColored(img, denoised, h);
|
|
}
|
|
catch (...) {
|
|
|
|
denoised = cv::Mat();
|
|
}
|
|
|
|
return denoised;
|
|
}
|
|
//==============================================================================
|
|
cv::Mat autoExposure(const cv::Mat &img)
|
|
{
|
|
qDebug() << "autoExposure";
|
|
cv::Mat result;
|
|
|
|
if (img.empty()) return result;
|
|
|
|
try {
|
|
|
|
cv::Mat lab;
|
|
cv::cvtColor(img, lab, cv::COLOR_BGR2Lab);
|
|
std::vector<cv::Mat> channels;
|
|
cv::split(lab, channels);
|
|
cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE();
|
|
clahe->setClipLimit(2.0);
|
|
clahe->apply(channels[0], channels[0]); // Применяем CLAHE к каналу L
|
|
cv::merge(channels, lab);
|
|
cv::cvtColor(lab, result, cv::COLOR_Lab2BGR);
|
|
}
|
|
catch (...) {
|
|
|
|
result = cv::Mat();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
//==============================================================================
|
|
cv::Mat grayWorldWb(const cv::Mat &img)
|
|
{
|
|
qDebug() << "grayWorldWB";
|
|
cv::Mat balanced;
|
|
|
|
if (img.empty()) return balanced;
|
|
|
|
try {
|
|
|
|
cv::Scalar avg = cv::mean(img);
|
|
double avgGray = (avg[0] + avg[1] + avg[2]) / 3;
|
|
balanced = img.clone();
|
|
balanced.forEach<cv::Vec3b>([&](cv::Vec3b &pixel, const int*) {
|
|
pixel[0] = cv::saturate_cast<uchar>(pixel[0] * avgGray / avg[0]);
|
|
pixel[1] = cv::saturate_cast<uchar>(pixel[1] * avgGray / avg[1]);
|
|
pixel[2] = cv::saturate_cast<uchar>(pixel[2] * avgGray / avg[2]);
|
|
});
|
|
}
|
|
catch (...) {
|
|
|
|
balanced = cv::Mat();
|
|
}
|
|
|
|
return balanced;
|
|
}
|
|
//==============================================================================
|
|
cv::Mat enhanceColors(const cv::Mat &img, float saturationFactor = 1.5)
|
|
{
|
|
qDebug() << "enhanceColors";
|
|
cv::Mat result;
|
|
|
|
if (img.empty()) return result;
|
|
|
|
try {
|
|
|
|
cv::Mat hsv;
|
|
cv::cvtColor(img, hsv, cv::COLOR_BGR2HSV);
|
|
hsv.forEach<cv::Vec3b>([&](cv::Vec3b &pixel, const int*) {
|
|
pixel[1] = cv::saturate_cast<uchar>(pixel[1] * saturationFactor); // Усиливаем насыщенность
|
|
});
|
|
|
|
cv::cvtColor(hsv, result, cv::COLOR_HSV2BGR);
|
|
}
|
|
catch (...) {
|
|
|
|
result = cv::Mat();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
//==============================================================================
|
|
cv::Mat removeVignette(const cv::Mat &img, double strength = 0.5)
|
|
{
|
|
qDebug() << "removeVignette";
|
|
cv::Mat result;
|
|
|
|
if (img.empty()) return result;
|
|
|
|
try {
|
|
|
|
cv::Mat mask(img.size(), CV_32F);
|
|
int centerX = img.cols / 2;
|
|
int centerY = img.rows / 2;
|
|
double radius = std::sqrt(centerX * centerX + centerY * centerY);
|
|
|
|
for (int y = 0; y < img.rows; ++y) {
|
|
|
|
for (int x = 0; x < img.cols; ++x) {
|
|
|
|
double dist = std::sqrt((x - centerX) * (x - centerX) + (y - centerY) * (y - centerY));
|
|
double factor = 1.0 - strength * (dist / radius);
|
|
|
|
mask.at<float>(y, x) = static_cast<float>( std::max(factor, 0.1) ); // Чтобы не было деления на 0
|
|
}
|
|
}
|
|
|
|
cv::Mat imgFloat;
|
|
img.convertTo(imgFloat, CV_32F);
|
|
|
|
std::vector<cv::Mat> channels;
|
|
cv::split(imgFloat, channels);
|
|
|
|
for (auto &channel : channels) {
|
|
|
|
channel = channel / mask;
|
|
}
|
|
cv::merge(channels, imgFloat);
|
|
|
|
|
|
imgFloat.convertTo(result, CV_8U);
|
|
}
|
|
catch (...) {
|
|
|
|
result = cv::Mat();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
//==============================================================================
|
|
cv::Mat applyClache(const cv::Mat &img)
|
|
{
|
|
qDebug() << "applyCLAHE";
|
|
cv::Mat result;
|
|
|
|
if (img.empty()) return result;
|
|
|
|
try {
|
|
|
|
cv::Mat lab;
|
|
cv::cvtColor(img, lab, cv::COLOR_BGR2Lab);
|
|
|
|
std::vector<cv::Mat> channels;
|
|
cv::split(lab, channels);
|
|
|
|
cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE();
|
|
clahe->setClipLimit(2.0);
|
|
clahe->apply(channels[0], channels[0]);
|
|
|
|
cv::merge(channels, lab);
|
|
|
|
cv::cvtColor(lab, result, cv::COLOR_Lab2BGR);
|
|
}
|
|
catch (...) {
|
|
|
|
result = cv::Mat();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//==============================================================================
|
|
// Форма просмотра изображения
|
|
//==============================================================================
|
|
ImageViewer::ImageViewer(const QImage &image, QWidget *parent) : QWidget(parent)
|
|
{
|
|
setWindowFlags( windowFlags() | Qt::Dialog);
|
|
setAttribute(Qt::WA_DeleteOnClose, true);
|
|
setMinimumSize(300, 200);
|
|
|
|
labelImage = image;
|
|
label = new QLabel(this);
|
|
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
label->setAlignment(Qt::AlignCenter);
|
|
label->setMinimumSize(1, 1); // Позволяет уменьшать окно
|
|
|
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
|
layout->addWidget(label);
|
|
|
|
QHBoxLayout *hLayout = new QHBoxLayout();
|
|
hLayout->addStretch();
|
|
|
|
QPushButton *btn = new QPushButton(this);
|
|
btn->setText(tr("Сохранить как..."));
|
|
|
|
connect(btn, &QPushButton::clicked, this, &ImageViewer::save);
|
|
|
|
hLayout->addWidget(btn);
|
|
layout->addLayout(hLayout);
|
|
|
|
setLayout(layout);
|
|
updateImage();
|
|
}
|
|
//==============================================================================
|
|
void ImageViewer::resizeEvent(QResizeEvent *event)
|
|
{
|
|
QWidget::resizeEvent(event);
|
|
updateImage();
|
|
}
|
|
//==============================================================================
|
|
void ImageViewer::updateImage()
|
|
{
|
|
if (!label) return;
|
|
|
|
label->clear();
|
|
|
|
if (!labelImage.isNull()) {
|
|
|
|
QPixmap pixmap = QPixmap::fromImage(labelImage);
|
|
label->setPixmap(pixmap.scaled(label->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
|
}
|
|
}
|
|
//==============================================================================
|
|
void ImageViewer::save() {
|
|
|
|
QString fileName = QFileDialog::getSaveFileName(
|
|
this, tr("Сохранить изображение"),
|
|
QString(),
|
|
QString("Изображения png (*.png)")
|
|
);
|
|
|
|
if (fileName.isEmpty()) return;
|
|
|
|
if (labelImage.save(fileName)) {
|
|
|
|
QMessageBox::information(this, tr("Игформация"), tr("Файл сохранён"));
|
|
}
|
|
else {
|
|
|
|
QMessageBox::critical(this, tr("Ошибка"), tr("Ошибка при сохранении файла"));
|
|
}
|
|
}
|
|
//==============================================================================
|
|
|
|
|
|
//==============================================================================
|
|
MainWindow::MainWindow(QWidget *parent)
|
|
: QMainWindow(parent)
|
|
, ui(new Ui::MainWindow)
|
|
{
|
|
ui->setupUi(this);
|
|
initialize();
|
|
updateControls();
|
|
actionsEnabled = true;
|
|
}
|
|
//==============================================================================
|
|
MainWindow::~MainWindow()
|
|
{
|
|
delete ui;
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::resizeEvent(QResizeEvent *event)
|
|
{
|
|
QMainWindow::resizeEvent(event);
|
|
updateLabelImage();
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::initialize()
|
|
{
|
|
QString title = qGuiApp->applicationDisplayName().isEmpty()
|
|
? qGuiApp->applicationName()
|
|
: qGuiApp->applicationDisplayName();
|
|
|
|
if (!title.isEmpty()) this->setWindowTitle( title );
|
|
|
|
connect(ui->actionExit, &QAction::triggered, this, &MainWindow::close);
|
|
connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::onActionOpen);
|
|
connect(ui->actionSave, &QAction::triggered, this, &MainWindow::onActionSaveAs);
|
|
connect(ui->actionShowImage, &QAction::triggered, this, &MainWindow::onActionShowImage);
|
|
connect(ui->actionUndo, &QAction::triggered, this, &MainWindow::onActionUndo);
|
|
connect(ui->actionReset, &QAction::triggered, this, &MainWindow::onActionReset);
|
|
connect(ui->pushButtonGain, &QPushButton::clicked, this, &MainWindow::btnGain);
|
|
connect(ui->pushButtonBrightnessAndContrast, &QPushButton::clicked, this, &MainWindow::btnBrightnessAndContrast);
|
|
connect(ui->pushButtonHistogramEqual, &QPushButton::clicked, this, &MainWindow::btnHistogramEqual);
|
|
connect(ui->pushButtonLaplacian, &QPushButton::clicked, this, &MainWindow::btnLaplacian);
|
|
connect(ui->pushButtonUnsharpMask, &QPushButton::clicked, this, &MainWindow::btnUnsharpMask);
|
|
connect(ui->pushButtonMedian, &QPushButton::clicked, this, &MainWindow::btnMedian);
|
|
connect(ui->pushButtonFastNlMeans, &QPushButton::clicked, this, &MainWindow::btnFastNlMeans);
|
|
connect(ui->pushButtonAutoExposure, &QPushButton::clicked, this, &MainWindow::btnAutoExposure);
|
|
connect(ui->pushButtonWhiteBalance, &QPushButton::clicked, this, &MainWindow::btnWhiteBalance);
|
|
connect(ui->pushButtonSaturation, &QPushButton::clicked, this, &MainWindow::btnSaturation);
|
|
connect(ui->pushButtonVinete, &QPushButton::clicked, this, &MainWindow::btnVinete);
|
|
connect(ui->pushButtonClahe, &QPushButton::clicked, this, &MainWindow::btnClahe);
|
|
|
|
ui->labelImage->setMinimumSize(1, 1);
|
|
ui->scrollArea->verticalScrollBar()->setValue(0);
|
|
imgList.reserve(maximumImagesCount);
|
|
}
|
|
//==============================================================================
|
|
cv::Mat MainWindow::prepareImage(const QString &operationName)
|
|
{
|
|
if (imgList.isEmpty()) return cv::Mat();
|
|
|
|
qDebug() << operationName;
|
|
|
|
return qImageToMat(imgList.last());
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::finallyImage(const cv::Mat &output)
|
|
{
|
|
if (output.empty()) {
|
|
|
|
qCritical() << "Ошибка при выполнении";
|
|
}
|
|
else {
|
|
|
|
imgList.append( matToQImage(output) );
|
|
|
|
while (imgList.size() > maximumImagesCount) imgList.removeAt(1);
|
|
|
|
}
|
|
|
|
updateLabelImage();
|
|
updateControls();
|
|
|
|
if (output.empty()) {
|
|
|
|
QMessageBox::critical(this, tr("Ошибка"), tr("Ошибка при выполнении"));
|
|
}
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::updateLabelImage()
|
|
{
|
|
ui->labelImage->clear();
|
|
|
|
QImage currentImage;
|
|
|
|
if (!imgList.isEmpty()) currentImage = imgList.last();
|
|
|
|
if (currentImage.isNull()) {
|
|
|
|
ui->labelImage->setText(tr("Изображение не загружено"));
|
|
return;
|
|
}
|
|
|
|
QPixmap pixmap = QPixmap::fromImage(currentImage);
|
|
ui->labelImage->setPixmap(
|
|
pixmap.scaled(ui->labelImage->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)
|
|
);
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::updateControls()
|
|
{
|
|
bool imageExist = !imgList.isEmpty() && !imgList.last().isNull();
|
|
|
|
ui->widget->setEnabled( imageExist );
|
|
ui->actionShowImage->setEnabled( imageExist );
|
|
ui->actionSave->setEnabled( imageExist );
|
|
ui->actionUndo->setEnabled( imgList.size() > 1 );
|
|
ui->actionReset->setEnabled( imgList.size() > 1 );
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::onActionOpen()
|
|
{
|
|
QString fileName = QFileDialog::getOpenFileName(
|
|
this, tr("Открыть изображение"),
|
|
QString(),
|
|
QString("Файлы изображений (*.png *.jpg *.jpeg)")
|
|
);
|
|
|
|
if (fileName.isEmpty()) return;
|
|
|
|
qDebug() << "Загрузка из файла: " << fileName;
|
|
|
|
ui->labelImage->clear();
|
|
imgList.clear();
|
|
|
|
QImage tmpImage( fileName );
|
|
|
|
if (tmpImage.isNull()) {
|
|
|
|
qCritical() << "После загрузки изображение isNull";
|
|
}
|
|
|
|
if (!tmpImage.isNull()) {
|
|
|
|
imgList.append(tmpImage);
|
|
}
|
|
|
|
updateLabelImage();
|
|
updateControls();
|
|
|
|
if (tmpImage.isNull()) {
|
|
|
|
qCritical() << "Ошибка при загрузке";
|
|
QMessageBox::critical(this, tr("Ошибка"), tr("Ошибка при загрузке файла"));
|
|
}
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::onActionSaveAs()
|
|
{
|
|
if (imgList.isEmpty()) return;
|
|
|
|
QString fileName = QFileDialog::getSaveFileName(
|
|
this, tr("Сохранить изображение"),
|
|
QString(),
|
|
QString("Изображения png (*.png)")
|
|
);
|
|
|
|
if (fileName.isEmpty()) return;
|
|
|
|
qDebug() << "Сохранение в файл: " << fileName;
|
|
|
|
if (imgList.last().save(fileName)) {
|
|
|
|
QMessageBox::information(this, tr("Информация"), tr("Файл сохранён"));
|
|
}
|
|
else {
|
|
|
|
qCritical() << "Ошибка при сохранении";
|
|
QMessageBox::critical(this, tr("Ошибка"), tr("Ошибка при сохранении файла"));
|
|
}
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::onActionShowImage()
|
|
{
|
|
if (imgList.isEmpty()) return;
|
|
|
|
ImageViewer *w = new ImageViewer(imgList.last(), this);
|
|
w->show();
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::onActionUndo()
|
|
{
|
|
if (imgList.size() > 1) imgList.removeLast();
|
|
|
|
updateLabelImage();
|
|
updateControls();
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::onActionReset()
|
|
{
|
|
while (imgList.size() > 1) imgList.removeLast();
|
|
|
|
updateLabelImage();
|
|
updateControls();
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::btnGain()
|
|
{
|
|
if (imgList.isEmpty()) return;
|
|
|
|
cv::Mat input = prepareImage("Простое усиление яркости");
|
|
cv::Mat output = applyGain(input, ui->doubleSpinBoxGain->value());
|
|
|
|
finallyImage(output);
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::btnBrightnessAndContrast()
|
|
{
|
|
if (imgList.isEmpty()) return;
|
|
|
|
cv::Mat input = prepareImage("Яркость и контраст");
|
|
cv::Mat output = adjustBrightnessContrast(input,
|
|
ui->doubleSpinBoxBrightness->value(),
|
|
ui->spinBoxContrast->value());
|
|
|
|
finallyImage(output);
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::btnHistogramEqual()
|
|
{
|
|
if (imgList.isEmpty()) return;
|
|
|
|
cv::Mat input = prepareImage("Гистограммная эквализация");
|
|
cv::Mat output = equalizeColorHist(input);
|
|
|
|
finallyImage(output);
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::btnLaplacian()
|
|
{
|
|
if (imgList.isEmpty()) return;
|
|
|
|
cv::Mat input = prepareImage("Фильтр Лапласа");
|
|
cv::Mat output = sharpenLaplacian(input);
|
|
|
|
finallyImage(output);
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::btnUnsharpMask()
|
|
{
|
|
if (imgList.isEmpty()) return;
|
|
|
|
cv::Mat input = prepareImage("Размытие + вычитание");
|
|
cv::Mat output = unsharpMask(input, ui->doubleSpinBoxSigma->value(),
|
|
ui->doubleSpinBoxAmount->value() );
|
|
|
|
finallyImage(output);
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::btnMedian()
|
|
{
|
|
if (imgList.isEmpty()) return;
|
|
|
|
if (ui->spinBoxMedian->value() % 2 == 0)
|
|
ui->spinBoxMedian->setValue( ui->spinBoxMedian->value() + 1 );
|
|
|
|
cv::Mat input = prepareImage("Медианный фильтр");
|
|
cv::Mat output = denoiseMedian(input, ui->spinBoxMedian->value() );
|
|
|
|
finallyImage(output);
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::btnFastNlMeans()
|
|
{
|
|
if (imgList.isEmpty()) return;
|
|
|
|
cv::Mat input = prepareImage("OpenCV FastNlMeans");
|
|
cv::Mat output = denoiseNlm(input, static_cast<float>(ui->doubleSpinBoxFastNlMeans->value()) );
|
|
|
|
finallyImage(output);
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::btnAutoExposure()
|
|
{
|
|
if (imgList.isEmpty()) return;
|
|
|
|
cv::Mat input = prepareImage("Автокорректировка экспозиции");
|
|
cv::Mat output = autoExposure(input);
|
|
|
|
finallyImage(output);
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::btnWhiteBalance()
|
|
{
|
|
if (imgList.isEmpty()) return;
|
|
|
|
cv::Mat input = prepareImage("Баланс белого");
|
|
cv::Mat output = grayWorldWb(input);
|
|
|
|
finallyImage(output);
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::btnSaturation()
|
|
{
|
|
if (imgList.isEmpty()) return;
|
|
|
|
cv::Mat input = prepareImage("Цветовая насыщенность");
|
|
cv::Mat output = denoiseNlm(input, static_cast<float>(ui->doubleSpinBoxSaturation->value()) );
|
|
|
|
finallyImage(output);
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::btnVinete()
|
|
{
|
|
if (imgList.isEmpty()) return;
|
|
|
|
cv::Mat input = prepareImage("Коррекция виньетирования");
|
|
cv::Mat output = removeVignette(input, ui->doubleSpinBoxVinete->value() );
|
|
|
|
finallyImage(output);
|
|
}
|
|
//==============================================================================
|
|
void MainWindow::btnClahe()
|
|
{
|
|
if (imgList.isEmpty()) return;
|
|
|
|
cv::Mat input = prepareImage("Адаптивное выравнивание гистограммы");
|
|
cv::Mat output = applyClache(input);
|
|
|
|
finallyImage(output);
|
|
}
|
|
//==============================================================================
|
|
|