图像的旋转是指以图像中的某一点为原点以逆时针或者顺时针方向旋转一定的角度。通常是绕图像的起始点以逆时针进行旋转。
图像旋转计算公式如下: i ′ = i cos θ − j sin θ i'=i\cos\theta-j\sin \theta i′=icosθ−jsinθ j ′ = i sin θ + j cos θ j'=i\sin\theta+j\cos\theta j′=isinθ+jcosθ
其中, ( i , j ) (i,j) (i,j)是原图像 f ( i , j ) f(i,j) f(i,j)中的像素点坐标; ( i ′ , j ′ ) (i',j') (i′,j′)是对应像素点 ( i , j ) (i,j) (i,j)经过旋转变换后的图像 g ( i ′ , j ′ ) g(i',j') g(i′,j′)的坐标。
设原图为 f = [ f 11 f 12 f 13 f 21 f 22 f 23 f 31 f 32 f 33 ] f = \begin{bmatrix} f_{11} & f_{12} & f_{13}\\ f_{21} & f_{22} & f_{23}\\ f_{31} & f_{32} & f_{33}\\ \end{bmatrix} f=⎣⎡f11f21f31f12f22f32f13f23f33⎦⎤
则 i i i构成的矩阵为: i = [ 1 1 1 2 2 2 3 3 3 ] i= \begin{bmatrix} 1 & 1 & 1\\ 2 & 2 & 2\\ 3 & 3 &3\\ \end{bmatrix} i=⎣⎡123123123⎦⎤
j j j构成的矩阵为: j = [ 1 2 3 1 2 3 1 2 3 ] j= \begin{bmatrix} 1 & 2 & 3\\ 1 & 2 & 3\\ 1 & 2 & 3\\ \end{bmatrix} j=⎣⎡111222333⎦⎤
假设此时要逆时针旋转30度,则根据上面的公式计算 i ′ i' i′和 j ′ j' j′。
i ′ = i cos 30 ° − j sin 30 ° i'=i\cos30\degree-j\sin 30\degree i′=icos30°−jsin30° j ′ = i sin 30 ° + j cos 30 ° j'=i\sin30\degree+j\cos30\degree j′=isin30°+jcos30°
如果要顺时针旋转30度,加个负号就可以了。
计算出的 i ′ i' i′构成的矩阵为:
i ′ = [ 0.4 − 0.1 − 0.6 1.2 0.7 0.2 2.1 1.6 1.1 ] i'= \begin{bmatrix} 0.4& -0.1 & -0.6\\ 1.2 & 0.7 & 0.2\\ 2.1 & 1.6 &1.1\\ \end{bmatrix} i′=⎣⎡0.41.22.1−0.10.71.6−0.60.21.1⎦⎤
此时我们可以看到 i ′ i' i′中出现了负数和小数,这在我们编程中是行不通的,所以我们将 i ′ i' i′这个矩阵先进行四舍五入。
i ′ = i ′ − min ( i ′ ) = i ′ − ( − 0.6 ) = [ 0 0 − 1 1 1 0 2 2 1 ] i'=i'-\min(i')=i'-(-0.6)=\begin{bmatrix} 0 & 0 & -1 \\ 1 & 1 & 0\\ 2 & 2 &1 \\ \end{bmatrix} i′=i′−min(i′)=i′−(−0.6)=⎣⎡012012−101⎦⎤
又因为坐标没有负数,所以我们将 i ′ i' i′中的每个元素加上2变成正整数得
i ′ = [ 2 2 1 3 3 2 4 4 3 ] i'= \begin{bmatrix} 2& 2 & 1\\ 3 & 3 & 2\\ 4 &4 &3\\ \end{bmatrix} i′=⎣⎡234234123⎦⎤
同理可得 j ′ = [ 1 2 3 2 3 4 2 3 4 ] j'= \begin{bmatrix} 1 & 2 & 3 \\ 2 & 3 & 4 \\ 2 & 3 &4 \\ \end{bmatrix} j′=⎣⎡122233344⎦⎤
现在我们可以找到对应关系了,例如:原图中的(1,1)点对应新图中的(2,1)点。旋转处理后的的图像数据为:
g = [ 0 0 f 13 0 f 11 f 12 0 f 23 0 f 21 f 22 f 33 0 f 31 f 32 0 ] g=\begin{bmatrix} 0 & 0 & f_{13} & 0\\ f_{11} & f_{12} & 0 & f_{23} \\ 0 & f_{21} &f_{22} &f_{33} \\ 0 & f_{31} & f_{32} & 0 \end{bmatrix} g=⎣⎢⎢⎡0f11000f12f21f31f130f22f320f23f330⎦⎥⎥⎤
从得到的 g g g矩阵中我们可以看到, g 23 g_{23} g23是一个空穴点,这一点我们需要利用插值法进行填补。其他为0的点则是属于画布上的空白点。
均值插值法是将空穴像素周围像素值的均值作为填充值填在该空穴点中,如下图所示:
背景为浅蓝色的点为空穴点,像素值为0。那为什么其他像素值为0的点不是空穴点呢?这里我们给出一个定义:如果一个像素值为0的点的上、下、左、右四个位置处的像素值都不为0,那我们就认为该点是空穴点,需要进行填充。
填充方式就是: g 23 = ( f 13 + f 12 + f 23 + f 22 ) / 4 g_{23}=(f_{13}+f_{12}+f_{23}+f_{22})/4 g23=(f13+f12+f23+f22)/4
int main()
{
cv::Mat img = cv::imread("Lena.bmp");
cv::Mat gray_img(img.cols,img.rows, CV_8UC1);
cv::cvtColor(img, gray_img, CV_BGR2GRAY);
float theta =45;
float curvature = theta / 180 * CV_PI;
cv::Mat x = cv::Mat::zeros(img.size(), CV_32FC1);
cv::Mat y = cv::Mat::zeros(img.size(), CV_32FC1);
for (int row = 0; row < img.rows; row++)
{
for (int col = 0; col < img.cols; col++)
{
// 计算新的坐标
x.at<float>(row, col) = round(row * cos(curvature) - col * sin(curvature));
y.at<float>(row, col) = round(row * sin(curvature) + col * cos(curvature));
}
}
double x_min, x_max;
double y_min, y_max;
cv::minMaxLoc(x, &x_min, &x_max);
cv::minMaxLoc(y, &y_min, &y_max);
x = x - x_min;
y = y - y_min;
cv::minMaxLoc(x, &x_min, &x_max); // 画布的高
cv::minMaxLoc(y, &y_min, &y_max); // 画布的宽
cv::Mat dst = cv::Mat::zeros(x_max+1, y_max+1, CV_8UC1); //幕布
cv::Mat flag = cv::Mat::zeros(x_max + 1, y_max + 1, CV_8UC1);
for (int row = 0; row < gray_img.rows; row++)
{
for (int col = 0; col < gray_img.cols; col++)
{
int i = (int)x.at<float>(row, col);
int j = (int)y.at<float>(row, col);
dst.at<uchar>(i, j) = gray_img.at<uchar>(row, col);
flag.at<uchar>(i, j) = 1;
}
}
//均值插值法对空穴进行插值
for (int row = 1; row < dst.rows-1; row++)
{
for (int col = 1; col < dst.cols-1; col++)
{
if (flag.at<uchar>(row, col - 1) == 1 && flag.at<uchar>(row, col + 1) == 1 &&
flag.at<uchar>(row - 1, col) == 1 && flag.at<uchar>(row + 1, col) == 1
&& flag.at<uchar>(row, col) == 0)
{
dst.at<uchar>(row, col) = uchar((dst.at<uchar>(row, col - 1) + dst.at<uchar>(row, col + 1) +
dst.at<uchar>(row - 1, col) + dst.at<uchar>(row + 1, col)) / 4);
}
}
}
cv::imshow("input", gray_img);
cv::imshow("output", dst);
cv::waitKey(0);
return 0;
}
1.数字图像处理基础.朱虹.