Thuật toán đóng gói kết cấu


52

Một thuật toán đóng gói kết cấu tốt là gì? Về mặt kỹ thuật, việc đóng gói binNP-hard , do đó, heuristic là những gì tôi thực sự theo đuổi.


Tôi đã giả định rằng bạn đang sử dụng điều này để tối ưu hóa bản đồ uv, nhưng tôi tò mò ứng dụng này là gì.
Jonathan Fischoff

ftgles là thư viện sử dụng opengl và freetype để kết xuất phông chữ. Tuy nhiên, mỗi glyph được lưu trữ trong kết cấu riêng của nó. Tôi muốn gói chúng thành một kết cấu.
deft_code

Câu trả lời:


58

Tôi đã dành một vài tháng cho một công việc với một thuật toán đóng gói kết cấu tốt hơn.

Thuật toán chúng tôi bắt đầu rất đơn giản. Thu thập tất cả các mục đầu vào. Sắp xếp chúng theo tổng số pixel được tiêu thụ, từ lớn đến nhỏ. Sắp xếp chúng theo kết cấu của bạn theo thứ tự quét, chỉ cần kiểm tra các thứ từ pixel topleft đến pixel topright, di chuyển xuống một dòng và lặp lại, đặt lại pixel topleft sau mỗi vị trí thành công.

Bạn có thể cần mã hóa chiều rộng hoặc đưa ra một heuristic khác cho việc này. Trong nỗ lực bảo toàn độ vuông góc, thuật toán của chúng tôi sẽ bắt đầu ở mức 128, sau đó tăng thêm 128 giây cho đến khi nó đưa ra kết quả không sâu hơn mức nào.

Vì vậy, chúng tôi đã có thuật toán đó và tôi quyết định cải thiện nó. Tôi đã thử một loạt các heuristic lập dị - cố gắng tìm các vật thể khớp với nhau, thực hiện một số trọng số trên một loạt các thuộc tính đóng gói không gian mong muốn, xoay và lật. Sau tất cả công việc của tôi, đúng nghĩa là ba tháng làm việc, cuối cùng tôi đã tiết kiệm được 3% dung lượng.

Vâng. 3%.

Và sau khi chúng tôi thực hiện quy trình nén của mình qua nó, nó thực sự đã kết thúc lớn hơn (mà tôi vẫn không thể giải thích) vì vậy chúng tôi đã ném toàn bộ ra và quay lại thuật toán cũ.

Sắp xếp các mục, mứt thành kết cấu theo thứ tự scanline. Có thuật toán của bạn. Thật dễ dàng để viết mã, chạy nhanh và bạn sẽ không khá hơn nếu không có một lượng công việc đáng kinh ngạc. Công việc đó không đáng giá trừ khi công ty của bạn có ít nhất 50 người, và có thể nhiều hơn.

văn bản thay thế

Và như một lưu ý phụ, tôi chỉ thực hiện thuật toán này (chiều rộng cố định 512 pixel) cho chính xác cùng một ứng dụng mà bạn đang thực hiện (không có ftgles, nhưng glyphs freetype được hiển thị opengl.) Đây là kết quả. Nó trông có vẻ mờ vì tôi đang sử dụng thuật toán kết xuất văn bản dựa trên trường khoảng cách của Valve , cũng chiếm khoảng trống giữa các glyphs. Rõ ràng, không còn nhiều khoảng trống còn lại, và nó làm tốt công việc nhồi nhét mọi thứ vào các điểm mở.

Tất cả mã cho điều này được cấp phép BSD và có sẵn tại github .


Tôi nhìn vào kết cấu của bạn và tự nghĩ "Tôi chắc chắn rằng trình đóng gói kết cấu của chúng tôi làm tốt hơn thế một chút". Và sau đó tôi đã đi và nhìn nó, và nhận ra rằng tôi đã phá vỡ nó một thời gian trước và không để ý (vì một khi nó hoạt động, ai nhìn vào kết cấu đầu ra?) ... Vì vậy, cảm ơn vì đã đăng - sẽ không tìm thấy lỗi khác :) (một khi tôi đã sửa lỗi, nó trông rất giống nhau - có thể là một sắc thái tốt hơn, nhưng thật khó để nói chính xác. "tốt như vậy" có lẽ là mô tả an toàn nhất).
JasonD

@JasonD, tôi rất muốn biết thuật toán của bạn làm gì, nếu nó có đầu ra tốt hơn :) Ngay cả khi nó có đầu ra gần tương đương theo một cách khác.
ZorbaTHut

1
Cảm ơn mô tả algo + thất bại thừa nhận + mã nguồn. Bài đăng tuyệt vời.
Calvin1602

1
Lý do nó trở nên lớn hơn sau khi nén có lẽ là do thuật toán nén. Vì việc nén thường dựa vào băm và tìm các mẫu nhị phân, nếu thuật toán có thể xác định đủ các mẫu, nó sẽ tạo ra một loạt chúng có thể làm cho kích thước mở rộng. một cách tuyệt vời để kiểm tra điều này chỉ đơn giản là nén lại một tập tin nhiều lần và cuối cùng nó sẽ bắt đầu trở lại lớn hơn do thiếu các mẫu.
Hanna

1
Để tìm kiếm phiên bản mới nhất của mã đóng gói của ZorbaTHut (font_baker.cpp), bạn có thể tìm thấy ở đây: github.com/zorbathut/glorp/blob/ trộm
mems

20

Luận án tiến sĩ của Andrea Lodi có tên Thuật toán cho các vấn đề về đóng gói và chuyển nhượng thùng hai chiều .
Luận án đi qua một số dạng khó hơn của vấn đề này. May mắn thay, kết cấu đóng gói là phiên bản dễ nhất. Thuật toán tốt nhất mà ông tìm thấy được gọi là Touching Perimet .

Để trích dẫn từ trang 52:

Thuật toán, được gọi là Touching Perimet (TPRF), bắt đầu bằng cách sắp xếp các mục theo khu vực không tăng (phá vỡ các mối quan hệ bằng cách không tăng các giá trị min {wj, hj}) và bằng cách định hướng theo chiều ngang của chúng. Sau đó, một L giới hạn thấp hơn trên giá trị giải pháp tối ưu được tính toán và các thùng rỗng L được khởi tạo. (Giới hạn L0 liên kết thấp hơn liên tục trong phần trước rõ ràng là hợp lệ cho 2BP | R | F; giới hạn tốt hơn được đề xuất bởi Dell'Amico, Martello và Vigo [56].) Thuật toán đóng gói một mục tại một thời điểm trong một thùng hiện có, hoặc bằng cách khởi tạo một cái mới. Vật phẩm đầu tiên được đóng gói trong thùng luôn được đặt ở góc dưới bên trái. Mỗi mục tiếp theo được đóng gói ở một vị trí được gọi là bình thường (xem Christo fi des và Whitlock [41]), nghĩa là,
Việc lựa chọn thùng và vị trí đóng gói được thực hiện bằng cách đánh giá điểm số, được định nghĩa là tỷ lệ phần trăm của chu vi vật phẩm chạm vào thùng và các vật phẩm khác đã được đóng gói. Chiến lược này ưu tiên các mô hình trong đó các mặt hàng được đóng gói không bị bẫy bẫy khu vực nhỏ, có thể khó sử dụng cho các vị trí tiếp theo. Đối với mỗi vị trí đóng gói ứng viên, điểm số được đánh giá hai lần, cho hai định hướng vật phẩm (nếu cả hai đều khả thi) và giá trị cao nhất được chọn. Mối quan hệ điểm bị phá vỡ bằng cách chọn thùng có diện tích đóng gói tối đa. Các thuật toán tổng thể là như sau.

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;

Cũng đáng quan tâm, bài viết mô tả một thuật toán để xác định kích thước của bản đồ kết cấu được đóng gói tối ưu. Điều đó sẽ hữu ích để xác định xem thậm chí có thể phù hợp với tất cả các kết cấu trong một tập bản đồ 1024x1024 hay không.


Thuật toán này giả định rằng kết cấu có dạng hình chữ nhật, đúng không?
dùng1767754

17

Nếu bất cứ ai vẫn quan tâm, tôi đã viết lại hoàn toàn thư viện orthpack2D để nó hiệu quả hơn.

Nó hoạt động bằng cách giữ một std::vectorkhoảng trống trong tập bản đồ, bắt đầu với một số kích thước tối đa ban đầu (thông thường, kích thước kết cấu tối đa được phép trên một GPU cụ thể), tách không gian trống khả thi đầu tiên và lưu lại các phần tách về vectơ.

Sự đột phá về hiệu suất đến với việc chỉ sử dụng một vectơ, thay vì giữ toàn bộ một cây, như đã được thực hiện trước đó.

Quy trình được mô tả chi tiết trong README .

Thư viện trực thuộc MIT, vì vậy tôi rất vui cho bạn nếu bạn thấy nó hữu ích!

Kết quả ví dụ:

Các thử nghiệm được tiến hành trên CPU Intel (R) Core (TM) i7-4770K @ 3.50GHz. Nhị phân được xây dựng với clang 6.0.0, sử dụng công tắc -03.

Sprite trò chơi tùy ý + glyphs Nhật Bản: tổng cộng 3264 đối tượng.

Thời gian chạy: 4 mili giây
pixel bị lãng phí: 15538 (0,31% - tương đương với hình vuông 125 x 125)

Đầu ra (2116 x 2382):

3

Trong màu sắc:
(màu đen là không gian lãng phí)

4

Glyphs Nhật Bản + một số họa tiết GUI: 3122 đối tượng.

Thời gian chạy: 3,5 - 7 ms
pixel bị lãng phí: 9288 (1,23% - tương đương với hình vuông 96 x 96)

Đầu ra (866 x 871):

5

Trong màu sắc:
(màu đen là không gian lãng phí)

6


2
Tôi đã tải xuống mã của bạn. Chỉ cần đọc các định nghĩa cấu trúc: ĐỐI TƯỢNG NÀY LÀ GÌ?! Nó trông giống như mã golf.
akaltar

3
Tuy nhiên, nó hoạt động, và nó đã giúp, vì vậy cảm ơn. Tôi không muốn thô lỗ.
akaltar

Không chắc chắn tại sao tôi bỏ qua câu trả lời này vì nó thậm chí còn nhanh hơn và đóng gói tốt hơn thuật toán của riêng tôi O_O cảm ơn
GameDeveloper

@akaltar Tôi có thể tưởng tượng rằng, tôi vẫn đang học ngôn ngữ trong thời gian đó :)
Patryk Czachurski

Cách tiếp cận khá đơn giản giúp thực hiện nhanh chóng và đạt kết quả tốt, cảm ơn :)
FlintZA

5

Một thuật toán heuristic tốt có thể được tìm thấy ở đây . Khi tôi đang thử một cái gì đó tương tự gần đây, tôi thấy điều này được tham chiếu như là điểm khởi đầu cơ bản cho hầu hết các triển khai tôi thấy.

Hoạt động đặc biệt tốt với nhiều vật phẩm có hình dạng thông thường, có kích thước tương tự hoặc kết hợp tốt các hình ảnh nhỏ và nhỏ hơn. Lời khuyên tốt nhất để đạt được kết quả tốt là hãy nhớ sắp xếp đầu vào của bạn theo kích thước hình ảnh, sau đó đóng gói từ lớn nhất đến nhỏ nhất vì các hình ảnh nhỏ hơn sẽ đóng gói vào không gian xung quanh các hình ảnh lớn hơn. Cách bạn thực hiện việc này sắp xếp theo bạn và có thể phụ thuộc vào mục tiêu của bạn. Tôi đã sử dụng chu vi thay vì diện tích như một xấp xỉ bậc 1 vì tôi cho rằng hình ảnh cao + mỏng / ngắn + rộng (sẽ có diện tích thấp) thực sự rất khó để đặt sau trong một gói, vì vậy bằng cách sử dụng chu vi bạn đẩy những hình dạng kỳ lạ về phía trước của trật tự.

Đây là một mẫu hiển thị đầu ra cho trình đóng gói của tôi trên một tập hợp hình ảnh ngẫu nhiên từ thư mục kết xuất hình ảnh trang web của tôi :). Đầu ra đóng gói

Các số trong các ô vuông là id của các khối chứa trong cây, do đó cung cấp cho bạn ý tưởng về thứ tự của các phần chèn thêm. Đầu tiên là ID "3" vì đây là nút đầu tiên (chỉ các lá chứa hình ảnh) và do đó có 2 cha mẹ).

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

3

Cá nhân, tôi chỉ sử dụng một hệ thống đầu tiên lớn nhất phù hợp với khối lớn. Nó không tối ưu, nhưng nó vẫn ổn.

Lưu ý rằng, nếu bạn có số lượng khối kết cấu hợp lý, bạn có thể tìm kiếm triệt để thứ tự tốt nhất ngay cả khi chính vấn đề là NP.


3

Một cái gì đó tôi đã sử dụng, hoạt động tốt ngay cả đối với bản đồ UV không đều, là biến miếng vá UV thành mặt nạ bitmap và duy trì mặt nạ cho chính kết cấu, tìm kiếm vị trí đầu tiên mà miếng dán UV sẽ phù hợp. Tôi sắp xếp các khối theo một số heuristic đơn giản (chiều cao, chiều rộng, kích thước, bất cứ thứ gì) và tôi cho phép xoay các khối để giảm thiểu hoặc tối đa hóa các heuristic đã chọn. Điều đó mang lại một không gian tìm kiếm có thể quản lý cho lực lượng vũ phu.

Nếu sau đó bạn có thể lặp lại việc thử một số phương pháp phỏng đoán và / hoặc áp dụng một yếu tố ngẫu nhiên trong việc chọn thứ tự và lặp lại cho đến khi hết thời gian giới hạn.

Với sơ đồ này, bạn sẽ có được các đảo UV nhỏ được đóng gói vào các khoảng trống được tạo bởi các đảo lớn và thậm chí trong các lỗ còn lại trong các bản vá UV đơn lẻ.


1

Gần đây chúng tôi đã phát hành một tập lệnh python sẽ đóng gói họa tiết vào nhiều tệp hình ảnh có kích thước nhất định.

Trích dẫn từ blog của chúng tôi:

"Mặc dù có rất nhiều trình đóng gói có thể được tìm thấy trực tuyến, nhưng khó khăn của chúng tôi là tìm kiếm bất kỳ trình xử lý nào có thể xử lý số lượng lớn hình ảnh trong nhiều thư mục. Do đó, trình đóng gói bản đồ của chúng tôi đã ra đời!

Như vậy, tập lệnh nhỏ của chúng ta sẽ bắt đầu trong thư mục cơ sở và tải tất cả .PNG vào một tập bản đồ. Nếu tập bản đồ đó được lấp đầy, nó sẽ tạo một cái mới. Sau đó, nó sẽ thử điều chỉnh phần còn lại của hình ảnh trong tất cả các tập trước đó trước khi tìm một vị trí trong ảnh mới. Bằng cách đó, mỗi tập bản đồ được đóng gói càng chặt chẽ càng tốt. Atlase được đặt tên dựa trên thư mục mà hình ảnh của họ là từ.

Bạn có thể thay đổi kích thước của tập bản đồ (dòng 65), định dạng của hình ảnh bạn muốn đóng gói (dòng 67), thư mục tải (dòng 10) và thư mục lưu (dòng 13) khá dễ dàng mà không có kinh nghiệm về Python. Như một sự từ chối nhỏ, điều này đã bị đánh bại trong một vài ngày để làm việc cụ thể với động cơ của chúng tôi. Tôi khuyến khích bạn yêu cầu các tính năng, nhận xét với các biến thể của riêng bạn và báo cáo bất kỳ lỗi nào, nhưng mọi thay đổi đối với tập lệnh sẽ xảy ra trong thời gian rảnh của tôi. "

Vui lòng kiểm tra mã nguồn đầy đủ tại đây: http://www.retroaffect.com/blog/159/Image_Atlas_Packer/#b


1

Việc đóng gói phông chữ khá dễ dàng vì tất cả (hoặc phần lớn) các kết cấu glyph có cùng kích thước. Làm điều đơn giản nhất xảy ra với bạn và nó sẽ rất gần với tối ưu.

Sự thông minh trở nên quan trọng hơn khi bạn đóng gói hình ảnh có kích thước rất khác nhau. Sau đó, bạn muốn có thể đóng gói vào các khoảng trống, vv Mặc dù vậy, mặc dù vậy, một thuật toán đơn giản như tìm kiếm thứ tự quét được thảo luận trước đó sẽ tạo ra kết quả rất hợp lý.

Không có thuật toán tiên tiến nào là ma thuật. Chúng sẽ không hiệu quả hơn 50% so với thuật toán simpel và bạn sẽ không nhận được lợi ích phù hợp từ chúng trừ khi bạn có số lượng kết cấu đáng kinh ngạc. đó là bởi vì những cải tiến nhỏ mà các thuật toán tốt hơn tạo ra sẽ chỉ được nhìn thấy trong tổng hợp.

Hãy đơn giản và chuyển sang một thứ mà những nỗ lực của bạn sẽ được đền đáp tốt hơn


0

Nếu nó đặc biệt cho kết cấu phông chữ, thì có lẽ bạn đã làm một cái gì đó không tối ưu nhưng đẹp và đơn giản:

Sắp xếp các ký tự theo chiều cao, cao nhất trước

Bắt đầu ở 0,0 Đặt ký tự đầu tiên tại các hợp đồng hiện tại, tiến lên X, đặt tiếp theo, lặp lại cho đến khi chúng ta không thể khớp với ký tự khác

Đặt lại X về 0, tăng Y xuống dưới theo chiều cao của ký tự cao nhất trong hàng và điền vào một hàng khác

Lặp lại cho đến khi chúng tôi hết ký tự hoặc không thể vừa một hàng khác.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.