diff --git a/clahe.cpp b/clahe.cpp index 9c8673c..86cbf79 100644 --- a/clahe.cpp +++ b/clahe.cpp @@ -1,38 +1,38 @@ #include "clahe.h" #include +#include CLAHE::CLAHE() { - _clahe = cv::createCLAHE(); } void CLAHE::loadFile(const QString &path) { cv::Mat tmp; tmp = cv::imread(path.toStdString(), cv::IMREAD_COLOR | cv::IMREAD_ANYDEPTH); - double scale = tmp.depth()==CV_8U ? 1.0/255 : 1.0/65535; - tmp.convertTo(tmp, CV_32F, scale); + int type = tmp.depth(); + _scale = type==CV_8U ? 1.0/255 : 1.0/65535; + tmp.convertTo(tmp, CV_32F, _scale); cv::cvtColor(tmp, tmp, cv::COLOR_BGR2Lab); cv::split(tmp, _image); - _image[0].convertTo(_lumImage, CV_16U, 655.35); - double min, max; - cv::minMaxLoc(_image[0], &min, &max); - qDebug() << scale << min << max; + _image[0].convertTo(_lumImage, type, 0.01/_scale); } void CLAHE::saveFile(const QString &path) { - + cv::Mat tmp; + cv::merge(_image, 3, tmp); + cv::cvtColor(tmp, tmp, cv::COLOR_Lab2BGR); + tmp.convertTo(tmp, _lumImage.depth(), 1/_scale); + cv::imwrite(path.toStdString(), tmp); } void CLAHE::apply(float clipLimit, int kernelSize) { cv::Mat lum; - _clahe->setClipLimit(clipLimit); - _clahe->setTilesGridSize(cv::Size(kernelSize, kernelSize)); - _clahe->apply(_lumImage, lum); - //lum = _lumImage; - lum.convertTo(_image[0], CV_32F, 1.0/655.35); + cv::Ptr clahe = cv::createCLAHE(clipLimit, cv::Size(kernelSize, kernelSize)); + clahe->apply(_lumImage, lum); + lum.convertTo(_image[0], CV_32F, 100*_scale); double min, max; cv::minMaxLoc(_image[0], &min, &max); qDebug() << min << max; @@ -44,7 +44,15 @@ QPixmap CLAHE::getImage() const cv::merge(_image, 3, tmp); cv::cvtColor(tmp, tmp, cv::COLOR_Lab2RGB); tmp.convertTo(tmp, CV_8U, 255); - QImage ret(tmp.data, tmp.cols, tmp.rows, tmp.step, QImage::Format_RGB888); + QImage ret(tmp.data, tmp.cols, tmp.rows, tmp.step1(), QImage::Format_RGB888); //ret.bits();//perform deep copy of tmp.data pointer return QPixmap::fromImage(ret); } + +QPixmap CLAHE::getLumImage() const +{ + cv::Mat tmp; + _image[0].convertTo(tmp, CV_8U, 2.55); + QImage ret(tmp.data, tmp.cols, tmp.rows, tmp.step1(), QImage::Format_Grayscale8); + return QPixmap::fromImage(ret); +} diff --git a/clahe.h b/clahe.h index 293383d..07830d6 100644 --- a/clahe.h +++ b/clahe.h @@ -11,13 +11,14 @@ class CLAHE { cv::Mat _lumImage; cv::Mat _image[3]; - cv::Ptr _clahe; + double _scale; public: CLAHE(); void loadFile(const QString &path); void saveFile(const QString &path); void apply(float clipLimit, int kernelSize); QPixmap getImage() const; + QPixmap getLumImage() const; }; #endif // CLAHE_H diff --git a/clahe.pro b/clahe.pro index 4443899..b407799 100644 --- a/clahe.pro +++ b/clahe.pro @@ -29,11 +29,13 @@ PKGCONFIG += opencv SOURCES += \ main.cpp \ mainwindow.cpp \ - clahe.cpp + clahe.cpp \ + imagescrollarea.cpp HEADERS += \ mainwindow.h \ - clahe.h + clahe.h \ + imagescrollarea.h # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin diff --git a/imagescrollarea.cpp b/imagescrollarea.cpp new file mode 100644 index 0000000..16ff5d6 --- /dev/null +++ b/imagescrollarea.cpp @@ -0,0 +1,121 @@ +#include "imagescrollarea.h" +#include +#include +#include +#include +#include + +ImageScrollArea::ImageScrollArea(QWidget *parent) : QScrollArea(parent), + m_scale(-1) +{ + m_label = new QLabel(this); + setWidget(m_label); + setAlignment(Qt::AlignCenter); + setBackgroundRole(QPalette::Dark); +} + +void ImageScrollArea::setImage(const QPixmap &img) +{ + m_pixmap = img; + QPixmap pix; + if(m_scale < 0) + pix = img.scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); + else + pix = img.scaled(img.size() * m_scale, Qt::KeepAspectRatio, Qt::SmoothTransformation); + + m_label->setPixmap(pix); + m_label->resize(pix.size()); + + horizontalScrollBar()->setValue(horizontalScrollBar()->maximum() / 2); + verticalScrollBar()->setValue(verticalScrollBar()->maximum() / 2); +} + +void ImageScrollArea::setScale(float scale) +{ + if(scale > 4 || (scale < 0.2 && scale > 0) || m_pixmap.isNull()) + return; + + m_scale = scale; + QSize newSize = m_scale < 0 ? size() : m_pixmap.size()*scale; + m_label->setPixmap(m_pixmap.scaled(newSize, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + m_label->resize(newSize); +} + +void ImageScrollArea::zoomIn() +{ + if(m_scale < 0) + m_scale = (float)size().width()/m_pixmap.size().width(); + + setScale(m_scale + 0.1); +} + +void ImageScrollArea::zoomOut() +{ + if(m_scale < 0) + m_scale = (float)size().width()/m_pixmap.size().width(); + + setScale(m_scale - 0.1); +} + +void ImageScrollArea::bestFit() +{ + setScale(-1); +} + +void ImageScrollArea::oneToOne() +{ + setScale(1); +} + +void ImageScrollArea::keyPressEvent(QKeyEvent *event) +{ + event->ignore(); +} + +void ImageScrollArea::keyReleaseEvent(QKeyEvent *event) +{ + event->ignore(); +} + +void ImageScrollArea::mouseMoveEvent(QMouseEvent *event) +{ + QPoint delta = m_lastPos - event->pos(); + horizontalScrollBar()->setValue(horizontalScrollBar()->value() + delta.x()); + verticalScrollBar()->setValue(verticalScrollBar()->value() + delta.y()); + m_lastPos = event->pos(); +} + +void ImageScrollArea::mousePressEvent(QMouseEvent *event) +{ + m_lastPos = event->pos(); +} + +void ImageScrollArea::resizeEvent(QResizeEvent *event) +{ + if(m_scale < 0 && !m_pixmap.isNull()) + { + m_label->setPixmap(m_pixmap.scaled(event->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); + m_label->resize(event->size()); + } + QScrollArea::resizeEvent(event); +} + +void ImageScrollArea::wheelEvent(QWheelEvent *event) +{ + if(m_scale < 0) + m_scale = (float)size().width()/m_pixmap.size().width(); + + QPointF top(horizontalScrollBar()->value(), verticalScrollBar()->value()); + QPointF mousePos = (top + event->posF()) / m_scale; + + QPoint delta = event->angleDelta(); + if(delta.y() > 0) + setScale(m_scale + 0.1); + else + setScale(m_scale - 0.1); + + mousePos *= m_scale; + top = mousePos - event->posF(); + horizontalScrollBar()->setValue(top.x()); + verticalScrollBar()->setValue(top.y()); +} diff --git a/imagescrollarea.h b/imagescrollarea.h new file mode 100644 index 0000000..d22240f --- /dev/null +++ b/imagescrollarea.h @@ -0,0 +1,32 @@ +#ifndef IMAGESCROLLAREA_H +#define IMAGESCROLLAREA_H + +#include +#include + +class ImageScrollArea : public QScrollArea +{ + Q_OBJECT + QPoint m_lastPos; + QLabel *m_label; + QPixmap m_pixmap; + float m_scale; +public: + explicit ImageScrollArea(QWidget *parent = 0); + void setImage(const QPixmap &img); + void setScale(float scale); +public slots: + void zoomIn(); + void zoomOut(); + void bestFit(); + void oneToOne(); +protected: + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mousePressEvent(QMouseEvent *event); + void resizeEvent(QResizeEvent *event); + void wheelEvent(QWheelEvent *event); +}; + +#endif // IMAGESCROLLAREA_H diff --git a/mainwindow.cpp b/mainwindow.cpp index 5fe6922..380f34c 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -2,36 +2,53 @@ #include #include #include +#include +#include +#include +#include #include "clahe.h" +const QString imageFilter = "Images (*.png *.jpg *.jpeg);;All files (*)"; + MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { setupUI(); + _clahe = new CLAHE; } MainWindow::~MainWindow() { + delete _clahe; } void MainWindow::openFile() { QStringList list = QStandardPaths::standardLocations(QStandardPaths::StandardLocation::PicturesLocation); list.append(""); - QString path = QFileDialog::getOpenFileName(this, tr("Open image"), list.first(), tr("Images (*.png *.jpg *.jpeg);;All files (*)")); + QString path = QFileDialog::getOpenFileName(this, tr("Open image"), list.first(), imageFilter); if(!path.isEmpty()) { - CLAHE cl; - cl.loadFile(path); - cl.apply(5, 64); - QPixmap pixmap; - pixmap = cl.getImage(); - _image->setPixmap(pixmap); + _clahe->loadFile(path); + applyClahe(); } } void MainWindow::saveFile() { + QStringList list = QStandardPaths::standardLocations(QStandardPaths::StandardLocation::PicturesLocation); + QString path = QFileDialog::getSaveFileName(this, tr("Save image"), list.first(), imageFilter); + if(!path.isEmpty()) + { + _clahe->saveFile(path); + } +} +void MainWindow::applyClahe() +{ + QPixmap pixmap; + _clahe->apply(_clipLimitSlider->value()/100.0f, _kernelSizeSelect->currentText().toInt()); + pixmap = _clahe->getImage(); + _imageScrollArea->setImage(pixmap); } void MainWindow::setupUI() @@ -43,6 +60,39 @@ void MainWindow::setupUI() fileMenu->addAction(tr("Save file"), this, SLOT(saveFile()), QKeySequence("Ctrl+S")); fileMenu->addAction(tr("Quit"), this, SLOT(close()), QKeySequence("Ctrl+Q")); - _image = new QLabel(this); - setCentralWidget(_image); + _clipLimitEdit = new QLineEdit; + _clipLimitEdit->setInputMask("000.00"); + _clipLimitEdit->setText("1.00"); + _clipLimitEdit->setMaximumWidth(100); + + _clipLimitSlider = new QSlider(Qt::Horizontal); + _clipLimitSlider->setRange(0, 10000); + _clipLimitSlider->setValue(100); + connect(_clipLimitSlider, SIGNAL(valueChanged(int)), this, SLOT(clipSliderValueChanged(int))); + + QPushButton *applyButton = new QPushButton(tr("Apply")); + connect(applyButton, SIGNAL(clicked()), this, SLOT(applyClahe())); + + _kernelSizeSelect = new QComboBox; + _kernelSizeSelect->addItems({"8", "16", "32", "64", "128", "256"}); + + QHBoxLayout *claheLayout = new QHBoxLayout; + claheLayout->addWidget(_clipLimitEdit); + claheLayout->addWidget(_clipLimitSlider); + claheLayout->addWidget(_kernelSizeSelect); + claheLayout->addWidget(applyButton); + + QDockWidget *claheDock = new QDockWidget(tr("CLAHE"), this); + QWidget *claheWidget = new QWidget(claheDock); + claheDock->setWidget(claheWidget); + claheWidget->setLayout(claheLayout); + addDockWidget(Qt::TopDockWidgetArea, claheDock); + + _imageScrollArea = new ImageScrollArea(this); + setCentralWidget(_imageScrollArea); +} + +void MainWindow::clipSliderValueChanged(int value) +{ + _clipLimitEdit->setText(QString::number(value/100.0)); } diff --git a/mainwindow.h b/mainwindow.h index fcb7545..ecf02dd 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -3,19 +3,29 @@ #include #include +#include +#include "clahe.h" +#include "imagescrollarea.h" class MainWindow : public QMainWindow { Q_OBJECT - QLabel *_image; + QComboBox *_kernelSizeSelect; + QSlider *_clipLimitSlider; + QLineEdit *_clipLimitEdit; + CLAHE *_clahe; + ImageScrollArea *_imageScrollArea; public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); public slots: void openFile(); void saveFile(); + void applyClahe(); private: void setupUI(); +private slots: + void clipSliderValueChanged(int value); }; #endif // MAINWINDOW_H