如何修改 d3js 鱼眼失真,使其支持半径

Posted

技术标签:

【中文标题】如何修改 d3js 鱼眼失真,使其支持半径【英文标题】:How to modify d3js fisheye distortion so that it will support radius 【发布时间】:2014-02-09 23:48:22 【问题描述】:

我正在尝试修改鱼眼this 项目,以便我可以使用半径函数来增加鱼眼大小。我的目标是在老鼠周围看到更多更大的细胞。当前实现不支持半径函数。如果我使用圆形而不是比例,我可以使用半径函数。但在这种情况下,我不知道如何使用循环。

无论哪种方式,我们都非常感谢您的帮助 :)

谢谢!

【问题讨论】:

【参考方案1】:

圆形鱼眼的半径参数为放大效果设置了一个边界。相比之下,在尺度/笛卡尔鱼眼中,整个图都被修改了。焦点单元被放大,其他单元根据它们离焦点的距离被压缩。没有边界,压缩继续平滑(逐渐压缩)直到绘图的边缘。见http://bost.ocks.org/mike/fisheye/#cartesian

如果您想要的是焦点附近的单元格没有被压缩太多(因此您仍然可以有效地比较相邻单元格),那么要更改的参数是 distortion 参数。较低的失真将减少焦点单元的放大量,因此为相邻单元留出更多空间。默认失真参数为 3,您使用的是更高的值,这会增加焦点单元的放大率,但会牺牲所有其他参数。

如果更改失真不满意,请尝试使用d3.fisheye.scale(d3.scale.sqrt) 更改比例类型;这将改变决定图像放大倍率在您远离焦点时如何变化的功能。 (我无法让其他比例类型工作 - 日志给出错误,并且使用幂比例无法设置指数。)

编辑

经过额外的尝试,我对更改输入比例类型的结果不满意。我误解了这将如何影响它:它不会改变失真的比例函数,而是改变原始数据,因此焦点上方的点与焦点下方的点相比,变化是不同的。您作为鱼眼比例参数提供的比例类型应该是对数据有意义的基础比例类型,并且与鱼眼效果不同。

相反,我尝试了一些自定义代码来将指数添加到计算中。要了解它是如何工作的,首先需要分解原始函数:

鱼眼鳞片的原代码是:

function fisheye(_) 
  var x = scale(_),
      left = x < a,
      range = d3.extent(scale.range()),
      min = range[0],
      max = range[1],
      m = left ? a - min : max - a;
  if (m == 0) m = max - min;
  return a + (left ? -1 : 1) * m * (d + 1) / (d + (m / Math.abs(x - a)));

_是输入值,scale通常是一个线性标度,已经设置了域和范围,a是输出范围内的焦点,d是失真参数。

换句话说:确定在扭曲比例上绘制值的点:

根据默认/不失真比例计算值的范围位置; 计算它与焦点的距离(distance,Math.abs(x-a)); 计算图形边缘与焦点之间的距离(total distance,m); 返回值从焦点偏移 total distance 乘以(d + 1) / (d + (total distance / distance) ); 根据值是低于还是高于焦点进行适当调整。

对于在不失真比例上位于焦点和图形边缘中间的输入点,内部分数 total distance/distance 将等于 2。因此,外部分数将为(d+1)/(d+2)。如果d 为 0(无失真),则等于 1/2,输出点也将位于焦点和图形边缘的中间。随着失真参数d 的增加,该比例也增加:在d=1,输出点将是从焦点到图形边缘的2/3;在d=2,它将是图边缘的 3/4;等等。

相反,当输入值非常接近焦点时,distance接近于0,所以内部分数接近无穷大,外部分数接近0,所以返回的点会非常接近焦点.

最后,当输入值非常接近图的边缘时,distance接近total distance,并且内外分数都接近1,所以返回的点也会很接近到图的边缘。

我们要保留的最后两个身份。我们只想改变两者之间的关系——随着输入点越来越远离焦点和靠近图形边缘,焦点的偏移量如何变化。更改失真参数会同等地更改近值和远值的失真量。如果您减少失真参数,您也会降低整体放大倍数,因为所有数据仍然必须适合相同的空间。

OP 希望降低焦点附近细胞之间的放大率变化率。减少失真参数可以做到这一点,但只能通过整体降低放大倍率来实现。理想的方法是改变距焦点的距离与失真程度之间的关系

我对同一功能的更改代码是:

function fisheye(_) 
    var x = scale(_),
        left = x < a,
        range = d3.extent(scale.range()),
        min = range[0],
        max = range[1],
        m = left ? a - min : max - a,
        dp = Math.pow(d, p);
    if (m == 0) return left? min : max;
    return a + (left ? -1 : 1) *  m  * 
      Math.pow( 
        (dp + 1)
      / (dp + (m / Math.abs(x-a) ) )
      , p);

我改变了两件事:我将分数 (d + 1)/(d + total distance/distance) 提高到一个幂,并且我还将原来的 d 值替换为它提高到相同的指数 (dp)。第一个变化是改变关系,第二个只是调整,以便给定的失真参数将具有大致相同的整体放大效果,而不管功率参数如何。

如果分数接近零,升幂的分数仍然接近零,如果分数接近一,它仍然接近一,所以基本恒等式保持不变。但是,当功率参数小于 1 时,变化率在边缘会变浅,而在中间会变陡。对于大于 1 的功率参数,边缘处的变化率将非常陡峭,而在焦点附近则较浅。

此处的示例:http://codepen.io/AmeliaBR/pen/zHqac 水平鱼眼刻度具有平方根函数(p = 0.5),垂直具有平方函数(p = 2)。两者都具有相同的未调整失真参数 (d = 6)。

平方根函数的效果是即使最远的列仍然有一些可见的宽度,但是焦点附近的列宽变化是显着的。 2 函数的效果是远离焦点的行被压缩到几乎不可见的高度,但焦点上方和下方的行仍然很大。我认为后一个版本实现了@piedpiper 所希望的。

我当然还在鱼眼刻度中添加了一个.power 函数以设置p 参数,并将p 的默认值设置为1,这将得到与原始鱼眼鳞片。我使用名称power 来区分power scales 的exponent 方法,如果它们基础标度(失真之前)具有权力关系,则将使用该方法。

【讨论】:

嗨@AmeliaBR,当降低笛卡尔鱼眼的失真时,我的眼睛无法分辨出太多差异,如果有的话,虽然你的解释是有道理的......也许逻辑宽度/高度相对太大。我想知道是否使用圆形鱼眼应用具有大半径的轻度失真(参见 Mike 工作中的第二张图表,其中圆形鱼眼应用于均匀网格)可能不会产生更符合 piedpiper 目标的结果......虽然我必须说我不完全清楚这些是什么。 @FernOfTheAndes 你是在更改鱼眼脚本中的默认失真参数,还是在初始化鱼眼鳞片时使用的实际参数? 在后者...实际参数。 @FernOfTheAndes 不知道为什么不;失真 10 时,我看到焦点单元占据页面的 1/4,失真 2 或 3 时,它只是略微放大。 @AmeliaBR...jeepers,我想我来回更改的速度太快了...我的系统中的 CodePen 更改生效有相当大的延迟。所以,是的,现在我明白了,将失真从 2 更改为 20 并返回,现在我明白了。

以上是关于如何修改 d3js 鱼眼失真,使其支持半径的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 OpenCV 使裁剪后的鱼眼图像不失真

d3js 画布 概念

反向鱼眼失真

鱼眼失真校正

如何通过openCV模拟鱼眼镜头效果?

在Matlab中去除图像的鱼眼镜头失真时出错[重复]