作者qazq (...)
看板C_and_CPP
标题Re: [问题] 图片转角度
时间Tue Sep 27 21:56:28 2005
※ 引述《sbty173 (我爱单眼皮)》之铭言:
: 各位前辈好
: 我最近用在做图片的旋转 用的是2003.net 的mfc写
: 可是没有什麽头绪
: 去程式讨论区发现有前辈做了一个类似的程式
: http://delphi.ktop.com.tw/topic.asp?topic_id=34152
: 他的button7 是作旋转的
: 但他是用BCB做
: 内容如下
: void __fastcall TForm1::Button7Click(TObject *Sender)
: {
: Image2->AutoSize=true;
: int angle=StrToInt(Edit2->Text);
: Graphics::TBitmap *Bitmap1=new Graphics::TBitmap;
: Graphics::TBitmap *Bitmap2=new Graphics::TBitmap;
: Bitmap1->Assign(Image1->Picture->Bitmap);
: float radians=(2*3.1416*angle)/360; //不懂为何这样设定??
: float cosine=(float)cos(radians);
: float sine=(float)sin(radians);
: float Point1x=(-Bitmap1->Height*sine);
: float Point1y=(Bitmap1->Height*cosine);
: float Point2x=(Bitmap1->Width*cosine-Bitmap1->Height*sine);
: float Point2y=(Bitmap1->Height*cosine+Bitmap1->Width*sine);
: float Point3x=(Bitmap1->Width*cosine);
: float Point3y=(Bitmap1->Width*sine);
: float minx=min(0,min(Point1x,min(Point2x,Point3x)));
: float miny=min(0,min(Point1y,min(Point2y,Point3y)));
: float maxx=max(Point1x,max(Point2x,Point3x));
: float maxy=max(Point1y,max(Point2y,Point3y));
: int Bitmap2Width=(int)ceil(maxx-minx);
: int Bitmap2Height=(int)ceil(maxy-miny);
: Bitmap2->Height=Bitmap2Height;
: Bitmap2->Width=Bitmap2Width;
: for(int x=0;x<Bitmap2Width;x++)
: {
: for(int y=0;y<Bitmap2Height;y++)
: {
: int Bitmap1x=(int)((x+minx)*cosine+(y+miny)*sine);
: int Bitmap1y=(int)((y+miny)*cosine-(x+minx)*sine);
: if(Bitmap1x>=0&&Bitmap1x<Bitmap1->Width&&Bitmap1y>=0&&Bitmap1y<Bitmap1->Height)
: {
: Bitmap2->Canvas->Pixels[x][y]=Bitmap1->Canvas->Pixels[Bitmap1x][Bitmap1y];
: }
: }
: }
: 我看不太懂为什麽要这样做运算
: 不晓得前辈们可以帮我解释一下吗
: 还有请问语法有些我不太懂 要怎麽转过来呢?
其实图片的旋转很简单,用到的都是数学的公式而已!:)
把一张图想成是放在一个数学座标上的图来想,应该会比较好理解。
在数学的旋转上面....
如果我要将 (x, y) 的点,逆时钟旋转 30 度变成 (x' , y')
哪麽公式是...
x' = cos30 * x - sin30 * y
y' = sin30 * x + cos30 * y
看了上面的公式,直觉的做法就是用双 for 将原图全部的 pixel 跑一次...
然後算出旋转後的新位置,存到新的阵列中。
但是这样做你会发现....
for (i = 0; i < old_bmp_h; i++)
for (j = 0; j < old_bmp_w; j++)
{
x' = cos(30度) * j - sin(30度) * i;
y' = cos(30度) * j - sin(30度) * i;
// cos() 里面要传入的是 "径度"
// radians=(2*3.1416*angle)/360; //不懂为何这样设定??
// 就是在转成径度用的。
new_bmp[x'][y'] = old_bmp[i][j];
}
如此作完,结果一定很难看....
因为 cos(30) * j - sin(30) * i 算出来的结果有可能是小数
如果说 x' = 30.1 那会被强制变成整数为 30
下一个 pixel 算出来 x' = 30.3 结果还是会是 30
所以,到最後的结果就会有很多旋转前的 pixel 都填入同一个旋转後的 pixel
而很多旋转後的 pixel 没有值填入。
因此!我们需要逆向思考!
for (i = 0; i < new_bmp_h; i++)
for (j = 0; j < new_bmp_w; j++)
{
x = ............... ;
y = ............... ;
// 倒过来个公式自己用数学代数算一下就出来了!:)
new_bmp[i][j] = old_bmp[x][y];
}
这样是把旋转後的每一个 pixel 都跑过一次....
反算应该要用旋转前的哪一个 pixel 填入。
做到这样,结果就会好很多了,但是会发现会有锯齿状。
所以有更好的演算法可以帮助你做的更好。
1. 最近邻居法。
最简单,但是效果非常有限。
2. biliner。
简单,效过又好。
3. bicubic。
复杂,效过最好。
建议可以做到2就可以了!^^
至於详细的内容,可以用 google 找一下就可以找到了。^^
2, 3 也是常常用在将图片放大、缩小的时候。
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 218.163.159.53
1F:→ qazq:反算的公式,发现原PO所贴的code里面就有了 :) 09/27 21:59
※ 编辑: qazq 来自: 218.163.159.53 (09/27 22:02)
2F:推 moonls:写的很清楚 !! 获益良多 09/28 00:23
3F:推 renderer:推 09/28 10:40