From 18d5b88aa7e030494521d1c9453457a6442fe9dc Mon Sep 17 00:00:00 2001 From: Timforce Date: Sun, 10 Feb 2019 19:21:34 +0800 Subject: [PATCH] First Commit --- .gitignore | 9 ++ Basic_image_processing.pro | 40 +++++++ Makefile | 41 +++++++ README.md | 20 ++++ interface.cpp | 196 ++++++++++++++++++++++++++++++++ interface.h | 50 +++++++++ interface.ui | 222 +++++++++++++++++++++++++++++++++++++ main.cpp | 11 ++ 8 files changed, 589 insertions(+) create mode 100644 .gitignore create mode 100644 Basic_image_processing.pro create mode 100644 Makefile create mode 100644 README.md create mode 100644 interface.cpp create mode 100644 interface.h create mode 100644 interface.ui create mode 100644 main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..310c517 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.o +*.jpg +*.png +*.bmp +*.user +build/ +ui_interface.h +moc_interface.cpp +Basic_image_processing \ No newline at end of file diff --git a/Basic_image_processing.pro b/Basic_image_processing.pro new file mode 100644 index 0000000..12d063e --- /dev/null +++ b/Basic_image_processing.pro @@ -0,0 +1,40 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2019-02-08T15:58:23 +# +#------------------------------------------------- + +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = Basic_image_processing +TEMPLATE = app + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which has been marked as deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +CONFIG += c++11 + +SOURCES += \ + main.cpp \ + interface.cpp + +HEADERS += \ + interface.h + +FORMS += \ + interface.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4e9664c --- /dev/null +++ b/Makefile @@ -0,0 +1,41 @@ +### Specify your qt install location here +### Ex. /opt/qt/5.12.0/gcc_64 +QT_DIR = /opt/qt/5.12.1/gcc_64 + +CXX = g++ +UIC = $(QT_DIR)/bin/uic +MOC = $(QT_DIR)/bin/moc +BUILD_DIR = ./build/ +DEFINES = -DQT_DEPRECATED_WARNINGS -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB +CXXFLAGS = -pipe -O2 -std=gnu++11 -Wall -W -D_REENTRANT -fPIC $(DEFINES) +INCPATH = -I. -I$(QT_DIR)/include -I$(QT_DIR)/include/QtWidgets -I$(QT_DIR)/include/QtGui -I$(QT_DIR)/include/QtCore +LFLAGS = -Wl,-O1 -Wl,-rpath,$(QT_DIR)/lib +LIBS = -L$(QT_DIR)/lib -lQt5Widgets -lQt5Gui -lQt5Core -lGL -lpthread +SOURCES = main.cpp interface.cpp moc_interface.cpp +OBJECTS = $(patsubst %.cpp, %.o, $(SOURCES)) +EXEOJBS = $(addprefix $(BUILD_DIR), $(OBJECTS)) +UIHEADER = ui_interface.h +MOCCXX = moc_interface.cpp +TARGET = Basic_image_processing + +all: build_dir $(TARGET) + +$(TARGET): $(UIHEADER) $(EXEOJBS) + $(CXX) $(LFLAGS) -o $(TARGET) $(EXEOJBS) $(LIBS) + +$(BUILD_DIR)%.o: %.cpp + $(CXX) $(CXXFLAGS) $(INCPATH) -c $< -o $@ + +$(MOCCXX): interface.h + $(MOC) $(DEFINES) $(INCPATH) interface.h -o $@ + +$(UIHEADER): interface.ui + $(UIC) interface.ui -o $@ + +build_dir: + mkdir -p $(BUILD_DIR) + +clean: + rm -rf $(BUILD_DIR) $(TARGET) $(MOCCXX) $(UIHEADER) + +.PHONY: clean diff --git a/README.md b/README.md new file mode 100644 index 0000000..c73a5db --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# Basic Image Processing - Qt +This is a basic linux GUI written with [Qt][qt-link] that do some basic image processing task. \ +At current state it contains following functionalities: +1. Loading and saving .jpg .png .bmp image files. +2. Image inversion. +3. Histogram equalization. +4. Gamma correction based on user input gamma value. + +[qt-link]: https://www.qt.io/ + +### Requirements: +GNU-g++, make, Qt community version>=4 + +### Program compile: +Specify the Qt installation location in Makefile and type ```make``` under the project directory. + +### Downloads +Check the [release][release-link] page + +[release-link]: https://github.com/timforce/Basic_Image_Processing-Qt/releases \ No newline at end of file diff --git a/interface.cpp b/interface.cpp new file mode 100644 index 0000000..c003c9a --- /dev/null +++ b/interface.cpp @@ -0,0 +1,196 @@ +#include "interface.h" +#include "ui_interface.h" +#include + +Interface::Interface(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::Interface) +{ + ui->setupUi(this); + ui->status->setText("Welcome!"); + this->setFixedSize(QSize(850, 550)); +} + +Interface::~Interface() +{ + delete ui; +} + +void Interface::on_actionOpen_triggered() +{ + QString filename = QFileDialog::getOpenFileName(this, "Open Image File", nullptr, + "Images (*.jpg *.jpeg *.JPG *.png *.PNG *.bmp *.BMP );;" + "JPG (*.jpg *.jpeg *.JPG);;" + "PNG (*.png *.PNG);;" + "BMP (*.bmp *.BMP)"); + if(!original_img.load(filename)) + { + if(static_cast(proc_stat & img_loaded)) + return; + ui->status->setText("Didn't load the image!"); + proc_stat = 0; + return; + } + else { + processed_img = original_img.copy(); + ui->orig_frame->setPixmap(original_img.scaled(ui->orig_frame->size(), Qt::KeepAspectRatio)); + ui->proc_frame->setPixmap(processed_img.scaled(ui->proc_frame->size(), Qt::KeepAspectRatio)); + ui->status->setText("Loaded image: " + filename); + proc_stat = 1; + } +} + +void Interface::on_imgInv_clicked() +{ + if (check_status(inv_done)) + return; + QImage proc = original_img.copy().toImage(); + uchar *bit = proc.bits(); + int h = proc.height(); + int w = proc.width(); + int i, j, dest_index; + for(i = 0; i < w; ++i){ + for(j = 0; j < h; ++j){ + dest_index = 4*i + 4*w*j; + bit[dest_index + 0 ] = ~(bit[dest_index + 0]); + bit[dest_index + 1 ] = ~(bit[dest_index + 1]); + bit[dest_index + 2 ] = ~(bit[dest_index + 2]); + } + } + processed_img = processed_img.fromImage(proc); + ui->proc_frame->setPixmap(processed_img.scaled(ui->proc_frame->size(), Qt::KeepAspectRatio)); + ui->status->setText("Image inversion done!"); + proc_stat |= inv_done; +} + +void Interface::on_imgHistEql_clicked() +{ + if (check_status(hist_done)) + return; + QImage proc = original_img.copy().toImage(); + uchar *bit = proc.bits(); + int h = proc.height(); + int w = proc.width(); + int pixels = w*h; + int i, j, dest_index; + float *crfR = new float[256] (); + float *crfG = new float[256] (); + float *crfB = new float[256] (); + for(i = 0; i < w; ++i){ + for(j = 0; j < h; ++j){ + dest_index = 4*i + 4*w*j; + crfR[static_cast(bit[dest_index + 0])]++; + crfG[static_cast(bit[dest_index + 1])]++; + crfB[static_cast(bit[dest_index + 2])]++; + } + } + for(i = 1; i <= 255; i++) + { + crfR[i] += crfR[i-1]; + crfG[i] += crfG[i-1]; + crfB[i] += crfB[i-1]; + } + for(i = 0; i < w; ++i){ + for(j = 0; j < h; ++j){ + dest_index = 4*i + 4*w*j; + bit[dest_index+0] = static_cast( + static_cast((crfR[static_cast(bit[dest_index+0])] / pixels * 255))); + bit[dest_index+1] = static_cast( + static_cast((crfG[static_cast(bit[dest_index+1])] / pixels * 255))); + bit[dest_index+2] = static_cast( + static_cast((crfB[static_cast(bit[dest_index+2])] / pixels * 255))); + } + } + delete[] crfR; + delete[] crfG; + delete[] crfB; + processed_img = processed_img.fromImage(proc); + ui->proc_frame->setPixmap(processed_img.scaled(ui->proc_frame->size(), Qt::KeepAspectRatio)); + ui->status->setText("Histogram equalization done!"); + proc_stat |= hist_done; +} + +void Interface::on_imgGammaCor_clicked() +{ + if(check_status()) + return; + QRegExp reg("[-+]?[0-9.]*"); + QString gamma_in = ui->gamma_input->text(); + double gamma_val; + if(reg.exactMatch(gamma_in)) + gamma_val = gamma_in.toDouble(); + else{ + ui->status->setText("Please set a valid value for gamma"); + return; + } + QImage proc = original_img.copy().toImage(); + uchar *bit = proc.bits(); + int h = proc.height(); + int w = proc.width(); + int i, j, dest_index; + for(i = 0; i < w; ++i){ + for(j = 0; j < h; ++j){ + dest_index = 4*i + 4*w*j; + bit[dest_index+0] = static_cast( + static_cast(255 * pow(static_cast(bit[dest_index+0]) / 255, gamma_val))); + bit[dest_index+1] = static_cast( + static_cast(255 * pow(static_cast(bit[dest_index+1]) / 255, gamma_val))); + bit[dest_index+2] = static_cast( + static_cast(255 * pow(static_cast(bit[dest_index+2]) / 255, gamma_val))); + } + } + processed_img = processed_img.fromImage(proc); + ui->proc_frame->setPixmap(processed_img.scaled(ui->proc_frame->size(), Qt::KeepAspectRatio)); + ui->status->setText("Gamma correction done! Input gamma value: " + QString::number(gamma_val)); + proc_stat |= gamma_done; +} + + +void Interface::on_imgReset_clicked() +{ + if(check_status()) + return; + processed_img = original_img.copy(); + ui->proc_frame->setPixmap(processed_img.scaled(ui->proc_frame->size(), Qt::KeepAspectRatio)); + ui->status->setText("Image reset to default state."); +} + +void Interface::on_actionSave_as_triggered() +{ + if(check_status()) + return; + QString saveFileName = QFileDialog::getSaveFileName(this, "Save processed image as..", "output.jpg", + "jpg, png, bmp (*.jpg, *.png *.bmp)"); + QString ext = QFileInfo(saveFileName).suffix(); + QPixmap pixmap; + if(ext=="jpg" || ext=="png" || ext=="bmp") + { + QFile outfile(saveFileName); + if (!outfile.open(QFile::WriteOnly)){ + QMessageBox::warning(this, "Warning", "Cannot save file: " + outfile.errorString()); + return; + } + processed_img.save(&outfile); + ui->status->setText("Saved processed image to: " + saveFileName); + } +} + +bool Interface::check_status(void) +{ + if(!static_cast(proc_stat & img_loaded)) + { + QMessageBox::information(this, "Info", "Please load an image file first"); + return true; + } + proc_stat = 1; + return false; +} + +bool Interface::check_status(const uchar proc) +{ + if(static_cast(proc_stat & proc)) + return true; + if(check_status()) + return true; + return false; +} diff --git a/interface.h b/interface.h new file mode 100644 index 0000000..cc04467 --- /dev/null +++ b/interface.h @@ -0,0 +1,50 @@ +#ifndef INTERFACE_H +#define INTERFACE_H + +#include +#include +#include +#include +#include +#include +#include + +namespace Ui { +class Interface; +} + +class Interface : public QMainWindow +{ + Q_OBJECT + +public: + explicit Interface(QWidget *parent = nullptr); + ~Interface(); + +private slots: + void on_actionOpen_triggered(); + + void on_imgInv_clicked(); + + void on_imgHistEql_clicked(); + + void on_imgGammaCor_clicked(); + + void on_actionSave_as_triggered(); + + void on_imgReset_clicked(); + +private: + Ui::Interface *ui; + QPixmap original_img; + QPixmap processed_img; + uchar proc_stat = 0; + const uchar img_loaded = 1 << 0; + const uchar hist_done = 1 << 1; + const uchar gamma_done = 1 << 2; + const uchar inv_done = 1 << 3; + bool check_status(void); + bool check_status(const uchar); +}; + +#endif // INTERFACE_H diff --git a/interface.ui b/interface.ui new file mode 100644 index 0000000..feacf4f --- /dev/null +++ b/interface.ui @@ -0,0 +1,222 @@ + + + Interface + + + + 0 + 0 + 850 + 550 + + + + Basic Image Processing + + + + + + 15 + 200 + 400 + 280 + + + + QFrame::Box + + + + + + Qt::AlignCenter + + + + + + 435 + 200 + 400 + 280 + + + + QFrame::Box + + + + + + Qt::AlignCenter + + + + + + 15 + 140 + 60 + 20 + + + + Status: + + + + + + 75 + 140 + 760 + 20 + + + + + + + + + + 15 + 60 + 170 + 30 + + + + Image Inversion + + + + + + 200 + 60 + 170 + 30 + + + + Histogram Equalization + + + + + + 385 + 60 + 170 + 30 + + + + Gamma Correction + + + + + + 740 + 60 + 80 + 29 + + + + Reset + + + + + + 400 + 100 + 55 + 20 + + + + Gamma: + + + + + + 460 + 100 + 70 + 25 + + + + 0.9 + + + Qt::AlignCenter + + + + + + 15 + 175 + 110 + 20 + + + + Original Image + + + + + + 435 + 175 + 110 + 20 + + + + Processed Image + + + + + + + 0 + 0 + 850 + 26 + + + + + File + + + + + + + + + + Open.. + + + + + Save as.. + + + + + + + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..fa3f7f2 --- /dev/null +++ b/main.cpp @@ -0,0 +1,11 @@ +#include "interface.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + Interface w; + w.show(); + + return a.exec(); +}