一步步实现看图工具(二)

1. 控件在对话框中的适配。

2. 图像和显示控件的适配。

3. 以鼠标点为中心, 滚轮缩放图片(类似于手机图库)

4. 鼠标拖动图片。(类似于手机图库)

5。双击100%显示图片, 再次双击显示全图(类似于手机图库)

6. 图像任意角度旋转。



1. 控件在对话框中的适配。

   可以先参考我这篇文章: 

   http://blog.csdn.net/fallingstar08/article/details/5182830

     现在的控件不多我就这么写了

 

void CEasyImageDlg::OnSize(UINT, int w, int h)
{
	if(m_player.GetSafeHwnd())
	{
		Move(&m_player, 0, 0, w, h, 0, 0, 800, 1000);
		Move(&xyPos, 0, 0, w, h, 840,  130,100, 30);
		Move(&textRGB, 0, 0, w, h, 840,  170,100, 30);
		Move(&textYUV, 0, 0, w, h, 840,  210, 100,30);
		Move(&imageInfo, 0, 0, w, h, 840,  50,100, 30);
		Move(&scaleInfo, 0, 0, w, h, 840,  90, 100,30);
		Move(GetDlgItem(IDC_BAR1), 0, 0, w, h, 801,  0, 2,1000);
		Move(GetDlgItem(IDC_BAR2), 0, 0, w, h, 803,  250, 197,2);
		m_player.Invalidate(FALSE);
	}
}

 

 

2. 图像和显示控件的适配。

   图像要尽量大的显示在控件上面, 并保持宽高比。

 

 这里分为两种情况:

  一: 图像宽高都比控件小。

       直接在控件中心位置显示图像。

  二: 图像的宽或高比控件大。

       分别计算图像宽度/控件宽度,  图像高度/控件高度。 取一个最大值,作为缩放比例。

       然后在控件中心位置显示图像。


 

void CImageView::CalcImageShowRect(const CRect& rc, const CRect& rcImage, CRect& rcShow)
{
	double dw= rcImage.Width()/(1.0*rc.Width());
	double dh= rcImage.Height()/(1.0*rc.Height());
	int cx = rc.Width()/2;
	int cy = rc.Height()/2;
	rcShow = rc;
	if(dw< 1.0 && dh < 1.0)
	{
		rcShow.left = cx - rcImage.Width()/2;
		rcShow.top = cy - rcImage.Height()/2;
		rcShow.right = rcShow.left + rcImage.Width();
		rcShow.bottom = rcShow.top + rcImage.Height();
		scale = 1.0;
	}
	else 
	{
		double s = max(dw, dh);
		double fw = rcImage.Width()/s;
		double fh = rcImage.Height()/s;
		rcShow.left = cx - fw/2;
		rcShow.right = cx + fw/2;
		rcShow.top = cy - fh/2;
		rcShow.bottom = cy + fh/2;

	}
}

 


3. 以鼠标点为中心, 滚轮缩放图片。

 

4. 鼠标拖动图片。

5。双击100%显示图片, 再次双击显示全图

 

   对话框响应滚轮事件然后传给控件。

   上面计算过的控件显示区域, 是不变的。 我们修改图像的显示区域来实现缩放功能。



先把坐标点从屏幕坐标系 映射到图像坐标系。

 

void CImagePlayer::ScreenToImage(CPoint& pt)
{
	CPoint ret;
	ret.x = rcImage.left + rcImage.Width()*(pt.x-rcShow.left)/rcShow.Width();
	ret.y = rcImage.top + rcImage.Height()*(pt.y-rcShow.top)/rcShow.Height();
	pt = ret;
}


定义一个scale变量来记录缩放倍数。

 

比如scale=2,那么 新的显示宽度=图像宽度/2.

由于缩放前后, 图像坐标系里的鼠标点离左右显示边界的距离是不变的。

推出

  (鼠标点横坐标-新的左边界)/新的宽度 = ((鼠标点横坐标-旧的左边界)/旧的宽度。


我们要求的是新的左边界, 其他参数已知。


 

void CImagePlayer::Scale(CPoint pt)
{
	if(scale == 1.0)
	{
		rcImage = CRect(0, 0, w, h);
	}
	else
	{
		ScreenToImage(pt);
		double dw = w/scale;
		double dh = h/scale;
		double ds = dw/rcImage.Width();
		rcImage.left = pt.x - ds*(pt.x-rcImage.left);
		rcImage.right = rcImage.left + dw;
		rcImage.top = pt.y - ds*(pt.y-rcImage.top);
		rcImage.bottom = rcImage.top + dh;
		if(rcImage.left < 0) rcImage.left = 0;
		if(rcImage.top < 0) rcImage.top = 0;
		if(rcImage.bottom > h) rcImage.bottom = h;
		if(rcImage.right > w) rcImage.right = w;
	}
	SendScaleInfo();
	Invalidate(FALSE);

}

效果图:

 


 




6. 图像任意角度旋转。

   图像旋转其实是很普通的几何问题。

   旋转矩阵:

   

    


OPENCV提供了计算旋转矩阵的函数, 和仿射变换函数

 

//! warps the image using affine transformation
CV_EXPORTS_W void warpAffine( InputArray src, OutputArray dst,
                              InputArray M, Size dsize,
                              int flags=INTER_LINEAR,
                              int borderMode=BORDER_CONSTANT,
                              const Scalar& borderValue=Scalar());

 

我的实现如下:

 

void rotateImage(Mat& img, double degree, int &w, int &h)
{
	double angle = degree  * CV_PI / 180.;
	double a = sin(angle), b = cos(angle);   
	int width = img.cols;    
	int height = img.rows;    
	w= int(height * fabs(a) + width * fabs(b));    
	h= int(width * fabs(a) + height * fabs(b));   

	w = ALIGN_UP(w, 4);
	h = ALIGN_UP(h, 4);
	float map[6];  
	CvMat map_matrix = cvMat(2, 3, CV_32F, map);    

	CvPoint2D32f center = cvPoint2D32f(width / 2, height / 2);    
	cv2DRotationMatrix(center, degree, 1.0, &map_matrix);    
	map[2] += (w - width) / 2;    
	map[5] += (h - height) / 2;
	warpAffine(img, img, Mat(&map_matrix), Size(w, h),
		CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS,BORDER_CONSTANT, Scalar::all(0));

}

 

 


 

posted @ 2013-07-08 14:31  xinyuyuanm  阅读(264)  评论(0编辑  收藏  举报