PDA

View Full Version : پردازش تصویر



SlowCode
سه شنبه 16 آبان 1391, 00:39 صبح
سلام
من دلفی رو تازه شروع کردم و میخواستم بدونم چطوری یه تصویر رو در جهت های vertical و horizontal چرخش بدم؟ یا مثلا 90 درجه به چپ؟
نمیخوام پیکسل ها رو یکی یکی بخونم و برعکس کنم، میخوام بدونم آیا همچین توابعی هست؟ چون سرعت برنامه برام خیلی مهمه.

با تشکر

BORHAN TEC
سه شنبه 16 آبان 1391, 01:28 صبح
سلام

از اونجایی که سرعت براتون خیلی مهمه شما باید از پردازنده کارت گرافیک برای این قبیل کارها استفاده کنید. استفاده از پردازنده کارت گرافیک به صورت مستقیم دشوار است و از این نظر شما مجبور هستید که از کلاسهای آماده برای این منظور استفاده کنید. ابزارهای مختلفی برای این کار وجود دارد که شما می توانید از آنها استفاده کنید. یکی از این ابزارها ImageEn هست.
البته اگر شما از نسخه های جدید دلفی(یعنی نسخه XE2 و XE3) استفاده می کنید به راحتی می توانید این کار را در FireMonkey انجام دهید. این هم نمونه ای برای FireMonkey:
Image1.RotationAngle := 90;
حال اگر از VCL استفاده می کنید برای استفاده از پردازنده کارت گرافیک می توانید از GDI+ ، OpenGL و یا همون ImagEn و ... استفاده کنید و اگر هم نمی خواستید از پردازنده GPU استفاده کنید می توانید از تابعی مثل این استفاده کنید:
procedure FlipPngVertical(filename: String);
var
PNG, PNG2: TPNGObject;
X, Y: Integer;
IsAlpha: Boolean;
begin
PNG := TPNGObject.Create;
PNG2 := TPNGObject.Create;
PNG.LoadFromFile(filename);
PNG2.Assign(PNG);
IsAlpha := PNG2.Header.ColorType in [COLOR_GRAYSCALEALPHA, COLOR_RGBALPHA];
for Y := 0 to PNG2.Height - 1 do
begin
if IsAlpha then
CopyMemory(PNG2.AlphaScanline[PNG2.Height - Y - 1],
PNG.AlphaScanline[Y], PNG2.Width);
for X := 0 to PNG2.Width - 1 do
PNG2.Pixels[X, Y] := PNG.Pixels[X, PNG2.Height - Y - 1];
end;
PNG2.SaveToFile(filename);
PNG2.Free;
PNG.Free;
end;
در مورد نحوه استفاده از OpenGL در دلفی هم پیشنهاد می کنم که برای راهنمایی از جناب محمد قدیانی با نام کاربری Ananas (http://barnamenevis.org/member.php?233990-Ananas) کمک بگیرید.

موفق باشید...

Felony
سه شنبه 16 آبان 1391, 06:26 صبح
چون سرعت برنامه برام خیلی مهمه.
در مورد تابع ScanLine کلاس TBitmap تحقیق کنید . { http://edn.embarcadero.com/article/22939 و یک نمونه http://www.efg2.com/Lab/ImageProcessing/RotateScanline.htm }

Ananas
سه شنبه 16 آبان 1391, 14:50 عصر
سلام.
OpenGL برای فقط یک دوران ساده ارزش استفاده نداره چون یک مقدار مشکل هست و به نظرم GDI برای این کار خوبه.
تو GDI می تونید با تابع PlgBlt تصویر رو بچرخونید.
برای استفاده ازین تابع باید اشاره گری به سه تا مختصه دو تایی (میتونیم از TPoint استفاده کنیم) به تابع ارسال کنید. این ها سه نقطه در تصویر مقصد هستند که متناظرند با سه نقطه در تصویر منبع. نقطه ی بالا سمت چپ و نقطه ی بالا سمت راست و نقطه ی پایین سمت چپ در تصویر منبع. نقطه ی آخر رو خودش متناسب با این سه نقطه پیدا میکنه.
مثال تابع PlgBlt برای 90 درجه :


var p : array[0..2] of TPoint;
begin
p[0] := Point(0, b.Width);
p[1] := Point(0, 0);
p[2] := Point(b.Height, b.Width);
PlgBlt(b90.Canvas.Handle, p, b.Canvas.Handle, 0, 0, b.Width, b.Height, 0, 0, 0);
end;

مثال از یک تابع که با گرفتن زاویه و مرکز دوران و یک offset برای جابجایی و ضریب های بزرگ نمایی تصویر رو انتقال میده :

function RotateBitmap(
DstB, SrcB : TBitmap;
AngleDeg : Single;
RotCentX : Single = 0;
RotCentY : Single = 0;
OffsetX : Integer = 0;
OffsetY : Integer = 0;
ScaleX : Single = 1.0;
ScaleY : Single = 1.0):Boolean;
const PI_DIV_180 = 0.01745329251994329576923690768489;
var
p : array[0..2] of TPoint;
cos_, sin_, x, y : Extended;
begin
if ((DstB = nil) or (SrcB = nil)) then
begin
Result := False;
Exit;
end;
SineCosine(-AngleDeg * PI_DIV_180, sin_, cos_);
(*
rotation 2D
x = x` cos - y` sin
y = x` sin + y` cos
*)
x := ScaleX * -RotCentX;
y := ScaleY * -RotCentY;
p[0] := Point( Trunc(cos_ * x - sin_ * y + RotCentX) + OffsetX,
Trunc(sin_ * x + cos_ * y + RotCentY) + OffsetY);
x := ScaleX * (SrcB.Width - RotCentX);
y := ScaleY * -RotCentY;
p[1] := Point( Trunc(cos_ * x - sin_ * y + RotCentX) + OffsetX,
Trunc(sin_ * x + cos_ * y + RotCentY) + OffsetY);
x := ScaleX * -RotCentX;
y := ScaleY * (SrcB.Height - RotCentY);
p[2] := Point( Trunc(cos_ * x - sin_ * y + RotCentX) + OffsetX,
Trunc(sin_ * x + cos_ * y + RotCentY) + OffsetY);
Result := PlgBlt(
DstB.Canvas.Handle,
p,
SrcB.Canvas.Handle,
0, 0, SrcB.Width, SrcB.Height,
0, 0, 0);
end;

مثال استفاده با زاویه ی دلخواه و مرکز تصویر اول با بزرگنمایی افقی 1 و عمودی 2 :

RotateBitmap(b2, b1, angle, b.Width / 2.0, b.Height / 2.0, 0.0, 0.0, 1.0, 2.0);

مثال استفاده برای مشخصا 90 درجه :

RotateBitmap(b90, b, 90, 0.0, 0.0, 0, b.Width, 1.0, 1.0);

SlowCode
چهارشنبه 17 آبان 1391, 07:05 صبح
ممنون از دوستان که جواب دادن.

Image1.RotationAngle := 90;
آقای عشایری این کد کار نکرد، من از نسخه Rad studio 2010 استفاده میکنم نمیدونم به چه ورژنی XE میگن.
این کد شما رو نوشتم ولی زیر TPNGObject و خیلی از متدهای دیگه خط قرمز کشید.خطای Undefined identifier میده!

ممنون اقای ananas ولی به تابع RotateBitmap خطای Undeclared identifierمیده! تابع SineCosine رو هم قبول نمیکنه.
ببخشید اینا رو میپرسم خوب تازه کارم دیگه.:خجالت:

ممنون

mbshareat
چهارشنبه 17 آبان 1391, 09:10 صبح
سلام
این همه دردسر نداره!
این هم کدش نیاز به نسخه بالای دلفی هم نداره و به اندازه کافی هم سریعه:

procedure FlipDown(src: Tbitmap);
var
dest:tbitmap;
w,h,x,y:integer;
pd,ps:pbytearray;
begin
w:=src.width;
h:=src.height;
dest:=tbitmap.create;
dest.width:=w;
dest.height:=h;
dest.pixelformat:=pf24bit;
src.pixelformat:=pf24bit;
for y:=0 to h-1 do begin
pd:=dest.scanline[y];
ps:=src.scanline[h-1-y];
for x:=0 to w-1 do begin
pd[x*3]:=ps[x*3];
pd[x*3+1]:=ps[x*3+1];
pd[x*3+2]:=ps[x*3+2];
end;
end;
src.assign(dest);
dest.free;
end;

procedure FlipRight(src: Tbitmap);
var
dest:tbitmap;
w,h,x,y:integer;
pd,ps:pbytearray;
begin
w:=src.width;
h:=src.height;
dest:=tbitmap.create;
dest.width:=w;
dest.height:=h;
dest.pixelformat:=pf24bit;
src.pixelformat:=pf24bit;
for y:=0 to h-1 do begin
pd:=dest.scanline[y];
ps:=src.scanline[y];
for x:=0 to w-1 do begin
pd[x*3]:=ps[(w-1-x)*3];
pd[x*3+1]:=ps[(w-1-x)*3+1];
pd[x*3+2]:=ps[(w-1-x)*3+2];
end;
end;
src.assign(dest);
dest.free;
end;

برای چرخش بیت مپ هم به این آدرس (http://delphi.about.com/cs/adptips2001/a/bltip1201_4.htm) برید.
اگر کلکسیونی از پروسیجرها برای کارهای گرافیکی روی بیت مپ میخواید هم این رو بردارید: افکتهای گرافیکی متنوع (http://jansfreeware.com/jandraw.zip)

Felony
چهارشنبه 17 آبان 1391, 10:06 صبح
این همه دردسر نداره!
این هم کدش نیاز به نسخه بالای دلفی هم نداره و به اندازه کافی هم سریعه:
پست شماره 3 هم همین هست !

Ananas
چهارشنبه 17 آبان 1391, 18:29 عصر
ممنون از دوستان که جواب دادن.
Image1.RotationAngle := 90;


آقای عشایری این کد کار نکرد، من از نسخه Rad studio 2010 استفاده میکنم نمیدونم به چه ورژنی XE میگن.
این کد شما رو نوشتم ولی زیر TPNGObject و خیلی از متدهای دیگه خط قرمز کشید.خطای Undefined identifier میده!
ورژنی که استفاده میکنید FireMonkey نداره و قدیمیتره و RotatioAngle قابلیتی هست که کنترل های فایر مانکی دارن (چون کلا سیستم نمایش فایر مانکی فرق داره) و کنترل های vcl ندارن.

ممنون اقای ananas ولی به تابع RotateBitmap خطای Undeclared identifierمیده! تابع SineCosine رو هم قبول نمیکنه.
ببخشید اینا رو میپرسم خوب تازه کارم دیگه.:خجالت:

خواهش میکنم. Undeclared identifier که خوب لابد تابع RotateBitmap رو تو برنامتون کپی نکردید. تابع RotateBitmap رو تو پست قبلیم تعریف کردم و کدش رو نوشتم که شما باید قبل از استفاده از اون (در خطوط بالاتر برنامه) تو کد خودتون کپی کنید. تابع SineCosine هم احتمالا تو ورژن جدید اضافه شده و به جای اون می تونید از تابع SinCos تو یونیت Math استفاده کنید(یادتون نره تو قسمت uses برنامه Math رو اضافه کنید). اگه اون رو هم نتونستید تک تک sin و cos رو حساب کنید مثال :

x := -AngleDeg * PI_DIV_180;
cos_ := Cos(x);
sin_ := Sin(x);

مهم اینه که ما سینوس و کسینوس رو داشته باشیم.


این همه دردسر نداره!
این هم کدش نیاز به نسخه بالای دلفی هم نداره و به اندازه کافی هم سریعه:

بله خوب اگه بخوایم 180 درجه بچرخونیم یا افقی یا عمودی معکوس کنیم درسته ولی در این صورت باز هم نیازی به scanline نیست با stretchdraw هم خیلی سریع انجام میشه ولی برای چرخش 90 درجه مسئله خیلی متفاوته و برای زاویه دلخواه هم که محاسبات زیاد لازم داره (پیکسل به پیکسل) پس برای دوران همون PlgBlt روش سریع خوبی هست. در واقع شما توابع Flip کردن رو نوشتید ولی Rotate خواسته شده.

mbshareat
چهارشنبه 17 آبان 1391, 22:38 عصر
نه من حوصله نداشتم چیزی بنویسم فقط کپی کردم.
اینجا رو هم توجه کنین:

برای چرخش بیت مپ هم به این آدرس برید.

Ananas
چهارشنبه 17 آبان 1391, 23:15 عصر
نه من حوصله نداشتم چیزی بنویسم فقط کپی کردم.
اینجا رو هم توجه کنین:
بله حضرت آقا:چشمک:، ارادت داریم خدمتتون، بنده با حوصله نوشته ی شما رو خوندم ولی آدرس رو دفعه ی قبل نتونستم باز کنم ولی الان دوباره دیدم. تابع PlgBlt خیلی سریع تر از حلقه ی نرم افزاری برای هر پیکسل عمل می کنه. مقایسه کنید. البته فرمول نرم افزاریش ارزش منده و شاید لازم بشه ازش استفاده کنیم به دلیل کنترل کیفیت خروجی و یا اعمال تغییرات بیشتر ولی برای چرخش معمولی و سریع فکر میکنم PlgBlt بهتر باشه.

mbshareat
پنج شنبه 18 آبان 1391, 10:08 صبح
ممنون. فکر می کردم PlgBlt مربوط به نسخه های بالا باشه!
آیا با PlgBlt میشه تصویر رو چرخوند؟ ممکنه یه مثال بزنید؟

Ananas
پنج شنبه 18 آبان 1391, 12:14 عصر
ممنون. فکر می کردم PlgBlt مربوط به نسخه های بالا باشه!

نه اصلا مخصوص دلفی نیست، تابع GDI ویندوز هست.


آیا با PlgBlt میشه تصویر رو چرخوند؟ ممکنه یه مثال بزنید؟

بله. پست اول بنده.

SlowCode
پنج شنبه 18 آبان 1391, 14:48 عصر
خوب الان تابع و sincos رو میشناسه.
من از TImage استفاده میکنم ولی وقتی کد رو اجرا میکنه خطای زیر رو میده:
[DCC Error] Unit1.pas(122): E2010 Incompatible types: 'TBitmap' and 'TImage'
بعد اومدم کد زیر رو نوشتم ولی خروجیش یه تصویر خالیه، حجمش 0 byte هست.
procedure TForm1.Button3Click(Sender: TObject);
var
b1,b2:TBitmap;
begin
b1:=TBitmap.Create;
b2:=TBitmap.Create;
b1.LoadFromFile('C:\1.bmp');
RotateBitmap(b2, b1, 90, 0.0, 0.0, 0, b1.Width, 1.0, 1.0);
b2.SaveToFile('C:\2.bmp');
end;

چرا دلفی اینقدر اذیت میکنه!!!

Ananas
پنج شنبه 18 آبان 1391, 17:53 عصر
چون که تغییر سایز اولیه ی TBitmap (در اینجا b2) به عهده ی شما هست و تابع RotateBitmap هیچ تغییر سایزی روی بیتمپ اعمال نمیکنه و شما باید قبل از استفاده از RotateBitmap کدی مشابه کد زیر رو استفاده کنید :
b2.SetSize(b1.Height, b1.Width);

SlowCode
پنج شنبه 18 آبان 1391, 18:06 عصر
خیلی خیلی ممنون.
حالا چطوری تصویر کنترل Image رو داخل خودش بچرخونم. بعدش هم این فقط روی bmp جواب میده، نمیشه تابعی برای همه فرمت ها نوشت؟
و آیا میشه از تصویرمون خروجی tif گرفت؟

BORHAN TEC
جمعه 19 آبان 1391, 11:26 صبح
سلام
برای خروجی گرفتن به صورت Tiff میتونید از کلاس TWICImage استفاده کنید. این کلاس در دلفی 2010 و نسخه های بعد از آن قرار دارد و برای بهره گیری از آن باید ویندوز شما حداقل XP SP2 باشد و حداقل دات نت 2 بر روی آن نصب شده باشد.

Ananas
جمعه 19 آبان 1391, 23:56 عصر
حالا چطوری تصویر کنترل Image رو داخل خودش بچرخونم.مثال چرخش 90 درجه :

function Rot90ImagePicture(img : TImage):Boolean;
var
b, bRot : TBitmap;
p : array[0..5] of Integer;
begin
if (img = nil) then
begin
Result := False;
Exit;
end;
b := TBitmap.Create;
b.Assign(img.Picture.Graphic);
bRot := TBitmap.Create;
bRot.SetSize(b.Height, b.Width);
p[0] := 0;
p[1] := b.Width;
p[2] := 0;
p[3] := 0;
p[4] := b.Height;
p[5] := b.Width;
Result := PlgBlt(
bRot.Canvas.Handle, p,
b.Canvas.Handle,
0, 0, b.Width, b.Height,
0, 0, 0);
img.Picture.Graphic.Assign(bRot);
p[0] := img.Width;
img.Width := img.Height;
img.Height := p[0];
b.Free;
bRot.Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Rot90ImagePicture(Image1);
end;


بعدش هم این فقط روی bmp جواب میده، نمیشه تابعی برای همه فرمت ها نوشت؟به نقل از هلپ دلفی :
A TImage may hold a bitmap, icon, PNG, GIF or JPEG image.