使用旋转矩阵:
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)
我将一个2d矩阵顺时针旋转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的预煮方法,我知道它们存在。
答案 0 :(得分:3)
我认为你正在尝试的是moire pattern的一种形式,因为当你旋转像素时,你不是旋转数学点,而是旋转小方块。我找到了一个很好的图像表示我正在说here(向下滚动)。
解决方案是应用抗锯齿旋转算法。
修改
一个快速而肮脏的解决方案,我想到了(我不知道有多大效果 - 你应该测试它),是在旋转之前对图像进行过采样,然后将其下采样到原始尺寸。但这 真的 在计算上浪费了。它可能用于测试。
答案 1 :(得分: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预煮方法?)
答案 2 :(得分: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
上的对应点有效。否则,它无效,并且不会被绘制。