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. / ...
随机推荐
- P3521 [POI2011]ROT-Tree Rotations (线段树合并)
P3521 [POI2011]ROT-Tree Rotations 题意: 给你一颗树,只有叶子节点有权值,你可以交换一个点的左右子树,问你最小的逆序对数 题解: 线段树维护权值个个数即可 然后左右子 ...
- quartz关闭DBUG日志
引用了Quartz组件后,打印日志时,整天都有相应的调试信息打印出来,严重影响了查找日志效率,谷歌一番后,修改nlog配置文件即可 <?xml version="1.0" e ...
- Pandas处理缺失数据
利用pandas.DataFrame.dropna处理含有缺失值的数据 1.使用形式: DataFrame.dropna(axis=0, how='any', thresh=None, subset= ...
- SSL/TLS 配置
Quick Start 下列说明将使用变量名 $CATALINA_BASE 来表示多数相对路径所基于的基本目录.如果没有为 Tomcat 多个实例设置 CATALINA_BASE 目录,则 $CATA ...
- Docker 说明
一.Docker 是什么? 1.为什么会有 Docker 出现 ? 一款产品从开发到上线,从操作系统,到运行环境,再到应用配置.作为开发+运维之间的协作我们需要关心很多东西,这也是很多互联网公司都不得 ...
- 0005 表格table
第01阶段.前端基础.表格 表格 table 目标: 理解: 能说出表格用来做什么的 表格的基本结构组成 应用: 能够熟练写出n行n列的表格 能简单的合并单元格 表格作用: 存在即是合理的. 表格 ...
- 计算机专业如何高质量的走完大学四年?毕业成为Offer收割机
前言:迷茫本就是青春该有的模样,但不要让未来的你讨厌现在的自己. "就要毕业了. 回头看自己所谓的大学生活, 我想哭,不是因为离别,而是因为什么都没学到. 我不知,简历该怎么写,若是以往我会 ...
- 微信支付与支付宝支付java开发注意事项
说明:这里只涉及到微信支付和淘宝支付 以官网的接口为准,主要关注[网关].[接口].[参数][加密方式][签名][回调] 第一步,了解自己的项目要集成的支付方式 常见的有扫码支付.网页支付.APP支付 ...
- 洛谷$P1155$ 双栈排序 贪心+二分图匹配
正解:贪心+二分图匹配 解题报告: 传送门$QwQ$ 跪了,,,我本来以为我$NOIp$做得差不多了,,,然后康了一眼发现没做多少啊其实$QAQ$ 然后来康题趴$QwQ$ 首先考虑如果只有一个栈的情况 ...
- 曹工说Spring Boot源码(7)-- Spring解析xml文件,到底从中得到了什么(上)
写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...