2

使用旋转矩阵:

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的预煮方法,我知道它们存在。

4

3 回答 3

3

我认为您正在尝试的是一种莫尔图案,因为当您旋转像素时,您不是在旋转数学点,而是在旋转小方块。我在这里找到了一个很好的图片表示(向下滚动)。

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

编辑

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

于 2013-09-08T16:06:13.367 回答
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 中像素的中心。

看起来opencv 中存在一些图像扭曲,应该可以满足您的需求。(或者有什么特殊原因你不想要 opencv 预煮方法?)

于 2013-09-08T16:36:36.807 回答
0

模式的数学解释:

考虑未旋转的点(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是有效的。否则无效,不绘制。

于 2015-01-03T00:40:47.130 回答