Sonsuz Bitmap [kapalı]


10

Çalışma zamanı sırasında bir bitmap oluşturmak istiyorum. Bitmap tüm taraflarda ölçeklenebilir olmalı ve piksel erişimi sessiz verimli olmalıdır.

Bazı illüstrasyonlar http://img546.imageshack.us/img546/4995/maptm.jpg

Resimde gösterilen komutlar arasında ve sonrasında Map.setPixel () ve Map.getPixel () bitmap'e kaydedilmiş verileri ayarlamalı / döndürmelidir.

Nasıl bir uygulama setPixel () / getPixel mümkün olduğunca hızlı olacak şekilde bellek ayırmak nasıl bir kavram beklemiyorum.


Gri alan her zaman (0,0) noktası mıdır, yoksa başka bir koordinat olabilir mi?
Falcon

2
Daha fazla ayrıntı gerekiyor. Ayarlanan pikseller seyrek olacak mı? Ve hızlı yapmak için extendXyöntemleri ne kadar yavaş yapmak istersiniz ? setPixelgetPixel
Peter Taylor

1
Bitmap belleğe sığmayacak kadar büyük olacak mı? Hızlı işlemler neler olmalıdır - expand, setPixel (), getPixel ()?

1
@Falcon: Hayır, yeterli zaman var
SecStone

3
Bu soruyu konu dışı olarak kapatmak için oy kullanıyorum çünkü soru büyük ölçüde silinmiş olan resme dahil. Şu anda yazıldığı gibi, pek mantıklı değil.

Yanıtlar:


9

Eğer extend()operasyon oldukça hızlı olması gerekiyor, bir dörtlü ağaç bir uygun olabilir; aslında açık genişletme işlemleri bile gerektirmez. Kuşkusuz, tek tek piksele rastgele erişim için en iyi performansı vermeyecektir, ancak yorumunuz birincil işleminizin, dörtlü bir ağacın çok hızlı, belki de neredeyse matris tabanlı bir uygulama kadar hızlı (ve daha hızlı yapabileceği) pikseller üzerinde yinelediğini söylüyor yineleme her zaman aynı şekilde gerçekleşmezse, matris yerleştirilir).

Gereksinimleriniz aslında Hayat Oyunu gibi bir hücresel otomat uygulamaya çalışıyormuşsunuz gibi geliyor. Yaşam Oyununu sonsuz bir ızgarada uygulamanın son derece yüksek performanslı bir yolu olan Hashlife'a bakmak isteyebilirsiniz . Bir Quadtree'ye dayalı olduğunu, ancak oyun kurallarının bulunduğu yere göre bazı akıllı ek optimizasyonlar yaptığını unutmayın.


Bu fikir için teşekkürler! Bazı testler yapacağım ve sonuçları rapor edeceğim.
SecStone

2

@SecStone, genişletme işlemi için yeterli zaman bulunduğunu, bu nedenle pikselleri depolamanın en kolay ve en etkili yolunun, tek bir düz dizi veya iki boyutlu bir dizi kullanmak olduğunu ve piksellerin sabit zamanda erişilebileceğini söyledi.


4
Genişlemenin nasıl ele alınması gerektiğini düşündüğünüz konusunda iyi bir öneri yaparsanız, bunu oylarım.
Doc Brown

@Doc Brown: Yeterli zaman varsa diziyi kaydırın. Ya da belki bir noktadan diziye ve yığın dizine (sabit zamanda da çalışır) bir parça işlevi ve bir çevirmen işlevi ile bir şeyler yapabilirsiniz.
Falcon

2

Elle

Bellek çok seyrek bir kaynak değilse, daha büyük parçalar halinde çalışmayı düşünüyorum.
İşte bazı sahte kodlar.

class Chunk {
    Chunk new(int size) {...}
    void setPixel(int x, int y, int value) {...}
    int getPixel(int x, int y) {...}
}

class Grid {
    Map<int, Map<Chunk>> chunks;
    Grid new(int chunkSize) {...}
    void setPixel(int x, int y, int value) {
         getChunk(x,y).setPixel(x % chunkSize, y % chunkSize, value);//actually the modulo could be right in Chunk::setPixel and getPixel for more safety
    }
    int getPixel(int x, int y) { /*along the lines of setPixel*/ }
    private Chunk getChunk(int x, int y) {
         x /= chunkSize;
         y /= chunkSize;
         Map<Chunk> row = chunks.get(y);
         if (row == null) chunks.set(y, row = new Map<Chunk>());
         Chunk ret = row.get(x);
         if (ret == null) row.set(x, ret = new Chunk(chunkSize));
         return ret;
    }
}

Bu uygulama oldukça naif.
Birincisi, getPixel'de parçalar oluşturur (temel olarak, bu konum için herhangi bir parça tanımlanmadıysa, 0 ya da öylesine dönmek iyi olur). İkincisi, Harita'nın yeterince hızlı ve ölçeklenebilir bir uygulamasına sahip olduğunuz varsayımına dayanmaktadır. Bildiğim kadarıyla her iyi dilde bir tane var.

Ayrıca yığın boyutu ile oynamak zorunda kalacak. Yoğun bitmapler için büyük bir yığın boyutu iyidir, seyrek bitmapler için daha küçük bir yığın boyutu daha iyidir. Aslında çok seyrek olanlar için, 1'lik bir "yığın boyutu" en iyisidir, "yığınları" geçersiz kılar ve veri yapısını bir int piksel haritasının int haritasına indirir.

Satışa hazır

Başka bir çözüm, bazı grafik kütüphanelerine bakmak olabilir. Aslında bir 2D tamponu diğerine çekmekte oldukça iyidirler. Bu, yalnızca daha büyük bir tampon ayıracağınız ve orijinalin ilgili koordinatlara çekileceğini gösterir.

Genel bir strateji olarak: "Dinamik olarak büyüyen bir hafıza bloğuna" sahip olduğunuzda, bir kez kullanıldığında, birden fazla yer ayırmak iyi bir fikirdir. Bu oldukça bellek yoğun bir işlemdir ancak ayırma ve kopyalama maliyetlerini önemli ölçüde azaltır . Çoğu vektör uygulaması, aşıldığında boyutlarının iki katını ayırır. Bu nedenle, özellikle kullanıma hazır bir çözümle giderseniz, arabelleği yalnızca 1 piksel uzatmayın, çünkü yalnızca bir piksel istendi. Ayrılan bellek ucuz. Yeniden tahsis, kopyalama ve bırakma pahalıdır.


1

Sadece birkaç tavsiye:

  • Bunu, bazı integral türlerinden oluşan bir dizi (veya dizilerinden oluşan bir dizi) olarak uygularsanız, bitleri kopyalarken kaydırmaktan kaçınmak için muhtemelen yedekleme dizisini her seferinde bir kaç bit / piksel büyütmeniz gerekir. Aşağı tarafı, daha fazla alan kullanmanızdır, ancak bitmap büyüdükçe boşa giden alan oranı düşer.

  • Harita tabanlı bir veri yapısı kullanırsanız, getPixelve setPixelçağrılarının x, y koordinat argümanlarını yeniden konumlandırarak bitmap'i büyütme sorununu kolaylaştırabilirsiniz .

  • Harita tabanlı bir veri yapısı kullanıyorsanız, yalnızca "olanlar" için harita girişlerine ihtiyacınız vardır. "Sıfırlar" bir girişin olmamasıyla belirtilebilir. Bu, özellikle bitmap çoğunlukla sıfırsa, önemli miktarda alan tasarrufu sağlar.

  • Harita Haritası kullanmanıza gerek yoktur. Bir intx, y çiftini tek olarak kodlayabilirsiniz long. Bir dizi diziyi bir diziye eşlemek için benzer bir işlem kullanılabilir.


Son olarak, 3 şeyi dengelemeniz gerekir:

  1. performansı getPixelve setPixel,
  2. extend*operasyonların performansı ve
  3. alan kullanımı.

1

Daha karmaşık bir şey denemeden önce ve her şeyi bellekte tutamadıkça, işleri basit tutun ve koordinat sisteminizin kökeni hakkındaki bilgilerle birlikte iki boyutlu bir dizi kullanın. Genişletmek için, aynı stratejiyi kullanın, örneğin, C ++ std :: vector does: dizinizin gerçek boyutu ile dizinin kapasitesi arasında bir ayrım yapın ve sınıra her ulaşıldığında parçalardaki kapasiteyi genişletin. Buradaki "kapasite", aralıklar (from_x, to_x), (from_y, to_y) cinsinden tanımlanmalıdır.

Bu, zaman zaman belleğin tamamen yeniden tahsis edilmesini gerektirebilir, ancak bu çok sık gerçekleşmediği sürece, amacınız için yeterince hızlı olabilir (aslında, bunu denemelisiniz / profilinizi oluşturmanız gerekir).


1

Piksel erişimi yapmanın en hızlı yolu, tek tek adreslenebilen piksellerden oluşan iki boyutlu bir dizidir.

Uzantılar için, her seferinde yeniden tahsis eden ve kopyalayan basit bir uygulama ile başlayın (çünkü yine de bu koda ihtiyacınız olacaktır). Profil oluşturma, çok fazla zaman harcadığınızı göstermezse, daha da hassaslaştırmanıza gerek yoktur.

Profil oluşturma, yeniden ayırma sayısını azaltma gereğini ortaya çıkarıyorsa ve bellek kısıtlaması yapmıyorsanız, her yönde yüzde olarak aşırı ayırmayı ve başlangıç ​​noktasına kadar bir uzaklık depolamayı düşünün. (Örneğin, 1x1'de yeni bir bitmap başlatır ve bunu tutmak için bir 9x9 dizisi ayırırsanız, ilk xve yofsetler olacaktır 4.) Buradaki dengeleme, ofseti uygulamak için piksel erişimi sırasında fazladan matematik yapmak zorunda.

Uzantılar gerçekten pahalı görünüyorsa, bunlardan birini veya her ikisini de deneyebilirsiniz:

  • Dikey ve yatay uzantıları farklı kullanın. Bir diziyi herhangi bir yönde dikey olarak genişletmek, yeni bir blok tahsis etmek ve mevcut dizinin tamamını tek bir kopyasını yeni bellekteki uygun ofsete yapmak suretiyle gerçekleştirilebilir. Bunu, mevcut veriler yeni blokta bitişik olmadığından, her satırda bir kez yapmanız gereken yatay uzantılarla karşılaştırın.

  • En sık uzama miktarını ve yönünü takip edin. Herhangi bir uzantı için yeniden tahsis etme ve kopyalama yapma olasılığını azaltacak yeni bir boyut ve ofset seçmek için bu bilgileri kullanın.

Şahsen, piksel erişim-uzantı oranı düşük olmadığı sürece bunlardan birine ihtiyacınız olacağından şüpheliyim.


1
  • Sabit boyutlu döşeme (örneğin, 256x256, ancak sonsuz sayıda karo ile)
  • Negatif piksel koordinatlarına izin veren bir bitmap sarmalayıcı sağlayın (görüntünün mevcut koordinat değerlerine referansları yeniden hesaplamak / senkronize etmek zorunda kalmadan dört yönde de genişletilebileceği izlenimini vermek için)
    • Bununla birlikte, gerçek bitmap sınıfı (paketleyicinin altında çalışma) yalnızca mutlak (negatif olmayan) koordinatları desteklemelidir.
  • Sarma makinesinin altında, bellek eşlemeli G / Ç kullanarak döşeme düzeyi (blok düzeyi) erişimi sağlayın
  • Tek bir pikseli tek seferde değiştiren Map.setPixel()ve Map.getPixel()değiştirenlerin yanı sıra , bir kerede bir piksel dikdörtgeni kopyalayan ve değiştiren yöntemler de sağlar . Bu, arayan kişinin erişebileceği bilgilere bağlı olarak arayan kişinin daha etkili erişim biçimini seçmesine olanak tanır.
    • Ticari kütüphaneler ayrıca güncelleme yöntemleri sağlar: bir piksel sırası, bir piksel sütunu, dağılım / toplama güncellemeleri ve tek adımda aritmetik / mantık karıştırıcı işlemleri (veri kopyalamayı en aza indirmek için).

(Komik cevapları oylamayalım ...)


0

En esnek ve belki de güvenilir uygulama, x koordinatı, y koordinatı ve bit değeri veren yapılara sahip bağlantılı bir listedir. Önce onu inşa edip çalışır hale getirirdim.

Ardından, çok yavaş ve / veya büyükse, hızlandırmanın olağan yollarını deneyin: dizi, matris, bitmap, sıkıştırma, önbellekleme, ters çevirme, sadece '1' değerlerini depolama, vb.

Yavaş ve doğru bir uygulama yapmak, buggy hızlı bir uygulamayı düzeltmekten daha hızlıdır. Ve 'hızlı' ikinci uygulamanızı test ederken karşılaştırmak için bir refernce standardına sahipsiniz.

Ve kim bilir, yavaş versiyonun yeterince hızlı olduğunu keşfedeceksiniz. Tüm yapı hafızaya uyduğu sürece, işler inanılmaz derecede hızlıdır.


3
-1: "hızlı yapmadan önce çalışmasını sağla", mümkün olan en kötü uygulama ile başlamak için iyi bir neden değildir. Ayrıca, temeldeki veri yapısı ile tamamen değişmesi gerekmeyecek pratikte hiçbir kod olmayacaktır, bu nedenle bu seviyede yineleme burada tamamen asinine bir öneridir.
Michael Borgwardt

Bu yüzden bir API'nız var. API, temeldeki uygulamayı gizler. SetValue (MyMatrix, X, Y, Value) ve GetValue (MyMatrix, X, Y), MyMatrix'in 1 veya 2 boyutlu bir dizi mi, yoksa bağlı bir liste mi, yoksa diskte mi, yoksa SQL tablosunda mı yoksa herhangi bir önbellekte mi saklandığını gizler. Arayanın yeniden derlemesi gerekebilir, ancak kodu değiştirmesi gerekmez.
Andy Canfield

0

Python'da şu uygulamayı öneriyorum:

class Map(dict): pass

Aşağıdaki avantajlara sahiptir:

  1. Aracılığıyla Al / Ayarla erişimi map[(1,2)]düşünülebilir O(1).
  2. Izgarayı açıkça genişletme ihtiyacı ortadan kalkar.
  3. Böcekler için çok az yer var.
  4. Gerekirse kolayca 3D'ye yükseltilir.

0

Aslında herhangi bir keyfi boyutta bir bitmap'e ihtiyacınız varsa - ve 1x1'den 1000000x1000000'e kadar bir şey kastediyorsanız ve talep üzerine genişletilebilir ... olası bir yol bir veritabanı kullanmaktır. İlk başta karşı sezgisel görünebilir, ancak gerçekten sahip olduğunuz bir depolama sorunu. Bir veritabanı tek tek piksellere erişmenize ve herhangi bir miktarda veriyi saklamanıza izin verir. Ben mutlaka bir SQL db, btw demek istemiyorum.

Amacınız için yeterince hızlı olacak mı? Bu bitmap ile ne yaptığınıza dair bir bağlam olmadığı için cevap veremiyorum. Ancak bu ekran görüntüsü içinse, tüm verileri değil, ekran kaydırılırken görüntülemek için genellikle yalnızca ek tarama satırlarını geri almanız gerektiğini düşünün.

Olduğu söyleniyor, yardım edemem ama yanlış bir şey olup olmadığını merak ediyorum. Bunun yerine, vektör tabanlı grafik kullanarak ve bellekteki bağımsız girişleri izleyip, yalnızca ekran için gereken kadar büyük bir bitmap oluşturmalısınız?


Belki bir OSGeo harita sunucusu.
rwong

0

İşte düzgün çalışması için adımlar:

  1. birlikte bitmap'i temsil eden bir nesne ağacı oluşturmak için bir arabirimden diğerine iletmeyi kullanın
  2. bitmap'in her "uzantısı" kendi belleğini ayıracak ve bunun için başka bir arayüz sağlayacaktır
  3. örnek uygulama:

    template<class T, class P>
    class ExtendBottom {
    public:
       ExtendBottom(T &t, int count) : t(t), count(count),k(t.XSize(), count) { }
       P &At(int x, int y) const { if (y<t.YSize()) return t.At(x,y); else return k.At(x, y-t.YSize()); }
       int XSize() const { return t.XSize(); }
       int YSize() const { return t.YSize()+count; }
    private:
       T &t;
       int count;
       MemoryBitmap k;
    };
    

Açıkçası gerçek uygulama için, XSize () ve YSize () işe yaramaz, ancak dizin numaralarını tutarlı tutmak için MinX (), MaxX (), MinY (), MaxY () veya buna benzer bir şeye ihtiyacınız olacaktır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.