Thuật toán để giảm mặt nạ bitmap vào danh sách các hình chữ nhật?


7

Trước khi tôi dành một buổi chiều để tự viết bài này, tôi nghĩ tôi đã hỏi liệu đã có một triển khai chưa - chỉ là một tài liệu tham khảo. Hình ảnh đầu tiên là một ví dụ về mặt nạ bitmap mà tôi muốn biến thành một danh sách các hình chữ nhật. Một thuật toán xấu sẽ trả về mọi pixel được đặt dưới dạng hình chữ nhật 1x1. Một thuật toán tốt sẽ trông giống như hình ảnh thứ hai, nơi nó trả về tọa độ của hình chữ nhật màu cam và màu đỏ. Thực tế là các hình chữ nhật trùng nhau không thành vấn đề, chỉ là có hai cái được trả về.

Tóm lại, kết quả lý tưởng sẽ là hai hình chữ nhật này (x, y, w, h): [ { 3, 1, 2, 6 }, { 1, 3, 6, 2 } ]

Câu trả lời:


6

Tôi không biết bất kỳ thuật toán nào cho vấn đề cụ thể đó, nhưng đây là một ý tưởng làm thế nào bạn có thể giải quyết vấn đề này:

  1. Hãy để XYđược0
  2. Tìm kiếm và tăng dần (X, Y)trong bitmap của bạn và truy cập từng pixel từ trái sang phải, từ trên xuống dưới cho đến khi bạn nhấn một pixel phía trước không mong muốn.
  3. Tìm kiếm từ (X, Y)bên phải, cho đến khi bạn nhấn pixel nền hoặc pixel đã truy cập. Giữ số lượng pixel tìm thấy là WIDTH. Đặt HEIGHTthành 1.
  4. Bắt đầu một tìm kiếm khác tại ( X, Y + HEIGHT)và tìm kiếm WIDTHpixel. Nếu tất cả các pixel này thuộc về nền trước, hãy tăng HEIGHTtheo 1.
  5. Lặp lại bước 3 cho đến khi bạn nhấn một pixel nền trong tìm kiếm. Sau đó, bạn thêm hình chữ nhật (được tạo bởi X, YWIDTH, HEIGHT) vào danh sách các hình chữ nhật được tìm thấy và đánh dấu tất cả các pixel mà nó kèm theo là đã truy cập.
  6. Tăng Xtheo WIDTHvà tiếp tục tìm kiếm của bạn (đi đến bước 2 ). Tìm kiếm của bạn kết thúc khi bạn đạt đến pixel dưới cùng bên phải của bitmap của bạn.

Thuật toán sẽ tạo ra 3 hình chữ nhật cho ví dụ cụ thể của bạn. Để có kết quả chỉ với 2 hình chữ nhật, bạn sẽ phải thay đổi bước 3 để nó chỉ dừng lại ở một pixel nền và bỏ qua các pixel đã truy cập. Tuy nhiên, điều này có khả năng dẫn đến rất nhiều hình chữ nhật chồng chéo. Vì vậy, bước 3 có thể được tối ưu hóa hơn nữa bằng cách theo dõi các pixel phía trước không được xem và truy cập. Nếu pixel cuối cùng (trước pixel nền) là pixel được truy cập, hãy sử dụng pixel nền trước không mong muốn cuối cùng để lấy WIDTH.

Cập nhật: Tôi đã đi trước và thực hiện các thuật toán tôi đề xuất. Đây là bản demo tương tác nơi bạn có thể tự thử (nhấp vào "Chơi flash toàn màn hình", nếu màn hình quá nhỏ).

Điều khiển: Nhấp và kéo trong vùng màu đen để vẽ pixel. Giữ Shiftvà vẽ để xóa. Nhấp đúp để xóa khung vẽ.

Đây là nguồn đầy đủ của flash demo .


Câu hỏi này gần đây đã bị va chạm trong lịch sử SO của tôi, nhưng thật lòng tôi không có hồi ức nào về việc hỏi câu hỏi này, chứ đừng nói tại sao tôi cần nó ngay từ đầu. :) Điều đó nói rằng, thuật toán và nguồn của bạn có vẻ phù hợp với hóa đơn cho ... bất cứ điều gì tôi muốn. Cảm ơn.
moswald

3

Vấn đề này chưa được xác định rõ. Vì bạn đã cung cấp một ví dụ đơn giản, đơn giản, bạn đã bỏ lỡ một số khả năng logic và các trường hợp góc quan trọng ở đây (hoặc, bạn chỉ không loại trừ chúng một cách rõ ràng). Chẳng hạn, vì bạn không đề cập đến ứng dụng được đề xuất cho việc này, nên bạn đã không chỉ định điều quan trọng hơn: Chia toàn bộ bản đồ thành các hình chữ nhật độc quyền có diện tích càng lớn càng tốt (Vấn đề đóng gói, có tùy ý số lượng giải pháp), hoặc đơn giản là nhận các hình chữ nhật cạnh phù hợp trong mỗi x và y, như bạn dường như ngụ ý ở trên. Tôi giả sử sau.

Hãy xem xét trường hợp bạn điền vào mỗi 4 ô xếp ngay lập tức ở các góc của bitmask hình chữ "dấu cộng" mà bạn đã vẽ ở trên ([2, 2] là trên cùng). Bây giờ các hình chữ nhật tối ưu là gì? Chỉ xem xét các cột, bạn sẽ thích hẹp hơn, các đường dài hơn với các cạnh mỏng hơn sang hai bên, hoặc hình vuông lớn hơn, rộng hơn mà bây giờ có thể được tìm thấy ở giữa hình ảnh, với các hình vuông ở trên và dưới?

Dù sao, giả sử các nhóm phù hợp với cạnh dài nhất có thể, hãy coi đây là giải pháp thử đầu tiên:

  • Chia bitmask thành các cột. Mỗi cột bắt đầu ở một số giá trị y0 và kết thúc ở một giá trị khác y1. Lưu trữ mỗi cột dưới dạng một tuple (y0, y1) trong danh sách.
  • Cột: Đánh giá danh sách. Tìm nơi các bộ dữ liệu liền kề có cùng y0 VÀ y1 giữa chúng (ví dụ: cột A bắt đầu tại y0 = 1 và kết thúc tại y1 = 4; cột B cũng vậy). Chừng nào mỗi cột liên tiếp DOES có những cái này giống nhau, hãy tăng chiều rộng của hình chữ nhật bạn đã tạo để thể hiện chúng, bằng một. Ngay sau khi họ KHÔNG, kết thúc hình chữ nhật này và lưu trữ nó, và tạo một cái mới cho bất kỳ nhóm cột tiếp theo nào.

Lặp lại cả hai bước trên cho các hàng, chính xác như đối với các cột (thay vào đó sử dụng x0 và x1 và một danh sách riêng).

Bây giờ bạn sẽ có một số danh sách các hình chữ nhật có cạnh rõ ràng. Tuy nhiên, chúng có thể không tối ưu cho ứng dụng của bạn; nếu đây là trường hợp, bạn sẽ cần một hệ thống heuristic cho phép đóng gói hiệu quả hơn. Xem vấn đề đóng gói để biết thêm.

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.