c#数字图像处理(十一)图像旋转
如果平面上的点绕原点逆时针旋转θº,则其坐标变换公式为:
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#数字图像处理(十一)图像旋转的更多相关文章
- Win8 Metro(C#) 数字图像处理--1 图像打开,保存
原文:Win8 Metro(C#) 数字图像处理--1 图像打开,保存 作为本专栏的第一篇,必不可少的需要介绍一下图像的打开与保存,一便大家后面DEMO的制作. Win8Metro编程中,图像相关 ...
- Win8 Metro(C#)数字图像处理--4图像颜色空间描述
原文:Win8 Metro(C#)数字图像处理--4图像颜色空间描述 图像颜色空间是图像颜色集合的数学表示,本小节将针对几种常见颜色空间做个简单介绍. /// <summary> / ...
- 数字图像处理,图像锐化算法的C++实现
http://blog.csdn.net/ebowtang/article/details/38961399 之前一段我们提到的算法都是和平滑有关, 经过平滑算法之后, 图像锐度降低, 降低到一定程度 ...
- 数字图像处理:图像的灰度变换(Matlab实现)
(1)线性变换:通过建立灰度映射来调整源图像的灰度. k>1增强图像的对比度:k=1调节图像亮度,通过改变d值达到调节亮度目的:0 i = imread('theatre.jpg');i = i ...
- 数字图像处理界标准图像 Lena 后面的故事
熟悉图像处理或者压缩的工程师.研究人员和学生,经常在他们的实验或者项目任务里使用"Lenna"或者"Lena"的图像.Lenna 图像已经成为被广泛使用的测试图 ...
- 【数字图像处理】六.MFC空间几何变换之图像平移、镜像、旋转、缩放具体解释
本文主要讲述基于VC++6.0 MFC图像处理的应用知识,主要结合自己大三所学课程<数字图像处理>及课件进行解说,主要通过MFC单文档视图实现显示BMP图片空间几何变换.包含图像平移.图形 ...
- 【python图像处理】图像的缩放、旋转与翻转
[python图像处理]图像的缩放.旋转与翻转 图像的几何变换,如缩放.旋转和翻转等,在图像处理中扮演着重要的角色,python中的Image类分别提供了这些操作的接口函数,下面进行逐一介绍. 1.图 ...
- 【数字图像处理】五.MFC图像点运算之灰度线性变化、灰度非线性变化、阈值化和均衡化处理具体解释
本文主要讲述基于VC++6.0 MFC图像处理的应用知识,主要结合自己大三所学课程<数字图像处理>及课件进行解说.主要通过MFC单文档视图实现显示BMP图片点运算处理.包含图像灰度线性变换 ...
- Win8 Metro(C#)数字图像处理--3.2图像方差计算
原文:Win8 Metro(C#)数字图像处理--3.2图像方差计算 /// <summary> /// /// </summary>Variance computing. / ...
随机推荐
- STlink下载和打断点Debug调试小结
一.下载 1.检查设备是否选择正确 2.检查SWDIO有没有识别到,如果没有,检查硬件连线是否正确. 3.检查Utilities选项 4.点击settings,添加FLASH. 二.Debug调试 前 ...
- (转)hibernate缓存机制详细分析
在本篇随笔里将会分析一下hibernate的缓存机制,包括一级缓存(session级别).二级缓存(sessionFactory级别)以及查询缓存,当然还要讨论下我们的N+1的问题. 随笔虽长,但我相 ...
- 记一次手工清除挖矿病毒WannaMine V4.0的经历
[作者:byeyear 邮箱:byeyear@hotmail.com 转载请注明] 前两天公司信息安全处通知我的计算机存在永恒之蓝漏洞并已被病毒感染,使用多方杀软及专杀工具均无法有效清除, ...
- java中把某个字符串中的单引号替换成双引号
String regexp = "\'";String str = "'good'";System.out.println("替换前:" + ...
- 分布式架构基石RPC的实现原理
RPC的由来 随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进. 单一应用架构 当网站流量很小时, ...
- Hadoop集群分布式安装
一 整体介绍 1.1 硬件环境 本文使用三台服务器搭建hadoop集群,使用Centos7.5系统,服务器均有独立ip 1.2 部署的软件 部署服务:namenode(HA),resourcemana ...
- C# 字段与属性的区别
在C#中,我们可以非常自由的.毫无限制的访问公有字段, 但在一些场合中,我们可能希望限制只能给字段赋于某个范围的值.或是要求字段只能读或只能写, 或是在改变字段时能改变对象的其他一些状态,这些单靠字段 ...
- 「UVA1328」「POJ1961」 Period 解题报告
UVA1328 Period 其他链接:luogu UVA1328 POJ1961 For each prefix of a given string S with N characters (eac ...
- 1070 结绳 (25 分)C语言
给定一段一段的绳子,你需要把它们串成一条绳.每次串连的时候,是把两段绳子对折,再如下图所示套接在一起.这样得到的绳子又被当成是另一段绳子,可以再次对折去跟另一段绳子串连.每次串连后,原来两段绳子的长度 ...
- 基于CentOS 7 部署MySQL 5.7的基本操作
关闭selinux # sed -i '/SELINUX/s/enforcing/disabled/' /etc/selinux/config重启后生效# sestatus 修改提示符配置# vi / ...