一步步实现看图工具(二)
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)); }