C#图片自由变换任意扭曲

更新时间:2023-06-24 11:47:59 阅读: 评论:0

C#图⽚⾃由变换任意扭曲
之前想过要做个地铁驾驶的游戏,其中想把⼀些原本是矩形图⽚弄成⼀个梯形,但是发现GID+上⾯没有类似的⽅法。于是在⾕歌⾕了⼀下。没有!只能找到令⼈垂涎的,并没有源码。按照⾃⼰的想法尝试了⼀两天,有点效果,但实际上不是那样。后来知道那个在数字图像处理中叫“透视变换”。于是上⽹找了相关资料,原理找了,看了不明⽩。代码没多少,有ActionScript的,不明;有C的,不明。真笨啊!后来在CodeProject上⾯看到⼀份外国⼈的博⽂,全英⽂看不太明⽩,但看了⼀幅图,⼤概知道他意思了。下了份源码看看,C++的。好不容易翻译成C#的(感觉还是保留了不少C++风格的东西),编译通过,运⾏正常。后来才⼀步⼀步的阅读代码。还没全懂,先把懂的部分记录下来。以后继续研究继续补充。
  先看看效果
迭字怎么读
旧人旧事  界⾯是仿照某个⼈(⽹上版本太多,找不到原作者)的弄出来的,界⾯不是重点,重点是算法。下⾯就直接贴⽼外的那幅图⼤致讲讲思想。
  ⾸先是从原本图⽚转化成⼀幅理想化的⽬标图⽚,那幅图⽚只是理想化的,最终的图⽚是最右边的那幅。转换的过程就是根据转换后图⽚的四个⾓计算出⽬标图⽚的size,⽣成⼀个矩阵,就是那个Destination Image,然后把理想化的⽬标图⽚覆盖过去,把理想化图⽚的每个“像素格”(已经不是真正的像素格了,因为经过了扭曲变形)跟那个矩阵对⽐,看看覆盖了哪些格⼦,覆盖的⾯积有多少,按百分⽐地把颜⾊累加到对应的格⼦上。实际上那个格⼦就相当于新图⽚的像素点了。按照矩阵⽣成最终的⽬标图。
接着就介绍算法⾥⾯调⽤的⽅法层次
  把已经弄懂(并不代表完全懂的)的代码贴出来,⾸先是最外层的⽅法
1public void CreateTransform(Bitmap src,ref Bitmap dst, List<double> xcorner, List<double> ycorner, Aaf_callback callbackfunc)
2        {
3int right = 0, bottom = 0;
4
5//主要是根据新图⽚的坐标,计算出图⽚的宽和⾼,构造⽬标图⽚的Bitmap的
6double offx = xcorner[0];
7double offy = ycorner[0];
8for (int i = 1; i < 4; i++)
9            {
10if (xcorner[i] < offx) offx = xcorner[i];
11if (ycorner[i] < offy) offy = ycorner[i];
12            }
13
14for (int i = 0; i < 4; i++)
15            {
16                xcorner[i] -= offx;
17                ycorner[i] -= offy;
18if (roundup(xcorner[i]) > right) right = roundup(xcorner[i]);
19if (roundup(ycorner[i]) > bottom) bottom = roundup(ycorner[i]);
20            }
21            dst = new Bitmap(right, bottom);
22            Transform(src, dst, xcorner, ycorner, null);
23        }
  上⾯这个⽅法只是定了⽬标图⽚的尺⼨,其余什么都没做。下⾯这个⽅法还没做多少转换的事
1private void Transform(Bitmap src,Bitmap dst, List<double> xcorner, List<double> ycorner, Aaf_callback callbackfunc)
2        {
3//Make sure the coordinates are valid
4if (xcorner.Count != 4 || ycorner.Count != 4)
5return ;
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
18double[] dx = { 0.0, 1.0, 1.0, 0.0 };
19double[] dy = { 0.0, 0.0, 1.0, 1.0 };
20for (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//这⾥计算出变换后起始点的坐标
32for (int i = 1; i < 4; i++)
33            {
34if (rounddown(xcorner[i]) < outstartx) outstartx = rounddown(xcorner[i]);
35if (rounddown(ycorner[i]) < outstarty) outstarty = rounddown(ycorner[i]);
36            }
37for (int i = 0; i < 4; i++)
38            {
研究生排名
39if (roundup(xcorner[i] - outstartx) > outwidth) outwidth = roundup(xcorner[i] - outstartx);
40if (roundup(ycorner[i] - outstarty) > outheight) outheight = roundup(ycorner[i] - outstarty);
41            }
42
43
44//fill out pixelgrid array
45//计算出理想⽬标图⽚中各个“像素格”中的左上⾓的坐标
46if (CreateGrid(src, xcorner, ycorner))
47            {
48//Do the transformation
49//进⾏转换
50                DoTransform(src,dst, callbackfunc);
51            }
四级报名流程52
53//Return if the function completed properly
54return ;
55        }
  下⾯这个⽅法则是计算出原图像中每个像素点的左上⾓的点到⽬标图像中的坐标,结果是存放在pixelgrid中,这个⼆维数组的⾏和列都⽐原图像的宽⾼多1,这个关系我当初没搞懂。⽤⽐较极限的思想,假设现在这幅图⽚只有⼀个像素组成,宽和⾼都是1,然后如果单纯存储⼀个左上⾓的坐标,是
⽆法组成⼀个四边形的,这就需要把其他三个⾓的坐标相应地记录,这是存储的数组的⾏和列均要⽐原图的宽和⾼多1,就是也就是⼀个2⾏2列的数组能存放4个数值,刚好就容纳了那⼀个像素点的四个⾓的坐标值。扩⼤到真实的图⽚也同样道理。不过这个⽅法我看不明⽩,貌似⽤到了向量的思想。⼤致是按照新图⽚四条边来计算的。
1private bool CreateGrid(Bitmap src, List<double> xcorner, List<double> ycorner)
2        {
3//mmm geometry
4double[] sideradius = new double[4];
5double[] sidecos = new double[4];
6double[] 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
9int j;
10for (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
19for (int x = 0; x < src.Width + 1; x++)
20            {
21double topdist = ((double)x / (src.Width)) * sideradius[0];
22double ptxtop = xcorner[0] + topdist * sidecos[0];
23double ptytop = ycorner[0] + topdist * sidesin[0];
24
25double botdist = (1.0 - (double)x / (src.Width)) * sideradius[2];
26double ptxbot = xcorner[2] + botdist * sidecos[2];
27double ptybot = ycorner[2] + botdist * sidesin[2];
28
29double Ah = ptybot - ptytop;
30double Bh = ptxtop - ptxbot;
31double Ch = Ah * ptxtop + Bh * ptytop;//叉乘
32
33for (int y = 0; y < src.Height + 1; y++)
34                {
35double leftdist = (1.0 - (double)y / (src.Height)) * sideradius[3];
36double ptxleft = xcorner[3] + leftdist * sidecos[3];
37double ptyleft = ycorner[3] + leftdist * sidesin[3];
38
39double rightdist = ((double)y / (src.Height)) * sideradius[1];
40double ptxright = xcorner[1] + rightdist * sidecos[1];
41double ptyright = ycorner[1] + rightdist * sidesin[1];
42
43double Av = ptyright - ptyleft;
44double Bv = ptxleft - ptxright;
45double Cv = Av * ptxleft + Bv * ptyleft;
46
47//Find where the lines interct and store that point in the pixelgrid array
48double det = Ah * Bv - Av * Bh;
49if (AafAbs(det) < 1e-9)
50                    {
51return fal;
52                    }
53el
54                    {
55int 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
63return true;
64        }
  下⾯这个⽅法就利⽤上⾯的⽅法计算出的坐标点集合进⾏按⽐例填⾊。上⾯每个像素点的四个⾓的坐标,都会在下⾯⽅法重新提取出来组回⼀个四边形,具体还是结合代码和注释看看
1private void DoTransform(Bitmap src,Bitmap dst, Aaf_callback callbackfunc)
2        {
3
4//Get source bitmap's information
5if (src == null) return ;
6
7//Create the source dib array and the dstdib array
8            aaf_dblrgbquad[] dbldstdib = new aaf_dblrgbquad[outwidth * outheight];
9for (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[] pofft = new AafPnt[4];
15
16//Loop through the source's pixels
17//遍历原图(实质上是pixelgrid)各个点
18for (int x = 0; x < src.Width; x++)
19            {
20for (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
31int mindx = int.MaxValue;
32int mindy = int.MaxValue;
33int maxdx = int.MinValue;
34int maxdy = int.MinValue;
35for (int i = 0; i < 4; i++)
36                    {
37if (rounddown(p[i].x) < mindx) mindx = rounddown(p[i].x);
分区表丢失
38if (roundup(p[i].x) > maxdx) maxdx = roundup(p[i].x);
39if (rounddown(p[i].y) < mindy) mindy = rounddown(p[i].y);
40if (roundup(p[i].y) > maxdy) maxdy = roundup(p[i].y);
41                    }
64位和32位的区别
42
43int 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
48for (int xx = mindx - 1; xx <= maxdx; xx++)
49                    {
50if (xx < 0 || xx >= dst.Width)
51continue;
52for (int yy = mindy - 1; yy <= maxdy; yy++)
53                        {
54if (yy < 0 || yy >= dst.Height)
55continue;
56
57//offt p and by (xx,yy) and put that into pofft
58for (int i = 0; i < 4; i++)
59                            {
60                                pofft[i].x = p[i].x - xx;
61                                pofft[i].y = p[i].y - yy;
62                            }

本文发布于:2023-06-24 11:47:59,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/1052718.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:坐标   计算   标图   四边形   看看   代码
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图