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

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. grep工具

    全面搜索正则表达式(Global search regular expression(RE) ,GREP)是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来. Unix/Lin ...

  2. git push报错大文件,删除后重新commit依然报错

    git push报错: github不能上传大文件,按道理删掉重新提交就行了 可是删掉后,git add -A,再git commit,再git push,依然报错 后来我想明白了 github上传时 ...

  3. slim中的请求头

    请求头 每个 HTTP 请求都有请求头.这些元数据描述了 HTTP 请求,但在请求体中不可见.Slim 的 PSR 7 请求对象提供了几个检查请求头的方法. 获取所有的请求头,返回一个数组:getHe ...

  4. 007.MFC_ComboBox_ListBox

    组合框.列表框 组合框的封装类:CComboBox 列表框的封装类:CListBox 一.创建名为ComboAndList的MFC工程,按照下图添加组件 修改static text Caption属性 ...

  5. JSR-133内存模型手册

    1.介绍 JVM支持多种线程的执行,Threads代表的是线程类,位于java.lang.Thread包下,唯一的方式就是为用户在这个类下的对象创建线程,每一个线程关联着一个对象,一个线程将在star ...

  6. centos利用OneinStack搭建环境

    介绍 OneinStack支持以下数种环境组合: LNMP(Linux + Nginx+ MySQL+ PHP) LAMP(Linux + Apache+ MySQL+ PHP) LNMPA(Linu ...

  7. $CH5501$ 环路运输 环形$+$单调队列

    CH Description 在一条环形公路旁均匀地分布着N座仓库,编号为1~N,编号为 i 的仓库与编号为 j 的仓库之间的距离定义为 dist(i,j)=min⁡(|i-j|,N-|i-j|),也 ...

  8. $Loj10155$ 数字转换(求树的最长链) 树形$DP$

    loj Description 如果一个数x的/约数和/y(不包括他本身)比他本身小,那么x可以变成y,y 也可以变成x.限定所有数字变换在不超过n的正整数范围内进行,求不断进行数字变换且不出现重复数 ...

  9. Keil uVision4的简单使用

    1. 项目创建 打开安装好的Keil uVision4可以看到如下界面 选择New uVision Project来创建一个新项目 选择项目存放的位置并为项目文件命名 选择要进行模拟仿真的设备(此处以 ...

  10. openjudge 7622 求排列的逆序数(归并)

    7622:求排列的逆序数 总时间限制:  1000ms   内存限制:  65536kB 描述 在Internet上的搜索引擎经常需要对信息进行比较,比如可以通过某个人对一些事物的排名来估计他(或她) ...