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. / ...
随机推荐
- dotnet 通过 WMI 获取系统启动的服务
本文告诉大家如何通过 WMI 获取系统启动的服务 通过 Win32_Service 可以获取系统启动的服务 获取的时候只需要拿Caption和State就可以 var mc = "Win32 ...
- C# dotnet 获取整个局域网的 ip 地址
局域网可以使用的 IP 地址有很多,我写了一段代码用来枚举所有可以用的 ip 地址 小伙伴都知道,局域网可以使用的 IP 范围如下 A类地址:10.0.0.0 - 10.255.255.255 B类地 ...
- python知识点总结02(不定时更新)
请用至少两种方式实现m与n值交换m=10,n=5 # 方式一 temp = 0 m = 10 n = 5 print(f'方式一交换前,m:{},n:{}') temp = m m = n n = t ...
- codeforces -1214 E
题目https://codeforces.com/problemset/problem/1214/E 就是得知奇数之间不产生影响,先造出一条链,再用偶数往里插就行.链要di从大到小排个序呀!! #in ...
- DOCKER学习_009:Docker的镜像管理
1 查看镜像 [root@docker-server3 ~]# docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE redis days ago ...
- appium整个环境安装详细教程(重要)
环境依赖 Node.js Appium Appium-desktop Appium-doctor Appium-Python-Client Python JDK Andriod SDK 以上所需的软件 ...
- .NET Core将促使.NET进一步衰落
我是十几年的.NET开发者,对.NET的历史发展比较了解,算是非常资深的了. 微软一向喜欢把开发者带到自己铺设的死路上,等开发者走到死路尽头的时候,会潇洒地再次铺设一条死路. .NET Core优势是 ...
- JDK12 的下载和没有jre的解决及环境配置
一.下载直接在官网上下载并点击安装即可,步骤省.jdk12下载中途已经没有跳出窗口下载jre的过程了,需要手动生成jre. 二.没有jre的解决:1.cmd 2.切换到jdk的安装目录,执行命令:bi ...
- vue 移动端在div上绑定click事件 失效
在.vue的文件中使用了better-scroll,在div标签上绑定click事件后,无效. 原因:使用了better-scroll,默认它会阻止touch事件.所以在配置中需要加上click: t ...
- 我是如何做到springboot自动配置原理解析
一前言 springboot 2.0.0版本分析,整体的自动配置流程如下: 具体配置参考官方文档:springboot-doc 二 @SpringBootApplication 核心注解@Spring ...