图像倒置?(35分)

金少

Unregistered / Unconfirmed
GUEST, unregistred user!
怎样用最简单的办法进行图像的左右, 上下的颠倒?
 
你在图像软件里处理好了就可以了呀。
 
要用程序实现呀!
 
这里有一段代码:
调用方法:
bmp_rotate(Image1.Picture.Bitmap, Image2.Picture.Bitmap, RAngle);

procedure TfrmColor.bmp_rotate(src,dst:tbitmap;angle:extended);
var
c1x,c1y,c2x,c2y:integer;
p1x,p1y,p2x,p2y:integer;
radius,n:integer;
alpha:extended;
c0,c1,c2,c3:tcolor;
begin
//将角度转换为PI值
angle := (angle / 180) * pi;
// 计算中心点,你可以修改它
c1x := src.width div 2;
c1y := src.height div 2;
c2x := dst.width div 2;
c2y := dst.height div 2;

// 步骤数值number
if c2x < c2y then
n := c2y
else
n := c2x;
dec (n,1);

// 开始旋转
for p2x := 0 to n do begin
for p2y := 0 to n do begin
if p2x = 0 then
alpha:= pi/2
else
alpha := arctan2(p2y,p2x);
radius := round(sqrt((p2x*p2x)+(p2y*p2y)));
p1x := round(radius * cos(angle+alpha));
p1y := round(radius * sin(angle+alpha));

c0 := src.canvas.pixels[c1x+p1x,c1y+p1y];
c1 := src.canvas.pixels[c1x-p1x,c1y-p1y];
c2 := src.canvas.pixels[c1x+p1y,c1y-p1x];
c3 := src.canvas.pixels[c1x-p1y,c1y+p1x];

dst.canvas.pixels[c2x+p2x,c2y+p2y]:=c0;
dst.canvas.pixels[c2x-p2x,c2y-p2y]:=c1;
dst.canvas.pixels[c2x+p2y,c2y-p2x]:=c2;
dst.canvas.pixels[c2x-p2y,c2y+p2x]:=c3;
end;
application.processmessages
end;
end;
*************8
----把一个点绕原点旋转α角度后,新的坐标位置与原坐标位置的
关系是:

X=x?cosα-y?sinα
Y=x?sinα+y?cosα
例如要把位图顺时针旋转90度,坐标变换公式为:X=-yY=x

----把这一公式用到Image构件上,显示位图的主要问题是Image构
件显示的位图只有一个象限,并且x、y坐标也是互相颠倒的,为了
解决这个问题,必须在Image构件上建立一个新的坐标原点。下面就
举例说明。

----1.新建一工程project1,在form1上添加image1、image2、
image3、image4,其Autosize属性设为True,image1用来显示原
图,image2、image3、image4分别用来显示旋转90度、180度和270
度后的图像。双击image1,选定一幅bmp图。

----2.添加Button1、Button2、Button3和Button4按钮,其
caption属性分别为"原图"、"旋转90度"、"旋转180度"、
"旋转270度"。

----3.编写"旋转90度"按钮的OnClick事件。

procedureTForm1.Button2Click(Sender:TObject);
var
i,j:integer;
begin
//确定旋转后位图的大小
image2.Picture.Bitmap.Height:=image1.picture.width;
image2.Picture.Bitmap.Width:=image1.picture.height;
fori:=0toimage1.Heightdo
forj:=0toimage1.Widthdo
image2.canvas.Pixels[(-i+image1.Height),
j]:=image1.canvas.Pixels[j,i];
end;


----4.编写"旋转180度"按钮的OnClick事件。

procedureTForm1.Button3Click(Sender:TObject);
var
i,j:integer;
begin
//确定旋转后位图的大小
image3.Picture.Bitmap.Height:=image1.picture.Height;
image3.Picture.Bitmap.Width:=image1.picture.Width;
fori:=0toimage1.Heightdo
forj:=0toimage1.Widthdo
image3.canvas.Pixels[(image1.Width
-j),(image1.Height-i)]:=image1.canvas.Pixels[j,i];
end;

----5.编写"旋转270度"按钮的OnClick事件。代码和步骤3相
似,只需要用image4替换image2,然后用以下的语句替换步骤3for
循环中的原有的语句。

image4.canvas.Pixels[i,(image1.Width-j)]:=image1.canvas.Pixels[j,i];

procedure Rotate(Bmp,Dst:TFastRGB;cx,cy:Integer;Angle:Extended);
var
cAngle,
sAngle: Double;
xDiff,
yDiff,
xpr,ypr,
ix,iy,
px,py,
x,y: Integer;
Tmp: PFColor;{what means?}
begin
Angle:=-Angle*Pi/180;
sAngle:=Sin(Angle);
cAngle:=Cos(Angle);
xDiff:=(Dst.Width-Bmp.Width)div 2;
yDiff:=(Dst.Height-Bmp.Height)div 2;
Tmp:=Dst.Bits;{what means?}
for y:=0 to Dst.Height-1 do
begin
py:=2*(y-cy)+1;
for x:=0 to Dst.Width-1 do
begin
px:=2*(x-cx)+1;
xpr:=Round(px*cAngle-py*sAngle);
ypr:=Round(px*sAngle+py*cAngle);
ix:=((xpr-1)div 2+cx)-xDiff;
iy:=((ypr-1)div 2+cy)-yDiff;
if(ix>-1)and(ix<Bmp.Width)and(iy>-1)and(iy<Bmp.Height)then
Tmp^:=Bmp.Pixels[iy,ix]; {what means?}
Inc(Tmp);
end;
Tmp:=Pointer(Integer(Tmp)+Dst.Gap); {what means?}
end;
end;

原理:
cos(Alpha), sin(Alpha), 0
只需要用源矩阵乘以 -sin(Alpha),cos(Alpha), 0
0, 0, 1

如果你发现转过来的图形带有很整齐的花点,解决的办法是反向计算,即从目标求的源点的坐标和像素值。
以上的例子就是这样的。

如果真的按下面下矩阵计算每个点,目标区有一些点会是白点(因为有些源点通过计算和四舍五入在目标中凑到一起了),我以前解决的办法是从目标求的源点的坐标和像素值,不过首先要取到目标区的区域(往往是斜的)。
cos(Alpha), sin(Alpha), 0
-sin(Alpha),cos(Alpha), 0
0, 0, 1

下载我说的控件吗,有现成的例子及DEMO!

http://www.crosswinds.net/~khojasteh/delphi-components.html

TRotateImage v1.21
This component is a visual component similar to TImage with ability to rotate the image in any arbitrary angle. TRotateImage can be used on Delphi 3, 4, and 5.
 
用copyrect,然后在TRect里做文章,把坐标颠倒就可以
 
zyg_zm说的很对,很简单的事情.不用弄得那么复杂
 
用pixels的话速度很慢,用scanline吧
 
简单:

var
r1,r2:trect;
begin
r1:=image1.BoundsRect;
r2:=rect(r1.Right,r1.Bottom,r1.Left,r1.top);

image1.Canvas.CopyRect(image1.BoundsRect,image1.Canvas,r2);
 

PROCEDURE FlipBitmap(SrcBitmap,DestBitmap:TBitmap;ResampleCallback:TResampleCallBack);
PROCEDURE MirrorBitmap(SrcBitmap,DestBitmap:TBitmap;ResampleCallback:TResampleCallBack);

PROCEDURE MirrorBitmap(SrcBitmap,DestBitmap:TBitmap;ResampleCallback:TResampleCallBack);
var i,j :Integer;
SrcRow,DestRow :pRGBArray;
begin
SetBitmapsEql(SrcBitmap,DestBitmap);
for i:=DestBitmap.Height-1 downto 0 do
begin
SrcRow:=SrcBitmap.ScanLine;
DestRow:=DestBitmap.ScanLine;

if Assigned(ResampleCallBack) then ResampleCallBack(0,100,Round((i/SrcBitmap.Height)*100));

for j:=0 to DestBitmap.Width-1 do begin
DestRow[j].rgbtBlue:=SrcRow[SrcBitmap.Width-j-1].rgbtBlue;
DestRow[j].rgbtGreen:=SrcRow[SrcBitmap.Width-j-1].rgbtGreen;
DestRow[j].rgbtRed:=SrcRow[SrcBitmap.Width-j-1].rgbtRed;
end;
end;
end;

PROCEDURE FlipBitmap(SrcBitmap,DestBitmap:TBitmap;ResampleCallback:TResampleCallBack);
var i,j :Integer;
SrcRow,DestRow :pRGBArray;
begin
SetBitmapsEql(SrcBitmap,DestBitmap);
for i:=DestBitmap.Height-1 downto 0 do
begin
SrcRow:=SrcBitmap.ScanLine[DestBitmap.Height-i-1];
DestRow:=DestBitmap.ScanLine;

if Assigned(ResampleCallBack) then ResampleCallBack(0,100,Round((i/SrcBitmap.Height)*100));

for j:=0 to DestBitmap.Width-1 do begin
DestRow[j].rgbtBlue:=SrcRow[j].rgbtBlue;
DestRow[j].rgbtGreen:=SrcRow[j].rgbtGreen;
DestRow[j].rgbtRed:=SrcRow[j].rgbtRed;
end;
end;
end;



调用方法:
var
TempBmp,tb:TBitmap;
begin
TempBMP:=TBitmap.Create;
TB:=TBitmap.Create;
try
case (Sender as TMenuItem).Tag of
1:begin
DoSthForUndo('垂直翻转');
FlipBitmap(Image1.Picture.Bitmap,TempBmp,nil);
Image1.Picture.Bitmap.Assign(TempBMP);
end;
2:begin
DoSthForUndo('水平翻转');
MirrorBitmap(Image1.Picture.Bitmap,TempBmp,nil);
Image1.Picture.Bitmap.Assign(TempBMP);
end;
3:
begin
DoSthForUndo('反相');
EffectNegative(Image1.Picture.Bitmap,TempBMP,nil);
Image1.Picture.Bitmap.Assign(TempBMP);
end;
4:begin
DoSthForUndo('彩色变灰度');
EffectGreyScale(Image1.Picture.Bitmap,TempBmp,nil);
Image1.Picture.Bitmap.Assign(TempBMP);
end;
5:begin
DoSthForUndo('翻转');
FlipBitmap(Image1.Picture.Bitmap,Image1.Picture.Bitmap,nil);
end;
6:begin
DoSthForUndo('翻转');
MirrorBitmap(Image1.Picture.Bitmap,Image1.Picture.Bitmap,nil);
end;
7:begin
DoSthForUndo('老照片效果');
EffectGreyScale(Image1.Picture.Bitmap,Image1.Picture.Bitmap,nil);
end;
end;
finally
TempBmp.Free;
TB.Free;
Image1.RePaint;
end;

 
用内存copy
 
同ldaan3,
我昨天说的意思就是 ldaan3 的做法,你去试试,保证行
 
[:)]据我知,用API 函数strechdibits即可.
 
以下的代码经过验证,但是只适用于BMP格式的文件,其他格式的文件需要转换

procedure TForm1.Button2Click(Sender: TObject);
var
Color:DWORD;
i,j:integer;
begin
for i:= 0 to Image1.Height do
begin
for j:= 0 to Image1.Width do
begin
Color:=DWORD(Image1.Canvas.Pixels[j,i]);
Image2.Canvas.Pixels[j,Image2.Height-i]:=Color;
end;
end;
end;
 
以下实现图像旋转,原理应该一样类似:

8. 图象旋转
在Form 中放一TImage(Image1)和一按钮,
将 Image1 的 AutoSize 设为 True.
连接按钮的以下点击事件:
点击按钮一下顺时针旋转 90 度。

90度:点一下。
180度:点两下。
270度:点击三下。

procedure TForm1.BitBtn1Click(Sender: TObject);
var
b:tImage;
i,j:Integer;
begin
with image1.Picture.Bitmap do
begin
b:=TImage.Create(nil);
b.Picture.Bitmap.assign(image1.Picture.Bitmap);
b.height:=Width;
b.Width:=Height;
for i:=0 to b.Height-1 do
for j := 0 to B.width -1 do
b.Canvas.Pixels[i,j]:=Canvas.Pixels[j,b.Height-1-i];
Assign(b.Picture.Bitmap);
end;
b.free;
end;
 
多人接受答案了。
 
顶部