图像处理 旋转图像c#

2007-01-28 00:35:00 ki1381 阅读数 24618
  • 书本封面文字识别

    学完这门课程可以掌握C#数据库信息管理技巧和方法 掌握chart图表控件的使用 Excel数据的导出 两个日期间的数据查询,并生成图表

    3人学习 金圣韬
    免费试看

拍摄的数码相片偶尔也有拍歪的时候。没关系,我们还是可以用C#来处理图片。

     /// <summary>
        
/// 任意角度旋转
        
/// </summary>
        
/// <param name="bmp">原始图Bitmap</param>
        
/// <param name="angle">旋转角度</param>
        
/// <param name="bkColor">背景色</param>
        
/// <returns>输出Bitmap</returns>

        public static Bitmap KiRotate(Bitmap bmp, float angle, Color bkColor)
        
{
            
int w = bmp.Width + 2;
            
int h = bmp.Height + 2;

            PixelFormat pf;

            
if (bkColor == Color.Transparent)
            
{
                pf 
= PixelFormat.Format32bppArgb;
            }

            
else
            
{
                pf 
= bmp.PixelFormat;
            }


            Bitmap tmp 
= new Bitmap(w, h, pf);
            Graphics g 
= Graphics.FromImage(tmp);
            g.Clear(bkColor);
            g.DrawImageUnscaled(bmp, 
11);
            g.Dispose();

            GraphicsPath path 
= new GraphicsPath();
            path.AddRectangle(
new RectangleF(0f, 0f, w, h));
            Matrix mtrx 
= new Matrix();
            mtrx.Rotate(angle);
            RectangleF rct 
= path.GetBounds(mtrx);

            Bitmap dst 
= new Bitmap((int)rct.Width, (int)rct.Height, pf);
            g 
= Graphics.FromImage(dst);
            g.Clear(bkColor);
            g.TranslateTransform(
-rct.X, -rct.Y);
            g.RotateTransform(angle);
            g.InterpolationMode 
= InterpolationMode.HighQualityBilinear;
            g.DrawImageUnscaled(tmp, 
00);
            g.Dispose();

            tmp.Dispose();

            
return dst;
        }

最近论坛里好像有观点认为C#不适合做图片处理。我的观点是:如果不是实时性要求特别高,否则C#可以胜任。

我做了个C#版的个人处理数码相片工具,处理速度令人满意。除了高斯虚化稍慢,别的都很快。

当然前提是要么用BitmapData,要么用GDI+,总之绝对绝对不能用GetPixel/SetPixel。

2018-03-28 14:21:00 weixin_30872337 阅读数 26
  • 书本封面文字识别

    学完这门课程可以掌握C#数据库信息管理技巧和方法 掌握chart图表控件的使用 Excel数据的导出 两个日期间的数据查询,并生成图表

    3人学习 金圣韬
    免费试看

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

                                                                                       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 0:
                    dstBmp = new Bitmap(srcBmp);
                    break;
                case -90:
                    dstBmp = new Bitmap(srcBmp.Height, srcBmp.Width);
                    srcBmpData = srcBmp.LockBits(new Rectangle(0, 0, srcBmp.Width, srcBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                    dstBmpData = dstBmp.LockBits(new Rectangle(0, 0, dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                    unsafe
                    {
                        byte* ptrSrc = (byte*)srcBmpData.Scan0;
                        byte* ptrDst = (byte*)dstBmpData.Scan0;
                        for (int i = 0; i < srcBmp.Height; i++)
                        {
                            for (int j = 0; j < srcBmp.Width; j++)
                            {
                                ptrDst[j * dstBmpData.Stride + (dstBmp.Height - i - 1) * 3] = ptrSrc[i * srcBmpData.Stride + j * 3];
                                ptrDst[j * dstBmpData.Stride + (dstBmp.Height - i - 1) * 3 + 1] = ptrSrc[i * srcBmpData.Stride + j * 3 + 1];
                                ptrDst[j * dstBmpData.Stride + (dstBmp.Height - i - 1) * 3 + 2] = ptrSrc[i * srcBmpData.Stride + j * 3 + 2];
                            }
                        }
                    }
                    srcBmp.UnlockBits(srcBmpData);
                    dstBmp.UnlockBits(dstBmpData);
                    break;
                case 90:
                    dstBmp = new Bitmap(srcBmp.Height, srcBmp.Width);
                    srcBmpData = srcBmp.LockBits(new Rectangle(0, 0, srcBmp.Width, srcBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                    dstBmpData = dstBmp.LockBits(new Rectangle(0, 0, dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                    unsafe
                    {
                        byte* ptrSrc = (byte*)srcBmpData.Scan0;
                        byte* ptrDst = (byte*)dstBmpData.Scan0;
                        for (int i = 0; i < srcBmp.Height; i++)
                        {
                            for (int j = 0; j < srcBmp.Width; j++)
                            {
                                ptrDst[(srcBmp.Width - j - 1) * dstBmpData.Stride + i * 3] = ptrSrc[i * srcBmpData.Stride + j * 3];
                                ptrDst[(srcBmp.Width - j - 1) * dstBmpData.Stride + i * 3 + 1] = ptrSrc[i * srcBmpData.Stride + j * 3 + 1];
                                ptrDst[(srcBmp.Width - j - 1) * dstBmpData.Stride + i * 3 + 2] = ptrSrc[i * srcBmpData.Stride + j * 3 + 2];
                            }
                        }
                    }
                    srcBmp.UnlockBits(srcBmpData);
                    dstBmp.UnlockBits(dstBmpData);
                    break;
                case 180:
                case -180:
                    dstBmp = new Bitmap(srcBmp.Width, srcBmp.Height);
                    srcBmpData = srcBmp.LockBits(new Rectangle(0, 0, srcBmp.Width, srcBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                    dstBmpData = dstBmp.LockBits(new Rectangle(0, 0, dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                    unsafe
                    {
                        byte* ptrSrc = (byte*)srcBmpData.Scan0;
                        byte* ptrDst = (byte*)dstBmpData.Scan0;
                        for (int i = 0; i < srcBmp.Height; i++)
                        {
                            for (int j = 0; j < srcBmp.Width; j++)
                            {
                                ptrDst[(srcBmp.Width - i - 1) * dstBmpData.Stride + (dstBmp.Height - j - 1) * 3] = ptrSrc[i * srcBmpData.Stride + j * 3];
                                ptrDst[(srcBmp.Width - i - 1) * dstBmpData.Stride + (dstBmp.Height - j - 1) * 3 + 1] = ptrSrc[i * srcBmpData.Stride + j * 3 + 1];
                                ptrDst[(srcBmp.Width - i - 1) * dstBmpData.Stride + (dstBmp.Height - j - 1) * 3 + 2] = ptrSrc[i * srcBmpData.Stride + j * 3 + 2];
                            }
                        }
                    }
                    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 / 2 * (1 - cos) + srcBmp.Height / 2 * sin);
                    int dy = (int)(srcBmp.Width / 2 * (0 - sin) + srcBmp.Height / 2 * (1 - cos));

                    int insertBeginX = srcBmp.Width / 2 - widthDst / 2;
                    int insertBeginY = srcBmp.Height / 2 - heightDst / 2;

                    //插值公式所需参数
                    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(0, 0, srcBmp.Width, srcBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                    dstBmpData = dstBmp.LockBits(new Rectangle(0, 0, dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                    unsafe
                    {
                        byte* ptrSrc = (byte*)srcBmpData.Scan0;
                        byte* ptrDst = (byte*)dstBmpData.Scan0;
                        for (int i = 0; i < heightDst; i++)
                        {
                            for (int j = 0; j < widthDst; j++)
                            {
                                fu = j * cu1 - i * cu2 + ku;
                                fv = j * cv1 + i * cv2 + kv;
                                if ((fv < 1) || (fv > srcBmp.Height - 1) || (fu < 1) || (fu > srcBmp.Width - 1))
                                {

                                    ptrDst[i * dstBmpData.Stride + j * 3] = 150;
                                    ptrDst[i * dstBmpData.Stride + j * 3 + 1] = 150;
                                    ptrDst[i * dstBmpData.Stride + j * 3 + 2] = 150;
                                }
                                else
                                {//双线性插值
                                    Iu = (int)fu;
                                    Iv = (int)fv;
                                    a = fu - Iu;
                                    b = fv - Iv;
                                    for (int k = 0; k < 3; k++)
                                    {
                                        F1 = (1 - b) * *(ptrSrc + Iv * srcBmpData.Stride + Iu * 3 + k) + b * *(ptrSrc + (Iv + 1) * srcBmpData.Stride + Iu * 3 + k);
                                        F2 = (1 - b) * *(ptrSrc + Iv * srcBmpData.Stride + (Iu + 1) * 3 + k) + b * *(ptrSrc + (Iv + 1) * srcBmpData.Stride + (Iu + 1) * 3 + k);
                                        *(ptrDst + i * dstBmpData.Stride + j * 3 + k) = (byte)((1 - a) * F1 + a * F2);
                                    }
                                }
                            }
                        }
                    }
                    srcBmp.UnlockBits(srcBmpData);
                    dstBmp.UnlockBits(dstBmpData);
                    break;
            }
            return true;
        }

 

 

 

 

 

转载于:https://www.cnblogs.com/dearzhoubi/p/8663596.html

2017-01-05 22:53:54 lhtzbj12 阅读数 17666
  • 书本封面文字识别

    学完这门课程可以掌握C#数据库信息管理技巧和方法 掌握chart图表控件的使用 Excel数据的导出 两个日期间的数据查询,并生成图表

    3人学习 金圣韬
    免费试看

简介

图像旋转功能在实际使用中出现得不多,Image自带RotateFlip方法可以简单的实现90、180等角度的旋转或者翻转,但是如果要实现任意角度的旋转该怎么做?对于一个有经验的同学估计不到半天时间就可以完成,如果让新手遇到,估计就傻了,毕竟里面涉及了三角函数、空间坐标等方面的知识,比较蛋疼的是,Graphics(或者矩形)的旋转变换都是以左上角为原点,如果要以其中心进行旋转该怎么做?想知道的话就继续往后看吧。
本文将重点向大家介绍怎么使用GDI+(Graphics)获取图像按任意角度旋转后的图像。

动手前先解决两个问题

获取图片旋转后所占的矩形区域宽高

黄色矩形框表示图像旋转后的区域
如动态图所示,我们已知源图宽高和旋转角度,需要计算动态图中黄色矩形的宽高,用数学示意图表示如下:
这里写图片描述
数学公式如下:
这里写图片描述
是不是有点晕,不用着急,直接看代码

/// <summary>
/// 计算矩形绕中心任意角度旋转后所占区域矩形宽高
/// </summary>
/// <param name="width">原矩形的宽</param>
/// <param name="height">原矩形高</param>
/// <param name="angle">顺时针旋转角度</param>
/// <returns></returns>
public Rectangle GetRotateRectangle(int width, int height, float angle)
{
      double radian = angle * Math.PI / 180; ;
      double cos = Math.Cos(radian);
      double sin = Math.Sin(radian);
      //只需要考虑到第四象限和第三象限的情况取大值(中间用绝对值就可以包括第一和第二象限)
      int newWidth = (int)(Math.Max(Math.Abs(width * cos - height * sin), Math.Abs(width * cos + height * sin)));
      int newHeight = (int)(Math.Max(Math.Abs(width * sin - height * cos), Math.Abs(width * sin + height * cos)));
      return new Rectangle(0, 0, newWidth, newHeight);
}     

已知一个矩形,如何绘制其绕其中心点旋转N度后的矩形区域

由于Graphics进行旋转平移等转换时的原点都是左上角,我们的要求是绕矩形中心旋转,需要三步完成
(1)将Graphics的原点移至矩形的中点,假设坐标为(x,y)
(2)将Graphics绕当前原点旋转N度
(3)将Graphics沿(-x,-y)移回
每步形成效果如下图所示。红色为原矩形,黄色为第一步,蓝色为第二步,绿色为第三步。
这里写图片描述
上面几步实现的具体代码如下:

int angle = int.Parse(txtDestAngle.Text);//txtDestAngle为界面中一个TextBox控件
var btn = sender as Button;
Graphics graphics = null;
try
{
    //假设待处理的矩形 长宽为
    var w = 120;
    var h = 60;
    //创建graphics
    graphics = pictureBox1.CreateGraphics(); ;//pictureBox1为界面中一个PictureBox控件
    graphics.Clear(Color.Gray);
    //原始位置
    //画出矩形中心点
    graphics.DrawEllipse(new Pen(Color.Red), new Rectangle(w / 2 - 2, h / 2 - 2, 4, 4));
    //画出矩形当前位置
    graphics.DrawRectangle(new Pen(Color.Red), new Rectangle(0, 0, w, h));
    //***第一步***
    //将graphics坐标原点移到矩形中心点
    graphics.TranslateTransform(w / 2, h / 2);
    //画出矩形当前位置
    graphics.DrawRectangle(new Pen(Color.Yellow), new Rectangle(0, 0, w, h));
    //***第二步***
    //graphics旋转相应的角度(绕当前原点)
    graphics.RotateTransform(angle);
    //画出矩形当前位置
    graphics.DrawRectangle(new Pen(Color.Blue), new Rectangle(0, 0, w, h));
    //***每三步***
    //恢复graphics在水平和垂直方向的平移(沿当前原点)
    graphics.TranslateTransform(-w / 2, -h / 2);
    //画出矩形当前位置
    graphics.DrawRectangle(new Pen(Color.Green), new Rectangle(0, 0, w, h));
    //重至绘图的所有变换
    graphics.ResetTransform();
    graphics.Save();
    //***结束***
}
catch (Exception ex)
{
     MessageBox.Show(ex.Message);
}
finally
{
     if (graphics != null)
          graphics.Dispose();
}

获得图像旋转任意角度后的图像

解决上述两个问题后,再想获取图像旋转任意角度后的图像就会变得很简单了
(1)已知一个原始图像srcImage和要旋转的角度
(2)获取这个图像按角度旋转后的宽高(rotateRect)
(3)根据旋转后的宽高定义Bitmap(rotateImage),定义Graphics,将Graphics按rotateImage的矩形区域中心进行旋转变换
(4)将srcImage绘制到rotateImage中心(即两个中心点重合)
(5)重置Graphics,完成
完整代码如下

/// <summary>
/// 获取原图像绕中心任意角度旋转后的图像
/// </summary>
/// <param name="rawImg"></param>
/// <param name="angle"></param>
/// <returns></returns>
public Image GetRotateImage(Image srcImage, int angle)
{
     angle = angle % 360;
     //原图的宽和高
     int srcWidth = srcImage.Width;
     int srcHeight = srcImage.Height;
     //图像旋转之后所占区域宽和高
     Rectangle rotateRec = GetRotateRectangle(srcWidth, srcHeight, angle);
     int rotateWidth = rotateRec.Width;
     int rotateHeight = rotateRec.Height;
     //目标位图
     Bitmap destImage = null;
     Graphics graphics = null;
     try
     {
          //定义画布,宽高为图像旋转后的宽高
          destImage = new Bitmap(rotateWidth, rotateHeight);
          //graphics根据destImage创建,因此其原点此时在destImage左上角
          graphics = Graphics.FromImage(destImage);
          //要让graphics围绕某矩形中心点旋转N度,分三步
          //第一步,将graphics坐标原点移到矩形中心点,假设其中点坐标(x,y)
          //第二步,graphics旋转相应的角度(沿当前原点)
          //第三步,移回(-x,-y)
          //获取画布中心点
          Point centerPoint = new Point(rotateWidth / 2, rotateHeight / 2);
          //将graphics坐标原点移到中心点
          graphics.TranslateTransform(centerPoint.X, centerPoint.Y);
          //graphics旋转相应的角度(绕当前原点)
          graphics.RotateTransform(angle);
          //恢复graphics在水平和垂直方向的平移(沿当前原点)
          graphics.TranslateTransform(-centerPoint.X, -centerPoint.Y);
          //此时已经完成了graphics的旋转

          //计算:如果要将源图像画到画布上且中心与画布中心重合,需要的偏移量
          Point Offset = new Point((rotateWidth - srcWidth) / 2, (rotateHeight - srcHeight) / 2);                
          //将源图片画到rect里(rotateRec的中心)
          graphics.DrawImage(srcImage, new Rectangle(Offset.X, Offset.Y, srcWidth, srcHeight));
          //重至绘图的所有变换
          graphics.ResetTransform();
          graphics.Save();
     }
     catch (Exception ex)
     {
          throw ex;
     }
     finally
     {
          if (graphics != null)
               graphics.Dispose();
     }
     return destImage;
}        

注意:如果发现有的方法没有具体实现,请移步《C#中基于GDI+(Graphics)图像处理系列之前言》获取完整的图像处理工具类源码

完整示例程序源码下载

http://download.csdn.net/detail/lhtzbj12/9730116
如果想查阅本系列其他文章,请移步《C#中基于GDI+(Graphics)图像处理系列之前言》

2017-06-16 15:11:14 liang890319 阅读数 3450
  • 书本封面文字识别

    学完这门课程可以掌握C#数据库信息管理技巧和方法 掌握chart图表控件的使用 Excel数据的导出 两个日期间的数据查询,并生成图表

    3人学习 金圣韬
    免费试看

C#图像处理(各种旋转、改变大小、柔化、锐化、雾化、底片、浮雕、黑白、滤镜效果)


c#图像处理入门(-bitmap类和图像像素值获取方法)


C#全屏截图

用C#实现屏幕截屏功能




脚本之家C#图像操作大全


2018-11-18 11:29:52 weixin_35811044 阅读数 1471
  • 书本封面文字识别

    学完这门课程可以掌握C#数据库信息管理技巧和方法 掌握chart图表控件的使用 Excel数据的导出 两个日期间的数据查询,并生成图表

    3人学习 金圣韬
    免费试看
  • 图像旋转需要一个圆心,通常以图像的中心點为圆心,图像旋转经过三个步骤:

  1. 从图像坐标系转换为以圆心为原点的直角坐标系。

  2. 通过旋转算法,将坐标旋转。

  3. 再将旋转后图像从直角坐标系转换回图像坐标系。

如图:

     

 

  • 旋转算法:

  1. 坐标系变换:

    1. 由图象坐标系换成直角坐标系:。(x , y 为直角坐标)
    2. 由直角坐标系换成图象坐标系:。(x , y 为图象坐标)
  2. 坐标旋转:

    1. Pixel到原点的距离为r与原点的夹角为  ,则Pixel坐标可以表示为:
    2. 当图象顺时针旋转\theta度,则Pixel坐标为:    , 如图:。           

                  即:

 

  • 旋转算法最终公式:

    1. 顺时针旋转:图像坐标 --> 直角坐标 -->  图像旋转  -->  图像坐标 。
    2. 逆运算:用于反向映射。
  • 两种旋转方式:

    1. 正向失真映射:当图像旋转时,原图Pixel的坐标经过旋转公式得到的新Pixel坐标通常不会是整数,而图像的Pixel坐标都是以整数记录的。所以,我们会采用四舍六入五凑偶的方式。但是,如果采用近似,那么就会出现多个Pixel旋转后都对应到新图中的同一个整数坐标,而新图中有些整数坐标点却没有pixel对应,这样就会出现空洞(失真)。另外,对于多点对一点,在新图中采用按先后顺序覆盖的方式。
    2. 反向映射:为了避免出现空洞(失真),我们采用反向映射(其中正向映射也能采用相同方法避免空洞)。原理是对旋转后的图片,其整数坐标进行逆变换,可得到对应在原图中的坐标。通常得到的原图坐标也不会是整数,但是我们可以采用下面两种方法取得Pixel然后赋值给旋转后的图片。
    3. 如图:

       

      1. 最邻近插值:直接对原图中映射的非整数坐标进行四舍五入,得到的整数坐标,取Pixel值赋值给旋转图像中对应的点。
      2. 双线性插值:先说说线性插值。如下图:已知 (x0, y0) 、(x1, y1)、x,求 y。根据斜率相同:即可得:

                                                                                    

现在來看看什么是双线性插值法,说白了就是进行两次线性插值。如下图:(x,y) 为映射到原图的pixel点,是位于(0,0)、(1,0)、(0,1)、(1,1)中的点。f(0,0)、f(1,0)、f(0,1)、f(1,1)为各点的Pixel值,求 (x,y)的pixel值 f(x,y) 。根据线性插值,先进行x方向线性插值:

                          在(0,0)、(1,0)之间:f(x,0) = f(0,0)*(1-x)/1 + f(1,0)*(x-0) /1 ;

                          在(0,1)、(1,1)之间:f(x,1) = f(0,1)*(1-x)/1 + f(1,1)*(x-0) /1

                         得到  f(x,0)f(x,1) 后再进行 y 方向上的线性插值:f(x,y)  = f(x,0)*(1-y)/1 + f(x,1)*(y-0)/1

                         f(x,y)即按比例权重求得的Pixel值赋值回旋转图像中对应的点。

                                                            双线性插值

 

 

  • C#实现正向失真和反向双线性:        

C#存在Cos、Sin精度问题,即 Cos(Pi/2)、Sin(Pi) 等,不会等于0之类。目前没有想到很好的解决办法,有人知道的话请指教。 

 //正向失真旋转
public Bitmap rotateImageDis(Bitmap Image, double degree)
        {
            int Wo = Image.Width;
            int Lo = Image.Height;
            //采用向上取整,求旋转后图像大小。
            //double Wt = Math.Ceiling(Math.Abs(Wo * Math.Cos(Math.PI * (degree / 180))) + Math.Abs(Lo * Math.Sin(Math.PI * (degree / 180)))-0.00001);
            //double Lt = Math.Ceiling(Math.Abs(Wo * Math.Sin(Math.PI * (degree / 180))) + Math.Abs(Lo * Math.Cos(Math.PI * (degree / 180)))-0.00001);
            //采用四舍五入,求旋转后图像大小。
            double Wt = (int)(Math.Abs(Wo * Math.Cos(Math.PI * (degree / 180))) + Math.Abs(Lo * Math.Sin(Math.PI * (degree / 180))) + 0.5);
            double Lt = (int)(Math.Abs(Wo * Math.Sin(Math.PI * (degree / 180))) + Math.Abs(Lo * Math.Cos(Math.PI * (degree / 180))) + 0.5);
            Bitmap rotateImageData = new Bitmap((int)Wt, (int)Lt, PixelFormat.Format24bppRgb);

            //x、y即原图坐标,x1、y1即旋转后坐标。
            for (int y = 0; y < Lo; y++)
            {
                for (int x = 0; x < Wo; x++)
                {
                    int x1 = (int)Math.Round(x * Math.Cos(Math.PI * (degree / 180)) - y * Math.Sin(Math.PI * (degree / 180)) - (Wo - 1) / 2.0 * Math.Cos(Math.PI * (degree / 180)) + (Lo - 1) / 2.0 * Math.Sin(Math.PI * (degree / 180)) + (Wt - 1) / 2.0);
                    int y1 = (int)Math.Round(x * Math.Sin(Math.PI * (degree / 180)) + y * Math.Cos(Math.PI * (degree / 180)) - (Wo - 1) / 2.0 * Math.Sin(Math.PI * (degree / 180)) - (Lo - 1) / 2.0 * Math.Cos(Math.PI * (degree / 180)) + (Lt - 1) / 2.0);
                    rotateImageData.SetPixel(x1, y1, Image.GetPixel(x, y));
                }
            }
            return rotateImageData;
        }
        //反向双线性插值法
        public Bitmap rotareImageOri(Bitmap Image, double degree)
        {
            int Wo = Image.Width;
            int Lo = Image.Height;
            //采用向上取整,求旋转后图像大小。
            //double Wt = Math.Ceiling(Math.Abs(Wo * Math.Cos(Math.PI * (degree / 180))) + Math.Abs(Lo * Math.Sin(Math.PI * (degree / 180))) - 0.0001);
            //double Lt = Math.Ceiling(Math.Abs(Wo * Math.Sin(Math.PI * (degree / 180))) + Math.Abs(Lo * Math.Cos(Math.PI * (degree / 180))) - 0.0001);
            //采用四舍五入,求旋转后图像大小。
            double Wt = (int)(Math.Abs(Wo * Math.Cos(Math.PI * (degree / 180))) + Math.Abs(Lo * Math.Sin(Math.PI * (degree / 180))) + 0.5);
            double Lt = (int)(Math.Abs(Wo * Math.Sin(Math.PI * (degree / 180))) + Math.Abs(Lo * Math.Cos(Math.PI * (degree / 180))) + 0.5);
            Bitmap rotateImageData = new Bitmap((int)Wt, (int)Lt, PixelFormat.Format24bppRgb);

            //x1、y旋转后图像坐标,x、y原图坐标。
            for (int y1 = 0; y1 < Lt; y1++)
            {
                for (int x1 = 0; x1 < Wt; x1++)
                {
                    double x = x1 * Math.Cos(Math.PI * (degree / 180)) + y1 * Math.Sin(Math.PI * (degree / 180)) - (Wt - 1) / 2.0 * Math.Cos(Math.PI * (degree / 180)) - (Lt - 1) / 2.0 * Math.Sin(Math.PI * (degree / 180)) + (Wo - 1) / 2.0;
                    double y = -x1 * Math.Sin(Math.PI * (degree / 180)) + y1 * Math.Cos(Math.PI * (degree / 180)) + (Wt - 1) / 2.0 * Math.Sin(Math.PI * (degree / 180)) - (Lt - 1) / 2.0 * Math.Cos(Math.PI * (degree / 180)) + (Lo - 1) / 2.0;
                    if (-0.001 <= x & x <= (Wo - 1) & -0.001 <= y & y <= (Lo - 1))
                    {
                        Color RGB = new Color();

                        int a1 = (int)x;
                        int b1 = (int)y;
                        int a2 = (int)Math.Ceiling(x);
                        int b2 = (int)y;
                        int a3 = (int)x;
                        int b3 = (int)Math.Ceiling(y);
                        int a4 = (int)Math.Ceiling(x);
                        int b4 = (int)Math.Ceiling(y);

                        double xa13 = x - a1;
                        double xa24 = a2 - x;
                        double yb12 = y - b1;
                        double yb34 = b3 - y;

                        if (xa13 != 0 & xa24 != 0 & yb12 != 0 & yb34 != 0)
                        {//对应回原图是非整数坐标,双线性插值。
                            byte R1 = Image.GetPixel(a1, b1).R;
                            byte G1 = Image.GetPixel(a1, b1).G;
                            byte B1 = Image.GetPixel(a1, b1).B;
                            byte R2 = Image.GetPixel(a2, b2).R;
                            byte G2 = Image.GetPixel(a2, b2).G;
                            byte B2 = Image.GetPixel(a2, b2).B;
                            byte R3 = Image.GetPixel(a3, b3).R;
                            byte G3 = Image.GetPixel(a3, b3).G;
                            byte B3 = Image.GetPixel(a3, b3).B;
                            byte R4 = Image.GetPixel(a4, b4).R;
                            byte G4 = Image.GetPixel(a4, b4).G;
                            byte B4 = Image.GetPixel(a4, b4).B;

                            byte R = (byte)((R1 * xa24 + R2 * xa13) * yb34 + (R3 * xa24 + R4 * xa13) * yb12);
                            byte G = (byte)((G1 * xa24 + G2 * xa13) * yb34 + (G3 * xa24 + G4 * xa13) * yb12);
                            byte B = (byte)((B1 * xa24 + B2 * xa13) * yb34 + (B3 * xa24 + B4 * xa13) * yb12);

                            RGB = Color.FromArgb(R, G, B);
                        }
                        else
                        {//对应回原图是整数坐标,直接取Pixel。
                            RGB = Image.GetPixel(a1, b1);
                        }
                        rotateImageData.SetPixel(x1, y1, RGB);
                    }
                }
            }
            return rotateImageData;
        }

                                                                                                                                

 

仅为个人理解,如有不足,请指教。  https://blog.csdn.net/weixin_35811044

 

 

 

C# 简单图像处理类

阅读数 3810