微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在二维图像中的不规则形状上应用纹理?

如何解决如何在二维图像中的不规则形状上应用纹理?

enter image description here

我正在尝试从 UIColor 图案图像在 CALayer 上应用纹理。纹理正在应用,但未正确透视转换。看起来我的绘图逻辑有问题,即我需要使用纹理图像并将其映射到不规则形状。我做了一些研究,发现可以通过 OpenGL 或金属将纹理图像映射到二维图像中的不规则形状来实现这一点。

寻求某种指导,如何正确透视变换瓷砖图案?

        let image = UIImage(named: "roofTiles_square")?.flattened
        
        if let realImage = image {
            let color = UIColor(patternImage: realImage)
            controller.quadView.quadLayer.fillColor = color.cgColor
        }

任何帮助将不胜感激。

谢谢

解决方法

我正在编写详细的解决方案,但我想确保我正在解决正确的问题。计划是创建一个转换,正确地扭曲“屋顶瓦片”模式(或任何此类模式)的一个版本,以便在映射到右侧图像中的四边形时正确地透视扭曲?即,四边形 ABCE 映射到四边形 A'B'C'E'?

enter image description here

第一步是计算将四边形 ABCD 映射到四边形 A'B'C'D' 的 homographyOpenCV provides methods for this,但让我们自己算一下。我们正在搜索一个 3x3 矩阵 H,它将点 A、B、C、D 映射到点 A'、B'、C'、D',如下所示(我们实际上会反过来做):>

enter image description here

使用 3D 同质向量 (x,y,w) 允许我们在 3-D 中工作,除以 w 提供必要的透视缩短(长话短说)。 事实证明,H 的任何比例倍数都有效,这意味着它只有 8 个自由度(而不是完整的 3*3 = 9)。这意味着我们希望 HA'A 的比例倍数,因此它们的叉积为零:

enter image description here

如果我们执行叉积,我们可以将最后一个等式重写为

enter image description here

上面的最后一个方程实际上是前两个方程的线性组合(第一个方程乘以 x,第二个方程乘以 y,然后将它们相加,得到第三个方程)。由于第三个方程线性相关,我们把它扔掉,只使用前两个。对第二个方程取反,交换它们,然后将它们转换为矩阵形式我们得到

enter image description here

因此一点对应A' - A 产生两个方程。 如果我们有 n 个点对应,我们得到 2n 个方程:

enter image description here

我们需要 n >= 4 至少有 8 个方程才能得出正确的解;即,我们至少需要 4 个(非共线)点。 因此我们有一个齐次方程组 我们使用 singular value decomposition 解决:

enter image description here

显然,简单的解决方案 h = 0 有效,但它不是很有用。 将 h 设置为 V 的最后一列导致最小二乘 我们系统的错误解决方案,其中 h 具有单位长度。

让我们为您的特定示例计算 H。让我们假设来源 要转换的图像是 WxH = 500x300,因此 A = (0,0)、B = (W,0)、C = (0,H) 和 D = (W,H)。目标图像是 484x217,我找到了 屋顶的角为 A' = (70.7,41.3),B' = (278.8,76.3),C' = (136.4,121,2),D' = (345.1,153,2)。我将使用 Eigen 来 做计算。所以我将加载我的源和目标 点成矩阵:

#include <Eigen/Dense>
...
constexpr double W = 500;
constexpr double H = 300;
constexpr size_t N = 4;

Eigen::Matrix<double,2,N> SRC;
SRC <<
    0,W,H,H;
Eigen::Matrix<double,N> DST;
DST <<
    70.7,278.8,136.4,345.1,41.3,76.3,121.2,153.2;

我如上所述构造了 8x9 矩阵 A

Eigen::Matrix<double,2*N,9> A;
A.setZero();
for (size_t i = 0; i < N; i++) {
    const double x_ = DST(0,i),y_ = DST(1,i);
    const double x  = SRC(0,y  = SRC(1,i);
    A(2*i,0) = A(2*i+1,3) = x_;
    A(2*i,1) = A(2*i+1,4) = y_;
    A(2*i,2) = A(2*i+1,5) = 1;
    A(2*i,6) = -x*x_;
    A(2*i,7) = -x*y_;
    A(2*i,8) = -x;
    A(2*i+1,6) = -y*x_;
    A(2*i+1,7) = -y*y_;
    A(2*i+1,8) = -y;
}

然后我计算 SVD,从 V 的最后一列,并将结果存储在 3x3 矩阵中:

Eigen::JacobiSVD<Eigen::Matrix<double,9>> svd(A,Eigen::ComputeFullV);
Eigen::Matrix<double,9,1> h = svd.matrixV().col(8);
Eigen::Matrix3d Homography;
Homography <<
    h(0),h(1),h(2),h(3),h(4),h(5),h(6),h(7),h(8);

产生所需的 3x3 矩阵 H:

  -0.016329     0.013427      0.599927
   0.004571    -0.0271779     0.799277
   1.78122e-06 -2.83812e-06  -0.00613631

我们可以看一下使用 OpenCV 的示例扭曲图像。 我加载我的源纹理和我的单应性 H 并使用 OpenCV warpPerspective function

#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>

int main() {
    cv::Mat sourceImage = imread("texture.png",cv::IMREAD_COLOR);
    cv::Matx33d H(-0.016329,0.013427,0.599927,0.004571,-0.0271779,0.799277,1.78122e-06,-2.83812e-06,-0.00613631);
    cv::Mat destImage;
    cv::warpPerspective(sourceImage,destImage,cv::Size(487,217),cv::INTER_LINEAR | cv::WARP_INVERSE_MAP);
    cv::imwrite("warped.png",destImage);
    return 0;
}

结果看起来很合理:

enter image description here enter image description here

,

用平行四边形屋顶的主要部分做同样的事情,也就是说,如果你不关心它在房子里的外观...... enter image description here

嗯,他们说一张图片值 1000 字。我修改的上图显示了您应该纹理映射到的绿色平行四边形。据推测,它的右侧部分将在主屋顶下方进行 Z 排序。同样的事情发生在一些房屋的阁楼上,建筑商不必支付材料费用,而是按小时支付。他们只会切入屋顶并插入一个直线预制件......

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。