ขั้นตอนวิธีการบรรจุพื้นผิว


52

อัลกอริทึมการบรรจุพื้นผิวที่ดีคืออะไร ในทางเทคนิคถังขยะนั้นบรรจุยากมากดังนั้นการเรียนรู้ด้วยตนเองจึงเป็นสิ่งที่ฉันต้องการจริงๆ


ฉันสันนิษฐานว่าคุณกำลังใช้สิ่งนี้เพื่อปรับปรุงแผนที่ UV แต่ฉันสงสัยว่าแอปพลิเคชันคืออะไร
Jonathan Fischoff

ftgles เป็นไลบรารีที่ใช้ opengl และ freetype เพื่อสร้างแบบอักษร อย่างไรก็ตามแต่ละสัญลักษณ์จะถูกเก็บไว้ในพื้นผิวของตัวเอง ฉันต้องการบรรจุลงในพื้นผิวเดียว
deft_code

คำตอบ:


58

ฉันใช้เวลาสองสามเดือนในการหางานด้วยอัลกอริธึมการบรรจุพื้นผิวที่ดีกว่า

อัลกอริทึมที่เราเริ่มต้นนั้นเรียบง่าย รวบรวมรายการอินพุตทั้งหมด จัดเรียงพวกเขาด้วยพิกเซลทั้งหมดที่บริโภคไปใหญ่ถึงเล็ก จัดวางสิ่งเหล่านี้ในพื้นผิวของคุณตามลำดับสแกนไลน์เพียงทดสอบสิ่งต่าง ๆ จากพิกเซลด้านบนสู่พิกเซลด้านบนย้ายลงบรรทัดแล้วทำซ้ำรีเซ็ตเป็นพิกเซลด้านบนหลังจากตำแหน่งที่ประสบความสำเร็จทุกตำแหน่ง

คุณต้องฮาร์ดโค้ดความกว้างหรือหาฮิวริสติกอื่นสำหรับสิ่งนี้ ในความพยายามที่จะรักษาความเป็นฉากกันอัลกอริทึมของเราจะเริ่มต้นที่ 128 จากนั้นเพิ่มขึ้นเป็น 128 วินาทีจนกว่าจะเกิดผลลัพธ์ที่ไม่ลึกกว่ากว้าง

ดังนั้นเรามีอัลกอริทึมนั้นและฉันตัดสินใจที่จะปรับปรุง ฉันลองใช้ฮิวริสติกที่แปลกประหลาด - พยายามหาวัตถุที่เข้าด้วยกันแล้วทำการลดน้ำหนักให้มากขึ้นเพื่อให้มีคุณสมบัติการบรรจุพื้นที่ที่ต้องการหมุนและพลิก หลังจากงานของฉันทั้งหมดสามเดือนฉันก็เลยประหยัดพื้นที่ได้ 3%

ใช่. 3%

และหลังจากที่เรารันรูทีนการบีบอัดมากกว่านั้นจริง ๆ แล้วมันก็ใหญ่ขึ้น(ซึ่งฉันยังอธิบายไม่ได้ไม่ได้) ดังนั้นเราจึงโยนทุกสิ่งออกไปและกลับไปที่อัลกอริธึมเก่า

เรียงลำดับรายการติดขัดในพื้นผิวตามลำดับการสแกน มีอัลกอริทึมของคุณ มันง่ายในการเขียนโค้ดเรียกใช้งานได้อย่างรวดเร็วและคุณจะไม่ดีขึ้นมากนักหากไม่มีงานที่น่าทึ่ง งานนี้ไม่คุ้มค่าเว้นแต่ บริษัท ของคุณจะมีอย่างน้อย 50 คนและอาจมากกว่านั้น

ข้อความแสดงแทน

และตามบันทึกด้านข้างฉันเพิ่งใช้อัลกอริธึมนี้ (ความกว้างคงที่ 512 พิกเซล) สำหรับแอปพลิเคชันเดียวกันที่คุณกำลังทำอยู่ (ไม่ใช่ ftgles แต่เป็น glyphs แบบ opengl ที่แสดงผล) นี่คือผลลัพธ์ ดูเหมือนว่าจะพร่ามัวเพราะฉันใช้อัลกอริธึมการแสดงผลข้อความทางไกลของ Valve ซึ่งจะอธิบายถึงช่องว่างพิเศษระหว่างร่ายมนตร์ เห็นได้ชัดว่ามีพื้นที่ว่างเหลืออยู่ไม่มากนักและมันก็เป็นงานที่ดีในการยัดเยียดสิ่งต่าง ๆ สู่ที่โล่ง

ทั้งหมดรหัสสำหรับเรื่องนี้คือ BSD ได้รับใบอนุญาตและสามารถใช้ได้ที่ GitHub


ฉันดูพื้นผิวของคุณและคิดกับตัวเองว่า "ฉันแน่ใจว่าพื้นผิวของเราบรรจุหีบห่อดีกว่านั้น" และจากนั้นฉันก็ไปดูมันและตระหนักว่าฉันทำมันพังเมื่อไม่นานมานี้และไม่ได้สังเกต (เพราะเมื่อมันทำงานแล้วใครจะดูพื้นผิวที่ส่งออก?) ... ดังนั้นขอบคุณสำหรับการโพสต์ - ไม่พบ ข้อผิดพลาดเป็นอย่างอื่น :) (เมื่อฉันแก้ไขข้อผิดพลาดมันดูคล้ายกันมาก - อาจเป็นร่มเงาดีกว่า แต่มันก็ยากที่จะบอกว่า "ดี" น่าจะเป็นคำอธิบายที่ปลอดภัยที่สุด)
JasonD

@JasonD ฉันชอบที่จะรู้ว่าอัลกอริทึมของคุณทำอะไรถ้ามันได้ผลลัพธ์ที่ดีกว่า :) ถึงแม้ว่ามันจะได้ผลลัพธ์ที่เทียบเท่ากันในทางที่ต่างออกไป
ZorbaTHut

1
ขอบคุณสำหรับคำอธิบายอัลโก + ความล้มเหลวที่ยอมรับ + ​​ซอร์สโค้ด โพสต์ยอดเยี่ยม
Calvin1602

1
เหตุผลที่มันมีขนาดใหญ่ขึ้นหลังจากการบีบอัดอาจเป็นเพราะขั้นตอนวิธีการบีบอัด เนื่องจากการบีบอัดมักขึ้นอยู่กับการแปลงแป้นพิมพ์และการหารูปแบบไบนารีหากอัลกอริทึมสามารถระบุรูปแบบที่เพียงพอก็จะสร้างกลุ่มทั้งหมดซึ่งอาจทำให้ขนาดขยาย วิธีที่ดีในการทดสอบไฟล์นี้คือเพียงแค่ซิปไฟล์ซ้ำแล้วซ้ำอีกและในที่สุดมันก็จะเริ่มมีขนาดใหญ่ขึ้นอีกครั้งเนื่องจากไม่มีรูปแบบ
Hanna

1
สำหรับวิธีการค้นหาเวอร์ชันล่าสุดของรหัสการบรรจุของ ZorbaTHut (font_baker.cpp) คุณสามารถดูได้ที่นี่: github.com/zorbathut/glorp/blob/
......

20

วิทยานิพนธ์ปริญญาเอกของอันเดรียโลดิมีสิทธิที่อัลกอริทึมสำหรับสองมิติถังบรรจุและการกำหนดปัญหา
วิทยานิพนธ์จะผ่านรูปแบบที่ยากขึ้นของปัญหานี้ โชคดีที่การบรรจุพื้นผิวเป็นรุ่นที่ง่ายที่สุด ขั้นตอนวิธีการที่ดีที่สุดเขาก็พบว่าถูกเรียกปริมณฑลสัมผัส

อ้างจากหน้า 52:

อัลกอริทึมที่เรียกว่า Touching Perimeter (TPRF) เริ่มต้นด้วยการเรียงลำดับรายการตามพื้นที่ที่ไม่เพิ่มขึ้น (การทำลายความสัมพันธ์โดยการไม่เพิ่มค่า min {wj, hj}) และการวางแนวในแนวนอน L ขอบเขตล่างบนค่าโซลูชันที่เหมาะสมจะถูกคำนวณและถังขยะว่าง L จะเริ่มต้นได้ (ขอบเขตล่างอย่างต่อเนื่อง L0 กำหนดไว้ในส่วนก่อนหน้านี้ถูกต้องสำหรับ 2BP | R | F เช่นกันขอบเขตที่ดีกว่าถูกเสนอโดย Dell'Amico, Martello และ Vigo [56]) อัลกอริธึมบรรจุหนึ่งรายการในแต่ละครั้ง ในถังขยะที่มีอยู่หรือโดยการเริ่มต้นใหม่ รายการแรกที่บรรจุในถังขยะจะอยู่ที่มุมซ้ายล่างเสมอ แต่ละรายการที่ตามมาบรรจุในตำแหน่งปกติที่เรียกว่า (ดู Christo fi des และ Whitlock [41]) คือ
ทางเลือกของถังขยะและตำแหน่งบรรจุทำได้โดยการประเมินคะแนนซึ่งคิดเป็นเปอร์เซ็นต์ของขอบเขตรายการที่สัมผัสกับถังขยะและรายการอื่น ๆ ที่บรรจุไว้แล้ว กลยุทธ์นี้สนับสนุนรูปแบบที่รายการที่บรรจุไม่ได้“ ดักจับ” พื้นที่ขนาดเล็กซึ่งอาจใช้งานได้ยากสำหรับตำแหน่งเพิ่มเติม สำหรับตำแหน่งการบรรจุแต่ละตำแหน่งผู้สมัครคะแนนจะถูกประเมินสองครั้งสำหรับการจัดวางรายการสองรายการ (หากทั้งคู่เป็นไปได้) และเลือกค่าสูงสุด ความสัมพันธ์ของคะแนนจะแตกโดยเลือกถังที่มีพื้นที่บรรจุสูงสุด อัลกอริทึมโดยรวมมีดังนี้

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;

กระดาษยังอธิบายถึงอัลกอริทึมเพื่อกำหนดขนาดของแผนที่พื้นผิวที่บรรจุอย่างเหมาะสม นั่นจะเป็นประโยชน์ในการพิจารณาว่าเป็นไปได้ที่จะปรับพื้นผิวทั้งหมดในแผนที่ขนาด 1024x1024 หรือไม่


อัลกอริทึมนี้อนุมานว่าพื้นผิวมีรูปร่างเป็นสี่เหลี่ยมใช่ไหม?
1767754

17

หากใครยังสนใจฉันได้เขียนไลบรารีrectpack2Dใหม่ทั้งหมดเพื่อให้มีประสิทธิภาพมากขึ้น

มันทำงานได้โดยการรักษาstd::vectorพื้นที่ว่างในแผนที่เริ่มต้นด้วยขนาดสูงสุดเริ่มต้นบางอย่าง (โดยทั่วไปคือขนาดพื้นผิวสูงสุดที่อนุญาตใน GPU เฉพาะ) แยกพื้นที่ว่างที่ทำงานได้แรกและบันทึกแยกกลับไปที่เวกเตอร์

การพัฒนาประสิทธิภาพนั้นมาพร้อมกับการใช้เวกเตอร์แทนที่จะทำให้ต้นไม้ทั้งต้นเหมือนที่เคยทำมาก่อนหน้านี้

ขั้นตอนที่จะอธิบายในรายละเอียดใน README

ห้องสมุดอยู่ภายใต้ MIT ดังนั้นฉันดีใจที่คุณถ้าคุณพบว่ามีประโยชน์!

ตัวอย่างผลลัพธ์:

การทดสอบดำเนินการกับ Intel (R) Core (TM) i7-4770K CPU @ 3.50GHz ไบนารีถูกสร้างด้วย clang 6.0.0 โดยใช้สวิตช์ -03

เทพดาเกมโดยพลการ + ร่ายมนตร์ภาษาญี่ปุ่น: 3264 วิชาโดยรวม

เวลา: 4 มิลลิวินาที
พิกเซลที่สูญเปล่า: 15538 (0.31% - เทียบเท่า 125 x 125 ตารางเมตร)

เอาต์พุต (2116 x 2382):

3

สี:
(สีดำเสียพื้นที่)

4

ร่ายมนตร์ภาษาญี่ปุ่น + สไปรท์ GUI บางรายการ: 3122 วิชา

เวลา: 3.5 - 7 ms
พิกเซลเสีย: 9288 (1.23% - เทียบเท่า 96 x 96 ตารางเมตร)

เอาต์พุต (866 x 871):

5

สี:
(สีดำเสียพื้นที่)

6


2
ฉันดาวน์โหลดรหัสของคุณแล้ว เพียงแค่อ่านคำจำกัดความของโครงสร้าง: อะไรคือสิ่งมหัศจรรย์นี้! ดูเหมือนว่ารหัสกอล์ฟ
akaltar

3
ยังใช้งานได้และมันช่วยได้ขอบคุณ ฉันไม่ต้องการที่จะหยาบคาย
akaltar

ไม่แน่ใจว่าทำไมฉันข้ามคำตอบนี้ได้เพราะมันเร็วกว่าและดีกว่าผู้
ห่อ

@akaltar ผมสามารถจินตนาการว่าผมยังคงเรียนรู้ภาษาในช่วงเวลาที่ :)
Patryk Czachurski

วิธีการที่ค่อนข้างง่ายที่ใช้งานได้อย่างรวดเร็วและได้รับผลลัพธ์ที่ดีขอบคุณ :)
FlintZA

5

ขั้นตอนวิธีการแก้ปัญหาที่ดีสามารถพบได้ที่นี่ เมื่อฉันพยายามทำสิ่งที่คล้ายกันเมื่อเร็ว ๆ นี้ฉันพบว่าการอ้างอิงนี้เป็นจุดเริ่มต้นพื้นฐานสำหรับการใช้งานส่วนใหญ่ที่ฉันเห็น

ใช้งานได้ดีโดยเฉพาะกับรูปทรงปกติขนาดรายการที่มีขนาดใกล้เคียงกันหรือกับภาพที่มีขนาดเล็กและใหญ่ คำแนะนำที่ดีที่สุดเพื่อให้ได้ผลลัพธ์ที่ดีคืออย่าลืมจัดเรียงอินพุตของคุณในแง่ของขนาดภาพจากนั้นแพ็คจากใหญ่ไปหาน้อยที่สุด วิธีการเรียงลำดับขึ้นอยู่กับคุณและอาจขึ้นอยู่กับเป้าหมายของคุณ ฉันใช้เส้นรอบวงมากกว่าพื้นที่เป็นลำดับที่ 1 โดยประมาณเนื่องจากฉันมองว่าภาพที่มีความสูง + บาง / สั้น + กว้าง (ซึ่งจะมีพื้นที่ที่ค่อนข้างต่ำ) จริง ๆ แล้วยากมากที่จะวางในภายหลังในแพ็คดังนั้นโดยใช้ขอบเขตที่คุณกด รูปร่างแปลก ๆ เหล่านี้ไปทางด้านหน้าของคำสั่ง

นี่คือตัวอย่าง visulization ของเอาท์พุทสำหรับ packer ของฉันในชุดภาพสุ่มจากไดเรกทอรีถ่ายโอนข้อมูลอิมเมจเว็บไซต์ของฉัน :) เอาต์พุตของ Packer

ตัวเลขในช่องสี่เหลี่ยมนั้นเป็น id ของบล็อคที่มีอยู่ในทรีดังนั้นให้คุณทราบถึงลำดับของการแทรก แรกคือ ID "3" เพราะมันเป็นโหนดใบแรก(ใบเท่านั้นมีภาพ) และจึงมี 2 ผู้ปกครอง)

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

3

โดยส่วนตัวฉันเพิ่งใช้ระบบแรกที่โลภมากที่สุดเหมาะกับระบบแรก มันไม่ได้ดีที่สุด แต่มันก็หลอกลวงได้

โปรดทราบว่าหากคุณมีบล็อกพื้นผิวในจำนวนที่สมเหตุสมผลคุณสามารถค้นหาการสั่งซื้อที่ดีที่สุดได้อย่างละเอียดแม้ว่าปัญหานั้นจะเป็นปัญหา


3

สิ่งที่ฉันใช้ซึ่งใช้งานได้ดีแม้ในแผนที่ UV ที่ผิดปกติคือการเปลี่ยนแผ่นแปะ UV ให้เป็นหน้ากากบิตแมปและรักษาหน้ากากสำหรับพื้นผิวของตัวเอง ฉันสั่งบล็อกตามฮิวริสติกแบบเรียบง่าย (ความสูงความกว้างขนาดอะไรก็ตาม) และฉันอนุญาตให้การหมุนของบล็อกลดหรือเพิ่มฮิวริสติกที่เลือกให้มากที่สุด ที่ให้พื้นที่การค้นหาที่จัดการได้สำหรับกำลังดุร้าย

หากคุณสามารถทำซ้ำที่ลองหลายฮิวริสติกและ / หรือใช้ปัจจัยสุ่มในการเลือกการสั่งซื้อและทำซ้ำจนกว่าจะถึงเวลาที่ จำกัด

ด้วยรูปแบบนี้คุณจะได้รับหมู่เกาะยูวีขนาดเล็กที่บรรจุอยู่ในช่องว่างของหมู่เกาะขนาดใหญ่และแม้กระทั่งในหลุมที่เหลืออยู่ภายในแผ่น UV ตัวเดียว


1

เราเพิ่งเปิดตัวสคริปต์ python ซึ่งจะบรรจุพื้นผิวเป็นไฟล์รูปภาพหลายไฟล์ในขนาดที่กำหนด

อ้างจากบล็อกของเรา:

"ในขณะที่มีผู้แบ่งบรรจุจำนวนมากที่สามารถพบได้ทั่วไปความยากลำบากของเราคือการค้นหาสิ่งใดก็ตามที่สามารถจัดการกับภาพจำนวนมากในหลาย ๆ ไดเรกทอรีดังนั้นผู้จัดทำสมุดแผนที่ของเราถือกำเนิดขึ้น!

เช่นเดียวกับสคริปต์เล็ก ๆ ของเราจะเริ่มต้นในไดเรกทอรีฐานและโหลด. PNG ทั้งหมดลงในแผนที่ หากแผนที่นั้นเต็มไปมันจะสร้างแผนที่ใหม่ จากนั้นจะลองปรับแต่งภาพส่วนที่เหลือในแผนที่ก่อนหน้าทั้งหมดก่อนที่จะหาจุดในภาพใหม่ ด้วยวิธีนี้แต่ละแผนที่มีความหนาแน่นมากที่สุด แผนที่มีชื่อตามโฟลเดอร์ที่เป็นรูปภาพ

คุณสามารถเปลี่ยนขนาดของแอตลาส (บรรทัดที่ 65) รูปแบบของภาพที่คุณต้องการแพ็ค (บรรทัดที่ 67) โหลดไดเรกทอรี (บรรทัดที่ 10) และบันทึกไดเรกทอรี (บรรทัดที่ 13) ได้อย่างง่ายดายโดยไม่ต้องใช้ Python ในฐานะที่เป็นข้อจำกัดความรับผิดชอบเล็ก ๆ น้อย ๆ สิ่งนี้ถูกรวมเข้าด้วยกันในสองสามวันเพื่อทำงานกับเครื่องยนต์ของเรา ฉันขอแนะนำให้คุณขอคุณสมบัติแสดงความคิดเห็นด้วยรูปแบบของคุณเองและรายงานข้อผิดพลาดใด ๆ แต่การเปลี่ยนแปลงใด ๆ ในสคริปต์จะเกิดขึ้นในเวลาว่างของฉัน "

อย่าลังเลที่จะตรวจสอบซอร์สโค้ดแบบเต็มได้ที่นี่: http://www.retroaffect.com/blog/159/Image_Atlas_Packer/#b


1

มันค่อนข้างง่ายที่จะแพ็คแบบอักษรเพราะทั้งหมด (หรือส่วนใหญ่) ของพื้นผิวสัญลักษณ์มีขนาดใกล้เคียงกัน ทำสิ่งที่ง่ายที่สุดที่เกิดขึ้นกับคุณและมันจะใกล้เคียงกับความเหมาะสมที่สุด

ความฉลาดนั้นสำคัญมากเมื่อคุณบรรจุภาพที่มีขนาดแตกต่างกันมาก จากนั้นคุณต้องการที่จะบรรจุลงในช่องว่าง ฯลฯ แม้กระนั้นอัลกอริธึมง่ายๆเช่นการค้นหาคำสั่งสแกนไลน์ที่กล่าวถึงก่อนหน้านี้จะให้ผลลัพธ์ที่สมเหตุสมผลมาก

ไม่มี algos ขั้นสูงที่เป็นเวทมนตร์ พวกมันจะไม่ได้ประสิทธิภาพมากกว่า 50% ของอัลโกทั่วไปและคุณจะไม่ได้รับผลประโยชน์ที่สม่ำเสมอจากพวกเขาเว้นแต่คุณจะมีแผ่นพื้นผิวจำนวนมาก นั่นเป็นเพราะการปรับปรุงเล็ก ๆ ที่อัลกอริทึมที่ดีกว่าจะเห็นได้ในภาพรวมเท่านั้น

ใช้ง่ายและไปยังสิ่งที่ความพยายามของคุณจะได้รับรางวัลดีกว่า


0

หากมันใช้กับพื้นผิวแบบอักษรโดยเฉพาะคุณอาจทำสิ่งที่ไม่เหมาะสม แต่ดีและเรียบง่าย:

จัดเรียงอักขระตามความสูงสูงสุดก่อน

เริ่มต้นที่ 0,0 วางอักขระตัวแรกที่ coords ปัจจุบันเลื่อน X วางตัวถัดไปทำซ้ำจนกว่าเราจะไม่พอดี

รีเซ็ต X เป็น 0 เลื่อน Y ลงไปตามความสูงของอักขระที่สูงที่สุดในแถวแล้วเติมอีกแถวหนึ่ง

ทำซ้ำจนกว่าเราจะหมดอักขระหรือไม่สามารถใส่แถวอื่นได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.