Bitmap vô hạn [đã đóng]


10

Tôi muốn xây dựng một bitmap trong thời gian chạy. Bitmap phải có khả năng mở rộng ở tất cả các phía và truy cập pixel phải yên tĩnh hiệu quả.

Một số hình minh họa http://img546.imageshack.us/img546/4995/maptm.jpg

Giữa và sau các lệnh được hiển thị trong hình, Map.setPixel () và Map.getPixel () sẽ đặt / trả lại dữ liệu được lưu trong bitmap.

Tôi không mong đợi việc triển khai chỉ là một khái niệm về cách phân bổ bộ nhớ theo cách sao cho setPixel () / getPixel càng nhanh càng tốt.


Là trường màu xám luôn luôn là điểm (0,0) hay nó cũng có thể là tọa độ khác?
Falcon

2
Thêm chi tiết cần thiết. Là các pixel thiết lập sẽ thưa thớt? Làm thế nào chậm bạn có sẵn sàng để thực hiện các extendXphương pháp để làm cho những cái setPixelgetPixelnhanh chóng?
Peter Taylor

1
Bitmap sẽ quá lớn để phù hợp với bộ nhớ? Các hoạt động nhanh - mở rộng, setPixel (), getPixel () là gì?

1
@Falcon: Không, có đủ thời gian khả dụng
SecStone

3
Tôi đang bỏ phiếu để đóng câu hỏi này ngoài chủ đề vì câu hỏi phụ thuộc rất nhiều vào hình ảnh được bao gồm, từ đó đã bị xóa. Như hiện tại được viết, nó không có nhiều ý nghĩa.

Câu trả lời:


9

Nếu extend()hoạt động cần phải nhanh chóng hợp lý, Quadtree có thể phù hợp; nó thực sự thậm chí sẽ không yêu cầu hoạt động mở rộng rõ ràng. Phải thừa nhận rằng, nó sẽ không mang lại hiệu suất tối ưu khi truy cập ngẫu nhiên vào từng pixel, nhưng nhận xét của bạn nói rằng hoạt động chính của bạn đang lặp lại qua các pixel, mà một phần tư có thể thực hiện rất nhanh, có thể nhanh như thực hiện dựa trên ma trận (và nhanh hơn nếu việc lặp không phải lúc nào cũng xảy ra theo cùng một cách mà ma trận được đặt ra).

Yêu cầu của bạn thực sự nghe giống như bạn đang cố gắng thực hiện một thiết bị tự động di động như Trò chơi cuộc sống. Bạn có thể muốn xem Hashlife , một cách cực kỳ hiệu quả để thực hiện Trò chơi cuộc sống trên một mạng lưới vô tận. Lưu ý rằng nó dựa trên Quadtree, nhưng thực hiện một số tối ưu hóa bổ sung rất thông minh dựa trên địa phương của các quy tắc trò chơi.


Cảm ơn bạn cho ý tưởng này! Tôi sẽ làm một số thử nghiệm và sẽ báo cáo kết quả.
SecStone

2

@SecStone nói rằng có đủ thời gian có sẵn cho hoạt động mở rộng, vì vậy cách dễ nhất và hiệu quả nhất để lưu trữ pixel là sử dụng một mảng phẳng đơn hoặc một mảng hai chiều, vì sau đó pixel có thể được truy cập trong thời gian không đổi.


4
Tôi sẽ bỏ phiếu này nếu bạn đưa ra một gợi ý tốt về cách bạn nghĩ rằng việc mở rộng nên được xử lý.
Doc Brown

@Doc Brown: Nếu có đủ thời gian thì chỉ cần thay đổi mảng. Hoặc có lẽ bạn có thể làm việc gì đó với các khối và chức năng dịch cho một chỉ số mảng và chỉ số khối (cũng chạy trong thời gian không đổi).
Falcon

2

Bằng tay

Nếu bộ nhớ không phải là một tài nguyên rất thưa thớt, tôi xem xét làm việc trong các khối lớn hơn.
Đây là một số mã giả.

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

Việc thực hiện này khá ngây thơ.
Đối với một, nó tạo ra các khối trong getPixel (về cơ bản sẽ ổn khi chỉ cần trả về 0 hoặc hơn, nếu không có khối nào được xác định cho vị trí đó). Thứ hai, nó dựa trên giả định rằng bạn có một triển khai Bản đồ đủ nhanh và có thể mở rộng. Theo hiểu biết của tôi, mỗi ngôn ngữ đàng hoàng đều có một.

Ngoài ra, bạn sẽ phải chơi với kích thước chunk. Đối với bitmap dày đặc, kích thước khối lớn là tốt, đối với bitmap thưa thớt, kích thước khối nhỏ hơn là tốt hơn. Trong thực tế đối với những cái rất thưa thớt, "kích thước khối" là 1 là tốt nhất, khiến cho các "khối" tự lỗi thời và giảm cấu trúc dữ liệu thành bản đồ int của một bản đồ int của pixel.

Tắt kệ

Một giải pháp khác có thể là xem xét một số thư viện đồ họa. Họ thực sự khá giỏi trong việc vẽ một bộ đệm 2D thành một bộ đệm khác. Điều đó có nghĩa là bạn chỉ cần phân bổ một bộ đệm lớn hơn và rút bản gốc vào nó theo tọa độ.

Như một chiến lược chung: Khi có một "khối bộ nhớ tăng trưởng động", bạn nên phân bổ bội số của nó, một khi nó được sử dụng hết. Đây là bộ nhớ khá dữ dội, nhưng giảm đáng kể chi phí phân bổ và sao chép . Hầu hết các triển khai vector phân bổ gấp đôi kích thước của chúng, khi nó vượt quá. Vì vậy, đặc biệt nếu bạn sử dụng giải pháp sẵn có, đừng mở rộng bộ đệm của bạn thêm 1 pixel, vì chỉ một pixel được yêu cầu. Bộ nhớ phân bổ là giá rẻ. Tái phân bổ, sao chép và phát hành là tốn kém.


1

Chỉ cần một vài lời khuyên:

  • Nếu bạn triển khai điều này dưới dạng một mảng của một số loại tích phân (hoặc một mảng các mảng của ...), bạn có thể nên tăng mảng sao lưu theo một số bit / pixel mỗi lần để tránh phải dịch chuyển các bit khi bạn sao chép chúng. Mặt trái là bạn sử dụng nhiều không gian hơn, nhưng tỷ lệ không gian bị lãng phí giảm xuống khi bitmap trở nên lớn hơn.

  • Nếu bạn sử dụng cấu trúc dữ liệu dựa trên Bản đồ, bạn có thể giải quyết vấn đề phát triển bitmap bằng cách di chuyển các đối số tọa độ x, y của các cuộc gọi getPixelsetPixel.

  • Nếu bạn sử dụng cấu trúc dữ liệu dựa trên Bản đồ, bạn chỉ cần các mục nhập bản đồ cho "những cái". Các "số không" có thể được biểu thị bằng sự vắng mặt của một mục. Điều này giúp tiết kiệm một lượng không gian đáng kể, đặc biệt nếu bitmap chủ yếu là số không.

  • Bạn không cần sử dụng Bản đồ. Bạn có thể mã hóa một intcặp x, y thành một long. Một quy trình tương tự có thể được sử dụng để ánh xạ một mảng các mảng thành một mảng.


Cuối cùng, bạn cần cân bằng 3 điều:

  1. hiệu suất của getPixelsetPixel,
  2. hiệu suất của các extend*hoạt động, và
  3. việc sử dụng không gian.

1

Trước khi thử bất cứ điều gì phức tạp hơn và trừ khi bạn không thể giữ mọi thứ trong bộ nhớ, hãy giữ mọi thứ đơn giản và sử dụng một mảng hai chiều cùng với thông tin về nguồn gốc của hệ tọa độ của bạn. Để mở rộng nó, hãy sử dụng cùng một chiến lược như, ví dụ, vectơ C ++: phân biệt giữa kích thước thực của mảng của bạn và dung lượng của mảng và mở rộng dung lượng trong các khối khi đạt đến giới hạn. "dung lượng" ở đây phải được xác định theo các khoảng (from_x, to_x), (from_y, to_y).

Điều này có thể cần một sự phân bổ lại hoàn toàn bộ nhớ theo thời gian, nhưng miễn là điều này không xảy ra quá thường xuyên, nó có thể đủ nhanh cho mục đích của bạn (thực tế, bạn phải thử / hồ sơ này).


1

Cách nhanh nhất tuyệt đối để thực hiện truy cập pixel là một mảng hai chiều của các pixel có địa chỉ riêng lẻ.

Đối với tiện ích mở rộng, hãy bắt đầu với một triển khai đơn giản, phân bổ lại và sao chép mọi lúc (vì dù sao bạn cũng sẽ cần mã đó). Nếu hồ sơ không chỉ ra rằng bạn dành nhiều thời gian cho nó, thì không cần phải tinh chỉnh thêm nữa.

Nếu cấu hình cho thấy cần phải giảm số lượng phân bổ lại và bạn không bị hạn chế về bộ nhớ, hãy xem xét phân bổ quá mức theo tỷ lệ phần trăm cho mỗi hướng và lưu trữ phần bù vào gốc. (Ví dụ, nếu bạn bắt đầu một bitmap mới tại 1x1 và phân bổ một mảng 9x9 để giữ nó, ban đầu xyhiệu số sẽ 4.) The trade-off ở đây là phải làm thêm toán trong truy cập pixel để áp dụng bù đắp.

Nếu tiện ích mở rộng thực sự đắt tiền, bạn có thể thử một hoặc cả hai:

  • Xử lý các phần mở rộng dọc và ngang khác nhau. Việc mở rộng một mảng theo chiều dọc theo bất kỳ hướng nào có thể được thực hiện bằng cách phân bổ một khối mới và thực hiện một bản sao duy nhất của toàn bộ mảng hiện tại vào phần bù thích hợp trong bộ nhớ mới. So sánh điều đó với các tiện ích mở rộng theo chiều ngang, nơi bạn phải thực hiện thao tác đó một lần trên mỗi hàng vì dữ liệu hiện tại không liền kề trong khối mới.

  • Theo dõi số lượng thường xuyên nhất và hướng gia hạn. Sử dụng thông tin đó để chọn kích thước mới và phần bù sẽ làm giảm khả năng phải thực hiện phân bổ lại và sao chép cho bất kỳ tiện ích mở rộng nào.

Cá nhân, tôi nghi ngờ bạn sẽ cần một trong hai thứ đó trừ khi tỷ lệ truy cập mở rộng pixel thấp.


1
  • Ốp lát kích thước không đổi (giả sử, 256x256, nhưng với số lượng gạch vô hạn)
  • Cung cấp trình bao bọc bitmap cho phép tọa độ pixel âm (để tạo ấn tượng rằng hình ảnh có thể được mở rộng theo cả bốn hướng mà không phải tính toán lại / đồng bộ hóa các tham chiếu đến các giá trị tọa độ hiện có)
    • Tuy nhiên, lớp bitmap thực tế (hoạt động bên dưới trình bao bọc), tuy nhiên, chỉ nên hỗ trợ tọa độ tuyệt đối (không âm).
  • Trong trình bao bọc, cung cấp quyền truy cập cấp độ khối (cấp độ khối) bằng cách sử dụng I / O được ánh xạ bộ nhớ
  • Ngoài Map.setPixel()Map.getPixel()sửa đổi từng pixel một, cũng cung cấp các phương thức sao chép và sửa đổi một hình chữ nhật của pixel tại một thời điểm. Điều này sẽ cho phép người gọi chọn hình thức truy cập hiệu quả hơn tùy thuộc vào thông tin có sẵn cho người gọi.
    • Các thư viện thương mại cũng cung cấp các phương thức để cập nhật: một hàng pixel, một cột pixel, phân tán / thu thập các cập nhật và các thao tác làm mờ số học / logic trong một bước (để giảm thiểu sao chép dữ liệu).

(Chúng ta đừng nêu lên những câu trả lời vui nhộn ...)


0

Việc triển khai linh hoạt nhất và có lẽ đáng tin cậy là một danh sách được liên kết với các cấu trúc cho tọa độ x, tọa độ y và giá trị bit. Tôi sẽ xây dựng nó đầu tiên và làm cho nó hoạt động.

Sau đó, nếu nó quá chậm và / hoặc lớn, hãy thử các cách thông thường để tăng tốc nó: mảng, ma trận, bitmap, nén, bộ đệm, đảo ngược, chỉ lưu trữ các giá trị '1', v.v.

Dễ dàng thực hiện một triển khai chính xác chậm nhanh hơn là sửa lỗi triển khai nhanh lỗi. Và trong khi thử nghiệm triển khai thứ hai 'nhanh' của bạn, bạn đã có một tiêu chuẩn giới thiệu để so sánh với nó.

Và, ai biết được, bạn sẽ phát hiện ra rằng phiên bản chậm là đủ nhanh. Miễn là toàn bộ cấu trúc vừa vặn trong bộ nhớ, mọi thứ đã nhanh đến mức đáng kinh ngạc.


3
-1: "làm cho nó hoạt động trước khi làm cho nó nhanh" không phải là một lý do tốt để bắt đầu với việc triển khai tồi tệ nhất có thể. Bên cạnh đó, thực tế sẽ không có mã nào không cần phải thay đổi hoàn toàn với cấu trúc dữ liệu cơ bản, vì vậy việc lặp lại ở cấp độ đó là một gợi ý hoàn toàn asinine ở đây.
Michael Borgwardt

Đó là lý do tại sao bạn có API. API ẩn việc thực hiện cơ bản. SetValue (MyMatrix, X, Y, Value) và GetValue (MyMatrix, X, Y) ẩn dù MyMatrix là mảng 1 hoặc 2, hoặc một danh sách được liên kết, hoặc được lưu trên bộ đệm, hoặc bảng SQL, hoặc bất cứ điều gì. Người gọi có thể cần biên dịch lại, nhưng không thay đổi mã.
Andy Canfield

0

Tôi đề xuất cách thực hiện sau trong Python:

class Map(dict): pass

Nó có những ưu điểm sau:

  1. Nhận / Đặt quyền truy cập thông qua map[(1,2)]có thể được xem xét O(1).
  2. Sự cần thiết phải mở rộng rõ ràng lưới biến mất.
  3. Có rất ít không gian cho lỗi.
  4. Nó dễ dàng được nâng cấp lên 3D, nếu cần thiết.

0

Nếu bạn thực sự cần một bitmap có kích thước tùy ý - và ý tôi là mọi thứ từ 1x1 đến 1000000x1000000 đều tăng và cần mở rộng theo yêu cầu ... một cách có thể là sử dụng cơ sở dữ liệu. Nó có vẻ phản trực giác lúc đầu, nhưng những gì bạn thực sự có là một vấn đề lưu trữ. Một cơ sở dữ liệu sẽ cho phép bạn truy cập từng pixel và lưu trữ bất kỳ lượng dữ liệu nào. Tôi không nhất thiết có nghĩa là một db db, btw.

Nó sẽ đủ nhanh cho mục đích của bạn? Điều đó tôi không thể trả lời, vì không có ngữ cảnh nào ở đây liên quan đến những gì bạn đang làm với bitmap này. Nhưng nếu điều này là để hiển thị màn hình, hãy xem xét rằng bạn thường chỉ cần kéo lại các hàng quét bổ sung để hiển thị khi cuộn màn hình, không phải tất cả dữ liệu.

Điều đó đang được nói, tôi không thể giúp đỡ nhưng tự hỏi nếu bạn đang đi về một cái gì đó sai. Có lẽ bạn nên thay vào đó bằng cách sử dụng đồ họa dựa trên vector và theo dõi các mục riêng lẻ trong bộ nhớ, và sau đó hiển thị một bitmap chỉ lớn như cần thiết cho màn hình?


Có lẽ một máy chủ bản đồ OSGeo.
rwong

0

Đây là các bước để làm cho nó hoạt động đúng:

  1. sử dụng chuyển tiếp từ giao diện này sang giao diện khác để xây dựng một cây các đối tượng cùng thể hiện bitmap
  2. mỗi "phần mở rộng" của bitmap sẽ phân bổ bộ nhớ riêng của nó và cung cấp giao diện khác cho nó
  3. ví dụ thực hiện sẽ là:

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

Rõ ràng để triển khai thực sự, XSize () và YSize () sẽ không hoạt động, nhưng bạn sẽ cần MinX (), MaxX (), MinY (), MaxY () hoặc một cái gì đó tương tự để giữ cho số chỉ mục nhất quán.

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.