You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为SVG图像应用Pincushion Distortion,实现曲面QR Code可靠识别?

为SVG二维码添加枕形畸变的实现方法

方案1:逐个修改QR码模块坐标(精准可控)

SVG二维码本质是一堆规则的矩形元素,我们可以通过计算每个矩形顶点的枕形变换坐标,把矩形替换成多边形,实现精准畸变。

步骤与代码

  1. 定位SVG和其中的二维码矩形元素
  2. 基于枕形变换公式,计算每个顶点的新坐标
  3. 用多边形替换原矩形
// 获取目标SVG和二维码矩形(根据实际情况调整选择器)
const svg = document.querySelector('svg');
const qrRects = svg.querySelectorAll('rect');
const svgWidth = parseFloat(svg.getAttribute('width'));
const svgHeight = parseFloat(svg.getAttribute('height'));
// SVG中心坐标
const cx = svgWidth / 2;
const cy = svgHeight / 2;
// 半宽高,用于归一化计算
const halfW = svgWidth / 2;
const halfH = svgHeight / 2;
// 枕形畸变系数:正值为枕形,越大畸变越明显,建议0.1-0.5之间调试
const distortionK = 0.3;

qrRects.forEach(rect => {
  // 获取原矩形的位置和尺寸
  const x = parseFloat(rect.getAttribute('x'));
  const y = parseFloat(rect.getAttribute('y'));
  const w = parseFloat(rect.getAttribute('width'));
  const h = parseFloat(rect.getAttribute('height'));
  
  // 提取矩形四个顶点
  const originalPoints = [
    {x, y},
    {x: x + w, y},
    {x: x + w, y: y + h},
    {x, y: y + h}
  ];
  
  // 对每个顶点应用枕形变换
  const transformedPoints = originalPoints.map(point => {
    const dx = point.x - cx;
    const dy = point.y - cy;
    // 归一化距离平方
    const rSquared = (dx / halfW) ** 2 + (dy / halfH) ** 2;
    // 计算变换后的偏移量
    const newDx = dx * (1 + distortionK * rSquared);
    const newDy = dy * (1 + distortionK * rSquared);
    // 转换回绝对坐标,拼接成字符串
    return `${cx + newDx},${cy + newDy}`;
  });
  
  // 创建多边形替换原矩形
  const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
  polygon.setAttribute('points', transformedPoints.join(' '));
  polygon.setAttribute('fill', rect.getAttribute('fill'));
  // 复制原矩形的其他样式属性(比如stroke)
  rect.parentNode.replaceChild(polygon, rect);
});

这个方法的优势是每个二维码模块的形状都能精准控制,不会出现模糊,对扫描识别的影响最小。

方案2:用SVG滤镜实现全局畸变

如果不想逐个修改元素,可以用SVG滤镜实现全局的枕形畸变,适合快速调试效果。

代码示例

在SVG中定义滤镜,然后应用到二维码所在的组元素上:

<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
  <!-- 定义枕形畸变滤镜 -->
  <filter id="pincushionFilter">
    <!-- 生成归一化坐标纹理 -->
    <feFlood result="base" flood-color="#000"/>
    <feComposite in="base" in2="SourceGraphic" operator="in" result="masked"/>
    <feComponentTransfer result="normCoords">
      <feFuncR type="linear" slope="0.005" intercept="-0.5"/>
      <feFuncG type="linear" slope="0.005" intercept="-0.5"/>
    </feComponentTransfer>
    <!-- 计算坐标平方和 -->
    <feColorMatrix type="matrix" values="1 0 0 0 0; 0 1 0 0 0; 0 0 0 0 0; 0 0 0 0 0" result="xy"/>
    <feComposite in="xy" in2="xy" operator="arithmetic" k2="1" k3="1" result="rSquared"/>
    <!-- 计算畸变缩放因子 -->
    <feComponentTransfer result="scaleFactor">
      <feFuncR type="linear" slope="0.6" intercept="1"/>
      <feFuncG type="linear" slope="0.6" intercept="1"/>
    </feComponentTransfer>
    <!-- 计算位移量 -->
    <feComposite in="normCoords" in2="rSquared" operator="arithmetic" k2="0.6" result="displacement"/>
    <!-- 应用位移映射 -->
    <feDisplacementMap in="SourceGraphic" in2="displacement" scale="60" xChannelSelector="R" yChannelSelector="G"/>
  </filter>

  <!-- 将滤镜应用到二维码组 -->
  <g filter="url(#pincushionFilter)">
    <!-- 这里放置你的二维码矩形元素 -->
  </g>
</svg>

需要调试的参数:feComponentTransfer里的slope、feDisplacementMap的scale,这些值决定了畸变的程度,需要根据实际场景反复调整。

调试注意事项

  • 先从小的畸变系数开始测试,逐步调整到扫描稳定的效果
  • 尽量模拟实际扫描的角度和距离,确保畸变后的二维码在目标场景下呈现平面效果
  • 如果需要打印后贴曲面,要考虑打印缩放比例,匹配畸变参数

内容的提问来源于stack exchange,提问作者BogisW

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 07:25:17