如果平面上的点绕原点逆时针旋转θº,则其坐标变换公式为:

x'=xcosθ+ysinθ   y=-xsinθ+ycosθ

其中,(x, y)为原图坐标,(x’, y’)为旋转后的坐标。它的逆变换公式为:

x=x'cosθ-y'sinθ   y=x'sinθ+y'cosθ

矩阵形式为:

和缩放类似,旋转后的图像的像素点也需要经过坐标转换为原始图像上的坐标来确定像素值,同样也可能找不到对应点,因此旋转也用到插值法。在此选用性能较好的双线性插值法。为提高速度,在处理旋转90º、-90º、±180º时使用了镜像来处理。

        /// <summary>
/// 图像旋转
/// </summary>
/// <param name="srcBmp">原始图像</param>
/// <param name="degree">旋转角度</param>
/// <param name="dstBmp">目标图像</param>
/// <returns>处理成功 true 失败 false</returns>
public static bool Rotation(Bitmap srcBmp, double degree, out Bitmap dstBmp)
{
if (srcBmp == null)
{
dstBmp = null;
return false;
}
dstBmp = null;
BitmapData srcBmpData = null;
BitmapData dstBmpData = null;
switch ((int)degree)
{
case :
dstBmp = new Bitmap(srcBmp);
break;
case -:
dstBmp = new Bitmap(srcBmp.Height, srcBmp.Width);
srcBmpData = srcBmp.LockBits(new Rectangle(, , srcBmp.Width, srcBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
dstBmpData = dstBmp.LockBits(new Rectangle(, , dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
unsafe
{
byte* ptrSrc = (byte*)srcBmpData.Scan0;
byte* ptrDst = (byte*)dstBmpData.Scan0;
for (int i = ; i < srcBmp.Height; i++)
{
for (int j = ; j < srcBmp.Width; j++)
{
ptrDst[j * dstBmpData.Stride + (dstBmp.Height - i - ) * ] = ptrSrc[i * srcBmpData.Stride + j * ];
ptrDst[j * dstBmpData.Stride + (dstBmp.Height - i - ) * + ] = ptrSrc[i * srcBmpData.Stride + j * + ];
ptrDst[j * dstBmpData.Stride + (dstBmp.Height - i - ) * + ] = ptrSrc[i * srcBmpData.Stride + j * + ];
}
}
}
srcBmp.UnlockBits(srcBmpData);
dstBmp.UnlockBits(dstBmpData);
break;
case :
dstBmp = new Bitmap(srcBmp.Height, srcBmp.Width);
srcBmpData = srcBmp.LockBits(new Rectangle(, , srcBmp.Width, srcBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
dstBmpData = dstBmp.LockBits(new Rectangle(, , dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
unsafe
{
byte* ptrSrc = (byte*)srcBmpData.Scan0;
byte* ptrDst = (byte*)dstBmpData.Scan0;
for (int i = ; i < srcBmp.Height; i++)
{
for (int j = ; j < srcBmp.Width; j++)
{
ptrDst[(srcBmp.Width - j - ) * dstBmpData.Stride + i * ] = ptrSrc[i * srcBmpData.Stride + j * ];
ptrDst[(srcBmp.Width - j - ) * dstBmpData.Stride + i * + ] = ptrSrc[i * srcBmpData.Stride + j * + ];
ptrDst[(srcBmp.Width - j - ) * dstBmpData.Stride + i * + ] = ptrSrc[i * srcBmpData.Stride + j * + ];
}
}
}
srcBmp.UnlockBits(srcBmpData);
dstBmp.UnlockBits(dstBmpData);
break;
case :
case -:
dstBmp = new Bitmap(srcBmp.Width, srcBmp.Height);
srcBmpData = srcBmp.LockBits(new Rectangle(, , srcBmp.Width, srcBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
dstBmpData = dstBmp.LockBits(new Rectangle(, , dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
unsafe
{
byte* ptrSrc = (byte*)srcBmpData.Scan0;
byte* ptrDst = (byte*)dstBmpData.Scan0;
for (int i = ; i < srcBmp.Height; i++)
{
for (int j = ; j < srcBmp.Width; j++)
{
ptrDst[(srcBmp.Width - i - ) * dstBmpData.Stride + (dstBmp.Height - j - ) * ] = ptrSrc[i * srcBmpData.Stride + j * ];
ptrDst[(srcBmp.Width - i - ) * dstBmpData.Stride + (dstBmp.Height - j - ) * + ] = ptrSrc[i * srcBmpData.Stride + j * + ];
ptrDst[(srcBmp.Width - i - ) * dstBmpData.Stride + (dstBmp.Height - j - ) * + ] = ptrSrc[i * srcBmpData.Stride + j * + ];
}
}
}
srcBmp.UnlockBits(srcBmpData);
dstBmp.UnlockBits(dstBmpData);
break;
default://任意角度
double radian = degree * Math.PI / 180.0;//将角度转换为弧度
//计算正弦和余弦
double sin = Math.Sin(radian);
double cos = Math.Cos(radian);
//计算旋转后的图像大小
int widthDst = (int)(srcBmp.Height * Math.Abs(sin) + srcBmp.Width * Math.Abs(cos));
int heightDst = (int)(srcBmp.Width * Math.Abs(sin) + srcBmp.Height * Math.Abs(cos)); dstBmp = new Bitmap(widthDst, heightDst);
//确定旋转点
int dx = (int)(srcBmp.Width / * ( - cos) + srcBmp.Height / * sin);
int dy = (int)(srcBmp.Width / * ( - sin) + srcBmp.Height / * ( - cos)); int insertBeginX = srcBmp.Width / - widthDst / ;
int insertBeginY = srcBmp.Height / - heightDst / ; //插值公式所需参数
double ku = insertBeginX * cos - insertBeginY * sin + dx;
double kv = insertBeginX * sin + insertBeginY * cos + dy;
double cu1 = cos, cu2 = sin;
double cv1 = sin, cv2 = cos; double fu, fv, a, b, F1, F2;
int Iu, Iv;
srcBmpData = srcBmp.LockBits(new Rectangle(, , srcBmp.Width, srcBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
dstBmpData = dstBmp.LockBits(new Rectangle(, , dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); unsafe
{
byte* ptrSrc = (byte*)srcBmpData.Scan0;
byte* ptrDst = (byte*)dstBmpData.Scan0;
for (int i = ; i < heightDst; i++)
{
for (int j = ; j < widthDst; j++)
{
fu = j * cu1 - i * cu2 + ku;
fv = j * cv1 + i * cv2 + kv;
if ((fv < ) || (fv > srcBmp.Height - ) || (fu < ) || (fu > srcBmp.Width - ))
{ ptrDst[i * dstBmpData.Stride + j * ] = ;
ptrDst[i * dstBmpData.Stride + j * + ] = ;
ptrDst[i * dstBmpData.Stride + j * + ] = ;
}
else
{//双线性插值
Iu = (int)fu;
Iv = (int)fv;
a = fu - Iu;
b = fv - Iv;
for (int k = ; k < ; k++)
{
F1 = ( - b) * *(ptrSrc + Iv * srcBmpData.Stride + Iu * + k) + b * *(ptrSrc + (Iv + ) * srcBmpData.Stride + Iu * + k);
F2 = ( - b) * *(ptrSrc + Iv * srcBmpData.Stride + (Iu + ) * + k) + b * *(ptrSrc + (Iv + ) * srcBmpData.Stride + (Iu + ) * + k);
*(ptrDst + i * dstBmpData.Stride + j * + k) = (byte)(( - a) * F1 + a * F2);
}
}
}
}
}
srcBmp.UnlockBits(srcBmpData);
dstBmp.UnlockBits(dstBmpData);
break;
}
return true;
}

c#数字图像处理(十一)图像旋转的更多相关文章

  1. Win8 Metro(C#) 数字图像处理--1 图像打开,保存

    原文:Win8 Metro(C#) 数字图像处理--1 图像打开,保存 作为本专栏的第一篇,必不可少的需要介绍一下图像的打开与保存,一便大家后面DEMO的制作.   Win8Metro编程中,图像相关 ...

  2. Win8 Metro(C#)数字图像处理--4图像颜色空间描述

    原文:Win8 Metro(C#)数字图像处理--4图像颜色空间描述  图像颜色空间是图像颜色集合的数学表示,本小节将针对几种常见颜色空间做个简单介绍. /// <summary> / ...

  3. 数字图像处理,图像锐化算法的C++实现

    http://blog.csdn.net/ebowtang/article/details/38961399 之前一段我们提到的算法都是和平滑有关, 经过平滑算法之后, 图像锐度降低, 降低到一定程度 ...

  4. 数字图像处理:图像的灰度变换(Matlab实现)

    (1)线性变换:通过建立灰度映射来调整源图像的灰度. k>1增强图像的对比度:k=1调节图像亮度,通过改变d值达到调节亮度目的:0 i = imread('theatre.jpg');i = i ...

  5. 数字图像处理界标准图像 Lena 后面的故事

    熟悉图像处理或者压缩的工程师.研究人员和学生,经常在他们的实验或者项目任务里使用"Lenna"或者"Lena"的图像.Lenna 图像已经成为被广泛使用的测试图 ...

  6. 【数字图像处理】六.MFC空间几何变换之图像平移、镜像、旋转、缩放具体解释

    本文主要讲述基于VC++6.0 MFC图像处理的应用知识,主要结合自己大三所学课程<数字图像处理>及课件进行解说,主要通过MFC单文档视图实现显示BMP图片空间几何变换.包含图像平移.图形 ...

  7. 【python图像处理】图像的缩放、旋转与翻转

    [python图像处理]图像的缩放.旋转与翻转 图像的几何变换,如缩放.旋转和翻转等,在图像处理中扮演着重要的角色,python中的Image类分别提供了这些操作的接口函数,下面进行逐一介绍. 1.图 ...

  8. 【数字图像处理】五.MFC图像点运算之灰度线性变化、灰度非线性变化、阈值化和均衡化处理具体解释

    本文主要讲述基于VC++6.0 MFC图像处理的应用知识,主要结合自己大三所学课程<数字图像处理>及课件进行解说.主要通过MFC单文档视图实现显示BMP图片点运算处理.包含图像灰度线性变换 ...

  9. Win8 Metro(C#)数字图像处理--3.2图像方差计算

    原文:Win8 Metro(C#)数字图像处理--3.2图像方差计算 /// <summary> /// /// </summary>Variance computing. / ...

随机推荐

  1. Android SDK目录具体结构及Android源码的具体结构

    Android SDK目录具体结构 1. add-ons:这里面保存着附加库,比如Google Maps,当然你如果安装了OphoneSDK,这里也会有一些类库在里面. 2. docs:这里面是And ...

  2. Keras cnn 手写数字识别示例

    #基于mnist数据集的手写数字识别 #构造了cnn网络拟合识别函数,前两层为卷积层,第三层为池化层,第四层为Flatten层,最后两层为全连接层 #基于Keras 2.1.1 Tensorflow ...

  3. 缓存, 队列(Redis,RabbitMQ)

    Redis Redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorte ...

  4. rest_framework框架下的Django声明和生命周期

    rest_framework框架下的Django声明和生命周期 Django声明周期(request) 客户端发起请求 请求经过wsgi wsgi: 是一个协议----web服务网关接口,即在web服 ...

  5. Web基础了解版11-Ajax-JSON

    Ajax AJAX即“Asynchronous Javascript And XML”:是,不发生页面跳转.异步请求载入内容并改写局部页面内容的技术. 也可以简单的理解为通过JS向服务器发送请求.   ...

  6. CSS常见的继承属性

    时间过得很快参加工作半个月了,利用属性的继承性可以有效提高代码的阅读性. 这里是我个人总结的,虽然不全,但是常见: 1.字体系列属性 font-family:字体系列 font-weight:字体的粗 ...

  7. 《带你装B,带你飞》pytest修仙之路3 - setup/teardown

    1. 简介 学过unittest的都知道里面用前置和后置setup和teardown非常好用,在每次用例开始前和结束后都去执行一次.当然还有更高级一点的setupClass和teardownClass ...

  8. License for package Android SDK Build-Tools 28.0.3 not accepted

    License for package Android SDK Build-Tools 28.0.3 not accepted 用flutter进行编写时出现了标题的错误,不是配置的原因,而是需要接受 ...

  9. java中一些常考知识

    一.static的作用 static是修饰符,用于修饰成员变量(静态变量/类变量). static修饰的成员被所有对象共享. static优先于对象存在. static修饰的成员可以用类名.静态成员来 ...

  10. Netty快速入门(06)Netty介绍

    前面简单的介绍了Java I/O 和NIO,写了示例程序. Java I/O是阻塞的,为了让它支持多个并发,就要针对每个链接启动线程,这种方式的结果就是在海量链接的情况下,会创建海量的线程,就算用线程 ...