【问题标题】:Rotating a 2d matrix clockwise produces odd results顺时针旋转二维矩阵会产生奇怪的结果
【发布时间】:2013-09-08 15:33:50
【问题描述】:

使用旋转矩阵:

R(-t) = [cos(t)  sin(t)]
        [-sin(t) cos(t)]

其中 t = 以弧度表示的角度

功能表示为:

[x] = [cos(t)  sin(t)][x]
[y]   [-sin(t) cos(t)][y]

x' =  x * cos(t) + y * sin(t)
y' = -x * sin(t) + y * cos(t)

我将一个二维矩阵顺时针旋转 30 度,尺寸为 150x150。

在代码中是这样的:

    cv::Mat m = cv::Mat::zeros(150, 150, CV_8UC1);
    cv::Mat nm = cv::Mat::zeros(150, 150, CV_8UC1);
    cv::rectangle(m, cv::Rect(0,0,150,150), cv::Scalar(100,100,100),-1);

    double rad = (30.0 * M_PI/180.0);
    for (int c = 0; c < m.cols;c++){
        for (int r = 0; r < m.rows;r++){
            int x = (m.cols*0.5)-c;
            int y = (m.rows*0.5)-r;
            int xn = (x*cos(rad) + y*sin(rad));
            int yn = (-x*sin(rad) + y*cos(rad));
            xn += (m.cols*0.5);
            yn += (m.rows*0.5);
            if (xn<0||xn>=m.cols||yn<0||yn>=m.rows){
                continue;
            }
            nm.at<uchar>(xn,yn)=m.at<uchar>(c,r);    
        }
    }

数学上看起来是正确的,但这些是我得到的结果

未旋转的矩阵:

旋转矩阵:

旋转后的矩阵呈现颗粒状。这是什么原因造成的?它是如何固定的?我更喜欢数学上的解释。

PS。请不要指向opencv的预煮方法,我知道它们存在。

【问题讨论】:

  • 可能是四舍五入或精度问题。
  • 如果您转动相机使其与矩阵对齐会发生什么?

标签: c++ math opencv matrix matrix-multiplication


【解决方案1】:

我认为您正在尝试的是moire pattern 的一种形式,因为当您旋转像素时,您不是在旋转数学点,而是在旋转小方块。我找到了一个很好的图片表示我在说什么here(向下滚动)。

解决方案是应用抗锯齿旋转算法。

编辑

我想到的一个快速而肮脏的解决方案(我不知道效果如何 - 你应该测试它)是在旋转之前对图像进行过采样,然后将其下采样到原始尺寸。但这真的在计算上是浪费的。不过它可能用于测试。

【讨论】:

  • 放大、旋转然后再次缩小确实解决了这个问题。抗锯齿旋转算法的示例是什么?
【解决方案2】:

诀窍是使用逆变换,然后对结果图像上的每个像素进行采样。假设您要旋转(或以其他方式变换)图像 I1(x1,y1) 以生成图像 I2(x2,y2),其中 I(x,y) 是一个函数,它给出了点 x,y 处的图像强度) .假设您想到的变换是 T(x1,y1) = (x2, y2)。

现在您遍历目标图像 I2 中的每个点,并应用逆变换 Tinv 来查找原始图像中的相应像素:

I2(x,y) := I1(Tinv(x,y))

这应该给你一个同质的盒子,至少在你的示例图像中。为了获得更好的图像质量,您需要使用一些插值,因为 Tinv(x,y) 不会正好是 I1 中像素的中心。

看起来有一些image warping in opencv,应该会给你你想要的。 (或者有什么特殊原因你不想要 opencv 预煮方法?)

【讨论】:

    【解决方案3】:

    模式的数学解释:

    考虑未旋转的点(0, 0)(1, 0)

    旋转 30 度后,它们分别变为 (0, 0)(0.866, -0.5)

    在数学上,这些是不同的点。但是,这两个点都分配给int 变量,正数向下取整,负数向上取整。

    这意味着旋转点的计算值实际上是(0, 0)(0, 0)

    因为这两个值都分配给同一个像素,所以会发生双倍放大,并且看起来好像一个像素从旋转的正方形中消失了。每次出现双倍像素坐标时,此过程都会继续发生,从而在旋转的正方形上产生图案。

    修复:

    要解决此问题,您必须倒退问题:将每个正方形视为已旋转,并查看它是否与原始正方形上的点相关。

    为此,您使用旋转矩阵的逆矩阵(您已完成):

    X' =  X * cos(t) + Y * sin(t)
    Y' = -X * sin(t) + Y * cos(t)
    

    所以,你需要做的就是稍微增加你的计算循环:

    for (int r = 0; r < nm.rows; ++r){ 
        for (int c = 0; c < nm.cols; ++c){ 
            int x = c - (m.cols*0.5); //Translates from the origin
            int y = r - (m.rows*0.5); 
            int xn = (x*cos(rad) + y*sin(rad)) + 0.5f; //The 0.5 corrects rounding
            int yn = (-x*sin(rad) + y*cos(rad)) + 0.5f;
            xn += (m.cols*0.5); //Translates back to the origin
            yn += (m.rows*0.5);
            if (xn >= 0 && xn < m.cols && yn >= 0 && yn < m.rows) 
                nm.at<uchar>(c,r) = m.at<uchar>(xn,yn);
                //Changed the above line so that nm is cycled through, not m
        }
    }
    

    修复的工作原理:

    您有两个矩形:一个空白的:nm,一个填充的:m

    最初,您循环遍历m 的所有像素,旋转它们,然后将旋转点设置在nm 上,从而产生了有趣的图案。

    增强版则相反:它循环遍历nm 的所有点并将它们旋转回原来的正方形。如果旋转回来时点在m 内部,则nm 上的对应点有效。否则无效,不绘制。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-15
      • 2015-10-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多