目录

前言

一、总体设计

1.1 主要功能

1.2 UML类图

二、代码实现

2.1 父类method

2.2 子类线性增强算法

2.3 gamma校正

2.4 管理类Manage.h

2.5 选择图像

2.6 保存和撤销图像

三、测试与分析

四、完整代码

4.1 method.h

4.2 method.cpp

4.3 manage.h

4.4 manage.cpp

4.5 mainwindow.cpp

总结


前言

本次课设实现的简易图像处理主要采用了C++和OpenCV+Qt结合的方式,利用C++类的封装、多态、继承继承机制实现了对输入图像进行线性灰度变换、对数变换、gamma变换、拉普拉斯算子增强、直方图均衡化、rbg2gray、模糊图像增强七种处理方式,在线性变换和gamma变换的时候利用QMessageBox类实现弹窗功能,用户可以自己输入相关值对图像进行增强或对比度降低的操作,同时还利用栈的方式对图像操作进行撤销,最后将算法打包成了动态库,采用隐式调用.dll文件的方式实现了代码共享。


提示:以下是本篇文章正文内容,下面案例可供参考

一、总体设计

1.1 主要功能

(1)选择一张图像,对图像进行线性灰度变换、对数变换、gamma变换、拉普拉斯算子增强、直方图均衡化、rbg2gray、模糊图像增强处理;

(2)显示图像、将结果图像保存到文件;

(3)对图像所做操作进行撤销;

(4)利用对话框的形式显示算法简介。

系统实现:首先将算法抽象成一个类模板method,然后对类method针对数据类型<cv::Mat>进行特化,将成员函数runMethod设置为纯虚函数,接着将线性灰度变换、对数变换、gamma变换、拉普拉斯算子增强、直方图均衡化、rbg2gray六种算法类作为子类继承模板特化类,分别在其内部实现runMethod方法。同时定义一个管理类manage,对图像的保存撤销操作进行管理。

1.2 UML类图

二、代码实现

2.1 父类method

将图像增强算法抽象成一个类method,并将其写成一个模板类,然后针对Mat数据类型对类实现特化,编写setImage,getImage,imag_gray,isEmpty成员函数。

template<typename T>
class METHOD_EXPORT method
{
public:method();method(string str)   //带参构造函数{setImage(str);}virtual ~method(){cout<<"end of method!"<<endl;}void setImage(string str){cv::Mat img = imread(str);   //cv::Mat是一种数据类型this->img = img;}cv::Mat getImage(){return this->img;}cv::Mat isEmpty(){if(img.empty()){cout<<"读入图片失败!"<<endl;}}virtual T runMethod()=0;        //纯虚函数,多态性
private:cv::Mat img;    //处理的图像
};template <>
class method<cv::Mat>    //模板类特化
{
public:method();method(string str){setImage(str);}virtual ~method(){cout<<"end of method!"<<endl;}void setImage(string str){cv::Mat img = imread(str);   //cv::Mat是一种数据类型this->img = img;}cv::Mat getImage(){return this->img;}cv::Mat img_gray(cv::Mat s)    //彩色图转换为灰度图像{cv::Mat gray;if(!isEmpty()){cvtColor(s,gray,CV_BGR2GRAY);return gray;}}bool isEmpty(){if(img.empty()){cout<<"Loading false!"<<endl;return true;}elsereturn false;}virtual Mat runMethod()=0;
private:cv::Mat img;
};

2.2 子类线性增强算法

线性灰度变换的公式是:g(x,y)=k*f(x,y)+b,类LinearChange继承父类method,定义两个私有变量k,b,根据用户需要进行输入,然后重写父类method方法,对输入图像逐像元进行线性变换。

//图像的灰度线性变化
class LinearChange:public method<cv::Mat>
{
public:LinearChange();LinearChange(string str):method(str){};~LinearChange(){cout<<"LInearChange is end!"<<endl;}cv::Mat runMethod();friend class MainWindow;
private:double k;         //线性变换方程:g(x,y)=k*f(x,y)+bdouble b;
};
Mat LinearChange::runMethod()
{Mat srcImg=this->getImage();if(srcImg.empty()){cout<<"读入图片失败"<<endl;}int RowsNum = srcImg.rows;int ColsNum = srcImg.cols;Mat dstImg(srcImg.size(),srcImg.type());//进行遍历图像像素,对每个像素进行相应的线性变换for(int i=0;i<RowsNum;i++){for(int j=0;j<ColsNum;j++){//c为遍历图像的三个通道for(int c=0;c<3;c++){//imread读取到的Mat图像数据,都是用uchar类型的数据存储,Vec3b可以认为是vector<uchar,3>dstImg.at<Vec3b>(i,j)[c] = saturate_cast<uchar>(k*(srcImg.at<Vec3b>(i,j)[c])+b);}}}return dstImg;
}

2.3 gamma校正

gamma变换的公式:s=c*f(x,y)^gamma,当gamma>1时,低灰度值区间的动态范围变小,高灰度值区间的动态范围变大,整体来说降低图像对比度,当0<gamma<1时,增强图像对比度。定义一个私有变量gamma,由用户自己输入,首先对0~255之间的每个整数执行一次预补偿操作,将其对应值存入一个预先建立的gamma校正查找表LUT,然后根据LUT对输入图像依次逐像元修改,最后返回结果图像。

//图像的gamma校正
class gammaTransform:public method<cv::Mat>
{
public:gammaTransform();gammaTransform(string str):method(str){};~gammaTransform(){cout<<"gammaTransform is end!"<<endl;}cv::Mat runMethod();friend class MainWindow;
private:double gamma;  //归一化-->预补偿-->反归一化;s=pow(f,r)
};
Mat gammaTransform::runMethod()
{Mat srcImg=this->getImage();if(srcImg.empty()){cout<<"读入图片失败"<<endl;}//gamma校正查找表unsigned char LUT[256];for(int i = 0; i < 256; i++){float f=(i+0.5f)/255;f=(float)pow(f,gamma);LUT[i] = saturate_cast<uchar>(f*255.0f-0.5f);}Mat imagegamma = srcImg.clone();//判断图像的通道数if(srcImg.channels()==1)   //灰度图像{//迭代器MatIterator_<uchar> iterator = imagegamma.begin<uchar>();MatIterator_<uchar> iteratorEnd = imagegamma.end<uchar>();for(;iterator!=iteratorEnd;iterator++){*iterator = LUT[(*iterator)];}}else{//输入通道为三通道时,需要对每个通道分别进行变换MatIterator_<Vec3b> iterator = imagegamma.begin<Vec3b>();MatIterator_<Vec3b> iteratorEnd = imagegamma.end<Vec3b>();//通过查表进行转换for(;iterator!=iteratorEnd;iterator++){(*iterator)[0] = LUT[((*iterator)[0])];(*iterator)[1] = LUT[((*iterator)[1])];(*iterator)[2] = LUT[((*iterator)[2])];}}return imagegamma;
}

2.4 管理类Manage.h

定义一个保存图像的栈,将每次操作的图像入栈,删除之后出栈,stack_Number用来计数栈中元素,sign作为标识,以便撤销处理,如果sign == false且stack_Number == 0,则弹出误操作提示框,如果sian==true且stack_Number == 0 则说明已撤销至初始状态。

class manage
{
public:string imagePath;            //图片路径stack<cv::Mat> imageStack;   //存储图片的栈//int stack_Number = 0;        //初始化栈中元素为空manage():imagePath("none"){};~manage();void giveImage_path(string path);void saveImage(string path);void pushStack(Mat image);      //将执行操作的图片入栈cv::Mat undoImage();   //撤销
};
#include "manage.h"
#include<iostream>
using namespace std;
manage::~manage()
{}
void manage::giveImage_path(string path)
{this->imagePath = path;
}
void manage::saveImage(string path)
{cv::Mat element;element=this->imageStack.top();imwrite(path,element);
}
void manage::pushStack(Mat image) //操作图像入栈
{this->imageStack.push(image);//this->stack_Number++;
}
cv::Mat manage::undoImage() //撤销
{cv::Mat dstImg;if(this->imageStack.size()==1){dstImg = this->imageStack.top();this->imageStack.pop();//this->stack_Number--;return dstImg;}else if(this->imageStack.size()>1){this->imageStack.pop();//this->stack_Number--;return this->imageStack.top();}
}

2.5 选择图像

//选择图像
void MainWindow::on_pushButton_9_clicked()
{QString fileName,openPath;fileName=QFileDialog::getOpenFileName(this,tr("选择图像"),"D:/FengGeQY/imgData/RGB4.jpg",tr("Images (*.png *.bmp *.jpg *.tif *.GIF )"));//四个参数说明//1、第一个参数parent,用于指定父组件//2、第二个参数caption,是对话框的标题//3、dir,是对话框显示时默认打开的目录//4、filter,是对话框的后缀名过滤器,比如我们使用"Image Files(*.jpg *.png)"就让它只能显示后缀名是jpg或者png的文件。//4、如果需要使用多个过滤器,使用";;"分割,比如"JPEG Files(*.jpg);;PNG Files(*.png)";if(!fileName.isEmpty()){QImage* img=new QImage;if(! ( img->load(fileName) ) ){ //加载图像QMessageBox::information(this,tr("打开图像失败"),tr("打开图像失败!"));delete img;}//将所得路径保存到图片管理栈中的路径string s1 =fileName.toStdString();imgManage.giveImage_path(s1);//显示图片Mat image=imread(s1,1);QImage qimage = Mat2QImage(image);ui->label_4->setPixmap(QPixmap::fromImage(qimage).scaled(ui->label_4->size()));//在文本框中显示图片路径openPath = QFileInfo(fileName).filePath();ui->lineEdit->setText(openPath);}
}

2.6 保存和撤销图像

//保存图像
void MainWindow::on_pushButton_10_clicked()
{//判断管理栈是否为空if(!(imgManage.imageStack.empty())){  //保存文件对话框API函数:getSaveFileNameQString filename1 = QFileDialog::getSaveFileName(this, tr("保存图像"), "", tr("Images (*.png *.bmp *.jpg *jpeg *GIF)"));//获取保存的图像的指针QScreen *screen = QGuiApplication::primaryScreen();//得到图像screen->grabWindow(ui->label_6->winId()).save(filename1);}else{int ret = QMessageBox::information(this,QObject::tr("提示"),QObject::tr("没有可保存的图像,请重新确认"),QMessageBox::Ok);if(ret == QMessageBox::Ok)qDebug()<<QObject::tr("提示!");}
}
//撤销图像
void MainWindow::on_pushButton_11_clicked()
{if(this->imgManage.imageStack.empty()){int ret = QMessageBox::critical(this,QObject::tr("误操作提示"),QObject::tr("没有可撤销的操作,请操作后再撤消!"),QMessageBox::YesAll);if(ret == QMessageBox::YesAll){qDebug()<<QObject::tr("提示!");}}else{cv::Mat img =this->imgManage.undoImage();//if(this->imgManage.imageStack.size()==0)if(this->imgManage.imageStack.empty()){ui->label_6->setText("            已无更多操作,恢复至初始状态       ");}else{QImage qimage=Mat2QImage(img);ui->label_6->setPixmap(QPixmap::fromImage(qimage).scaled(ui->label_6->size()));}}
}

三、测试与分析

(1)初始界面

图 3-1 系统初始界面

(2)依次展示系统功能

图 3-2 线性灰度变换输入系数K

图 3-3 线性灰度变换结果

图 3-4 对数灰度变换c=3时的结果

                                            图 3-5 gamma=0.4时的结果

图 3-6 gamma=2.6时的结果

图 3-7 拉普拉斯算子增强结果

图 3-8 直方图均衡化增强结果

                                      图 3-9  转换为灰度图像

图 3-10  模糊图像变清晰

(3)保存图像

图 3-11 选择保存路径

图 3-12  保存结果展示

(4)撤销操作

图 3-13  撤销结果展示

(5)算法简介按钮

图 3-14 算法简介按钮结果展示

根据测试结果来看,拉普拉斯算子增强算法对对比度高的图像增强效果不佳,会使图像过于偏亮、失真,因为它是根据拉普拉斯算子局部增强,如果再图像中一个较暗的区域中出现一个亮点,那么用拉普拉斯算子会使这个亮点变得更亮。通常适用于以突出图像中孤立点、孤立线或线端点为目的的场合。接下来用一张对比度低的图像测试:

可以看到图像对比度提升了不少,使图像细节更清晰。还有模糊图像变清晰算法也是只有部分增强,没有达到理想效果。

(6)动态库

四、完整代码

4.1 method.h

#ifndef METHOD_H
#define METHOD_H#include "method_global.h"
#include<iostream>
using namespace std;
#include<string>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include<opencv2/imgproc/types_c.h>
using namespace cv;
template<typename T>
class METHOD_EXPORT method
{
public:method();method(string str)   //带参构造函数{setImage(str);}virtual ~method(){cout<<"end of method!"<<endl;}void setImage(string str){cv::Mat img = imread(str);   //cv::Mat是一种数据类型this->img = img;}cv::Mat getImage(){return this->img;}cv::Mat isEmpty(){if(img.empty()){cout<<"读入图片失败!"<<endl;}}virtual T runMethod()=0;        //纯虚函数,多态性
private:cv::Mat img;    //处理的图像
};template <>
class method<cv::Mat>    //模板类特化
{
public:method();method(string str){setImage(str);}virtual ~method(){cout<<"end of method!"<<endl;}void setImage(string str){cv::Mat img = imread(str);   //cv::Mat是一种数据类型this->img = img;}cv::Mat getImage(){return this->img;}cv::Mat img_gray(cv::Mat s)    //彩色图转换为灰度图像{cv::Mat gray;if(!isEmpty()){cvtColor(s,gray,CV_BGR2GRAY);return gray;}}bool isEmpty(){if(img.empty()){cout<<"Loading false!"<<endl;return true;}elsereturn false;}virtual Mat runMethod()=0;
private:cv::Mat img;
};
//图像的灰度线性变化
class LinearChange:public method<cv::Mat>
{
public:LinearChange();LinearChange(string str):method(str){};~LinearChange(){cout<<"LInearChange is end!"<<endl;}cv::Mat runMethod();friend class MainWindow;
private:double k;         //线性变换方程:g(x,y)=k*f(x,y)+bdouble b;};
//图像的对数变换
class LogTransform:public method<cv::Mat>
{
public:LogTransform();LogTransform(string str):method(str){};~LogTransform(){cout<<"LogTransform is end!"<<endl;}cv::Mat runMethod();friend class MainWindow;
private:double c;        //对数变换形式:s=c*log(1+r)
};
//图像的gamma校正
class gammaTransform:public method<cv::Mat>
{
public:gammaTransform();gammaTransform(string str):method(str){};~gammaTransform(){cout<<"gammaTransform is end!"<<endl;}cv::Mat runMethod();friend class MainWindow;
private:double gamma;  //归一化-->预补偿-->反归一化;s=pow(f,r)
};//拉普拉斯算子增强局部的图像对比度
class LaplaeOperator:public method<cv::Mat>
{
public:LaplaeOperator();LaplaeOperator(string str):method(str){};~LaplaeOperator(){cout<<"LaplaeOperator is end!"<<endl;}cv::Mat runMethod();};//直方图均衡化
class HistogramEq:public method<cv::Mat>
{
public:HistogramEq();HistogramEq(string str):method(str){};~HistogramEq(){cout<<"HistorgramEq is end!"<<endl;}cv::Mat runMethod();
};
class Transform:public method<cv::Mat>
{
public:Transform();Transform(string str):method(str){};~Transform(){cout<<"Transform is end!"<<endl;}cv::Mat runMethod(){cout<<"this is a empty!"<<endl;}//模糊图片变清晰cv::Mat Emphasize();cv::Mat RGB_to_Gray()     //彩色图像转换为灰度图像{//转换公式:Gray = 0.2989*R +0.5870*G + 0.1140*B//速度优化(将浮点运算转换为整数运算):Gray = (0.2989*R +0.5870*G + 0.1140*B)/10000-->76*R+150*G+30*BMat imgRGB = this->getImage();//创建结果图像Mat imgGray = Mat(imgRGB.rows, imgRGB.cols, CV_8UC1);for(int i = 0 ;i < imgRGB.rows ; i++){for(int j = 0 ;j < imgRGB.cols ; j++){//OpenCV读取彩色影像的是BGR的顺序uchar B = imgRGB.at<Vec3b>(i,j)[0];uchar G = imgRGB.at<Vec3b>(i,j)[1];uchar R = imgRGB.at<Vec3b>(i,j)[2];imgGray.at<uchar>(i,j) = 0.2126*R + 0.7152*G + 0.0722*B;  //两种效果差不多//imgGray.at<uchar>(i,j) = 0.2989*R + 0.587*G + 0.114*B;}}return imgGray;}
};#endif // METHOD_H

4.2 method.cpp

#include "method.h"//图像的灰度线性变化
Mat LinearChange::runMethod()
{Mat srcImg=this->getImage();if(srcImg.empty()){cout<<"读入图片失败"<<endl;}int RowsNum = srcImg.rows;int ColsNum = srcImg.cols;Mat dstImg(srcImg.size(),srcImg.type());//进行遍历图像像素,对每个像素进行相应的线性变换for(int i=0;i<RowsNum;i++){for(int j=0;j<ColsNum;j++){//c为遍历图像的三个通道for(int c=0;c<3;c++){//imread读取到的Mat图像数据,都是用uchar类型的数据存储,Vec3b可以认为是vector<uchar,3>dstImg.at<Vec3b>(i,j)[c] = saturate_cast<uchar>(k*(srcImg.at<Vec3b>(i,j)[c])+b);}}}return dstImg;
}
//图像的对数变换
Mat LogTransform::runMethod()
{Mat srcImg=this->getImage();if(srcImg.empty()){cout<<"读入图片失败"<<endl;}//转换成32位浮点数Mat imageLog(srcImg.size(),CV_32FC3);for(int i = 0;i < srcImg.rows; i++){for(int j = 0;j < srcImg.cols; j++){imageLog.at<Vec3f>(i,j)[0] = c*log(1+srcImg.at<Vec3b>(i,j)[0]);imageLog.at<Vec3f>(i,j)[1] = c*log(1+srcImg.at<Vec3b>(i,j)[1]);imageLog.at<Vec3f>(i,j)[2] = c*log(1+srcImg.at<Vec3b>(i,j)[2]);}}//归一化到0~255normalize(imageLog,imageLog,0,255,CV_MINMAX);//convertScaleAbs():先缩放元素再取绝对值,最后转换格式为8bit型//这里仅为将格式转换为8bit型convertScaleAbs(imageLog,imageLog);return imageLog;
}//图像的gamma校正
Mat gammaTransform::runMethod()
{Mat srcImg=this->getImage();if(srcImg.empty()){cout<<"读入图片失败"<<endl;}//gamma校正查找表unsigned char LUT[256];for(int i = 0; i < 256; i++){float f=(i+0.5f)/255;f=(float)pow(f,gamma);LUT[i] = saturate_cast<uchar>(f*255.0f-0.5f);}Mat imagegamma = srcImg.clone();//判断图像的通道数if(srcImg.channels()==1)   //灰度图像{//迭代器MatIterator_<uchar> iterator = imagegamma.begin<uchar>();MatIterator_<uchar> iteratorEnd = imagegamma.end<uchar>();for(;iterator!=iteratorEnd;iterator++){*iterator = LUT[(*iterator)];}}else{//输入通道为三通道时,需要对每个通道分别进行变换MatIterator_<Vec3b> iterator = imagegamma.begin<Vec3b>();MatIterator_<Vec3b> iteratorEnd = imagegamma.end<Vec3b>();//通过查表进行转换for(;iterator!=iteratorEnd;iterator++){(*iterator)[0] = LUT[((*iterator)[0])];(*iterator)[1] = LUT[((*iterator)[1])];(*iterator)[2] = LUT[((*iterator)[2])];}}return imagegamma;
}//拉普拉斯算子增强局部的图像对比度
Mat LaplaeOperator::runMethod()
{Mat image=this->getImage();if(image.empty()){cout<<"读入图像失败"<<endl;}Mat imageLaplace;Mat kernel = (Mat_<float>(3,3)<<0,-1,0,0,5,0,0,-1,0);//Mat kernel = (Mat_<float>(3,3)<<1,1,1,1,-8,1,1,1,1);//filter2D():opencv进行图像卷积运算的函数filter2D(image,imageLaplace,CV_8UC3,kernel);return imageLaplace;
}//彩色直方图均衡化
Mat HistogramEq::runMethod()
{Mat image=this->getImage();if(image.empty()){cout<<"读入图像失败"<<endl;}Mat imageHist;Mat imageHistRGB[3];//通道分离,opencv中三个通道顺序B,G,Rsplit(image,imageHistRGB);for(int i = 0 ; i < 3 ; i++){//直方图均衡化函数,第一个参数:8位单通道图像,第二个参数:目标图像equalizeHist(imageHistRGB[i],imageHistRGB[i]);}//合并通道,参数依次是图像矩阵数据,要合并矩阵的个数,输出merge(imageHistRGB,3,imageHist);return imageHist;
}
//模糊图片变清晰
cv::Mat Transform::Emphasize()
{//公式res= round(input-mean)*factor))+input//等价于在MaskHeight、MaskWidth的空间内 中心化后增加方差Mat image=this->getImage();    //输入的图像Mat dstImg;                   //输出的图像Mat mean;int MaskWidth=20,MaskHeight=20;float factor = 1.3;//等价于求指定范围窗口内的均值blur(image,mean,Size(MaskWidth,MaskHeight));dstImg.create(image.size(),image.type());if(image.type()==CV_8UC1){for(int i=0;i<image.rows;i++){uchar *rptr = image.ptr<uchar>(i);uchar *mptr = mean.ptr<uchar>(i);uchar *optr = dstImg.ptr<uchar>(i);for(int j=0;j<dstImg.cols;j++){optr[j] = saturate_cast<uchar>( round((rptr[j] - mptr[j])*factor)+rptr[j]*1.0f);}}}else if(image.type()==CV_8UC3){for(int i=0;i<image.rows;i++){uchar *rptr = image.ptr<uchar>(i);uchar *mptr = mean.ptr<uchar>(i);uchar *optr = dstImg.ptr<uchar>(i);for(int j=0;j<dstImg.cols;j++){//饱和转换 小于0的值会被置为0 大于255的值会被置为255optr[j*3] = saturate_cast<uchar>(round((rptr[j * 3] - mptr[j * 3])*factor) + rptr[j * 3] * 1.0f);optr[j*3+1] = saturate_cast<uchar>(round((rptr[j * 3+1] - mptr[j * 3+1])*factor) + rptr[j * 3+1] * 1.0f);optr[j*3+2] = saturate_cast<uchar>(round((rptr[j * 3+2] - mptr[j * 3+2])*factor) + rptr[j * 3+2] * 1.0f);}}}return dstImg;
}

4.3 manage.h

#ifndef MANAGE_H
#define MANAGE_H
#include"method.h"
#include<stack>
class manage
{
public:string imagePath;            //图片路径stack<cv::Mat> imageStack;   //存储图片的栈//int stack_Number = 0;        //初始化栈中元素为空manage():imagePath("none"){};~manage();void giveImage_path(string path);void saveImage(string path);void pushStack(Mat image);      //将执行操作的图片入栈cv::Mat undoImage();   //撤销
};
#endif // MANAGE_H

4.4 manage.cpp

#include "manage.h"
#include<iostream>
using namespace std;
manage::~manage()
{}
void manage::giveImage_path(string path)
{this->imagePath = path;
}
void manage::saveImage(string path)
{cv::Mat element;element=this->imageStack.top();imwrite(path,element);
}
void manage::pushStack(Mat image) //操作图像入栈
{this->imageStack.push(image);//this->stack_Number++;
}
cv::Mat manage::undoImage() //撤销
{cv::Mat dstImg;if(this->imageStack.size()==1){dstImg = this->imageStack.top();this->imageStack.pop();//this->stack_Number--;return dstImg;}else if(this->imageStack.size()>1){this->imageStack.pop();//this->stack_Number--;return this->imageStack.top();}
}

4.5 mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
using namespace cv;
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);}MainWindow::~MainWindow()
{delete ui;
}QImage Mat2QImage(const cv::Mat &mat){switch (mat.type()){// 8-bit, 4 channelcase CV_8UC4:{QImage image(mat.data, mat.cols, mat.rows, static_cast<int>(mat.step), QImage::Format_ARGB32);return image;}// 8-bit, 3 channelcase CV_8UC3:{QImage image(mat.data, mat.cols, mat.rows, static_cast<int>(mat.step), QImage::Format_RGB888);return image.rgbSwapped();}// 8-bit, 1 channelcase CV_8UC1:{#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)QImage image(mat.data, mat.cols, mat.rows, static_cast<int>(mat.step), QImage::Format_Grayscale8);#elsestatic QVector<QRgb>  sColorTable;// only create our color table the first timeif (sColorTable.isEmpty()){sColorTable.resize( 256 );for ( int i = 0; i < 256; ++i ){sColorTable[i] = qRgb( i, i, i );}}QImage image(mat.data, mat.cols, mat.rows, static_cast<int>(mat.step), QImage::Format_Indexed8 );image.setColorTable(sColorTable);#endifreturn image;}// wrongdefault:qDebug() << "ERROR: Mat could not be converted to QImage.";break;}return QImage();
}//void MainWindow::on_pushButton_7_clicked()
//{
//    //QMessageBox::information(NULL,"这是测试","123456",QMessageBox::Yes | QMessageBox::No,QMessageBox::Yes);
//    createMenu();
//}//模糊图片变清晰
void MainWindow::on_pushButton_8_clicked()
{Transform img = Transform(imgManage.imagePath);cv::Mat run = img.Emphasize();this->imgManage.imageStack.push(run);QImage qimage=Mat2QImage(run);ui->label_6->setPixmap(QPixmap::fromImage(qimage).scaled(ui->label_6->size()));}
//选择图像
void MainWindow::on_pushButton_9_clicked()
{QString fileName,openPath;fileName=QFileDialog::getOpenFileName(this,tr("选择图像"),"D:/FengGeQY/imgData/RGB4.jpg",tr("Images (*.png *.bmp *.jpg *.tif *.GIF )"));//四个参数说明//1、第一个参数parent,用于指定父组件//2、第二个参数caption,是对话框的标题//3、dir,是对话框显示时默认打开的目录//4、filter,是对话框的后缀名过滤器,比如我们使用"Image Files(*.jpg *.png)"就让它只能显示后缀名是jpg或者png的文件。//4、如果需要使用多个过滤器,使用";;"分割,比如"JPEG Files(*.jpg);;PNG Files(*.png)";if(!fileName.isEmpty()){QImage* img=new QImage;if(! ( img->load(fileName) ) ){ //加载图像QMessageBox::information(this,tr("打开图像失败"),tr("打开图像失败!"));delete img;}//将所得路径保存到图片管理栈中的路径string s1 =fileName.toStdString();imgManage.giveImage_path(s1);//显示图片Mat image=imread(s1,1);QImage qimage = Mat2QImage(image);ui->label_4->setPixmap(QPixmap::fromImage(qimage).scaled(ui->label_4->size()));//在文本框中显示图片路径openPath = QFileInfo(fileName).filePath();ui->lineEdit->setText(openPath);}
}
//保存图像
void MainWindow::on_pushButton_10_clicked()
{//判断管理栈是否为空if(!(imgManage.imageStack.empty())){  //保存文件对话框API函数:getSaveFileNameQString filename1 = QFileDialog::getSaveFileName(this, tr("保存图像"), "", tr("Images (*.png *.bmp *.jpg *jpeg *GIF)"));//获取保存的图像的指针QScreen *screen = QGuiApplication::primaryScreen();//得到图像screen->grabWindow(ui->label_6->winId()).save(filename1);}else{int ret = QMessageBox::information(this,QObject::tr("提示"),QObject::tr("没有可保存的图像,请重新确认"),QMessageBox::Ok);if(ret == QMessageBox::Ok)qDebug()<<QObject::tr("提示!");}
}
//撤销图像
void MainWindow::on_pushButton_11_clicked()
{if(this->imgManage.imageStack.empty()){int ret = QMessageBox::critical(this,QObject::tr("误操作提示"),QObject::tr("没有可撤销的操作,请操作后再撤消!"),QMessageBox::YesAll);if(ret == QMessageBox::YesAll){qDebug()<<QObject::tr("提示!");}}else{cv::Mat img =this->imgManage.undoImage();//if(this->imgManage.imageStack.size()==0)if(this->imgManage.imageStack.empty()){ui->label_6->setText("            已无更多操作,恢复至初始状态       ");}else{QImage qimage=Mat2QImage(img);ui->label_6->setPixmap(QPixmap::fromImage(qimage).scaled(ui->label_6->size()));}}}//直方图均衡化增强
void MainWindow::on_pushButton_5_clicked()
{HistogramEq hist = HistogramEq(imgManage.imagePath);cv::Mat run=hist.runMethod();this->imgManage.imageStack.push(run);QImage qimage=Mat2QImage(run);ui->label_6->setPixmap(QPixmap::fromImage(qimage).scaled(ui->label_6->size()));
}
//拉普拉斯算子局部增强
void MainWindow::on_pushButton_4_clicked()
{LaplaeOperator la = LaplaeOperator(imgManage.imagePath);cv::Mat run = la.runMethod();this->imgManage.imageStack.push(run);QImage qimage=Mat2QImage(run);ui->label_6->setPixmap(QPixmap::fromImage(qimage).scaled(ui->label_6->size()));
}
//gamma变换
void MainWindow::on_pushButton_3_clicked()
{bool ok;double value1= QInputDialog::getDouble(this,QObject::tr("输入参数"),QObject::tr("输入参数gamma:0~10之间的数值"),0.5,0,10,1,&ok);//四个数字分别是初始显示数,起始数,终止数,保留小数位数if(ok){qDebug()<<"value1:"<<value1;gammaTransform ga = gammaTransform(imgManage.imagePath);ga.gamma = value1;cv::Mat run = ga.runMethod();this->imgManage.imageStack.push(run);QImage qimage=Mat2QImage(run);ui->label_6->setPixmap(QPixmap::fromImage(qimage).scaled(ui->label_6->size()));}
}
//对数变换
void MainWindow::on_pushButton_2_clicked()
{bool ok;int value2= float(QInputDialog::getInt(this,QObject::tr("对数变换:s=c*log(1+r)"),QObject::tr("输入参数c:-10~10之间的数值"),1,-10,10,1,&ok));//四个数字分别是初始显示数,起始数,终止数,保留小数位数if(ok){qDebug()<<"value2:"<<value2;LogTransform log = LogTransform(imgManage.imagePath);log.c = value2;cv::Mat run = log.runMethod();this->imgManage.imageStack.push(run);QImage qimage=Mat2QImage(run);ui->label_6->setPixmap(QPixmap::fromImage(qimage).scaled(ui->label_6->size()));}
}
//彩色图像转换为灰度图像
void MainWindow::on_pushButton_6_clicked()
{Transform img = Transform(imgManage.imagePath);cv::Mat run = img.RGB_to_Gray();this->imgManage.imageStack.push(run);QImage qimage=Mat2QImage(run);ui->label_6->setPixmap(QPixmap::fromImage(qimage).scaled(ui->label_6->size()));
}//线性灰度变换
void MainWindow::on_pushButton_clicked()
{bool ok1,ok2;double value1= QInputDialog::getDouble(this,QObject::tr("线性灰度变换"),QObject::tr("输入参数k:-10~10之间的数值"),1,-10,10,1,&ok1);//四个数字分别是初始显示数,起始数,终止数,保留小数位数int value2= QInputDialog::getInt(this,QObject::tr("线性灰度变换"),QObject::tr("输入参数b:-20~20之间的数值"),0,-20,20,1,&ok2);//线性灰度变换:k>1时,增加图像对比度,整体效果被增强;k=1,通过调整b,实现对图像亮度的调整;//当0<k<1时,图像的对比度被削弱;k<0时,原来图像量的区域变暗,暗的区域变亮if(ok1 && ok2){qDebug()<<"value1:"<<value1;qDebug()<<"value2:"<<value2;LinearChange li = LinearChange(imgManage.imagePath);li.k = value1;li.b = value2;cv::Mat run = li.runMethod();this->imgManage.imageStack.push(run);QImage qimage=Mat2QImage(run);ui->label_6->setPixmap(QPixmap::fromImage(qimage).scaled(ui->label_6->size()));}}//算法简介
void MainWindow::on_pushButton_7_clicked()
{int ret1 = QMessageBox::information(this,QObject::tr("算法简介"),QObject::tr("线性灰度变换:k>1时,增加图像对比度;k=1,通过调整b,实现对图像亮度的调整当0<k<1时,图像的对比度被削弱;k<0时,原来图像量的区域变暗,暗的区域变亮"),QMessageBox::Ok);if(ret1 == QMessageBox::Ok)qDebug()<<QObject::tr("提示!");int ret2 = QMessageBox::information(this,QObject::tr("拉普拉斯算子"),QObject::tr("使用中心为5的8邻域拉普拉斯算子与图像卷积可以达到锐化增强图像的目的"),QMessageBox::Ok);if(ret2 == QMessageBox::Ok)qDebug()<<QObject::tr("提示!");
}

4.6 mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QWidget>
#include <QMenu>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include<QMessageBox>
#include<QInputDialog>
#include<QDebug>
#include<QFileDialog>
#include<string>
#include<QImage>
#include<QScreen>
#include"method.h"
#include"manage.h"QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }namespace Ui { class Widget; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();manage imgManage;private slots:void on_pushButton_8_clicked();void on_pushButton_9_clicked();void on_pushButton_10_clicked();void on_pushButton_11_clicked();void on_pushButton_5_clicked();void on_pushButton_4_clicked();void on_pushButton_3_clicked();void on_pushButton_2_clicked();void on_pushButton_6_clicked();void on_pushButton_clicked();void on_pushButton_7_clicked();private:Ui::MainWindow *ui;
};QImage Mat2QImage(const cv::Mat &mat);
#endif // MAINWINDOW_H

该处使用的url网络请求的数据。


总结

此时实验基于前面实验的学习和OpenCV标准库实现,抽象类的设计运行了C++面向对象编程的核心:封装、继承和多态。将方法抽象成一个method作为父类并设计成模板类,针对Mat数据类型对模板类特化,使其对图像处理进行实现,成员函数runMethod设置为纯虚函数,让其真正的算法子类进行实现,再子类中对于输入变量用private修饰,在外部调用的时候用友元类的方式实现。同时实验中我用到OpenCV库进行图像,使其弹窗、读入图像等都更方便实现,另外第一次接触Qt图形化界面,也是花费了大量时间,相比于JAVA它直接拖动功能功能标签,然后转到槽会自动关系按钮与事件,我们只需要实现行为代码,在制作图像化界面的时候很方便,最后还将主要算法封装成动态库,达到了代码共享的效果。

待改进之处:本次系统只有一个页面,功能单一,而且在读入图像的时候是默认读入彩色图像,不能自己选择图像类型,因此没有将灰度图转为彩色图像的功能;不会制作多级菜单,于是在算法简介功能中是采用弹框的形式,这显然不符合软件设计,可以在将算法简介按钮设置成转换界面按钮,点击跳转到简介页面,根据需要点击相关按钮在页面显示算法的基本内容,而且将算法简介可以从文件读取或链接的形式实现,避免在代码中出现过多文字。

Qt配置OpenCV:(1条消息) Qt配置OpenCV教程,亲测已试过(详细版)_Wi~的博客-CSDN博客_qt配置opencv

创建动态库方法:QT生成动态链接库及调用详细步骤_姗郁的博客-CSDN博客_qt 动态库

C++课设 简易图像处理系统相关推荐

  1. c语言售票系统主要函数,c语言课设电影院售票系统.docx

    struct inf { char rate[20]; char name[20]; int time_hour; int time_min; int seat; int sell; }inf[10] ...

  2. c语言课设报告仓库管理系统,c语言课设仓库管理系统.doc

    c语言课设仓库管理系统.doc 高级语言程序设计课程设计小型仓库管理系统题 目班 级学生学号学生姓名 同组成员 指导老师 提交时间 成 绩华南农业大学 信息学院一. 需求分析1 系统概述本系统用于小型 ...

  3. 广东工业大学数据库课设(点歌系统)

    摘 要 本论文主要是对卡拉OK点歌系统的设计过程,后端设计,前端设计,数据库设计的分析.结合软件工程已经数据库系统概论所学知识,详细内容是系统的实现背景,设计过程,功能的实现以及部分的个人感悟. 前言 ...

  4. 软件工程课设--在线投票系统

    这次课设我做的是一个在线投票系统,系统功能:登录.注册.浏览投票项目.投票.添加投票. 用户登录 用户注册 投票列表 投票页 新加项目 github:git@github.com:forali/Onl ...

  5. C语言课设学生考勤系统(大作业)

    一.设计功能(文章仅供参考) (1)确定界面,使用户可选择操作项目 (2)录入功能:使用结构体,要求用户输入相应信息,并存入文件 (3)查询功能:通过姓名或学号查找,利用循环找到对应结构体数组元素,然 ...

  6. 语言课设医院诊疗系统_江苏孤独症孩子有哪些典型特征?上海六一儿童医院

    孤独症孩子有哪些典型特征 当家长发现孩子不爱说话,不理人的时候,有时怀疑孩子是不是患了孤独症.对于孤独症的孩子和正常的孩子还是不一样的.那么,孤独症孩子有哪些典型的特征呢? 孤独症孩子的典型特征主要有 ...

  7. 【密码学】Java课设-文件加密系统(适用于任何文件)

    Java实现文件加密解密 前言 一.密码学入门 1.对称加密 2.非对称加密 二.程序代码 1.welcome类(欢迎界面) 2.Log类(登录界面) 3.Register类(注册界面) 4.Inde ...

  8. C语言课设飞机票订购系统

    一.问题描述: 某航空公司只有M架N个座位的飞机,每架飞机每天飞行一趟.通过该系统来实现机票的订购问题. 二.功能要求:1.本系统采用一个包含M×N个数据的结构体数组,每个数据的结构应当包括:起飞地. ...

  9. C语言课设会员计费系统(大作业)

    一.项目简介 通过"会员卡计费系统"C语言课程设计的实践,掌握函数.数据的读取和存储. 二.任务概述 (1)新会员登记. (2)会员信息修改. (3)会员续费. (4)会员消费结算 ...

最新文章

  1. docker容器互联
  2. 红茶一杯话Binder(ServiceManager篇)
  3. Netgear wndr3700v2 路由器刷OpenWrt打造全能服务器(五)SVN服务
  4. DNA甲基化检测服务
  5. Effective Java之用实例域代替序数(三十一)
  6. python包numpy_NumPy Python科学计算软件包的终极指南
  7. bug记录:虚拟机ping不通外网
  8. 文件跨服务器传输_跨桌面设备传输文件的最优选?
  9. 什么是CloudComputing
  10. CentOS7-Python系列】之一【python-devel教训】
  11. ArcGIS Pro + PS 矢量化用地规划图
  12. 图像处理(一)图像灰度化的三种方式
  13. PTA练习4-11 统计素数并求和
  14. 计算机网络信息管理制度,计算机网络及信息管理制度
  15. Redis 发布订阅功能
  16. 河北省选调生上传报名表显示服务器错误,关于服务器错误
  17. Xtrabackup 增量备份
  18. 联想集团是一家在信息产业内多元化发展的大型企业集团
  19. 12月18日第壹简报,星期日,农历十一月廿五
  20. Python Turtle:小海龟创意绘画,仰望星空,脚踏实地,配有背景音乐哦!(附源码,可以学习如何添加背景音乐,如何使用渐变色)

热门文章

  1. steam账号连接服务器遇到问题,无法预料的服务器浏览器反应 - Steam Support
  2. 用ros输出hello,world(c++版)
  3. 定积分的基本性质6 积分第一中值定理
  4. Ansible之Inventory定义
  5. html和css最全的知识点归纳总结,html和css的面试知识点总结(附示例)
  6. 生产环境和开发环境_环境部署:开发、测试和线上环境的区别
  7. 学以致用——微博文章内容统计分析之一(Excel+GraphLab)
  8. 手机拍摄证件照可以用什么软件
  9. 5G基站到底长啥样?和4G有啥区别?
  10. 安装brat的jquery错误