C#实现图片自由变换 任意扭曲的算法

之前想过要做个地铁驾驶的游戏,其中想把一些原本是矩形图片弄成一个梯形,但是发现GID+上面没有类似的方法。于是在谷歌谷了一下。没有!只能找到令人垂涎的,并没有源码。按照自己的想法尝试了一两天,有点效果,但实际上不是那样。后来知道那个在数字图像处理中叫“透视变换”。于是上网找了相关资料,原理找了,看了不明白。代码没多少,有ActionScript的,不明;有C的,不明。真笨啊!后来在CodeProject上面看到一份外国人的博文【http://www.codeproject.com/Articles/13201/Anti-Aliased-Image-Transformation-Aaform】,全英文看不太明白,但看了一幅图,大概知道他意思了。下了份源码看看,C++的。好不容易翻译成C#的(感觉还是保留了不少C++风格的东西),编译通过,运行正常。后来才一步一步的阅读代码。还没全懂,先把懂的部分记录下来。以后继续研究继续补充。

  先看看效果

 

  界面是仿照某个人(网上版本太多,找不到原作者)的弄出来的,界面不是重点,重点是算法。下面就直接贴老外的那幅图大致讲讲思想。

  首先是从原本图片转化成一幅理想化的目标图片,那幅图片只是理想化的,最终的图片是最右边的那幅。转换的过程就是根据转换后图片的四个角计算出目标图片的size,生成一个矩阵,就是那个Destination Image,然后把理想化的目标图片覆盖过去,把理想化图片的每个“像素格”(已经不是真正的像素格了,因为经过了扭曲变形)跟那个矩阵对比,看看覆盖了哪些格子,覆盖的面积有多少,按百分比地把颜色累加到对应的格子上。实际上那个格子就相当于新图片的像素点了。按照矩阵生成最终的目标图。

接着就介绍算法里面调用的方法层次

  把已经弄懂(并不代表完全懂的)的代码贴出来,首先是最外层的方法

复制代码
 1         public void CreateTransform(Bitmap src,ref Bitmap dst, List<double> xcorner, List<double> ycorner, Aaf_callback callbackfunc)
 2         {
 3             int right = 0, bottom = 0;
 4 
 5             //主要是根据新图片的坐标,计算出图片的宽和高,构造目标图片的Bitmap的
 6             double offx = xcorner[0];
 7             double offy = ycorner[0];
 8             for (int i = 1; i < 4; i++)
 9             {
10                 if (xcorner[i] < offx) offx = xcorner[i];
11                 if (ycorner[i] < offy) offy = ycorner[i];
12             }
13 
14             for (int i = 0; i < 4; i++)
15             {
16                 xcorner[i] -= offx;
17                 ycorner[i] -= offy;
18                 if (roundup(xcorner[i]) > right) right = roundup(xcorner[i]);
19                 if (roundup(ycorner[i]) > bottom) bottom = roundup(ycorner[i]);
20             }
21             dst = new Bitmap(right, bottom);
22             Transform(src, dst, xcorner, ycorner, null);
23         }
复制代码

  上面这个方法只是定了目标图片的尺寸,其余什么都没做。下面这个方法还没做多少转换的事

复制代码
 1         private void Transform(Bitmap src,Bitmap dst, List<double> xcorner, List<double> ycorner, Aaf_callback callbackfunc)
 2         {
 3             //Make sure the coordinates are valid
 4             if (xcorner.Count != 4 || ycorner.Count != 4)
 5                 return ;
 6 
 7             //Load the src bitmaps information
 8 
 9             //create the intial arrays
10             //根据原图片生成一个比原图宽高多一个单位的图片,
11             //这个矩阵就是用来记录转换后各个像素点左上角的坐标
12             pixelgrid = new AafPnt[(src.Width + 1) * (src.Height + 1)];
13             polyoverlap = new AafPnt[16];
14             polysorted = new AafPnt[16];
15             corners = new AafPnt[4];
16 
17             //Load the corners array
18             double[] dx = { 0.0, 1.0, 1.0, 0.0 };
19             double[] dy = { 0.0, 0.0, 1.0, 1.0 };
20             for (int i = 0; i < 4; i++)
21             {
22                 corners[i].x = dx[i];
23                 corners[i].y = dy[i];
24             }
25 
26             //Find the rectangle of dst to draw to
27             outstartx = rounddown(xcorner[0]);
28             outstarty = rounddown(ycorner[0]);
29             outwidth = 0;
30             outheight = 0;
31             //这里计算出变换后起始点的坐标
32             for (int i = 1; i < 4; i++)
33             {
34                 if (rounddown(xcorner[i]) < outstartx) outstartx = rounddown(xcorner[i]);
35                 if (rounddown(ycorner[i]) < outstarty) outstarty = rounddown(ycorner[i]);
36             }
37             for (int i = 0; i < 4; i++)
38             {
39                 if (roundup(xcorner[i] - outstartx) > outwidth) outwidth = roundup(xcorner[i] - outstartx);
40                 if (roundup(ycorner[i] - outstarty) > outheight) outheight = roundup(ycorner[i] - outstarty);
41             }
42 
43 
44             //fill out pixelgrid array
45             //计算出理想目标图片中各个“像素格”中的左上角的坐标
46             if (CreateGrid(src, xcorner, ycorner))
47             {
48                 //Do the transformation
49                 //进行转换
50                DoTransform(src,dst, callbackfunc);
51             }
52 
53             //Return if the function completed properly
54             return ;
55         }
复制代码

  下面这个方法则是计算出原图像中每个像素点的左上角的点到目标图像中的坐标,结果是存放在pixelgrid中,这个二维数组的行和列都比原图像的宽高多1,这个关系我当初没搞懂。用比较极限的思想,假设现在这幅图片只有一个像素组成,宽和高都是1,然后如果单纯存储一个左上角的坐标,是无法组成一个四边形的,这就需要把其他三个角的坐标相应地记录,这是存储的数组的行和列均要比原图的宽和高多1,就是也就是一个2行2列的数组能存放4个数值,刚好就容纳了那一个像素点的四个角的坐标值。扩大到真实的图片也同样道理。不过这个方法我看不明白,貌似用到了向量的思想。大致是按照新图片四条边来计算的。

复制代码
 1         private bool CreateGrid(Bitmap src, List<double> xcorner, List<double> ycorner)
 2         {
 3             //mmm geometry
 4             double[] sideradius = new double[4];
 5             double[] sidecos = new double[4];
 6             double[] sidesin = new double[4];
 7 
 8             //First we find the radius, cos, and sin of each side of the polygon created by xcorner and ycorner
 9             int j;
10             for (int i = 0; i < 4; i++)
11             {
12                 j = ja[i];
13                 sideradius[i] = Math.Sqrt((xcorner[i] - xcorner[j]) * (xcorner[i] - xcorner[j]) + (ycorner[i] - ycorner[j]) * (ycorner[i] - ycorner[j]));
14                 sidecos[i] = (xcorner[j] - xcorner[i]) / sideradius[i];
15                 sidesin[i] = (ycorner[j] - ycorner[i]) / sideradius[i];
16             }
17 
18             //Next we create two lines in Ax + By = C form
19             for (int x = 0; x < src.Width + 1; x++)
20             {
21                 double topdist = ((double)x / (src.Width)) * sideradius[0];
22                 double ptxtop = xcorner[0] + topdist * sidecos[0];
23                 double ptytop = ycorner[0] + topdist * sidesin[0];
24 
25                 double botdist = (1.0 - (double)x / (src.Width)) * sideradius[2];
26                 double ptxbot = xcorner[2] + botdist * sidecos[2];
27                 double ptybot = ycorner[2] + botdist * sidesin[2];
28 
29                 double Ah = ptybot - ptytop;
30                 double Bh = ptxtop - ptxbot;
31                 double Ch = Ah * ptxtop + Bh * ptytop;//叉乘
32 
33                 for (int y = 0; y < src.Height + 1; y++)
34                 {
35                     double leftdist = (1.0 - (double)y / (src.Height)) * sideradius[3];
36                     double ptxleft = xcorner[3] + leftdist * sidecos[3];
37                     double ptyleft = ycorner[3] + leftdist * sidesin[3];
38 
39                     double rightdist = ((double)y / (src.Height)) * sideradius[1];
40                     double ptxright = xcorner[1] + rightdist * sidecos[1];
41                     double ptyright = ycorner[1] + rightdist * sidesin[1];
42 
43                     double Av = ptyright - ptyleft;
44                     double Bv = ptxleft - ptxright;
45                     double Cv = Av * ptxleft + Bv * ptyleft;
46 
47                     //Find where the lines intersect and store that point in the pixelgrid array
48                     double det = Ah * Bv - Av * Bh;
49                     if (AafAbs(det) < 1e-9)
50                     {
51                         return false;
52                     }
53                     else
54                     {
55                         int ind = x + y * (src.Width + 1);
56                         pixelgrid[ind].x = (Bv * Ch - Bh * Cv) / det;
57                         pixelgrid[ind].y = (Ah * Cv - Av * Ch) / det;
58                     }
59                 }
60             }
61 
62             //Yayy we didn't fail
63             return true;
64         }
复制代码

  下面这个方法就利用上面的方法计算出的坐标点集合进行按比例填色。上面每个像素点的四个角的坐标,都会在下面方法重新提取出来组回一个四边形,具体还是结合代码和注释看看

复制代码
  1         private void DoTransform(Bitmap src,Bitmap dst, Aaf_callback callbackfunc)
  2         {
  3             
  4             //Get source bitmap's information
  5             if (src == null) return ;
  6 
  7             //Create the source dib array and the dstdib array
  8             aaf_dblrgbquad[] dbldstdib = new aaf_dblrgbquad[outwidth * outheight];
  9             for (int i = 0; i < dbldstdib.Length; i++)
 10                 dbldstdib[i] = new aaf_dblrgbquad();
 11 
 12             //Create polygon arrays
 13             AafPnt[] p = new AafPnt[4];
 14             AafPnt[] poffset = new AafPnt[4];
 15 
 16             //Loop through the source's pixels
 17             //遍历原图(实质上是pixelgrid)各个点
 18             for (int x = 0; x < src.Width; x++)
 19             {
 20                 for (int y = 0; y < src.Height; y++)
 21                 {
 22                     //取当前点 下一点 右一点 斜右下角点 四点才组成一个四边形
 23                     //这个四边形是原图像的一个像素点
 24                     //Construct the source pixel's rotated polygon from pixelgrid
 25                     p[0] = pixelgrid[x + y * (src.Width + 1)];
 26                     p[1] = pixelgrid[(x + 1) + y * (src.Width + 1)];
 27                     p[2] = pixelgrid[(x + 1) + (y + 1) * (src.Width + 1)];
 28                     p[3] = pixelgrid[x + (y + 1) * (src.Width + 1)];
 29 
 30                     //Find the scan area on the destination's pixels
 31                     int mindx = int.MaxValue;
 32                     int mindy = int.MaxValue;
 33                     int maxdx = int.MinValue;
 34                     int maxdy = int.MinValue;
 35                     for (int i = 0; i < 4; i++)
 36                     {
 37                         if (rounddown(p[i].x) < mindx) mindx = rounddown(p[i].x);
 38                         if (roundup(p[i].x) > maxdx) maxdx = roundup(p[i].x);
 39                         if (rounddown(p[i].y) < mindy) mindy = rounddown(p[i].y);
 40                         if (roundup(p[i].y) > maxdy) maxdy = roundup(p[i].y);
 41                     }
 42 
 43                     int SrcIndex = x + y * src.Width;
 44                     //遍历四边形包含了目标图几个像素点
 45                     //按照相交面积占整个像素的的百分比,把颜色按照该比例存放一个目标像素点颜色的数组中
 46                     //这里计算出来的颜色只是初步颜色,还没到最终结果
 47                     //loop through the scan area to find where source(x, y) overlaps with the destination pixels
 48                     for (int xx = mindx - 1; xx <= maxdx; xx++)
 49                     {
 50                         if (xx < 0 || xx >= dst.Width)
 51                             continue;
 52                         for (int yy = mindy - 1; yy <= maxdy; yy++)
 53                         {
 54                             if (yy < 0 || yy >= dst.Height)
 55                                 continue;
 56 
 57                             //offset p and by (xx,yy) and put that into poffset
 58                             for (int i = 0; i < 4; i++)
 59                             {
 60                                 poffset[i].x = p[i].x - xx;
 61                                 poffset[i].y = p[i].y - yy;
 62                             }
 63 
 64                             //FIND THE OVERLAP *a whole lot of code pays off here*
 65                             //这里则是计算出覆盖了面积占当前像素的百分比
 66                             double dbloverlap = PixOverlap(poffset);
 67                             //按照百分比来为目标像素点累加颜色
 68                             //因为一个目标像素点有可能有几个原来像素的覆盖了
 69                             if (dbloverlap > 0)
 70                             {
 71                                 int dstindex = xx + yy * outwidth;
 72                                 int srcWidth = src.Width;
 73                                 Color srcColor;
 74                                 if (SrcIndex == 0)
 75                                     srcColor = src.GetPixel(0, 0);
 76                                 else
 77                                  srcColor = src.GetPixel(SrcIndex%src.Width  ,  SrcIndex/src.Width );
 78                                 //Add the rgb and alpha values in proportion to the overlap area
 79                                 dbldstdib[dstindex].Red += (double)((srcColor.R) * dbloverlap);
 80                                 dbldstdib[dstindex].Blue += (double)(srcColor.B) * dbloverlap;
 81                                 dbldstdib[dstindex].Green += (double)(srcColor.G) * dbloverlap;
 82                                 dbldstdib[dstindex].Alpha += dbloverlap;
 83                             }
 84                         }
 85                     }
 86                 }
 87                 if (callbackfunc != null)
 88                 {
 89                     //Send the callback message
 90                     double percentdone = (double)(x + 1) / (double)(src.Width);
 91                     if (callbackfunc(percentdone))
 92                     {
 93                         dbldstdib = null;
 94                         p = null;
 95                         poffset = null;
 96                         return ;
 97                     }
 98                 }
 99             }
100 
101             //Free memory no longer needed
102 
103 
104             //Create final destination bits
105             RGBQUDA[] dstdib = new RGBQUDA[dst.Width * dst.Height];
106             for (int i = 0; i < dstdib.Length; i++)
107                 dstdib[i] = new RGBQUDA(){R= 0,G= 0,B= 0};
108 
109             //这里是实际上真正像素点的颜色,并且填到了目标图片中去
110             //Write to dstdib with the information stored in dbldstdib
111             for (int x = 0; x < outwidth; x++)
112             {
113                 if (x + outstartx >= dst.Width)
114                     continue;
115                 for (int y = 0; y < outheight; y++)
116                 {
117                     if (y + outstarty >= dst.Height)
118                         continue;
119                     int offindex = x + y * outwidth;
120                     int dstindex = x + outstartx + (y + outstarty) * dst.Width;
121 
122                     int dstIndexX = dstindex / dst.Width;
123                     int dstIndexY = dstindex % dst.Width;
124                     if (dbldstdib[offindex].Alpha > 1)
125                     {
126                         //handles wrap around for non-convex transformations
127                         dstdib[dstindex].R = byterange(dbldstdib[offindex].Red / dbldstdib[offindex].Alpha);
128                         dstdib[dstindex].G = byterange(dbldstdib[offindex].Green / dbldstdib[offindex].Alpha);
129                         dstdib[dstindex].B = byterange(dbldstdib[offindex].Blue / dbldstdib[offindex].Alpha);
130                     }
131                     else
132                     {
133                         //Color dstColor = dst.GetPixel(dstIndexX, dstIndexY);
134                         dstdib[dstindex].R = byterange(dbldstdib[offindex].Red + (1 - dbldstdib[offindex].Alpha) * (double)dstdib[dstindex].R);
135                         dstdib[dstindex].G = byterange(dbldstdib[offindex].Green + (1 - dbldstdib[offindex].Alpha) * (double)dstdib[dstindex].G);
136                         dstdib[dstindex].B = byterange(dbldstdib[offindex].Blue + (1 - dbldstdib[offindex].Alpha) * (double)dstdib[dstindex].B);
137                     }
138                     dst.SetPixel(dstIndexY,dstIndexX , Color.FromArgb(dstdib[dstindex].R, dstdib[dstindex].G, dstdib[dstindex].B));
139                 }
140             }
141 
142             //:D
143             return ;
144         }
复制代码

  里面调用到的计算相交面积的方法PixOverlap就不列出来了,因为还没看懂,看明白了也会在本文中补充。若想看的,本文最后会贴出所有源码。

  希望有看的明白的朋友能多指点一下,谢谢!还要感谢一个人,sa姐,在我阅读这个算法时给了不少灵感为我。搞这个算法,让我想起了大三上的一门课《医学图像处理》,我的老师涂泳秋老师。

复制代码
  1     public delegate bool Aaf_callback(double paraDouble);
  2 
  3     struct AafPnt
  4     {
  5         public double x, y;
  6         public AafPnt(double x, double y)
  7         {
  8             this.x = x < 0 ? 0 : x;
  9             this.y = y < 0 ? 0 : y;
 10         }
 11     }
 12 
 13     class aaf_dblrgbquad
 14     {
 15         public double Red{get;set;}
 16         public double Green{get;set;}
 17         public double  Blue{get;set;}
 18         public double Alpha { get; set; }
 19     }
 20 
 21     class aaf_indll
 22     {
 23         public aaf_indll next;
 24         public int ind;
 25     }
 26 
 27     class Aaform
 28     {
 29         private AafPnt[] pixelgrid;
 30         private AafPnt[] polyoverlap;
 31         private AafPnt[] polysorted;
 32         private AafPnt[] corners;
 33 
 34         private int outstartx;
 35         private int outstarty;
 36         private int outwidth;
 37         private int outheight;
 38 
 39         int polyoverlapsize;
 40         int polysortedsize;
 41 
 42         int[] ja = new int[] { 1, 2, 3, 0 };
 43 
 44         public void CreateTransform(Bitmap src,ref Bitmap dst, List<double> xcorner, List<double> ycorner, Aaf_callback callbackfunc)
 45         {
 46             int right = 0, bottom = 0;
 47 
 48             //主要是根据新图片的坐标,计算出图片的宽和高,构造目标图片的Bitmap的
 49             double offx = xcorner[0];
 50             double offy = ycorner[0];
 51             for (int i = 1; i < 4; i++)
 52             {
 53                 if (xcorner[i] < offx) offx = xcorner[i];
 54                 if (ycorner[i] < offy) offy = ycorner[i];
 55             }
 56 
 57             for (int i = 0; i < 4; i++)
 58             {
 59                 xcorner[i] -= offx;
 60                 ycorner[i] -= offy;
 61                 if (roundup(xcorner[i]) > right) right = roundup(xcorner[i]);
 62                 if (roundup(ycorner[i]) > bottom) bottom = roundup(ycorner[i]);
 63             }
 64             dst = new Bitmap(right, bottom);
 65             Transform(src, dst, xcorner, ycorner, null);
 66         }
 67 
 68         private void Transform(Bitmap src,Bitmap dst, List<double> xcorner, List<double> ycorner, Aaf_callback callbackfunc)
 69         {
 70             //Make sure the coordinates are valid
 71             if (xcorner.Count != 4 || ycorner.Count != 4)
 72                 return ;
 73 
 74             //Load the src bitmaps information
 75 
 76             //create the intial arrays
 77             //根据原图片生成一个比原图宽高多一个单位的图片,
 78             //这个矩阵就是用来记录转换后各个像素点左上角的坐标
 79             pixelgrid = new AafPnt[(src.Width + 1) * (src.Height + 1)];
 80             polyoverlap = new AafPnt[16];
 81             polysorted = new AafPnt[16];
 82             corners = new AafPnt[4];
 83 
 84             //Load the corners array
 85             double[] dx = { 0.0, 1.0, 1.0, 0.0 };
 86             double[] dy = { 0.0, 0.0, 1.0, 1.0 };
 87             for (int i = 0; i < 4; i++)
 88             {
 89                 corners[i].x = dx[i];
 90                 corners[i].y = dy[i];
 91             }
 92 
 93             //Find the rectangle of dst to draw to
 94             outstartx = rounddown(xcorner[0]);
 95             outstarty = rounddown(ycorner[0]);
 96             outwidth = 0;
 97             outheight = 0;
 98             //这里计算出变换后起始点的坐标
 99             for (int i = 1; i < 4; i++)
100             {
101                 if (rounddown(xcorner[i]) < outstartx) outstartx = rounddown(xcorner[i]);
102                 if (rounddown(ycorner[i]) < outstarty) outstarty = rounddown(ycorner[i]);
103             }
104             for (int i = 0; i < 4; i++)
105             {
106                 if (roundup(xcorner[i] - outstartx) > outwidth) outwidth = roundup(xcorner[i] - outstartx);
107                 if (roundup(ycorner[i] - outstarty) > outheight) outheight = roundup(ycorner[i] - outstarty);
108             }
109 
110 
111             //fill out pixelgrid array
112             //计算出理想目标图片中各个“像素格”中的左上角的坐标
113             if (CreateGrid(src, xcorner, ycorner))
114             {
115                 //Do the transformation
116                 //进行转换
117                DoTransform(src,dst, callbackfunc);
118             }
119 
120             //Return if the function completed properly
121             return ;
122         }
123 
124         private bool CreateGrid(Bitmap src, List<double> xcorner, List<double> ycorner)
125         {
126             //mmm geometry
127             double[] sideradius = new double[4];
128             double[] sidecos = new double[4];
129             double[] sidesin = new double[4];
130 
131             //First we find the radius, cos, and sin of each side of the polygon created by xcorner and ycorner
132             int j;
133             for (int i = 0; i < 4; i++)
134             {
135                 j = ja[i];
136                 sideradius[i] = Math.Sqrt((xcorner[i] - xcorner[j]) * (xcorner[i] - xcorner[j]) + (ycorner[i] - ycorner[j]) * (ycorner[i] - ycorner[j]));
137                 sidecos[i] = (xcorner[j] - xcorner[i]) / sideradius[i];
138                 sidesin[i] = (ycorner[j] - ycorner[i]) / sideradius[i];
139             }
140 
141             //Next we create two lines in Ax + By = C form
142             for (int x = 0; x < src.Width + 1; x++)
143             {
144                 double topdist = ((double)x / (src.Width)) * sideradius[0];//每个像素点变换后的坐标点
145                 double ptxtop = xcorner[0] + topdist * sidecos[0];
146                 double ptytop = ycorner[0] + topdist * sidesin[0];
147 
148                 double botdist = (1.0 - (double)x / (src.Width)) * sideradius[2];
149                 double ptxbot = xcorner[2] + botdist * sidecos[2];
150                 double ptybot = ycorner[2] + botdist * sidesin[2];
151 
152                 double Ah = ptybot - ptytop;
153                 double Bh = ptxtop - ptxbot;
154                 double Ch = Ah * ptxtop + Bh * ptytop;//叉乘
155 
156                 for (int y = 0; y < src.Height + 1; y++)
157                 {
158                     double leftdist = (1.0 - (double)y / (src.Height)) * sideradius[3];
159                     double ptxleft = xcorner[3] + leftdist * sidecos[3];
160                     double ptyleft = ycorner[3] + leftdist * sidesin[3];
161 
162                     double rightdist = ((double)y / (src.Height)) * sideradius[1];
163                     double ptxright = xcorner[1] + rightdist * sidecos[1];
164                     double ptyright = ycorner[1] + rightdist * sidesin[1];
165 
166                     double Av = ptyright - ptyleft;
167                     double Bv = ptxleft - ptxright;
168                     double Cv = Av * ptxleft + Bv * ptyleft;
169 
170                     //Find where the lines intersect and store that point in the pixelgrid array
171                     double det = Ah * Bv - Av * Bh;
172                     if (AafAbs(det) < 1e-9)
173                     {
174                         return false;
175                     }
176                     else
177                     {
178                         int ind = x + y * (src.Width + 1);
179                         pixelgrid[ind].x = (Bv * Ch - Bh * Cv) / det;
180                         pixelgrid[ind].y = (Ah * Cv - Av * Ch) / det;
181                     }
182                 }
183             }
184 
185             //Yayy we didn't fail
186             return true;
187         }
188 
189         private void DoTransform(Bitmap src,Bitmap dst, Aaf_callback callbackfunc)
190         {
191             
192             //Get source bitmap's information
193             if (src == null) return ;
194 
195             //Create the source dib array and the dstdib array
196             aaf_dblrgbquad[] dbldstdib = new aaf_dblrgbquad[outwidth * outheight];
197             for (int i = 0; i < dbldstdib.Length; i++)
198                 dbldstdib[i] = new aaf_dblrgbquad();
199 
200             //Create polygon arrays
201             AafPnt[] p = new AafPnt[4];
202             AafPnt[] poffset = new AafPnt[4];
203 
204             //Loop through the source's pixels
205             //遍历原图(实质上是pixelgrid)各个点
206             for (int x = 0; x < src.Width; x++)
207             {
208                 for (int y = 0; y < src.Height; y++)
209                 {
210                     //取当前点 下一点 右一点 斜右下角点 四点才组成一个四边形
211                     //这个四边形是原图像的一个像素点
212                     //Construct the source pixel's rotated polygon from pixelgrid
213                     p[0] = pixelgrid[x + y * (src.Width + 1)];
214                     p[1] = pixelgrid[(x + 1) + y * (src.Width + 1)];
215                     p[2] = pixelgrid[(x + 1) + (y + 1) * (src.Width + 1)];
216                     p[3] = pixelgrid[x + (y + 1) * (src.Width + 1)];
217 
218                     //Find the scan area on the destination's pixels
219                     int mindx = int.MaxValue;
220                     int mindy = int.MaxValue;
221                     int maxdx = int.MinValue;
222                     int maxdy = int.MinValue;
223                     for (int i = 0; i < 4; i++)
224                     {
225                         if (rounddown(p[i].x) < mindx) mindx = rounddown(p[i].x);
226                         if (roundup(p[i].x) > maxdx) maxdx = roundup(p[i].x);
227                         if (rounddown(p[i].y) < mindy) mindy = rounddown(p[i].y);
228                         if (roundup(p[i].y) > maxdy) maxdy = roundup(p[i].y);
229                     }
230 
231                     int SrcIndex = x + y * src.Width;
232                     //遍历四边形包含了目标图几个像素点
233                     //按照相交面积占整个像素的的百分比,把颜色按照该比例存放一个目标像素点颜色的数组中
234                     //这里计算出来的颜色只是初步颜色,还没到最终结果
235                     //loop through the scan area to find where source(x, y) overlaps with the destination pixels
236                     for (int xx = mindx - 1; xx <= maxdx; xx++)
237                     {
238                         if (xx < 0 || xx >= dst.Width)
239                             continue;
240                         for (int yy = mindy - 1; yy <= maxdy; yy++)
241                         {
242                             if (yy < 0 || yy >= dst.Height)
243                                 continue;
244 
245                             //offset p and by (xx,yy) and put that into poffset
246                             for (int i = 0; i < 4; i++)
247                             {
248                                 poffset[i].x = p[i].x - xx;
249                                 poffset[i].y = p[i].y - yy;
250                             }
251 
252                             //FIND THE OVERLAP *a whole lot of code pays off here*
253                             //这里则是计算出覆盖了面积占当前像素的百分比
254                             double dbloverlap = PixOverlap(poffset);
255                             //按照百分比来为目标像素点累加颜色
256                             //因为一个目标像素点有可能有几个原来像素的覆盖了
257                             if (dbloverlap > 0)
258                             {
259                                 int dstindex = xx + yy * outwidth;
260                                 int srcWidth = src.Width;
261                                 Color srcColor;
262                                 if (SrcIndex == 0)
263                                     srcColor = src.GetPixel(0, 0);
264                                 else
265                                  srcColor = src.GetPixel(SrcIndex%src.Width  ,  SrcIndex/src.Width );
266                                 //Add the rgb and alpha values in proportion to the overlap area
267                                 dbldstdib[dstindex].Red += (double)((srcColor.R) * dbloverlap);
268                                 dbldstdib[dstindex].Blue += (double)(srcColor.B) * dbloverlap;
269                                 dbldstdib[dstindex].Green += (double)(srcColor.G) * dbloverlap;
270                                 dbldstdib[dstindex].Alpha += dbloverlap;
271                             }
272                         }
273                     }
274                 }
275                 if (callbackfunc != null)
276                 {
277                     //Send the callback message
278                     double percentdone = (double)(x + 1) / (double)(src.Width);
279                     if (callbackfunc(percentdone))
280                     {
281                         dbldstdib = null;
282                         p = null;
283                         poffset = null;
284                         return ;
285                     }
286                 }
287             }
288 
289             //Free memory no longer needed
290 
291 
292             //Create final destination bits
293             RGBQUDA[] dstdib = new RGBQUDA[dst.Width * dst.Height];
294             for (int i = 0; i < dstdib.Length; i++)
295                 dstdib[i] = new RGBQUDA(){R= 0,G= 0,B= 0};
296 
297             //这里是实际上真正像素点的颜色,并且填到了目标图片中去
298             //Write to dstdib with the information stored in dbldstdib
299             for (int x = 0; x < outwidth; x++)
300             {
301                 if (x + outstartx >= dst.Width)
302                     continue;
303                 for (int y = 0; y < outheight; y++)
304                 {
305                     if (y + outstarty >= dst.Height)
306                         continue;
307                     int offindex = x + y * outwidth;
308                     int dstindex = x + outstartx + (y + outstarty) * dst.Width;
309 
310                     int dstIndexX = dstindex / dst.Width;
311                     int dstIndexY = dstindex % dst.Width;
312                     if (dbldstdib[offindex].Alpha > 1)
313                     {
314                         //handles wrap around for non-convex transformations
315                         dstdib[dstindex].R = byterange(dbldstdib[offindex].Red / dbldstdib[offindex].Alpha);
316                         dstdib[dstindex].G = byterange(dbldstdib[offindex].Green / dbldstdib[offindex].Alpha);
317                         dstdib[dstindex].B = byterange(dbldstdib[offindex].Blue / dbldstdib[offindex].Alpha);
318                     }
319                     else
320                     {
321                         //Color dstColor = dst.GetPixel(dstIndexX, dstIndexY);
322                         dstdib[dstindex].R = byterange(dbldstdib[offindex].Red + (1 - dbldstdib[offindex].Alpha) * (double)dstdib[dstindex].R);
323                         dstdib[dstindex].G = byterange(dbldstdib[offindex].Green + (1 - dbldstdib[offindex].Alpha) * (double)dstdib[dstindex].G);
324                         dstdib[dstindex].B = byterange(dbldstdib[offindex].Blue + (1 - dbldstdib[offindex].Alpha) * (double)dstdib[dstindex].B);
325                     }
326                     dst.SetPixel(dstIndexY,dstIndexX , Color.FromArgb(dstdib[dstindex].R, dstdib[dstindex].G, dstdib[dstindex].B));
327                 }
328             }
329 
330             //:D
331             return ;
332         }
333 
334         double PixOverlap(AafPnt[] p)
335         {
336             polyoverlapsize = 0;
337             polysortedsize = 0;
338 
339             double minx, maxx, miny, maxy;
340             int j;
341 
342             double z;
343 
344             for (int i = 0; i < 4; i++)
345             {
346                 //Search for source points within the destination quadrolateral
347                 if (p[i].x >= 0 && p[i].x <= 1 && p[i].y >= 0 && p[i].y <= 1)
348                     polyoverlap[polyoverlapsize++] = p[i];
349 
350                 //Search for destination points within the source quadrolateral
351                 if (PtinConvexPolygon(p, corners[i]))
352                     polyoverlap[polyoverlapsize++] = corners[i];
353 
354                 //Search for line intersections
355                 j = ja[i];
356                 minx = aaf_min(p[i].x, p[j].x);
357                 miny = aaf_min(p[i].y, p[j].y);
358                 maxx = aaf_max(p[i].x, p[j].x);
359                 maxy = aaf_max(p[i].y, p[j].y);
360 
361                 if (minx < 0.0 && 0.0 < maxx)
362                 {//Cross left
363                     z = p[i].y - p[i].x * (p[i].y - p[j].y) / (p[i].x - p[j].x);
364                     if (z >= 0.0 && z <= 1.0)
365                     {
366                         polyoverlap[polyoverlapsize].x = 0.0;
367                         polyoverlap[polyoverlapsize++].y = z;
368                     }
369                 }
370                 if (minx < 1.0 && 1.0 < maxx)
371                 {//Cross right
372                     z = p[i].y + (1 - p[i].x) * (p[i].y - p[j].y) / (p[i].x - p[j].x);
373                     if (z >= 0.0 && z <= 1.0)
374                     {
375                         polyoverlap[polyoverlapsize].x = 1.0;
376                         polyoverlap[polyoverlapsize++].y = z;
377                     }
378                 }
379                 if (miny < 0.0 && 0.0 < maxy)
380                 {//Cross bottom
381                     z = p[i].x - p[i].y * (p[i].x - p[j].x) / (p[i].y - p[j].y);
382                     if (z >= 0.0 && z <= 1.0)
383                     {
384                         polyoverlap[polyoverlapsize].x = z;
385                         polyoverlap[polyoverlapsize++].y = 0.0;
386                     }
387                 }
388                 if (miny < 1.0 && 1.0 < maxy)
389                 {//Cross top
390                     z = p[i].x + (1 - p[i].y) * (p[i].x - p[j].x) / (p[i].y - p[j].y);
391                     if (z >= 0.0 && z <= 1.0)
392                     {
393                         polyoverlap[polyoverlapsize].x = z;
394                         polyoverlap[polyoverlapsize++].y = 1.0;
395                     }
396                 }
397             }
398 
399             //Sort the points and return the area
400             SortPoints();
401             return Area();
402         }
403 
404         private double Area()
405         {
406             double ret = 0.0;
407             //Loop through each triangle with respect to (0, 0) and add the cross multiplication
408             for (int i = 0; i + 1 < polysortedsize; i++)
409                 ret += polysorted[i].x * polysorted[i + 1].y - polysorted[i + 1].x * polysorted[i].y;
410             //Take the absolute value over 2
411             return AafAbs(ret) / 2.0;
412         }
413 
414         private void SortPoints()
415         {
416             //Why even bother?
417             if (polyoverlapsize < 3)
418                 return;
419 
420             //polyoverlap is a triangle, points cannot be out of order
421             if (polyoverlapsize == 3)
422             {
423                 polysortedsize = polyoverlapsize - 1;
424                 polysorted[0].x = polyoverlap[1].x - polyoverlap[0].x;
425                 polysorted[0].y = polyoverlap[1].y - polyoverlap[0].y;
426                 polysorted[1].x = polyoverlap[2].x - polyoverlap[0].x;
427                 polysorted[1].y = polyoverlap[2].y - polyoverlap[0].y;
428                 return;
429             }
430 
431 
432             aaf_indll root = new aaf_indll();
433             root.next = null;
434 
435             //begin sorting the points.  Note that the first element is left out and all other elements are offset by it's values
436             for (int i = 1; i < polyoverlapsize; i++)
437             {
438                 polyoverlap[i].x = polyoverlap[i].x - polyoverlap[0].x;
439                 polyoverlap[i].y = polyoverlap[i].y - polyoverlap[0].y;
440 
441                 aaf_indll node = root;
442                 //Loop until the point polyoverlap[i] is can be sorted (counter) clockwiswe (I'm not sure which way it's sorted)
443                 while (true)
444                 {
445                     if (node.next != null)
446                     {
447                         if (polyoverlap[i].x * polyoverlap[node.next.ind].y - polyoverlap[node.next.ind].x * polyoverlap[i].y < 0)
448                         {
449                             //Insert point before this element
450                             aaf_indll temp = node.next;
451                             node.next = new aaf_indll();
452                             node.next.ind = i;
453                             node.next.next = temp;
454                             break;
455                         }
456                     }
457                     else
458                     {
459                         //Add point to the end of list
460                         node.next = new aaf_indll();
461                         node.next.ind = i;
462                         node.next.next = null;
463                         break;
464                     }
465                     node = node.next;
466                 }
467             }
468 
469             //We can leave out the first point because it's offset position is going to be (0, 0)
470             polysortedsize = 0;
471 
472             aaf_indll node2 = root;
473             aaf_indll temp2;
474 
475             //Add the sorted points to polysorted and clean up memory
476             while (node2 != null)
477             {
478                 temp2 = node2;
479                 node2 = node2.next;
480                 if (node2 != null)
481                     polysorted[polysortedsize++] = polyoverlap[node2.ind];
482 
483             }
484         }
485 
486         private bool PtinConvexPolygon(AafPnt[] p, AafPnt pt)
487         {
488             int dir = 0;
489             int j;
490 
491             //Basically what we are doing is seeing if pt is on the same side of each face of the polygon through cross multiplication
492             for (int i = 0; i < 4; i++)
493             {
494                 j = ja[i];
495                 double cross = (p[i].x - pt.x) * (p[j].y - pt.y) - (p[j].x - pt.x) * (p[i].y - pt.y);
496 
497                 if (cross == 0)
498                     continue;
499 
500                 if (cross > 0)
501                 {
502                     if (dir == -1)
503                         return false;
504 
505                     dir = 1;
506                 }
507                 else
508                 {
509                     if (dir == 1)
510                         return false;
511 
512                     dir = -1;
513                 }
514             }
515             return true;
516         }
517 
518         int roundup(double a) { if (AafAbs(a - round(a)) < 1e-9) return round(a); else if ((int)a > a) return (int)a; else return (int)a + 1; }
519         int rounddown(double a) { if (AafAbs(a - round(a)) < 1e-9) return round(a); else if ((int)a < a) return (int)a; else return (int)a - 1; }
520         int round(double a) { return (int)(a + 0.5); }
521         byte byterange(double a) { int b = round(a); if (b <= 0) return 0; else if (b >= 255) return 255; else return (byte)b; }
522         double AafAbs(double a) { return (((a) < 0) ? (-(a)) : (a)); }
523         double aaf_min(double a, double b) { if (a < b) return a; else return b; }
524         double aaf_max(double a, double b) { if (a > b) return a; else return b; }
525     }
526 
527     class RGBQUDA
528     {
529         public byte R { get; set; }
530         public byte G { get; set; }
531         public byte B { get; set; }
532     }
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要在 C# 中使用 OpenCV 库进行图片的傅立叶变换,你需要安装并引用 OpenCVSharp 库。下面是一个示例代码: ```csharp using OpenCvSharp; class Program { static void Main(string[] args) { // 加载图像 Mat image = Cv2.ImRead("input.jpg", ImreadModes.Grayscale); // 执行傅立叶变换 Mat fourier = new Mat(); Cv2.Dft(image, fourier, DftFlags.None); // 平移傅立叶变换结果,将低频部分移到中心 Cv2.ShiftDft(fourier, fourier); // 计算幅度谱 Mat magnitude = new Mat(); Cv2.Magnitude(fourier.Real, fourier.Imaginary, magnitude); // 对数变换,增强可视化效果 Cv2.Add(magnitude, new Scalar(1), magnitude); Cv2.Log(magnitude, magnitude); // 归一化到 [0, 1] 范围 Cv2.Normalize(magnitude, magnitude, 0, 1, NormTypes.MinMax); // 显示傅立叶变换结果 Cv2.ImShow("Magnitude Spectrum", magnitude); Cv2.WaitKey(0); } } ``` 在上述代码中,首先使用 `ImRead` 方法加载图像(这里假设图像为灰度图像)。然后使用 `Dft` 方法执行傅立叶变换。接下来,通过 `ShiftDft` 方法将低频部分移到中心,以便更好地可视化。然后使用 `Magnitude` 方法计算幅度谱,并进行对数变换和归一化,以增强可视化效果。最后,使用 `ImShow` 方法显示傅立叶变换的结果,并使用 `WaitKey` 方法等待用户按下任意键关闭窗口。 请确保已正确安装和配置 OpenCVSharp 库,并将示例代码中的图像路径替换为你要处理的图像路径。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值