最近在学Qt,以下为学习过程(在VS中运用Qt):建菜单栏时可以直接在Qt Designer里建,但是之后在建立菜单栏信号和槽连接的时候我不知道怎么弄了,百度了下说是在Designer里面只能使用里面自带的槽。所以我最后用直接写代码的方式建立了菜单栏和工具栏,代码如下:
#include "mainwindow.h"
#include <QAction>
MainWindow::MainWindow(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
ui.setupUi(this);
openAction = new QAction(tr("&Open"), this);
openAction->setShortcut(QKeySequence::Open);
QMenu *file = menuBar()->addMenu(tr("&File"));
file->addAction(openAction);
QToolBar *toolBar = addToolBar(tr("&File"));
toolBar->addAction(openAction);
connect(openAction, SIGNAL(triggered()), this, SLOT(openImage()));
}
void MainWindow::openImage()
{
QString filename=QFileDialog::getOpenFileName(this,
tr("Open Image"),"",
tr("BMP(*.bmp);;JPG(*.jpg);;ALL files(*.*)"));
if(filename.isEmpty())
{
QMessageBox::information(this,tr("Open Image"),
tr("Please select an image to open"));
filename=QFileDialog::getOpenFileName(this,
tr("Open Image"),"",
tr("BMP(*.bmp);;JPG(*.jpg);;ALL files(*.*)"));
}
if(!(img1.load(filename,0)))
{
QMessageBox::information(this,tr("Unable to open the Image"),
tr("Please select a valid image."));
return;
}
QWidget::update();
QString filename2=QFileDialog::getOpenFileName(this,
tr("Open Image"),"",
tr("BMP(*.bmp);;JPG(*.jpg);;ALL files(*.*)"));
if(filename2.isEmpty())
{
QMessageBox::information(this,tr("Open Image"),
tr("Please select an image to open"));
filename=QFileDialog::getOpenFileName(this,
tr("Open Image"),"",
tr("BMP(*.bmp);;JPG(*.jpg);;ALL files(*.*)"));
}
if(!(img2.load(filename2,0)))
{
QMessageBox::information(this,tr("Unable to open the Image"),
tr("Please select a valid image."));
return;
}
QWidget::update();
}
我们使用了setShortcut函数。shortcut是这个动作的快捷键。Qt的QKeySequence已经为我们定义了很多内置的快捷键,比如我们使用的Open。你可以通过查阅API文档获得所有的快捷键列表,或者是在QtCreator中输入::后会有系统的自动补全功能显示出来。这个与我们自己定义的有什么区别呢?简单来说,我们完全可以自己定义一个tr("Ctrl+O")来实现快捷键。原因在于,这是Qt跨平台性的体现。比如PC键盘和Mac键盘是不一样的,一些键在PC键盘上有,而Max键盘上可能并不存在,或者反之,所以,推荐使用QKeySequence类来添加快捷键,这样,它会根据平台的不同来定义不同的快捷键。
QMainWindow有一个menuBar()函数,会返回菜单栏,也就是最上面的那一条。如果不存在会自动创建,如果已经存在就返回那个菜单栏的指针。直接使用返回值添加一个菜单,也就是addMenu,参数是一个QString,也就是显示的菜单名字。然后使用这个QMenu指针添加这个QAction。类似的,使用addToolBar函数的返回值添加了一个工具条,并且把这个QAction添加到了上面。
void MainWindow::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
if(!img1.isNull())
painter.drawImage(50,50,img1);
if(!img2.isNull())
painter.drawImage(50,250,img2);
}
接下来又用双缓冲绘图的方法在窗口中画矩形(选取图片上的某个区域),该方法可以使用鼠标在界面上绘制一个任意大小的矩形。这里需要两张画布,其中一个tempPix用来作为临时缓冲区,当鼠标正在拖动矩形进行绘制时,将内容先绘制到tempPix上,然后将tempPix绘制到界面上;而另一个pix作为缓冲区,用来保存已经完成的绘制。当松开鼠标完成矩形的绘制后,则将tempPix的内容复制到pix上,为了绘制时不显示拖影,那么在移动鼠标的过程中,每绘制一次,都要在刚开始绘制这个矩形的图像上进行绘制,所以需要在每次绘制tempPix之前,先将pix的内容复制到tempPix上。代码如下:
void MainWindow::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
if(!img1.isNull())
painter.drawPixmap(50,50,img1);
if(!img2.isNull())
painter.drawPixmap(400,50,img2);
tempPix=pix;
isDrawing=false;
int x=startPoint.x();
int y=startPoint.y();
int width=endPoint.x()-x;
int height=endPoint.y()-y;
painter.begin(&tempPix);
QPen pen(Qt::blue,2,Qt::DotLine);
painter.setPen(pen);
painter.drawRect(x,y,width,height);
painter.end();
painter.begin(this);
painter.drawPixmap(0,0,tempPix);
if (!isDrawing)
pix=tempPix;
}
void MainWindow::mousePressEvent(QMouseEvent *event)
{
if (event->button()==Qt::LeftButton)
{
startPoint=event->pos();
isDrawing=true;
}
}
void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton)
{
endPoint=event->pos();
tempPix=pix;
update();
}
}
void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button()==Qt::LeftButton)
{
endPoint=event->pos();
isDrawing=false;
update();
}
}
虽然可以画出矩形,但是最好是限定在所显示的图像区域内,后期再改进。
以上我其实是先读取两张图片,然后在其中一张图片上用矩形框选取一部分图像,然后把这部分图像粘贴到另一张另图片,目的达到无缝融合。
我先试着把所选取的这部分图片直接复制粘贴到另一张图像上,然后再试着用泊松融合来处理。直接复制粘贴的这部分代码如下:
void MainWindow::paintEvent(QPaintEvent *event)
{
if (flag==false)
{
QPainter painter(this);
if(!img1.isNull())
painter.drawImage(0,50,img1);
if(!img2.isNull())
painter.drawImage(400,50,img2);
tempPix=pix;
isDrawing=false;
int x=startPoint.x();
int y=startPoint.y();
width=endPoint.x()-x;
height=endPoint.y()-y;
painter.begin(&tempPix);
QPen pen(Qt::red,2,Qt::DotLine);
painter.setPen(pen);
painter.drawRect(x,y,width,height);
painter.end();
painter.begin(this);
painter.drawImage(0,0,tempPix);
if (!isDrawing)
pix=tempPix;
Boundarypoint[0]=x;
Boundarypoint[1]=y;
Boundarypoint[2]=width;
Boundarypoint[3]=height;
}
else if (flag==true)
{
QPainter painter(this);
painter.begin(this);
QRect rect1 = QRect(0, 50, img1.width(), img1.height());
painter.drawImage(rect1, img1);
QRect rect2 = QRect(400,50, img2.width(), img2.height());
painter.drawImage(rect2, img2);
painter.end();
painter.begin(this);
int newx=startPoint.x();
int newy=startPoint.y();
int k=0;
for (int i=newx-400;i<newx-400+width;i++)
{
int m=0;
for (int j=newy-50;j<newy+height-50;j++)
{
QRgb value;
value=img1.pixel(Boundarypoint[0]+k,Boundarypoint[1]-50+m);
m++;
img2.setPixel(i,j,value);
}
k++;
}
QRect rect = QRect(400, 50, img2.width(), img2.height());
painter.drawImage(rect, img2);
painter.end();
}
}
void MainWindow::copyImage()
{
flag=true;
}
做了好几天终于把泊松融合用Qt做出来了,简单描述一下:
这里要感谢博主http://blog.youkuaiyun.com/hjimce/article/details/45716603,看完这个就会对泊松编辑有更好的理解
按步骤首先是要先求所选区域的梯场,再求背景图片的梯度场,然后求融合图像的梯度场,最后求融合图像的散度(对梯度求导),这些要求解系数矩阵。我用的是MKL来求解泊松方程。
我做的时候就直接求了所选区域内部点的散度,粘贴到背景图中。而原图中所选的区域的边界点是不求的,而用的是边界点在背景图的位置处来求解,把背景图放在边界点方程的右边,前面求的内部点的散度也放在右边,根据那个链接里的拉普拉斯算子来求。方程左边就是该点相邻的内部点取1,和该点取-4,最后求解。因为我是直接求的散度,所以还是有所偏差。
说的不清楚,就是记录一下怕自己忘了。有很多不足的地方待改进。
部分代码如下:(方法比较笨)
void MainWindow::PossionEquation(numc::RowMat<double>& rmt, vector<double>& rhsR, vector<double>& rhsG,vector<double>& rhsB)
{
int divR,divG,divB;
k=0; //标号0,1......
for (int y1=Boundarypoint[1]-50+1;y1<Boundarypoint[1]-50+height-1;y1++) //求矩形区内部点的散度
{
for (int x1=Boundarypoint[0]+1;x1<Boundarypoint[0]+width-1;x1++)
{
QColor color(img1.pixel(x1,y1));
QColor color1(img1.pixel(x1+1,y1));
QColor color2(img1.pixel(x1-1,y1));
QColor color3(img1.pixel(x1,y1+1));
QColor color4(img1.pixel(x1,y1-1));
divR=color1.red()+color2.red()+color3.red()+color4.red()-4*color.red();
rhsB[k]=divR;
divG=color1.green()+color2.green()+color3.green()+color4.green()-4*color.green();
rhsG[k]=divG;
divB=color1.blue()+color2.blue()+color3.blue()+color4.blue()-4*color.blue();
rhsB[k]=divB;
k++;
}
}
for(int i=0;i<n;i++)
{
if (i==0)
{
rmt[0][1]=1;
rmt[0][width-2]=1;
QColor Co1(img2.pixel(newx-399,newy-50));
QColor Co2(img2.pixel(newx-400,newy-49));
rhsR[i]=rhsB[i]-Co1.red()-Co2.red();
rhsG[i]=rhsG[i]-Co1.green()-Co2.green();
rhsB[i]=rhsB[i]-Co1.blue()-Co2.blue();
}
else if(i==width-3)
{
rmt[i][width-4]=1;
rmt[i][2*width-5]=1;
QColor Co1(img2.pixel(newx+width-402,newy-50));
QColor Co2(img2.pixel(newx+width-401,newy-49));
rhsR[i]=rhsB[i]-Co1.red()-Co2.red();
rhsG[i]=rhsG[i]-Co1.green()-Co2.green();
rhsB[i]=rhsB[i]-Co1.blue()-Co2.blue();
}
else if (i==n-width+2)
{
rmt[i][n-2*width+4]=1;
rmt[i][n-width+3]=1;
QColor Co1(img2.pixel(newx-399,newy+height-51));
QColor Co2(img2.pixel(newx-400,newy+height-52));
rhsR[i]=rhsB[i]-Co1.red()-Co2.red();
rhsG[i]=rhsG[i]-Co1.green()-Co2.green();
rhsB[i]=rhsB[i]-Co1.blue()-Co2.blue();
}
else if (i==n-1)
{
rmt[i][n-width+1]=1;
rmt[i][n-2]=1;
QColor Co1(img2.pixel(newx+width-401,newy+height-52));
QColor Co2(img2.pixel(newx+width-402,newy+height-51));
rhsR[i]=rhsB[i]-Co1.red()-Co2.red();
rhsG[i]=rhsG[i]-Co1.green()-Co2.green();
rhsB[i]=rhsB[i]-Co1.blue()-Co2.blue();
}
else if (i>0&&i<width-3)
{
rmt[i][i-1]=1;
rmt[i][i+1]=1;
rmt[i][i+width-2]=1;
QColor Co(img2.pixel(newx+i-399,newy-50));
rhsR[i]=rhsB[i]-Co.red();
rhsG[i]=rhsG[i]-Co.green();
rhsB[i]=rhsB[i]-Co.blue();
}
else if (i%(width-2)==0)
{
rmt[i][i-width+2]=1;
rmt[i][i+1]=1;
rmt[i][i+width-2]=1;
QColor Co(img2.pixel(newx-400,newy-49+i/(width-2))); ////
rhsR[i]=rhsB[i]-Co.red();
rhsG[i]=rhsG[i]-Co.green();
rhsB[i]=rhsB[i]-Co.blue();
}
else if (i>(n-width+2)&&i<n-1)
{
rmt[i][i-1]=1;
rmt[i][i+1]=1;
rmt[i][i-width+2]=1;
QColor Co(img2.pixel(newx-399+i-(n-width+2),newy+height-51));
rhsR[i]=rhsB[i]-Co.red();
rhsG[i]=rhsG[i]-Co.green();
rhsB[i]=rhsB[i]-Co.blue();
}
else if (i%(width-2)==(width-3))
{
rmt[i][i-width+2]=1;
rmt[i][i-1]=1;
rmt[i][i+width-2]=1;
QColor Co(img2.pixel(newx+width-401,newy-49+i/(width-3)));////
rhsR[i]=rhsB[i]-Co.red();
rhsG[i]=rhsG[i]-Co.green();
rhsB[i]=rhsB[i]-Co.blue();
}
else
{
rmt[i][i-width+2]=1;
rmt[i][i-1]=1;
rmt[i][i+1]=1;
rmt[i][i+width-2]=1;
}
rmt[i][i]=-4;
}
}
void MainWindow::PossionSolver()
{
using namespace numc;
RowMat<double> rmt(n, n); //根据内部点像素建立方程
vector<double> rhsR(n); //R通道右边值
vector<double> rhsG(n); //G通道右边值
vector<double> rhsB(n); //B通道右边值
PossionEquation(rmt,rhsR,rhsG,rhsB);
#if TEST
std::ofstream outfile1;
outfile1.open("equation.txt");
for (int i =0; i < n; i ++)
{
outfile1 << "Row" << i << std::endl;
for (int j = 0; j < n; j ++)
{
outfile1 << rmt[i][j] << " ";
}
outfile1 << "\t" << rhsU[i] << " " << rhsV[i] << std::endl;
}
outfile1.close();
#endif
CSRMatrix<double> m;
CreateCSRMatrixFromRowMap(m,rmt);
numc::CLeastSquareSpareSolver solver;
solver.init(m);
vector<double> resultR(n);
vector<double> resultG(n);
vector<double> resultB(n);
solver.solve(&rhsR.front(), &resultR.front());
solver.solve(&rhsG.front(), &resultG.front());
solver.solve(&rhsB.front(), &resultB.front());
#if TEST
for (int i = 0; i < n ; i++)
{
std::cout<<resultU[i]<<" " << resultV[i] << std::endl;
}
#endif
for (int q=0;q<n;q++)
{
if (resultR[q]>255)
resultR[q]=255;
if (resultR[q]<0)
resultR[q]=0;
if (resultG[q]>255)
resultG[q]=255;
if (resultG[q]<0)
resultG[q]=0;
if (resultB[q]>255)
resultB[q]=255;
if (resultB[q]<0)
resultB[q]=0;
}
int p=0;
for (int j=newy-50+1;j<newy-50+height-1;j++)
{
for (int i=newx-400+1;i<newx-400+width-1;i++)
{
QRgb value=qRgba(resultR[p],resultG[p],resultB[p],255);
img2.setPixel(i,j,value);
p++;
}
}
}
这里因为图片显示的位置和窗口的位置存在一个相对位置,所以要-400,-50结果如下:
注:初学,还有很多不懂不明白的地方,还望有大神可以指导改进一下。