บิตแมปไม่มีที่สิ้นสุด [ปิด]


10

ฉันต้องการสร้างบิตแมประหว่างรันไทม์ บิตแมปควรปรับขนาดได้ในทุกด้านและการเข้าถึงพิกเซลควรมีประสิทธิภาพที่เงียบ

ภาพประกอบบางส่วน http://img546.imageshack.us/img546/4995/maptm.jpg

ระหว่างและหลังคำสั่งที่แสดงในภาพ Map.setPixel () และ Map.getPixel () ควรตั้ง / ส่งคืนข้อมูลที่บันทึกในบิตแมป

ฉันไม่ได้คาดหวังว่าการใช้งานเป็นเพียงแนวคิดในการจัดสรรหน่วยความจำในลักษณะที่ setPixel () / getPixel นั้นเร็วที่สุด


ฟิลด์สีเทาเป็นจุดเสมอ (0,0) หรือเป็นพิกัดอื่นด้วยหรือไม่
เหยี่ยว

2
ต้องการรายละเอียดเพิ่มเติม พิกเซลที่ตั้งไว้จะเบาบางหรือไม่? คุณเต็มใจที่จะทำช้าแค่ไหนเพื่อให้extendXวิธีการsetPixelและวิธีgetPixelที่รวดเร็ว?
Peter Taylor

1
บิตแมปจะใหญ่เกินไปที่จะใส่ในหน่วยความจำหรือไม่? สิ่งที่ควรดำเนินการอย่างรวดเร็ว - การขยายตัว setPixel (), getPixel ()?

1
@ ฟอลคอน: ไม่มีเวลาเพียงพอ
SecStone

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

คำตอบ:


9

หากการextend()ดำเนินการต้องรวดเร็วพอสมควรQuadtreeอาจเหมาะสม จริง ๆ แล้วมันไม่จำเป็นต้องมีการดำเนินการขยายอย่างชัดเจน เป็นที่ยอมรับกันว่ามันจะไม่ให้ประสิทธิภาพที่ดีที่สุดสำหรับการเข้าถึงแบบสุ่มไปยังแต่ละพิกเซล แต่ความคิดเห็นของคุณบอกว่าการดำเนินการหลักของคุณคือการทำซ้ำพิกเซลซึ่ง quadtree สามารถทำได้เร็วมาก หากการวนซ้ำไม่ได้เกิดขึ้นในลักษณะเดียวกันเมทริกซ์จะถูกจัดวาง)

ความต้องการของคุณดูเหมือนว่าคุณกำลังพยายามติดตั้งหุ่นยนต์อัตโนมัติเช่น Game of Life คุณอาจต้องการดูHashlifeซึ่งเป็นวิธีที่มีประสิทธิภาพสูงมากในการนำเกมแห่งชีวิตไปใช้บนกริดที่ไม่มีที่สิ้นสุด โปรดทราบว่ามันใช้ Quadtree แต่มีการเพิ่มประสิทธิภาพบางอย่างที่ชาญฉลาดขึ้นอยู่กับกฎของเกม


ขอบคุณสำหรับความคิดนี้! ฉันจะทำการทดสอบและจะรายงานผล
SecStone

2

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


4
ฉันจะลงคะแนนนี้ถ้าคุณทำข้อเสนอแนะที่ดีเกี่ยวกับวิธีที่คุณคิดว่าการขยายตัวควรได้รับการจัดการ
Doc Brown

@Doc Brown: หากมีเวลาเพียงพอเพียงแค่เลื่อนอาร์เรย์ หรือบางทีคุณสามารถหาอะไรสักอย่างกับ chunks และฟังก์ชั่นล่ามสำหรับชี้ไปที่ array และ index chunk (ซึ่งทำงานในเวลาคงที่เช่นกัน)
เหยี่ยว

2

ด้วยมือ

หากหน่วยความจำไม่ใช่ทรัพยากรที่กระจัดกระจายมากฉันพิจารณาการทำงานในกลุ่มที่ใหญ่กว่า
นี่คือบางส่วนของรหัสเทียม

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;
    }
}

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

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

ออกจากชั้นวาง

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

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


1

เพียงไม่กี่คำแนะนำ:

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

  • หากคุณใช้โครงสร้างข้อมูลบนแผนที่คุณสามารถกลเม็ดในการเพิ่มบิตแมปได้โดยเพียงแค่ย้ายตำแหน่งพิกัด x, y ของอาร์กิวเมนต์getPixelและการsetPixelเรียก

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

  • คุณไม่จำเป็นต้องใช้แผนที่ของแผนที่ คุณสามารถเข้ารหัสintx คู่ Y longเป็นหนึ่งเดียว กระบวนการแบบอะนาล็อกสามารถใช้เพื่อแม็พอาร์เรย์ของอาร์เรย์กับอาร์เรย์


ในที่สุดคุณต้องสมดุล 3 สิ่ง:

  1. ประสิทธิภาพการทำงานของgetPixelและsetPixel,
  2. ประสิทธิภาพของการextend*ดำเนินการและ
  3. การใช้พื้นที่

1

ก่อนที่จะลองทำสิ่งอื่นที่ซับซ้อนกว่านี้และถ้าคุณไม่สามารถเก็บทุกอย่างไว้ในหน่วยความจำได้ให้ทำสิ่งที่ง่ายและใช้อาร์เรย์สองมิติพร้อมกับข้อมูลเกี่ยวกับที่มาของระบบพิกัดของคุณ หากต้องการขยายให้ใช้กลยุทธ์เดียวกันเช่น C ++ std :: vector ทำ: สร้างความแตกต่างระหว่างขนาดที่แท้จริงของอาเรย์กับความจุของอาเรย์และขยายความจุเป็นชิ้น ๆ เมื่อถึงขีด จำกัด "ความจุ" ที่นี่ควรกำหนดไว้ในรูปของช่วงเวลา (from_x, to_x), (from_y, to_y)

นี่อาจจำเป็นต้องทำการจัดสรรหน่วยความจำใหม่เป็นครั้งคราว แต่ตราบใดที่สิ่งนี้ไม่ได้เกิดขึ้นบ่อยครั้งมันอาจเร็วพอสำหรับจุดประสงค์ของคุณ (อันที่จริงคุณต้องลอง / โพรไฟล์นี้)


1

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

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

หากการทำโปรไฟล์เผยให้เห็นความจำเป็นที่จะต้องจัดสรรจำนวนการจัดสรรใหม่ลงและคุณไม่ได้ จำกัด หน่วยความจำให้พิจารณาการจัดสรรเกินโดยเปอร์เซ็นต์ในแต่ละทิศทางและจัดเก็บออฟเซ็ตไปที่จุดเริ่มต้น (เช่นถ้าคุณเริ่มบิตแมปใหม่ที่ 1x1 และจัดสรรอาร์เรย์ 9x9 เพื่อเก็บไว้การเริ่มต้นxและyออฟเซ็ตจะเป็น4) การแลกเปลี่ยนที่นี่ต้องทำคณิตศาสตร์พิเศษในระหว่างการเข้าถึงพิกเซลเพื่อใช้ออฟเซ็ต

หากส่วนขยายเปิดออกเพื่อจะจริงๆแพงคุณอาจจะลองหนึ่งหรือทั้งสองเหล่านี้:

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

  • ติดตามจำนวนและทิศทางการขยายที่พบบ่อยที่สุด ใช้ข้อมูลนั้นเพื่อเลือกขนาดและออฟเซ็ตใหม่ซึ่งจะลดความน่าจะเป็นที่ต้องทำการจัดสรรใหม่และคัดลอกสำหรับส่วนขยายใด ๆ

โดยส่วนตัวแล้วฉันสงสัยว่าคุณจะต้องการสิ่งใดสิ่งหนึ่งเว้นแต่ว่าอัตราส่วนการเข้าถึงพิกเซลต่อส่วนขยายนั้นต่ำ


1
  • การปูกระเบื้องขนาดคงที่(พูด, 256x256 แต่มีจำนวนไม่ จำกัด )
  • จัดเตรียมแผ่นหุ้มบิตแมปที่อนุญาตให้ใช้พิกัดพิกเซลเชิงลบ (เพื่อสร้างความประทับใจว่าภาพสามารถขยายได้ในทั้งสี่ทิศทางโดยไม่ต้องคำนวณใหม่ / ซิงโครไนซ์การอ้างอิงกับค่าพิกัดที่มีอยู่)
    • คลาสบิตแมปจริง (ทำงานภายใต้ wrapper) อย่างไรก็ตามควรสนับสนุนเฉพาะพิกัด (ไม่ใช่ลบ) เท่านั้น
  • ภายใต้ wrapper ให้การเข้าถึงระดับไทล์ (ระดับบล็อก) โดยใช้ I / O ที่แม็พหน่วยความจำ
  • นอกเหนือไปMap.setPixel()และMap.getPixel()ที่แก้ไข pixel เดียวในเวลานี้ยังให้วิธีการซึ่งเล่มและปรับเปลี่ยนหนึ่งสี่เหลี่ยมผืนผ้าพิกเซลในเวลา วิธีนี้จะช่วยให้ผู้โทรเลือกรูปแบบการเข้าถึงที่มีประสิทธิภาพมากขึ้นโดยขึ้นอยู่กับข้อมูลที่มีอยู่ของผู้โทร
    • ไลบรารีเชิงพาณิชย์ยังมีวิธีการอัปเดต: พิกเซลหนึ่งแถว, คอลัมน์หนึ่งพิกเซล, โปรโมต / รวบรวมการอัปเดตและการดำเนินการตัวคำนวณเลขคณิต / ตรรกะในหนึ่งขั้นตอน (เพื่อลดการคัดลอกข้อมูล)

(อย่าปล่อยคำตอบที่เฮฮา ... )


0

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

จากนั้นหากช้าเกินไปและ / หรือใหญ่ลองใช้วิธีเร่งความเร็วตามปกติ: อาร์เรย์, เมทริกซ์, บิตแมป, การบีบอัด, แคช, การผกผัน, การจัดเก็บค่า '1' เท่านั้นเป็นต้น

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

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


3
-1: "ทำให้มันทำงานก่อนที่จะทำให้เร็ว" ไม่ใช่เหตุผลที่ดีที่จะเริ่มต้นด้วย implementaiton ที่เลวร้ายที่สุดที่เป็นไปได้ นอกจากนี้จะไม่มีรหัสที่ไม่จำเป็นต้องเปลี่ยนแปลงอย่างสมบูรณ์กับโครงสร้างข้อมูลพื้นฐานดังนั้นการทำซ้ำในระดับนั้นจึงเป็นข้อเสนอแนะที่สมบูรณ์แบบที่นี่
Michael Borgwardt

นั่นเป็นเหตุผลที่คุณมี API API จะซ่อนการใช้งานพื้นฐาน SetValue (MyMatrix, X, Y, Value) และ GetValue (MyMatrix, X, Y) จะซ่อนว่า MyMatrix เป็นอาร์เรย์แบบ 1 หรือ 2 มิติหรือรายการที่ลิงก์หรือแคชบนดิสก์หรือตาราง SQL หรืออะไรก็ตาม ผู้โทรอาจจำเป็นต้องคอมไพล์ใหม่ แต่ต้องไม่เปลี่ยนรหัส
Andy Canfield

0

ฉันเสนอการใช้งานต่อไปนี้ใน Python:

class Map(dict): pass

มันมีข้อดีดังต่อไปนี้:

  1. รับ / ชุดการเข้าถึงผ่านทางได้รับการพิจารณาmap[(1,2)]O(1)
  2. ความจำเป็นในการขยายกริดหายไปอย่างชัดเจน
  3. มีพื้นที่ว่างเล็กน้อยสำหรับแมลง
  4. อัพเกรดเป็น 3D ได้ง่ายถ้าจำเป็น

0

ถ้าคุณจริงต้องบิตแมปที่มีขนาดใด ๆ โดยพล - และฉันอะไรเฉลี่ยจาก 1x1 ไป 1000000x1000000 เอเอ็มดีขึ้นและจำเป็นต้องขยายได้ตามความต้องการ ... วิถีทางหนึ่งคือการใช้ฐานข้อมูล ในตอนแรกมันอาจจะดูเข้าใจง่าย แต่สิ่งที่คุณมีคือปัญหาในการจัดเก็บ ฐานข้อมูลจะช่วยให้คุณเข้าถึงพิกเซลแต่ละพิกเซลและจัดเก็บข้อมูลจำนวนเท่าใดก็ได้ ฉันไม่จำเป็นต้องหมายถึง SQL db, btw

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

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


อาจเป็นเซิร์ฟเวอร์แผนที่ OSGeo
rwong

0

นี่คือขั้นตอนในการทำงานอย่างถูกต้อง:

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

    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;
    };
    

เห็นได้ชัดว่าสำหรับการใช้งานจริง XSize () และ YSize () จะไม่ทำงาน แต่คุณจะต้องมี MinX (), MaxX (), MinY (), MinY (), MaxY () หรืออะไรทำนองนั้นเพื่อให้หมายเลขดัชนีสอดคล้องกัน

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