5
\$\begingroup\$

I'm new to C++ programming, and I'm building an Image Editor software like Lightroom/CameraRaw from scratch. Apart from procedural code (actually seems that all the functions works correctly) my goal is to improve the Design of my classes. For this I used OpenCV just for basics functions like opening/reading, converting-colorSpace, and Qt creator, for the Ui.

as below:

ImgHandling.h

class ImgHandling
{
public:
    ImgHandling();

    cv::Mat calculateHist(cv::Mat img, int channel);
    QImage Mat2Qimg(cv::Mat img);

    void imgLoad(QString path);
    void imgSave(QString path);

    cv::Mat src;
    cv::Mat dst;
};

#endif // IMGHANDLING_H

ImgHandling.cpp

ImgHandling::ImgHandling() = default;

void ImgHandling::imgLoad(QString path){

    const std::string imagePath(path.toStdString());

    src = cv::imread(imagePath);
    cvtColor(src, src, CV_BGR2RGB);
    dst = src.clone();
}

void ImgHandling::imgSave(QString path){

    QImage qimg = Mat2Qimg(dst);

    if(!qimg.isNull())
        qimg.save(path);
    else
        QMessageBox::critical(nullptr, "Errore","Impossibile salvare il file");
}

cv::Mat ImgHandling::calculateHist(cv::Mat img, int channel){

    int k = 0, r = 255, g = 255, b = 255;
    switch (channel)
    {
        case 0:
             k = 0; r = 255; g = 0; b = 0;
        break;

        case 1:
             k = 1; r = 0; g = 255; b = 0;
        break;

        case 2:
            k = 2; r = 0; g = 0; b = 255;
        break;
    }

    int hist[256];
    for(int i = 0; i < 255; i++)
        hist[i] = 0;

    for(int i = 0; i < img.rows; i++)
        for(int j = 0; j < img.cols; j++)
            hist[(int)img.at<cv::Vec3b>(i,j)[k]]++;

    int hist_w = 299;
    int hist_h = 69;
    int bin_w = cvRound((double) hist_w/256);

    cv::Mat histImage(hist_h, hist_w, CV_8UC3, cv::Scalar(50, 50, 50));

    int max = hist[0];

    for(int i = 1; i < 256; i++)
        if(max < hist[i])
            max = hist[i];

    for(int i = 0; i < 255; i++)
            hist[i] = ((double)hist[i]/max)*histImage.rows;

    for(int i = 0; i < 255; i++)
        line(histImage, cv::Point(bin_w*(i), hist_h), cv::Point(bin_w*(i), hist_h - hist[i]), cv::Scalar(r,g,b), 1, 8, 0);

    return histImage;
}

QImage ImgHandling::Mat2Qimg(cv::Mat img){

    QImage qimg((const uchar*) img.data, img.cols, img.rows, img.step, QImage::Format_RGB888);
    qimg.bits();

    return qimg;
}

process.h

class process{    
public:
    process(cv::Mat& src, cv::Mat& dst);
    virtual ~process();

    virtual void doProcess() = 0;

protected:
    cv::Mat src;
    cv::Mat dst;
};

#endif // PROCESS_H

process.cpp

process::process(cv::Mat& s, cv::Mat& d) : src(s), dst(d){
}

process::~process(){
}

flip.h

#ifndef FLIP_H
#define FLIP_H

#include "process.h"

class flip : process
{
public:
    flip(cv::Mat& src, cv::Mat& dst, bool h = false);

    void doProcess() override;

private:
    bool is_Horiented; //false = verticale, true orizzontale
};

#endif // FLIP_H

flip.cpp

#include "flip.h"

flip::flip(cv::Mat& src, cv::Mat& dst, bool h) : process (src, dst), is_Horiented(h){
}

void flip::doProcess(){

    if(is_Horiented){
        for (int i = 0; i < src.rows; i++)
            for (int j = 0; j < src.cols; j++) {
                int x = -i + src.rows;
                dst.at<cv::Vec3b>(i,j) = src.at<cv::Vec3b>(x,j);
            }
    }else {
        for (int i = 0; i < src.rows; i++)
            for (int j = 0; j < src.cols; j++) {
                int y = -j + src.cols;
                dst.at<cv::Vec3b>(i,j) = src.at<cv::Vec3b>(i,y);
            }
    }
}

blur.h

#ifndef BLUR_H
#define BLUR_H

#include "kernels.h"

class Blur : Kernels
{
public:
    Blur(cv::Mat& src, cv::Mat& dst);

    void applyKernel() override; //Still need to implement cpp 

private:
    const int Blur_Mat[3][3] = {
        {1, 2, 1} ,
        {2, 4, 2} ,
        {1, 2, 1}
        };

};

#endif // BLUR_H

hsl_process.h

#ifndef HSL_PROCESS_H
#define HSL_PROCESS_H

#include "process.h"

class HSL_process : process
{
public:
    HSL_process(cv::Mat& src, cv::Mat& dst, int h = 0, int s = 0, int l = 0);

    void doProcess() override;

private:
    int hue;
    int saturation;
    int luminance;
};

#endif // HSL_PROCESS_H

hsl_process.cpp

#include "hsl_process.h"

    HSL_process::HSL_process(cv::Mat& src, cv::Mat& dst, int h, int s, int l)
        : process (src, dst), hue(h), saturation(s), luminance(l){
    }

    void HSL_process::doProcess(){
        cv::cvtColor(src, src, CV_RGB2HLS);
        cv::cvtColor(dst, dst, CV_RGB2HLS);

        for(int i = 0; i < src.rows; i++)
            for(int j = 0; j < src.cols; j++)
                for(int k = 0; k < 3; k++){
                    if(k == 0)  //_H
                        dst.at<cv::Vec3b>(i,j)[k] = cv::saturate_cast<uchar>(src.at<cv::Vec3b>(i,j)[k] + hue);
                    if(k == 1)  //_L
                        dst.at<cv::Vec3b>(i,j)[k] = cv::saturate_cast<uchar>(src.at<cv::Vec3b>(i,j)[k] + luminance);
                    if(k == 2)  //_S
                        dst.at<cv::Vec3b>(i,j)[k] = cv::saturate_cast<uchar>(src.at<cv::Vec3b>(i,j)[k] + saturation);
                }

        cv::cvtColor(src, src, CV_HLS2RGB);
        cv::cvtColor(dst, dst, CV_HLS2RGB);
    }

rgb_process.h

#ifndef RGB_PROCESS_H
#define RGB_PROCESS_H

#include "process.h"


class RGB_process : process
{
public:
    RGB_process(cv::Mat& src, cv::Mat& dst, int exp = 0, double c = 1, int r = 0, int g = 0, int b = 0);

    void doProcess() override;

private:
    int exposure_Val;
    double contrast_Val;
    int red_Val;
    int green_Val;
    int blue_Val;
};

#endif // RGB_PROCESS_H

rgb_process.cpp

#include "rgb_process.h"

RGB_process::RGB_process(cv::Mat& src, cv::Mat& dst, int exp, double c, int r, int g, int b)
    : process (src, dst), exposure_Val(exp), contrast_Val(c), red_Val(r), green_Val(g), blue_Val(b){
}

void RGB_process::doProcess(){

    for(int i = 0; i < src.rows; i++)
        for(int j = 0; j < src.cols; j++)
            for(int k = 0; k < 3; k++){
                if(k == 0)  //_R
                    dst.at<cv::Vec3b>(i,j)[k] = cv::saturate_cast<uchar>((src.at<cv::Vec3b>(i,j)[k] + exposure_Val + red_Val )*(259 * (contrast_Val + 255) / (255 * (259 - contrast_Val))));
                if(k == 1)  //_G
                    dst.at<cv::Vec3b>(i,j)[k] = cv::saturate_cast<uchar>((src.at<cv::Vec3b>(i,j)[k] + exposure_Val + green_Val )*(259 * (contrast_Val + 255) / (255 * (259 - contrast_Val))));
                if(k == 2)  //_B
                    dst.at<cv::Vec3b>(i,j)[k] = cv::saturate_cast<uchar>((src.at<cv::Vec3b>(i,j)[k] + exposure_Val + blue_Val )*(259 * (contrast_Val + 255) / (255 * (259 - contrast_Val))));
            }
}

kernels.h

#ifndef KERNELS_H
#define KERNELS_H

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>

class Kernels{
public:
    Kernels(cv::Mat& s, cv::Mat& d);
    virtual ~Kernels();

    virtual void applyKernel() = 0;


protected:
    cv::Mat src;
    cv::Mat dst;
};

#endif // KERNELS_H

kernels.cpp

#include "kernels.h"

Kernels::Kernels(cv::Mat& s, cv::Mat& d) : src(s), dst(d){
}

Kernels::~Kernels(){
}

matrix_filters.h

    #ifndef MATRIX_FILTERS_H
    #define MATRIX_FILTERS_H

    #include "process.h"

    class Matrix_Filters : process
    {
    public:
        Matrix_Filters(cv::Mat& src, cv::Mat& dst, std::string c = "BW");

        void doProcess() override;

    private:

        std::string choice;

        const double sepia_Mat[3][3] = {
            {0.393, 0.769, 0.189} ,
            {0.349, 0.686, 0.168} ,
            {0.272, 0.534, 0.131}
            };

        const double bw_Mat[3][3] = {
            {0.2126, 0.7152, 0.0722} ,
            {0.2126, 0.7152, 0.0722} ,
            {0.2126, 0.7152, 0.0722}
            };
    };

    #endif // MATRIX_FILTERS_H

matrix_filter.cpp

#include "matrix_filters.h"

Matrix_Filters::Matrix_Filters(cv::Mat& src, cv::Mat& dst, std::string c)
    : process (src, dst), choice(c) {
}

void Matrix_Filters::doProcess(){

    if(choice == "BW")
        for(int i = 0; i < src.rows; i++)
            for(int j = 0; j < src.cols; j++)
                for(int k = 0; k < 3; k++){
                    dst.at<cv::Vec3b>(i,j)[k] = 0;
                    for (int m = 0; m < 3; m++)
                        dst.at<cv::Vec3b>(i,j)[k] += (bw_Mat[k][m]*(src.at<cv::Vec3b>(i,j)[m]));
                }

    if(choice == "SEPIA")
        for(int i = 0; i < src.rows; i++)
            for(int j = 0; j < src.cols; j++)
                for(int k = 0; k < 3; k++){
                    dst.at<cv::Vec3b>(i,j)[k] = 0;
                    for (int m = 0; m < 3; m++)
                        dst.at<cv::Vec3b>(i,j)[k] += (sepia_Mat[k][m]*(src.at<cv::Vec3b>(i,j)[m]));
                }
}

rotation.h

#ifndef ROTATION_H
#define ROTATION_H

#include "process.h"

class rotation : process
{
public:
    rotation(cv::Mat& src, cv::Mat& dst, int a = 0);

    void doProcess() override;

protected:
    int angle;
};

#endif // ROTATION_H

rotation.cpp

#include "rotation.h"

rotation::rotation(cv::Mat& src, cv::Mat& dst, int a) : process (src, dst), angle(a){
}

void rotation::doProcess(){

    float rads = angle*3.1415926/180.0;
    float _cos = cos(-rads);
    float _sin = sin(-rads);
    float xcenter = (float)(src.cols)/2.0;
    float ycenter = (float)(src.rows)/2.0;

    for(int i = 0; i < src.rows; i++)
        for(int j = 0; j < src.cols; j++){

            int x = ycenter + ((float)(i)-ycenter)*_cos - ((float)(j)-xcenter)*_sin;
            int y = xcenter + ((float)(i)-ycenter)*_sin + ((float)(j)-xcenter)*_cos;
            if (x >= 0 && x < src.rows && y >= 0 && y < src.cols) {
                     dst.at<cv::Vec3b>(i ,j) = src.at<cv::Vec3b>(x, y);
                  }
            else
                dst.at<cv::Vec3b>(i ,j) = 128;
        }

}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "ui_mainwindow.h"

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>

#include "process.h"
#include "imghandling.h"
#include "rgb_process.h"
#include "hsl_process.h"
#include "matrix_filters.h"
#include "flip.h"
#include "rotation.h"
#include "blur.h"


namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow() override;

private slots:

    void on_open_btn_clicked();
    void on_save_btn_clicked();

    void on_exposure_slider_valueChanged();
    void on_contrast_slider_valueChanged();

    void on_red_slider_valueChanged();
    void on_green_slider_valueChanged();
    void on_blue_slider_valueChanged();

    void on_rotate_slider_valueChanged();
    void on_flipV_btn_clicked();
    void on_flipH_btn_clicked();

    void on_hue_slider_valueChanged();
    void on_saturation_slider_valueChanged();
    void on_luminance_slider_valueChanged();

    void on_hsl_btn_clicked();
    void on_ok_btn_clicked();

    void on_black_and_white_clicked();
    void on_sepia_btn_clicked();

    void on_tabWidget_currentChanged(){ handling.src = handling.dst.clone(); }

private:

    void updateUi(cv::Mat img);

    Ui::MainWindow *ui;
    ImgHandling handling;
};



#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include <QFileDialog>

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow){
    ui->setupUi(this);
}

MainWindow::~MainWindow(){
    delete ui;
}

void MainWindow::updateUi(cv::Mat img){

    cv::Mat hist_r = handling.calculateHist(img, 0);
    cv::Mat hist_g = handling.calculateHist(img, 1);
    cv::Mat hist_b = handling.calculateHist(img, 2);

    QImage qHr = handling.Mat2Qimg(hist_r);
    QImage qHg = handling.Mat2Qimg(hist_g);
    QImage qHb = handling.Mat2Qimg(hist_b);

    QImage qimg = handling.Mat2Qimg(img);

    ui->r_hist->setPixmap(QPixmap::fromImage(qHr).scaled(ui->r_hist->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
    ui->g_hist->setPixmap(QPixmap::fromImage(qHg).scaled(ui->g_hist->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
    ui->b_hist->setPixmap(QPixmap::fromImage(qHb).scaled(ui->b_hist->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));

    ui->img_lbl->setPixmap(QPixmap::fromImage(qimg).scaled(ui->img_lbl->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
}

void MainWindow::on_open_btn_clicked(){

    QString path = QFileDialog::getOpenFileName(nullptr, QObject::tr("Open File"), "", QObject::tr(".JPG (*.jpg , *.jpeg) ;; .PNG (*.png) ;; .TIFF (*.tiff , *.tif)"));

    if(!path.isEmpty()){

        handling.imgLoad(path);

        updateUi(handling.src);

        ui->red_slider->setEnabled(true);
        ui->red_spin->setEnabled(true);

        ui->green_slider->setEnabled(true);
        ui->green_spin->setEnabled(true);

        ui->blue_slider->setEnabled(true);
        ui->blue_spin->setEnabled(true);

        ui->exposure_slider->setEnabled(true);
        ui->exposure_spin->setEnabled(true);

        ui->contrast_slider->setEnabled(true);
        ui->contrast_spin->setEnabled(true);

        ui->reset_btn->setEnabled(true);
        ui->hsl_btn->setEnabled(true);
        ui->save_btn->setEnabled(true);

    }
}

void MainWindow::on_save_btn_clicked(){

    QString path = QFileDialog::getSaveFileName(this, QObject::tr("Save File"), "", QObject::tr(".JPG (*.jpg) ;; .PNG (*.png) ;; .TIFF (*.tif)"));

    if(!path.isEmpty())
        handling.imgSave(path);
}

void MainWindow::on_exposure_slider_valueChanged(){

    RGB_process m (handling.src, handling.dst, ui->exposure_slider->value(), ui->contrast_slider->value(),
                   ui->red_slider->value(), ui->green_slider->value(), ui->blue_slider->value());

    m.doProcess();
    updateUi(handling.dst);

}

void MainWindow::on_contrast_slider_valueChanged(){

    RGB_process m (handling.src, handling.dst, ui->exposure_slider->value(), ui->contrast_slider->value(),
                   ui->red_slider->value(), ui->green_slider->value(), ui->blue_slider->value());

    m.doProcess();
    updateUi(handling.dst);
}

void MainWindow::on_red_slider_valueChanged(){

    RGB_process m (handling.src, handling.dst, ui->exposure_slider->value(), ui->contrast_slider->value(),
                   ui->red_slider->value(), ui->green_slider->value(), ui->blue_slider->value());

    m.doProcess();
    updateUi(handling.dst);

}

void MainWindow::on_green_slider_valueChanged(){

    RGB_process m (handling.src, handling.dst, ui->exposure_slider->value(), ui->contrast_slider->value(),
                   ui->red_slider->value(), ui->green_slider->value(), ui->blue_slider->value());

    m.doProcess();
    updateUi(handling.dst);
}

void MainWindow::on_blue_slider_valueChanged(){

    RGB_process m (handling.src, handling.dst, ui->exposure_slider->value(), ui->contrast_slider->value(),
                   ui->red_slider->value(), ui->green_slider->value(), ui->blue_slider->value());

    m.doProcess();
    updateUi(handling.dst);
}

void MainWindow::on_rotate_slider_valueChanged(){

    rotation m(handling.src, handling.dst, ui->rotate_slider->value());
    m.doProcess();
    updateUi(handling.dst);
}

void MainWindow::on_flipV_btn_clicked(){

    flip m(handling.src, handling.dst, true);
    m.doProcess();
    updateUi(handling.dst);
    handling.src = handling.dst.clone();
}

void MainWindow::on_flipH_btn_clicked()
{
    flip m(handling.src, handling.dst, false);
    m.doProcess();
    updateUi(handling.dst);

    handling.src = handling.dst.clone();
}

void MainWindow::on_hue_slider_valueChanged(){

    HSL_process process(handling.src, handling.dst, ui->hue_slider->value(), ui->saturation_slider->value(), ui->luminance_slider->value());
    process.doProcess();
    updateUi(handling.dst);
}

void MainWindow::on_saturation_slider_valueChanged(){

    HSL_process process(handling.src, handling.dst, ui->hue_slider->value(), ui->saturation_slider->value(), ui->luminance_slider->value());
    process.doProcess();
    updateUi(handling.dst);
}

void MainWindow::on_luminance_slider_valueChanged(){

    HSL_process process(handling.src, handling.dst, ui->hue_slider->value(), ui->saturation_slider->value(), ui->luminance_slider->value());
    process.doProcess();
    updateUi(handling.dst);
}

void MainWindow::on_hsl_btn_clicked(){

    handling.src = handling.dst.clone();

    ui->red_slider->setValue(0);
    ui->red_slider->setEnabled(false);
    ui->red_spin->setEnabled(false);
    ui->green_slider->setValue(0);
    ui->green_slider->setEnabled(false);
    ui->green_spin->setEnabled(false);
    ui->blue_slider->setValue(0);
    ui->blue_slider->setEnabled(false);
    ui->blue_spin->setEnabled(false);

    ui->exposure_slider->setValue(0);
    ui->exposure_slider->setEnabled(false);
    ui->exposure_spin->setEnabled(false);

    ui->contrast_slider->setValue(0);
    ui->contrast_slider->setEnabled(false);
    ui->contrast_spin->setEnabled(false);

    ui->exposure_slider->setValue(0);
    ui->exposure_slider->setEnabled(false);
    ui->exposure_spin->setEnabled(false);

    ui->hue_slider->setEnabled(true);
    ui->hue_spin->setEnabled(true);
    ui->saturation_slider->setEnabled(true);
    ui->saturation_spin->setEnabled(true);
    ui->luminance_slider->setEnabled(true);
    ui->luminance_spin->setEnabled(true);
    ui->ok_btn->setEnabled(true);
    ui->hsl_btn->setEnabled(false);
}

void MainWindow::on_ok_btn_clicked(){

    handling.src = handling.dst.clone();

    ui->hue_slider->setValue(0);
    ui->saturation_slider->setValue(0);
    ui->luminance_slider->setValue(0);
    ui->hue_slider->setEnabled(false);
    ui->hue_spin->setEnabled(false);
    ui->saturation_slider->setEnabled(false);
    ui->saturation_spin->setEnabled(false);
    ui->luminance_slider->setEnabled(false);
    ui->luminance_spin->setEnabled(false);
    ui->ok_btn->setEnabled(false);
    ui->hsl_btn->setEnabled(true);

    ui->red_slider->setEnabled(true);
    ui->red_spin->setEnabled(true);

    ui->green_slider->setEnabled(true);
    ui->green_spin->setEnabled(true);

    ui->blue_slider->setEnabled(true);
    ui->blue_spin->setEnabled(true);

    ui->exposure_slider->setEnabled(true);
    ui->exposure_spin->setEnabled(true);

    ui->contrast_slider->setEnabled(true);
    ui->contrast_spin->setEnabled(true);
}

void MainWindow::on_black_and_white_clicked(){

    Matrix_Filters bw(handling.src, handling.dst, "BW");
    bw.doProcess();
    updateUi(handling.dst);
}

void MainWindow::on_sepia_btn_clicked(){

    Matrix_Filters sepia(handling.src, handling.dst, "SEPIA");
    sepia.doProcess();
    updateUi(handling.dst);
}
\$\endgroup\$
0

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Browse other questions tagged or ask your own question.