在保持居中的同时缩放位图

时间:2017-07-07 09:51:35

标签: c image algorithm coordinates

我在嵌入式系统(stm32f4)上开发了一个支持多点触控的图像查看器。为了突出显示图像查看器应该具有类似于智能手机上的功能。 我已经完成了图像缩放和捏合/缩放手势识别部分。但它只能从源图像原点坐标进行缩放。因此,如果我的原点(x,y [0,0])在左上方,那么它将在该点之后缩放。如果我想在右下角看一些东西我必须使用捏手势并移动到我想要的位置这是不可取的。这就是它的外观 http://i.imgur.com/IWR4wls.gifv 。它应该遵循2个手指的中心。

如何在遵循2个手指中心的同时实现缩放?我尝试这样做会导致工作但跳跃,摇摇欲坠的版本基本上无法使用。

我的缩放工作通过使源图像始终打开(在ram中)获取源图像[x,y]坐标并且我想要缩放[w,h]缩放矩形以显示[w,h]并显示它。通过移动源图像中的zoomRect [x,y]坐标来完成移动(平移手势)。这意味着每次我移动手指时我都必须移动zoomedRect(增加[x,y])缩放zoomedRect并显示它。因此,由于ram有限,因此没有存储完全缩放的图像。

Source image width,height[640, 480]:
+-------------------------------+
|   zoomedRect                  |
|   +--------------+            |
|   |              |            |
|   |              |            |
|   |              |            |
|   |              |            |
|   |              |            |
|   +--------------+            |
|                               |
|                               |
|                               |
+-------------------------------+

I take zoomedRect i.e. x,y[50, 50] width,height[160, 120]
and scale it to display size w,h[640x480]
Display:
+-------------------------------+
|                               |
|                               |
|                               |
|                               |
|                               |
|                               |
|                               |
|                               |
|                               |
+-------------------------------+

这是我拥有/可以计算的内容:

  1. 双指中心。

  2. 将2个手指的中心转换为源图像(即使放大时也是如此)。

  3. 缩放分数(1.32,1.45 ...)和(源图像宽度|高度)/(缩放宽度|高度)。

  4. 修改

    我试图在两根手指触摸时计算中心点,然后将该中心用于将来的计算,这就是所有跳跃和震动发生的原因。但也许我身边有错误,所以我会添加一些相关的代码。 正如我前面提到的,我改变zoomRect宽度,高度来缩放图像

    newWidth = tempWidth / scale;
    newHeight = tempHeight / scale;
    

    所以在变焦之后我还需要移动到中心附近,我通过计算变化的比例来做到这一点 (lastWidth - newWidth)

    newSourceX = newSourceX + ((int16_t)lastWidth - newWidth);
    newSourceY = newSourceY + ((int16_t)lastHeight - newHeight);
    

    现在我们需要停在两个手指之间的计算中心,而不是越界[0,0]:

    #define LIMIT(value, min, max) (value < min ? min : (value > max ? max : value))
    newSourceX = LIMIT(newSourceX + ((int16_t)lastWidth - newWidth), 0, centerSourceX);
    newSourceY = LIMIT(newSourceY + ((int16_t)lastHeight - newHeight), 0, centerSourceY);
    

    到目前为止一直很好,但没有正确集中

    Calculate center between two fingers on Display and translate to Source.
    Because it's fully zoomed out(Source and Display are identical)
    centers are also in identical positions.
    Display/Source:
    +-------------------------------+
    |                               |
    |                               |
    |                               |
    |                               |
    |                               |
    |                centerPos      |
    |                     *         |
    |                               |
    |                               |
    +-------------------------------+
    
    So if we zoom in zoomRect will be newWidth / 2 and newHeight / 2 further than needed.
    
    Source:
    +-------------------------------+
    |                               |
    |                               |
    |                               |
    |                               |
    |                               |
    |                centerPos      |
    |                     *---------+
    |                     | zoomRect|
    |                     |         |
    +---------------------+---------+
    
    To account for this I modify code as follows:
    newSourceX = LIMIT(newSourceX + ((int16_t)lastWidth - newWidth), 0, centerSourceX - newWidth / 2);
    newSourceY = LIMIT(newSourceY + ((int16_t)lastHeight - newHeight), 0, centerSourceY - newHeight / 2);
    
    Success!!! Or not?
    
    Source:
    +-------------------------------+
    |                               |
    |                               |
    |                               |
    |             zoomRect          |
    |                +---------+    |
    |                |centerPos|    |
    |                |     *   |    |
    |                +---------+    |  
    |                               |
    +-------------------------------+
    
    When zooming from 1:1 it works perfectly but when I want to zoom again when
    I'm zoomed in a little bit the "jump" happens because:
    newSourceX + ((int16_t)lastWidth - newWidth) > centerSourceX - newWidth / 2
    

    结果:http://i.imgur.com/x1t6X2q.gifv

2 个答案:

答案 0 :(得分:2)

您有源图片,源坐标(x,y)在屏幕坐标中通过仿射变换进行转换。我认为缩放是均匀的。在开头Scale=1; dx, dy=0

ScrX = (x - dx) * Scale
ScrY = (y - dy) * Scale

因此我们看到从dx,dy点切割并通过缩放时间延伸的源图像。

dx = 1,dy = 1,Scale = 2的示例。左侧是右侧,右侧是屏幕。

enter image description here

让我们开始(b前缀)放大手指(bx0, by0)(bx1, by1)的屏幕位置 - 相对的矩形角。结束(或中间)职位(e前缀)为(ex0, ey0)(ex1, ey1)

让矩形的对角线对应于比例度:

eScale = bScale * Sqrt(((ex1-ex0)^2 + (ey1-ey0)^2) / ((bx1-bx0)^2 + (by1-by0)^2)) 
//use Math.Hypot or Vector.Length if available

所以我们有了新的规模。

我们有开始和结束中心点

 bcx = (bx0 + bx1) / 2
 bcy = (by0 + by1) / 2
 ecx = (ex0 + ex1) / 2
 ecy = (ey0 + ey1) / 2

这两个屏幕点都应对应相同的源坐标

bcx = (xx - bdx) * bScale
ecx = (xx - edx) * eScale

不包括xx我们得到

edx = bdx + bcx / bScale - ecx / eScale

和edy的类似公式

所以我们有了新的换档参数。

答案 1 :(得分:1)

也许从第一点锁定图像的中心点,你可以计算两个手指的中心,然后从这一点开始缩放。而不是每次重新计算中心点。把它作为变焦点然后从这一点开始移动手指是变焦因子,应该停止你提到的摇动效果