请教:如何快速任意角度旋转一个图形?(200分)

  • 主题发起人 主题发起人 hwave
  • 开始时间 开始时间
H

hwave

Unregistered / Unconfirmed
GUEST, unregistred user!
模拟行车,把前方的地形抓取下来,送到驾驶者的显示窗口,由于视角不同,又是即时的,
所以需要快速的算法.那位大侠给指点一下.
 
显然,这是三维方面的问题,应该用DirectX或OPENGL
 
var
newbmp:Tbitmap;
r,a,t:real;
x1,y1:integer;
k,i,j,bmpheight,bmpwidth:integer;

begin
K:=90://旋转度数
T:=K*3.14/180;
newbmp:=tbitmap.create;
newbmp.width:=image1.width;
newbmp.height:=image1.height;
bmpheight:=image1.height;
bmpwidth:=image1.width;
for i:=1 to bmpwidth do
for j:=1 to bmpheight do
begin
R:=Sqrt(Sqr(i)+sqr(j));
A:=Arctan(j/i);
X1:=round(R*Cos(a+t));
Y1:=round(R*Sin(a+t));
newbmp.cavas.pixels[x1,y1]:=image1.canvas.pixels[i,j];
end;
image2.picture.assign(newbmp);
newbmp.free;
end;
 
to longdiao
我按你的说法做,但是结果
image2显示出来的是一片白色,
请问是何原因
 
这是别人写的程序,我略改了一下,速度还是比较快的。

uses
Windows, ExtCtrls, Graphics;

const
MaxPixelCount = 32768;
{$EXTERNALSYM MaxPixelCount}

type
pRGBArray = ^TRGBArray;
{$EXTERNALSYM pRGBArray}
TRGBArray = array[0..MaxPixelCount - 1] of TRGBTriple;

procedure BitmapRotate(BmpOrigin, BmpDest: TBitmap; Angle: Double);

implementation

procedure BitmapRotate(BmpOrigin, BmpDest: TBitmap; Angle: Double);
var
i, j, iRotationAxis, iOriginal, iPrime, iPrimeRotated, jRotationAxis, jOriginal,
jPrime, jPrimeRotated: Integer;
RowOriginal: pRGBArray;
RowRotated: pRGBArray;
Theta, sinTheta, cosTheta: DOUBLE;
R, G, B: byte;
FColor: TColor;
SavFormat: TPixelFormat;
begin
BmpDest.Width := BmpOrigin.Width;
BmpDest.Height := BmpOrigin.Height;
// BmpDest.PixelFormat := pf24bit;
SavFormat := BmpOrigin.PixelFormat;
BmpOrigin.PixelFormat := pf24bit;
BmpDest.PixelFormat := BmpOrigin.PixelFormat;
iRotationAxis := BmpOrigin.Width div 2;
jRotationAxis := BmpOrigin.Height div 2;
Theta := Angle * PI / 180;
sinTheta := SIN(Theta);
cosTheta := COS(Theta);
FColor := BmpOrigin.Canvas.Pixels[0, 0];
B := FColor mod 256;
G := round((FColor div 256) mod 256);
R := round((FColor div 256) div 256);

for j := BmpDest.Height - 1 downto 0 do
begin
RowRotated := BmpDest.Scanline[j];
jPrime := 2 * (j - jRotationAxis) + 1;
for i := BmpDest.Width - 1 downto 0 do
begin
iPrime := 2 * (i - iRotationAxis) + 1;
iPrimeRotated := ROUND(iPrime * CosTheta - jPrime * sinTheta);
jPrimeRotated := ROUND(iPrime * sinTheta + jPrime * cosTheta);
iOriginal := (iPrimeRotated - 1) div 2 + iRotationAxis;
jOriginal := (jPrimeRotated - 1) div 2 + jRotationAxis;
if (iOriginal >= 0) and (iOriginal <= BmpOrigin.Width - 1) and
(jOriginal >= 0) and (jOriginal <= BmpOrigin.Height - 1)
then
begin
RowOriginal := BmpOrigin.Scanline[jOriginal];
RowRotated := RowOriginal[iOriginal]
end
else
begin
RowRotated.rgbtBlue := B;
RowRotated.rgbtGreen := G;
RowRotated.rgbtRed := R
end;
end;
end;
BmpOrigin.PixelFormat := SavFormat;
BmpDest.PixelFormat := BmpOrigin.PixelFormat;
end;
 
关于“图像旋转”,有专家如下说法:
*************************************
如果有人试着通先选择新的中心点再旋转图片这个方法来得到新的图片的话,会发觉图片
上有许多“洞”,这是因为不连续的空间和整数的数学运算造成的。为了解决这个问题,
我们可以使用一个“相反”的方法。仔细考虑新图像中的每个像素,查找它在原图像中的
位置。这样的技术就会解决任何在图像中的“洞”。
*************************************
我个人观点:
通用的算法原理都一样,及选择旋转后的图像的点,通过算法查找气再原图中对应的香素点!
我觉得旋转后的图像有效区域的象素点数和原图片的象素点数一样多,怎么会出现“洞”呢?
我不理解上面这段话的含义,哪位仁兄理解了帮忙解释一下
 
我的代码,是用C++Builder写的自已改成Delphi的吧!
void S_PicXZ(Graphics::TBitmap *Source,Graphics::TBitmap *NewPic,int angle)//图片旋转
{
if(angle>180)angle=360-angle;
if(angle<-180)angle=360+angle;

float radians=3.1415/180.00*angle;
float cosine=(float)cos(radians);
float sine=(float)sin(radians);
if (cosine < 0)cosine = -cosine;
if (sine < 0)sine = -sine;


float Point1x=(-Source->Height*sine);
float Point1y=(Source->Height*cosine);
float Point2x=(Source->Width*cosine-Source->Height*sine);
float Point2y=(Source->Height*cosine+Source->Width*sine);
float Point3x=(Source->Width*cosine);
float Point3y=(Source->Width*sine);
float minx=min((float)0,min(Point1x,min(Point2x,Point3x)));
float miny=min((float)0,min(Point1y,min(Point2y,Point3y)));
float maxx=max(Point1x,max(Point2x,Point3x));
float maxy=max(Point1y,max(Point2y,Point3y));
int DestBitmapWidth,DestBitmapHeight;
if(angle>90&amp;&amp;angle<180)
DestBitmapWidth=(int)ceil(-minx);
else
DestBitmapWidth=(int)ceil(maxx-minx);

if(angle>-180&amp;&amp;angle<-90)
DestBitmapHeight=(int)ceil(-miny);
else
DestBitmapHeight=(int)ceil(maxy-miny);

NewPic->Height=DestBitmapHeight;
NewPic->Width=DestBitmapWidth;
for(int x=0;x<DestBitmapWidth;x++)
{
for(int y=0;y<DestBitmapHeight;y++){
int SrcBitmapx=(int)((x+minx)*cosine+(y+miny)*sine);
int SrcBitmapy=(int)((y+miny)*cosine-(x+minx)*sine);
if(SrcBitmapx>=0&amp;&amp;SrcBitmapx<Source->Width&amp;&amp;SrcBitmapy>=0&amp;&amp;
SrcBitmapy<Source->Height)
{
NewPic->Canvas->Pixels[x][y]=
Source->Canvas->Pixels[SrcBitmapx][SrcBitmapy];
}
}
}
}
 
首先需要弄明白的是:图形的旋转和图象的旋转是完全不同的两个概念!
按照你的意思,你应该是对图象进行任意角度的旋转了;
记得在旋转图象后,图象的大小会发生变化,你需要考虑这一点。


下面是围绕图象中心点进行任意角度的旋转,^_^

设定Image1.AutoSize :=True;

procedure GraphicRotate(var Src, Dst :TBitmap; cx, cy :Integer; Angle :Extended);
type
TFColor =record
b,g,r :Byte;
end;
var
Top,Bottom,Left,Right,eww,nsw,fx,fy,wx,wy :Extended;
cAngle,sAngle :Double;
xDiff,yDiff,ifx,ify,px,py,ix,iy,x,y,LL :Integer;
nw,ne,sw,se :TFColor;
P1,P2,P3 :PByteArray;
begin
Angle :=Angle;
Angle :=-Angle*Pi/180;
sAngle :=Sin(Angle);
cAngle :=Cos(Angle);
xDiff :=(Dst.Width-Src.Width)div 2;
yDiff :=(Dst.Height-Src.Height)div 2;
LL :=Dst.Height;
for y :=0 to Dst.Height-1 do
begin
P3 :=Dst.scanline[y];
py :=2*(y-cy)+1;
for x :=0 to Dst.Width-1 do
begin
px :=2*(x-cx)+1;
fx :=(((px*cAngle-py*sAngle)-1)/ 2+cx)-xDiff;
fy :=(((px*sAngle+py*cAngle)-1)/ 2+cy)-yDiff;
ifx :=Round(fx);
ify :=Round(fy);
if(ifx>-1) and (ifx<Src.Width)and (ify>-1) and (ify<Src.Height) then
begin
eww :=fx-ifx;
nsw :=fy-ify;
iy :=TrimInt(ify+1,0,Src.Height-1);
ix :=TrimInt(ifx+1,0,Src.Width-1);
P1 :=Src.scanline[ify];
P2 :=Src.scanline[iy];
nw.r :=P1[ifx*3];
nw.g :=P1[ifx*3+1];
nw.b :=P1[ifx*3+2];
ne.r :=P1[ix*3];
ne.g :=P1[ix*3+1];
ne.b :=P1[ix*3+2];
sw.r :=P2[ifx*3];
sw.g :=P2[ifx*3+1];
sw.b :=P2[ifx*3+2];
se.r :=P2[ix*3];
se.g :=P2[ix*3+1];
se.b:=P2[ix*3+2];
Top :=nw.b+eww*(ne.b-nw.b);
Bottom :=sw.b+eww*(se.b-sw.b);
P3[x*3+2] :=IntToByte(Round(Top+nsw*(Bottom-Top)));
Top :=nw.g+eww*(ne.g-nw.g);
Bottom :=sw.g+eww*(se.g-sw.g);
P3[x*3+1] :=IntToByte(Round(Top+nsw*(Bottom-Top)));
Top :=nw.r+eww*(ne.r-nw.r);
Bottom :=sw.r+eww*(se.r-sw.r);
P3[x*3] :=IntToByte(Round(Top+nsw*(Bottom-Top)));
end;
end;
end;
end;

procedure TForm1.Button1Click(Sender: TObject); //arbitrary degree rotation
var
Angle :Extended;
S: String;
Corners :array of TPoint;
x1,x2,y1,y2 :Integer;
SrcBmp,DstBmp :TBitmap;
Center :TPoint;

function MyRotate(var p :TPoint; ang :Extended):TPoint;
begin
Result.x :=Round((p.x*cos(DegToRad(ang)))-(p.y*sin(DegToRad(ang)))); //绕坐标原点旋转
Result.y :=Round((p.y*cos(DegToRad(ang)))+(p.x*sin(DegToRad(ang))));
end;

begin
S :='45';
Angle :=0.0;
SrcBmp :=TBitmap.Create;
SrcBmp.Assign(Form1.Image1.Picture.Bitmap);
if InputQuery('图象旋转','旋转角度(正值顺时针,负值逆时针):',S) then
Angle :=StrToFloat(S);
SetLength(Corners,4);
Corners[0].x :=-SrcBmp.Width div 2; // 0 1
Corners[0].y :=SrcBmp.Height div 2; // 2 3
Corners[1].x :=-Corners[0].x;
Corners[1].y :=Corners[0].y;
Corners[2].x :=Corners[0].x; //原坐标系角点坐标
Corners[2].y :=-Corners[0].y;
Corners[3].x :=-Corners[0].x;
Corners[3].y :=-Corners[0].y;
//BaseAngle :=BaseAngle+Angle; //BaseAngle 是基于原始位图的旋转
Corners[0] :=MyRotate(Corners[0],Angle{BaseAngle});
Corners[1] :=MyRotate(Corners[1],Angle{BaseAngle}); //新坐标系角点坐标
Corners[2] :=MyRotate(Corners[2],Angle{BaseAngle});
Corners[3] :=MyRotate(Corners[3],Angle{BaseAngle});
x1 :=MinIntValue([Corners[0].x,Corners[1].x,Corners[2].x,Corners[3].x]);
x2 :=MaxIntvalue([Corners[0].x,Corners[1].x,Corners[2].x,Corners[3].x]);
y1 :=MinIntValue([Corners[0].y,Corners[1].y,Corners[2].y,Corners[3].y]);
y2 :=MaxIntvalue([Corners[0].y,Corners[1].y,Corners[2].y,Corners[3].y]);
Corners :=nil;
DstBmp :=TBitmap.Create;
DstBmp.PixelFormat :=pf24bit;
DstBmp.Width :=x2-x1;
DstBmp.Height :=y2-y1;
Center.x :=DstBmp.Width div 2; //新的中心点
Center.y :=DstBmp.Height div 2;
GraphicRotate(SrcBmp,DstBmp,Center.x,Center.y,Angle{BaseAngle});
Form1.Image1.Picture.Bitmap.Assign(DstBmp);
SrcBmp.Free;
DstBmp.Free;
end;
 
多人接受答案了。
 
卷大侠的算法在我这里运行真是奇慢,不知为什么
 
后退
顶部