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\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.