Bitmap'in piksel ortalamasını almak

Delphi'de kod yazma ile ilgili sorularınızı bu foruma yazabilirsiniz.
Cevapla
Akin_Delphi
Üye
Mesajlar: 163
Kayıt: 06 Nis 2006 12:22

Bitmap'in piksel ortalamasını almak

Mesaj gönderen Akin_Delphi »

Sayın Arkadaşlar,

Bir bitmap'in piksellerinin ortalamasını almaya çalışıyorum. Amacım bitmapin aydınlık değerini tespit etmek (belki yanlış yoldan yapıyorumdur o da ayrı mesele).

Şimdi 640 x 480 piksellik bir bitmapi piksel piksel taradığımda maalesef CPU kullanımının % 50'leri geçtiğini gördüm.

Bunun üzerine ben de jedi'nin ManagedThread bileşen setini kullanmaya karar verdim. Amacım CPU kullanımını azaltmaktı. Ancak yine de CPU kullanımı miktarım çok fazla (yani aynı :oops: ).

Kullandığım prosedür (ki bu prosedür TJVMTThread componentinin OnExecute olay işleyicisidir) aşağıdaki şekilde:

Kod: Tümünü seç

var
xt, yt : integer;
SatirOrtalamalariArrayi,
GecerliSatirArrayi : Array of DWord;
begin
try
ABitmap.Canvas.Lock;
SetLength(SatirOrtalamalariArrayi,ABitmap.Height);
SetLength(GecerliSatirArrayi,ABitmap.Width);
  For yt:= 0 to ABitmap.Height-1 do
  begin
    For xt:= 0 to ABitmap.Width-1 do
    begin
    GecerliSatirArrayi[xt] := ABitmap.Canvas.Pixels[xt, yt];
    end;
  SatirOrtalamalariArrayi[yt] := WordMean(GecerliSatirArrayi);
  end;
ResimParlakligi := WordMean(SatirOrtalamalariArrayi);
finally
ABitmap.Canvas.Unlock;
MTThread.Terminate;
end;
Buradaki WordMean benim yazdığım Word değişken tipli elemanlar içeren bir Array'in ortalamasını alan bir fonksiyon:

Kod: Tümünü seç

function WordMean(WordAr: Array of DWord):DWord;
Var
I : Integer;
Toplam : LongInt;
begin
//burada dizi içerisindeki word değerlerin ortalamasını al
Toplam := 0;
try
  For I:= 0 to Length(WordAr)-1 do
  begin
  Toplam := Toplam + WordAr[I];
  end;
  If Toplam = 0 then
  begin
  Result := 0;
  end
  else
  begin
  Result := Round(Abs(Toplam) div Length(WordAr));
  end;
except
end;
//burada dizi içerisindeki word değerlerin ortalamasını al SON
end;
Şimdi sorum şu ki, bir Bitmap'in piksellerinin ortalamasını alan daha verimli bir algoritma ya da bileşen var mıdır :?: Ya da yaptığım şey tamamen saçma ya da yanlış mıdır?

Saygılarımla ve Şimdiden Teşekkürler.
Kullanıcı avatarı
bluekid
Kıdemli Üye
Mesajlar: 541
Kayıt: 11 Haz 2004 10:45
İletişim:

Mesaj gönderen bluekid »

fduman
Moderator
Mesajlar: 2749
Kayıt: 17 Ara 2004 12:02
Konum: Ankara

Mesaj gönderen fduman »

Birşeyi düzeltmek istiyorum. Thread kullanmakla işlemci yükü hafifletilemez. Thread'lerin amacı aynı anda bir çok işlemi birarada, diğerini bekletmeden yapmaktır. Dolayısıyla yine aynı CPU kullanım oranına erişmeniz sürpriz olmamış. Hatta thread'lerin çalışma mantığı dolayısıyla bir kısım işlemci gücünü kaybettiğinizi de söyleyebilirim.

Eğer 2 veya daha fazla işlemcili bir bilgisayarda çalışsaydınız ve algoritmayı da thread'ler paralel çalışabilecek şekilde oluştursaydınız, tabii ki daha kısa sürede işini bitirecekti. Ancak bence sorun Canvas.Pixels ile okumayı yapmanız. @bk'nın dediği gibi scanline daha iyi bir performans verebilir.

İstatistik dersinden hatırladığım kadarıyla, aydınlık değerini elde etmek için bütün pikselleri taramanız gerekmiyor. Pikseller arasından rastgele çekeceğiniz belirli sayıdaki örnek ile de istediğiniz değeri hesaplamanız mümkün olabilir.
Akin_Delphi
Üye
Mesajlar: 163
Kayıt: 06 Nis 2006 12:22

Mesaj gönderen Akin_Delphi »

Aslında ilk bu işlemi yapmaya çalıştığımda scanline'ı kullanmıştım. Ancak şimdi karşıma pointer çıktığında - söylemesi ayıp - ben alık alık bakmaya başlıyorum, çünkü tek tek elemanlara erişmek sorun kafamı karıştırıyor.


Birde taranan bitmap'in PixelFormat özelliği de çok sorun oluyor.

Örneğin eğer piksel formatı pf1bit ise başka işlem, pf24bit ise başka işlem yapmak gerekiyor. Bu durumda beni açıkçası zorluyor.

İlk yapmaya çalıştığımda, öncelikle taranan satırın uzunluğunu bulmakta başarısız olmuştum. :(

Neyse üzerinde çalışıp piksel formatına göre fonksiyon içerisinde ayrı dallandırmalar yapmaya çalışacağım. :evil:

@coderlord

Hocam bir şey merak ediyorum. Şimdi istastikten bahsetmişsiniz, hepsini tek tek karşılaştırmamak benim de işime gelir. Ancak % olarak ne kadarını karşılaştırmam gerekir? Bu konuda bir fikriniz var mı acaba?
Saygılarımla,
fduman
Moderator
Mesajlar: 2749
Kayıt: 17 Ara 2004 12:02
Konum: Ankara

Mesaj gönderen fduman »

Tabii ki ne kadar örnek çekerseniz, alacağınız değerin hassaslığı o kadar artacaktır. Ne kadar hassas bir değere ulaşmak istediğinize bağlı olarak buna siz karar vermelisiniz.
Kullanıcı avatarı
Opt2000
Üye
Mesajlar: 216
Kayıt: 09 Tem 2003 10:04

Mesaj gönderen Opt2000 »

Selam,

Eğer Scanline kullanırken PixelFormat pf1bit iken de scanline çalıştıracak olursan büyük ihtimalle kuracağın döngü hem seni çıldırtacak, hem de bahsedeceğim yöntemden daha yavaş olacaktır. Çünkü 1 bit resimlerde, 8 pixel bir byte içine yazılır ve iç içe iki döngü ile sürekli bitwise işlemleri yapman gerekecek. Tabii dıştaki döngünün üst sınırını da sen hesaplamak zorunda kalacaksın (Sanırım Width div 8 + 1 gibi bir şeydi, tam hatırlamıyorum)

Mesajı ilk yazarken verdiğin örneğin boyutu 640*480. Bu da günümüz bilgisayarları için gerçekten de büyük sayılmayacak bir boyut. Benim tavsiyem kodu 2 formata göre yaz.
1. pf8bit
2. pf24bit

Diğer bütün formatları bu ikisinden birisine çevir. pf32bit de buna dahil, çünkü zaten Delphi'nin resim formatları 32 bit dosyaları desteklemiyor. Bu yüzden burada yapacağız bir düşürme herhangi bir sorun yaratmayacak. Tabii orjinal resmi bozmamak için resmin bir kopyasını çıkarman gerekecek. Bunu da bellekte yapacağın için zaten yeterince hızlı olacaktır. Resim formatı pfDevice olanları da 24 bite çevir.

pf8bit ve pf24bit için örnek kodlar yanlış hatırlamıyorsam (bk'nın da dediği gibi) efg'nin sitesinde vardı. Oradan bakabilirsin.

Son olarak küçük bir de tavsiyede bulunayım. Eğer gerçekten de güçlü uygulamak yazmak istiyorsan, pointer kullanmayı en kısa zamanda öğrenmen çok iyi olacaktır. Çünkü pointer kullanımı aslında bir programcının, program yazarken elindeki en büyük güçlerden birisidir. Özellikle yüksek performans gerektiren uygulamalarda. Eğer yazdığın programlarda bugüne kadar ihtiyacın olmamışsa (ki farkında olmadan aslında sürekli kullanıyorsun) ileride C, C++ gibi dillere geçeceğin zaman daha az sıkıntı çekersin.

Kolay gelsin,
Bahadır Alkaç
Akin_Delphi
Üye
Mesajlar: 163
Kayıt: 06 Nis 2006 12:22

Mesaj gönderen Akin_Delphi »

Opt2000 yazdı:Selam,

Eğer Scanline kullanırken PixelFormat pf1bit iken de scanline çalıştıracak olursan büyük ihtimalle kuracağın döngü hem seni çıldırtacak, hem de bahsedeceğim yöntemden daha yavaş olacaktır. Çünkü 1 bit resimlerde, 8 pixel bir byte içine yazılır ve iç içe iki döngü ile sürekli bitwise işlemleri yapman gerekecek. Tabii dıştaki döngünün üst sınırını da sen hesaplamak zorunda kalacaksın (Sanırım Width div 8 + 1 gibi bir şeydi, tam hatırlamıyorum)

Mesajı ilk yazarken verdiğin örneğin boyutu 640*480. Bu da günümüz bilgisayarları için gerçekten de büyük sayılmayacak bir boyut. Benim tavsiyem kodu 2 formata göre yaz.
1. pf8bit
2. pf24bit

Diğer bütün formatları bu ikisinden birisine çevir. pf32bit de buna dahil, çünkü zaten Delphi'nin resim formatları 32 bit dosyaları desteklemiyor. Bu yüzden burada yapacağız bir düşürme herhangi bir sorun yaratmayacak. Tabii orjinal resmi bozmamak için resmin bir kopyasını çıkarman gerekecek. Bunu da bellekte yapacağın için zaten yeterince hızlı olacaktır. Resim formatı pfDevice olanları da 24 bite çevir.

pf8bit ve pf24bit için örnek kodlar yanlış hatırlamıyorsam (bk'nın da dediği gibi) efg'nin sitesinde vardı. Oradan bakabilirsin.
Öncelikle teşekkürler, scanline'ı kullanmadım ancak yaptığım optimizasyonu (yani CoderLord'un dediği istatistik yöntemini) kullanarak, şu anda uygulamanın toplam CPU kullanım oranı (kanalların çalışmasına ve durmasına bağlı olarak) % 8 ila % 13 arasında dalgalanıyor.

Kanalsız yaptığımdan bile daha iyi hale geldi. Verdiğiniz fikirler için teşekkürler.
Son olarak küçük bir de tavsiyede bulunayım. Eğer gerçekten de güçlü uygulamak yazmak istiyorsan, pointer kullanmayı en kısa zamanda öğrenmen çok iyi olacaktır. Çünkü pointer kullanımı aslında bir programcının, program yazarken elindeki en büyük güçlerden birisidir. Özellikle yüksek performans gerektiren uygulamalarda. Eğer yazdığın programlarda bugüne kadar ihtiyacın olmamışsa (ki farkında olmadan aslında sürekli kullanıyorsun) ileride C, C++ gibi dillere geçeceğin zaman daha az sıkıntı çekersin.

Kolay gelsin,
Bahadır Alkaç
Çok çok haklısınız, benim pointerla ilgili (bir amatör uygulama geliştirici olarak) sorunum şu, eğer bir şeyi hayalimde canlandıramıyorsam basitçe yapamıyorum. Ve yukarıda dediğim alık alık bakma olayı meydana geliyor :oops: . Belki Borland'ın Cpp Builder'ına bir göz atabilirim. Oldukça iyi olduğu söyleniyor. Yaptığım ufak tefek şeyleri bir de onda yapmayı deneyeyim bakalım.

Pointerlara referans yaparken ve dereferans yaparken kafam karışıyor. Çünkü pointer verinin kendisi değil de adresi olduğundan dolayı ona nasıl gönderme yapacağımı ve ulaştığım verinin tam olarak neyi temsil ettiğini ve de en önemlisi verinin nerede bittiği konularını hayalimde canlandıramıyorum.

Şimdi (diğer bütün pointerlar gibi) bu PByteArray dedikleri değişkenin belirli bir uzunluğu yok. Dolayısıyla tarama yaparken (yani adresi tek tek artırırken), nereden başlayacağımı biliyorum ancak nerede duracağımı anlayamıyorum.

Ayrıca OutputDebugString prosedürünü çok kullanırım (ne yaptığımı görmek için). Pointer olduğunda pointer'ın sakladığı bilgiyi maalesef gösteremiyorum. Bu da işlediğim veriyi görmemi engelliyor.
Yine de herkese verdiği yanıttan dolayı teşekkürler.
Saygılarımla
Akin_Delphi
Üye
Mesajlar: 163
Kayıt: 06 Nis 2006 12:22

Mesaj gönderen Akin_Delphi »

Arkadaşlar,

Sizinle paylaşmak istediğim bir konu var: JVManagedThread setini kullanarak CPU kullanımını (istatistik yönteminin yardımıyla) azalttığımı belirtmiştim.

Ancak belki benim acemiliğimden kaynaklanıyor olabilir ancak JVManagedThread seti korkunç bellek sızıntısına neden oluyor. (Örneğim 10 dakika gibi bir sürede uygulamanın kullandığı bellek miktarı 10 K'dan 25 - 30 K'ya çıkıyor :cry: ) Belki birileri benim yazdıklarımı görüp kullanmaya kalkmasın diye uyarayım dedim.

Bu nedenle kendi kanallarımı yazarak sorunu oldukça çözebildim.

Şimdi iki konuda yardımınızı isteyeceğim: Şimdi sayın CoderLord'un verdiği tavsiyeyle istatistik yöntemini kullandım. Ancak şimdi burada kafama takılan bir konu var. Rasgele rakamları (yani satır numaralarını) her kanal oluşturulduğunda Randomize ve Random kullanarak elde ediyorum. Dolayısıyla her kanalın çalıştığında aydınlık değerini hesaplamak için değişik değişik rasgele rakamlar kullanılmış oluyor.

Şimdi sormak istediğim şudur ki: Acaba bir kere (yani uygulamanın ilk açılışında) komple rasgele rakam seti mi kullanayım, yoksa her kanal içerisinde ayrı ayrı rasgele satırları taramak mı daha iyi istatisksel açıdan?

Diğer bir konu ise optimizasyon ve bellek sızıntılarını önlemekle ilgili. Bunun için MemProof diye bir uygulama var onu kullanıyorum.

Uygulamayı delphi içerisinde build etmeden önce Project Options üzerindeki Compiler kartındaki belirli seçenekleri (yani stack frames, use debug DCUs) açıp belirli seçenekleri (yani Optimization) kapatıyorsunuz, Linker kartında da include TD32 debug info seçeneğini açıyorsunuz. Daha sonra build edip MemProof içerisinden çalıştırıyorsunuz. (Memproof'da da search directories komutunu kullanıp hem delphi ve delphinin kullandığı unitlerin yolunu hem de uygulamanızın kaynak kodunun bulunduğu yolu da girmeniz gerekmekte.)

Şimdi bu ücretsiz uygulamayı kullanarak bellek sızıntılarını minimuma indirdim. Ancak sormak istediğim şudur ki, MemProof'la tecrübesi olan var mıdır?

Yani karıştıra karıştıra bir şeyler yapabildim fakat çok verimsiz bir şekilde yapmış oldum. Bu süreç neredeyse üç günümü aldı (Tabi bu süreye JVManagedThread kullanmaktan vazgeçip, kendi kanallarımı oluşturmaya karar vermem ve bu dönüşümü gerçekleştirmem dahil).

Saygılarımla,
Cevapla