目录
前言
本次课设实现的简易图像处理主要采用了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;
}
else
return 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)+b
double 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--;