简介
实现一个在图片上框选区域的类,可以缩放图像方便操作,当图片过大的时候,只显示其中一部分,可以通过鼠标选定显示的区域中心。
材料收集
在csdn找到了两篇很有帮助的文章
第一篇,了解一下鼠标响应的基础
关于opencv2中鼠标响应操作.
我是在下面的项目下进行修改
opencv实现鼠标画矩形框、显示十字线、滚轮缩放.
开始设计
1,将鼠标响应函数封装到类
opencv的鼠标响应函数:
CV_EXPORTS void setMouseCallback(const string& winname, MouseCallback onMouse, void* userdata = 0);
在将普通的类成员函数,定义为鼠标响应函数时,会发生参数个数不匹配的问题,因为普通成员函数都‘隐藏’了一个参数,一个指向对象本身的指针this,我们可以通过this指针访问对象成员
// 相当于
//void DetetColor::setColorId(int id,int colorSize,DetetColor * this)
void DetetColor::setColorId(int id,int colorSize)
{
this->id=id;
}
静态成员函数不能访问对象的普通成员函数,普通数据成员,所以默认不包括指向对象的指针this。我们通过void * 指针传入对象指针,访问对象成员函数
void displayPicture::on_Mouse_ComplexRect(int event, int x, int y,int flag)//实现画矩形框
{
if (event == EVENT_RBUTTONDOWN) //按下右键,减少一个已框选区域,重画
{
。。。。
}
}
void displayPicture::on_Mouse_ComplexRect(int events, int x, int y, int flag, void* userdata)
{
displayPicture* temp = reinterpret_cast<displayPicture*>(userdata);
temp->on_Mouse_ComplexRect( events,x,y,flag);
}
2.合理的显示图像
显示器的界面是有限的,图片是大小不一的,合理的显示图像应该考虑的
1.图片小于窗口,显示全部图片
2.图片大于窗口,根据选定的中心,显示部分图片
void displayPicture::showImageInRange()
{
int x1,x2,y1,y2;
Point zoomCenter=Point(imageCenter.x*scale,imageCenter.y*scale);
if(zoomImage.cols<rangewidth)
{
x1=0;
x2=zoomImage.cols-1;
}
else if(zoomCenter.x<rangewidth/2)
{
x1=0;
x2=rangewidth;
}
else if(zoomImage.cols-rangewidth/2<zoomCenter.x)
{
x1=zoomImage.cols-rangewidth;
x2=zoomImage.cols-1;
}else
{
x1=zoomCenter.x-rangewidth/2;
x2=zoomCenter.x+rangewidth/2;
}
if(zoomImage.rows<rangeheight)
{
y1=0;
y2=zoomImage.rows-1;
}
else if(zoomCenter.y<rangeheight/2)
{
y1=0;
y2=rangeheight;
}
else if(zoomImage.rows-rangeheight/2<zoomCenter.y)
{
y1=zoomImage.rows-rangeheight;
y2=zoomImage.rows-1;
}else
{
y1=zoomCenter.y-rangeheight/2;
y2=zoomCenter.y+rangeheight/2;
}
Rect roi(x1,y1,x2-x1,y2-y1);
rectOfRoi=roi;
rectOfRoi.x/=scale;
rectOfRoi.y/=scale;
rectOfRoi.width/=scale;
rectOfRoi.height/=scale;
roiImage=zoomImage(roi);
}
全部代码
头文件
#pragma once
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
class displayPicture
{
public:
displayPicture(Mat image);
~displayPicture(void);
void display();
//选定感兴趣区域,获得与图像大小相同的mask
void getMask(Mat &mask);
//选定不感兴趣区域,获得mask
void getMaskINV(Mat &mask);
int rangewidth;
int rangeheight;
vector<Rect> selectRect;
private:
Mat srcImage;
Mat zoomImage; //缩放的图片
Mat roiImage; //区域图像
Mat showImage; //显示的图片
Point imageCenter; //图像的中心
Rect rectOfRoi;
Point g_center;
bool select_flag;
double scale;
double step;
string WINNAME;
void changeScale(double newscale);
void on_Mouse_ComplexRect(int event, int x, int y,int flag);
static void on_Mouse_ComplexRect(int events, int x, int y, int, void* userdata);
void drawAllRectangle();
void draw_crossline(Mat &img, const Point &pt);
void showImageInRange();
};
cpp文件
#include "displayPicture.h"
displayPicture::displayPicture(Mat image)
{
scale=1;
step=0.05;
select_flag=false;
rangewidth=1600;
rangeheight=900;
imageCenter=Point(0,0);
WINNAME="画板";
srcImage=image;
zoomImage=srcImage.clone();
showImageInRange();
showImage=roiImage.clone();
}
displayPicture::~displayPicture(void)
{
}
void displayPicture::changeScale(double newscale)
{
scale=newscale;
if(newscale>1)
scale=1;
if(newscale<0.1)
scale=0.1;
resize(srcImage,zoomImage,Size(),scale,scale);
showImageInRange();
drawAllRectangle();
}
void displayPicture::display()
{
namedWindow(WINNAME, 1);
//添加鼠标相应
setMouseCallback(WINNAME, displayPicture::on_Mouse_ComplexRect, this);
drawAllRectangle();
imshow(WINNAME,showImage);
int key;
while (1)
{
key = waitKey(40);
if (key == ' ')
{
cv::destroyWindow(WINNAME);
break;
}
else if (key == toascii('q'))
{
changeScale(scale -step);
imshow(WINNAME,showImage);
//zoom(mouseParam.img, mouseParam.imgZoomBackup, mouseParam.pt1, mouseParam.scale);
}
else if (key == toascii('e'))
{
changeScale(scale + step);
imshow(WINNAME,showImage);
//zoom(mouseParam.img, mouseParam.imgZoomBackup, mouseParam.pt1, mouseParam.scale);
}
}
}
void displayPicture::draw_crossline(Mat &img, const Point &pt)
{
int width = img.cols;
int height = img.rows;
cv::Point ptv1;
cv::Point ptv2;
cv::Point pth1;
cv::Point pth2;
ptv1 = cv::Point(pt.x, 0);
ptv2 = cv::Point(pt.x, height);
pth1 = cv::Point(0, pt.y);
pth2 = cv::Point(width, pt.y);
cv::line(img, ptv1, ptv2, Scalar(255, 255, 0), 1);
ptv1.x+=1;
ptv2.x+=1;
cv::line(img, ptv1, ptv2, Scalar(0, 0, 255), 1);
cv::line(img, pth1, pth2, Scalar(255, 255, 0), 1);
pth1.y+=1;
pth2.y+=1;
cv::line(img, pth1, pth2, Scalar(0, 0, 255), 1);
}
void displayPicture::showImageInRange()
{
int x1,x2,y1,y2;
Point zoomCenter=Point(imageCenter.x*scale,imageCenter.y*scale);
if(zoomImage.cols<rangewidth)
{
x1=0;
x2=zoomImage.cols-1;
}
else if(zoomCenter.x<rangewidth/2)
{
x1=0;
x2=rangewidth;
}
else if(zoomImage.cols-rangewidth/2<zoomCenter.x)
{
x1=zoomImage.cols-rangewidth;
x2=zoomImage.cols-1;
}else
{
x1=zoomCenter.x-rangewidth/2;
x2=zoomCenter.x+rangewidth/2;
}
if(zoomImage.rows<rangeheight)
{
y1=0;
y2=zoomImage.rows-1;
}
else if(zoomCenter.y<rangeheight/2)
{
y1=0;
y2=rangeheight;
}
else if(zoomImage.rows-rangeheight/2<zoomCenter.y)
{
y1=zoomImage.rows-rangeheight;
y2=zoomImage.rows-1;
}else
{
y1=zoomCenter.y-rangeheight/2;
y2=zoomCenter.y+rangeheight/2;
}
Rect roi(x1,y1,x2-x1,y2-y1);
rectOfRoi=roi;
rectOfRoi.x/=scale;
rectOfRoi.y/=scale;
rectOfRoi.width/=scale;
rectOfRoi.height/=scale;
roiImage=zoomImage(roi);
}
void displayPicture::on_Mouse_ComplexRect(int event, int x, int y,int flag)//实现画矩形框
{
static Point p1, p2;
if (event == EVENT_RBUTTONDOWN) //按下右键,减少一个已框选区域,重画
{
// selectRect.clear();
selectRect.pop_back();
drawAllRectangle();
imshow(WINNAME, showImage);
}
else if ((flag&CV_EVENT_FLAG_CTRLKEY) &&event == EVENT_MOUSEMOVE) //按下ctrl并移动鼠标
{
Mat temp;
showImage.copyTo(temp);
Point clickPoint(x,y);
draw_crossline(temp, clickPoint);
imshow(WINNAME, temp);
}
else if( event == EVENT_LBUTTONDOWN&&flag-event ==CV_EVENT_FLAG_CTRLKEY) //Crtl+左键点击
{
imageCenter.x=rectOfRoi.x+x/scale;
imageCenter.y=rectOfRoi.y+y/scale;
showImageInRange();
drawAllRectangle();
}
else if (event == EVENT_LBUTTONDOWN) //左键点击
{
p1.x = x;
p1.y = y;
g_center=p1;
select_flag = true;
}
else if (select_flag &&flag==CV_EVENT_FLAG_LBUTTON) //鼠标拖拽
{
Mat temp;
showImage.copyTo(temp);
p2 = Point(x, y);
rectangle(temp, p1, p2, Scalar(0, 255, 0), 2);
imshow(WINNAME, temp);
}
else if (select_flag && event == EVENT_LBUTTONUP) //松开鼠标
{
Rect roi = Rect(p1, Point(x, y));
if (roi.width && roi.height)//点一下时会没有反应
{
roi.x/=scale;
roi.y/=scale;
roi.width/=scale;
roi.height/=scale;
roi.x+=rectOfRoi.x;
roi.y+=rectOfRoi.y;
selectRect.push_back(roi);
drawAllRectangle();
}
select_flag = false;
}
}
void displayPicture::on_Mouse_ComplexRect(int events, int x, int y, int flag, void* userdata)
{
displayPicture* temp = reinterpret_cast<displayPicture*>(userdata);
temp->on_Mouse_ComplexRect( events,x,y,flag);
}
void displayPicture::drawAllRectangle()
{
showImage=roiImage.clone();
for(int i=0;i<selectRect.size();i++)
{
Scalar color(125,255,0);
Rect rect=selectRect[i];
rect.x-=rectOfRoi.x;
rect.y-=rectOfRoi.y;
rect.x*=scale;
rect.y*=scale;
rect.width*=scale;
rect.height*=scale;
rectangle(showImage,rect,color,2);
}
string temp="zoom with 'q' or 'e' ,set the Image center with ctrl+click ,out with space"; //将鼠标点击的二维坐标显示到屏幕上
cv::putText(showImage,temp,Point(0, 16), FONT_HERSHEY_PLAIN, 1.f, Scalar(120, 255, 125), 1);
// imshow(WINNAME, showImage);
}
//选定感兴趣区域,获得与图像大小相同的mask
void displayPicture::getMask(Mat &mask)
{
display();
mask=Mat::zeros(srcImage.rows,srcImage.cols,CV_8U);
Mat all255(srcImage.rows,srcImage.cols,CV_8U,Scalar(255));
for(int i=0;i<selectRect.size();i++)
{
Mat roi=mask(selectRect[i]);
Mat roi2=all255(selectRect[i]);
roi2.copyTo(roi);
}
//显示mask
displayPicture displayMask(mask);
displayMask.display();
}
//选定不感兴趣区域,获得mask
void displayPicture::getMaskINV(Mat &mask)
{
display();
mask=Mat(srcImage.rows,srcImage.cols,CV_8U,Scalar(255));
Mat allZeros=Mat::zeros(srcImage.rows,srcImage.cols,CV_8U);
for(int i=0;i<selectRect.size();i++)
{
Mat roi=mask(selectRect[i]);
Mat roi2=allZeros(selectRect[i]);
roi2.copyTo(roi);
}
//显示mask
displayPicture displayMask(mask);
displayMask.display();