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

浙公网安备 33010602011771号