Doku paketleme algoritması


52

İyi bir doku paketleme algoritması nedir? Teknik olarak, bin ambalaj olan NP-zor bir sezgisel Gerçekten peşinde olduğumu budur yani.


Bunu uv haritalarını optimize etmek için kullandığınızı varsayıyordum, ancak uygulamanın ne olduğunu merak ediyorum.
Jonathan Fischoff

ftgles, yazı tiplerini oluşturmak için opengl ve freetype kullanan bir kütüphanedir. Ancak, her glif kendi dokusunda depolanır. Onları tek bir dokuya koymak istiyorum.
deft_code

Yanıtlar:


58

Daha iyi bir doku paketleme algoritmasıyla birkaç ay çalıştım.

Başladığımız algoritma basitti. Tüm girdi öğelerini topla. Bunları tüketilen, büyükten küçüğe toplam piksel sayısına göre sıralayın. Bunları tek bir tarama sırasına göre sıralayın, sadece üst piksel pikselinden üst piksel pikseline kadar test edin, bir çizgiyi aşağı doğru hareket ettirin ve her başarılı yerleştirme işleminden sonra tekrar üst piksel pikseline sıfırlayarak tekrarlayın.

Bunun için bir genişlik kodlamanız veya başka bir sezgisel buluşma bulmanız gerekir. Kareliği korumak için algoritmamız 128'de başlayacak, daha sonra genişliğinden daha derin olmayan bir sonuç ortaya çıkıncaya kadar 128'ler artacaktır.

Böylece bu algoritmaya sahiptik ve onu geliştirmeye karar verdim. Bir sürü tuhaf sezgisellik denedim - birbirine uyan nesneler bulmaya çalıştım, bir miktar istenen uzay paketleme özelliklerine ağırlık vererek, döndürerek ve çevirerek yaptım. Bütün çalışmalarımdan sonra, tam anlamıyla üç aylık çalışma süresinde% 3 yer tasarrufu sağladım.

Evet. % 3.

Sıkıştırma yordamını çalıştırdıktan sonra, aslında daha da büyük oldu (hala açıklayamam), bu yüzden her şeyi attık ve eski algoritmaya geri döndük.

Öğeleri sıralayın, tarama sırasına göre dokuda sıkışın. İşte algoritmanız. Kodlaması kolaydır, çalıştırması hızlıdır ve inanılmaz miktarda çalışma yapmadan çok daha iyi olamayacaksınız. Şirketiniz en az 50 kişi büyük ve muhtemelen daha fazla değilse, bu işe değmez .

alt metin

Ve bir yan not olarak, bu algoritmayı (sabit genişlik 512 piksel) tam anlamıyla tam olarak yaptığınız aynı uygulama için uyguladım. Maden, glifler arasındaki fazladan boşluğu da hesaba katan Valve'ın mesafe-alan tabanlı metin işleme algoritmasını kullandığı için bulanık görünüyor . Açıkçası, geride çok fazla boş alan kalmadı ve işleri açık noktalara sıkıştırmak iyi bir iş çıkardı.

Bunun için tüm kod BSD lisanslıdır ve github'da mevcuttur .


Dokunuza baktım ve kendime "Doku paketleyicimizin bundan biraz daha iyisini yaptığına eminim" dedim. Sonra gittim ve baktım ve bir süre önce kırdığımı fark ettim ve fark etmedim (çünkü bir kez çalışıyor, çıktı dokularına kim bakıyor?) ... Gönderdiğiniz için teşekkürler - bulamazdı Aksi takdirde :) (bir kez düzelttim, çok benzer görünüyor - belki daha iyi bir gölge, ama tam olarak söylemek zor. "iyi" muhtemelen en güvenli açıklamadır).
JasonD

@JasonD, daha iyi çıktı alırsa, algoritmanızın ne yaptığını bilmek isterim :) Farklı bir şekilde kabaca eşdeğer çıktılar alsa bile.
ZorbaTHut

1
Algo açıklaması için + kabul başarısız + kaynak kodu. Harika yazı.
Calvin1602

1
Sıkıştırmadan sonra büyümesinin nedeni muhtemelen sıkıştırma algoritmasından kaynaklanmaktadır. Sıkıştırma çoğu zaman karma ve ikili modelleri bulmaya dayandığından, algoritma yeteri kadar modeli tanımlayabiliyorsa, büyüklüğünün genişlemesine neden olabilecek bir demet oluşturur. Bunu bir dosyayı tekrar tekrar açmak için test etmek için harika bir yol ve sonunda desen eksikliği nedeniyle yeniden büyüyecek.
Hanna

1
ZorbaTHut'un paketleme kodunun (font_baker.cpp) en son sürümünü aramak için burada bulabilirsiniz: github.com/zorbathut/glorp/blob/…
mems

20

Andrea Lodi'nin Doktora Tezi, İki Boyutlu Çöp Paketleme ve Atama Problemleri için Algoritmalara sahiptir .
Tez, bu sorunların daha zorlu biçimlerinin bazılarını ele alıyor. Neyse ki, doku ambalajı en kolay versiyondur. Bulduğu en iyi algoritma, Perching Perimetre olarak adlandırıldı .

Sayfa 52'den alıntı yapmak için:

Dokunma Çevresi (TPRF) olarak adlandırılan algoritma, öğeleri azalan olmayan alana göre sıralayarak (bağları azalan olmayan min {wj, hj} değerleriyle kırarak) ve yatay olarak yönlendirerek başlar. Optimal çözelti değeri üzerindeki daha düşük bir L bağlı hesaplanır ve L boş kutular başlatılır. (Önceki bölümde tanımlanan L0 sürekli alt sınır açıkça 2BP | R | F için de geçerlidir; Dell'Amico, Martello ve Vigo [56] tarafından daha iyi sınırlar önerilmiştir.) Mevcut bir kutuda veya yenisini başlatarak. Bir kutuya konulan ilk madde her zaman sol alt köşeye yerleştirilir. Her takip eden öğe normal bir pozisyonda paketlenir (bakınız Christo and des ve Whitlock [41]), yani,
Kutunun ve paketleme pozisyonunun seçimi, kutuya ve önceden paketlenmiş diğer maddelere değen madde çevresinin yüzdesi olarak belirlenen bir puanı değerlendirerek yapılır. Bu strateji, paketlenmiş öğelerin daha küçük yerleşimler için kullanımı zor olabilecek küçük alanları “hapsetmediği” kalıpları desteklemektedir. Her aday paketleme pozisyonu için puan, iki madde oryantasyonu için (her ikisi de uygunsa) iki kez değerlendirilir ve en yüksek değer seçilir. Paketlenmiş maksimum alana sahip çöp kutusu seçilerek puan bağları kopar. Genel algoritma aşağıdaki gibidir.

touching_perimeter:
  sort the items by nonincreaseing w,h values, and horizontally orient them;
  comment: Phase 1;
  compute a lower bound L on the optimal solution value, and open L empty bins;
  comment: Phase 2;
  for j := 1 to n do
     score := 0;
     for each normal packing position in an open bin do
        let score1 and score2 be scores with tow orientations;
        score := max{score,score1,score2};
     end for;
     if score > 0 then
        pack item j in the bin, position and orientation corresponding to score;
     else
        open a new bin and horizontally pack item j into i;
     end if;
  end for;
end;

Ayrıca, ilgi alanı olan makale, optimal olarak paketlenmiş doku haritasının boyutunu belirlemek için bir algoritmayı da açıklar. Bu, tüm dokuların tek bir 1024x1024 atlasına sığmasının mümkün olup olmadığını belirlemek yararlı olacaktır.


Bu algoritma, dokuların dikdörtgen şeklinde olduğunu varsayar, doğru mu?
user1767754

17

Eğer hala herkes ilgileniyorsa, rectpack2D kütüphanesini tamamen yeniden yazdım , böylece daha verimli olacak.

std::vectorAtlasta boş alanların bir kısmı tutularak çalışır , bazı ilk maksimum boyuttan başlayarak (tipik olarak belirli bir GPU'da izin verilen maksimum doku boyutu), ilk uygun boş alanı böler ve bölmeleri vektöre geri kaydeder.

Performans atılımı, daha önce yapıldığı gibi tüm bir ağacı tutmak yerine sadece bir vektör kullanarak geldi.

Prosedür README'de ayrıntılı olarak açıklanmıştır .

Kütüphane MIT'in altında, bu yüzden yararlı bulursanız sizin için mutlu olurum!

Örnek sonuçlar:

Testler bir Intel (R) Çekirdek (TM) i7-4770K CPU @ 3.50GHz üzerinde yapıldı. İkili bir -03 anahtarı kullanılarak clang 6.0.0 ile kuruldu.

Oyun keyfi sprite + Japon glifleri: Toplamda 3264 konu.

Çalışma Zamanı: 4 milisaniye
Harcanan piksel sayısı: 15538 (% 0,31 - 125 x 125 kareye eşdeğer)

Çıktı (2116 x 2382):

3

Renkte:
(siyah boşa harcanır)

4

Japon glifleri + bazı GUI sprite: 3122 denek.

Çalışma Zamanı: 3,5 - 7 ms
Harcanan piksel sayısı: 9288 (% 1,23 - 96 x 96 karenin eşdeğeri)

Çıktı (866 x 871):

5

Renkte:
(siyah boşa harcanır)

6


2
Kodunu indirdim. Sadece yapı tanımlarını okuyarak: BU MONSTROSITY NEDİR ?! Kod golf gibi gözüküyor.
akaltar

3
Yine de çalışıyor ve yardımcı oldu, çok teşekkürler. Kaba olmak istemedim.
akaltar

Ben bile daha hızlı ve daha Packer iyi kendi algoritması O_O sayesinde daha olduğundan bu cevabı skipeed emin niçin
GameDeveloper

@akaltar Hayal edebiliyorum, o sırada hala dili öğreniyordum :)
Patryk Czachurski

Uygulaması hızlı ve iyi sonuçlar alan oldukça basit bir yaklaşım, teşekkürler :)
FlintZA

5

İyi bir sezgisel algoritma bulunabilir burada . Son zamanlarda benzer bir şey deniyorken, bu referansı gördüğüm çoğu uygulama için temel başlangıç ​​noktası olarak buldum.

Özellikle çok sayıda düzenli şekilli, benzer boyuttaki öğeyle veya küçük ve daha küçük görüntülerin iyi bir karışımıyla iyi çalışır. İyi sonuçlar elde etmek için en iyi tavsiye, girişinizi görüntü boyutuna göre sıralamayı, ardından daha küçük resimlerin daha büyük resimlerin etrafındaki alana sığacağı için en büyüğünden en küçüğüne kadar paketlemektir. Bunu size göre nasıl yaptığınız ve hedeflerinize bağlı olabilir. Uzun boylu + ince / kısa + geniş görüntülerin (alçakça alana sahip olan) aslında daha sonra bir paket içine yerleştirmek çok zor olduğu görüşündeyken, 1. sıradaki bir yaklaşım yerine alan yerine çevre kullandım. bu garip şekiller emrin önüne doğru.

İşte web sitemdeki imaj dökümü dizininden rastgele bir dizi görüntü üzerinde toplayıcımın çıktısının örnek bir görselleştirilmesi :). Paketleyici çıkışı

Karelerdeki sayılar ağaçta bulunan blokların kimlikleridir, bu yüzden size eklerin sırası hakkında bir fikir verir. İlk ID "3" dür, çünkü ilk yaprak düğümüdür (sadece yapraklar resim içerir) ve sonuç olarak 2 ebeveyni vardır).

        Root[0]
       /      \
   Child[1]  Child[2]
      |
    Leaf[3]

3

Şahsen, sadece ilk sisteme uyan açgözlü en büyük bloklu bir açılışı kullanıyorum. Bu optimal değil, ama hile yapar.

Makul miktarda doku bloğunuz varsa, sorunun kendisi NP olsa bile en iyi sıralamayı ayrıntılı olarak arayabileceğinizi unutmayın.


3

Düzensiz UV haritaları için bile iyi çalıştığım bir şey UV yamasını bir bitmap maskesine dönüştürmek ve doku için UV yamasının sığacağı ilk konumu bulmak için maske sağlamaktır. Blokları basit sezgisellere göre sıralarım (yükseklik, genişlik, boyut, ne olursa olsun) ve blokların rotasyonlarının seçilen sezgiselleri en aza indirgemesine veya en üst düzeye çıkarmasına izin veririm. Bu kaba kuvvet için yönetilebilir bir arama alanı sağlar.

Daha sonra, çeşitli sezgisel deneyimleri denemeyi yineleyebilir ve / veya bir zaman sınırı doluncaya kadar sipariş ve yinelemenin seçiminde rastgele bir faktör uygulayabilirsiniz.

Bu şema ile küçük UV adalarını büyük olanlar tarafından açılan boşluklara, hatta tek UV yamaları içinde kalan deliklere bile sahip olacaksınız.


1

Geçenlerde dokuları belirli bir boyuttaki birden fazla görüntü dosyasına paketleyecek bir python betiği yayınladık.

Blogumuzdan alıntı:

“Çevrimiçi olarak bulunabilecek çok sayıda paketleyici olsa da, zorluğumuz birden fazla dizinde çok sayıda görüntüyü kaldırabilecek birini bulmaktı. Böylece, kendi atlas paketleyicimiz doğdu!

Olduğu gibi, küçük betiğimiz temel dizinde başlayacak ve tüm .PNG'leri bir atlaya yükleyecektir. Eğer bu atlas doldurulursa, yeni bir tane yaratır. Daha sonra, yenisinde bir yer bulamadan önce kalan tüm atlaslara görüntülerin geri kalanını yerleştirmeye çalışacaktır. Bu şekilde, her atlas mümkün olduğunca sıkı doludur. Atlaslar, görüntülerinin geldiği klasöre göre adlandırılır.

Atlas'ın boyutunu (satır 65), paketlemek istediğiniz görüntülerin biçimini (satır 67), yük dizinini (satır 10) ve kaydetme dizinini (satır 13) Python'da hiç bir deneyim olmadan kolayca değiştirebilirsiniz. Küçük bir feragatname olarak, bu, özellikle motorumuzla çalışmak için birkaç gün içinde bir araya getirildi. Sizden özellikler istemenizi, kendi varyasyonlarınızla yorum yapmanızı ve hataları bildirmenizi öneririm, ancak senaryodaki değişiklikler boş zamanlarımda gerçekleşecek. ”

Burada tam kaynak kodunu kontrol etmekten çekinmeyin: http://www.retroaffect.com/blog/159/Image_Atlas_Packer/#b


1

Yazı tiplerini paketlemek oldukça kolaydır, çünkü glif dokularının tümü (veya büyük çoğunluğu) neredeyse aynı boyuttadır. Başınıza gelen en basit şeyi yapın ve en iyi duruma çok yakın olacak.

Çok farklı boyutlarda görüntüleri paketlerken akıllılık daha önemli hale gelir. Öyleyse boşlukları vb. İçine sığdırmak isteyebilirsiniz. Yine de, daha önce tartışılan tarama sırası sıralaması gibi basit bir algoritma çok makul sonuçlar doğurur.

Gelişmiş alglerin hiçbiri sihir değil. Basit bir algoya göre% 50 daha verimli olmayacaklar ve şaşırtıcı sayıda doku kağıdınız olmadığı sürece onlardan tutarlı bir şekilde faydalanamayacaksınız. Bunun nedeni, daha iyi algoritmaların yaptığı küçük iyileştirmelerin yalnızca toplu halde görülmesidir.

Basit olun ve çabalarınızın daha iyi ödüllendirileceği bir şeye geçin


0

Özellikle font dokuları içinse, muhtemelen optimal olmayan ama hoş ve basit bir şey yaparsınız:

Karakterleri yüksekliğe göre sıralayın, önce en uzun

0,0'dan başla İlk karakterleri geçerli kodlara yerleştir, X, ilerlet, sonraki

X'i 0'a sıfırlayın, Y'yi satırdaki en yüksek karakterin yüksekliğine kadar aşağı ilerletin ve başka bir satır doldurun

Karakterimiz bitene kadar tekrar edin veya başka bir satıra sığamaz.

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.