Những gì bạn đang mô tả là vấn đề phân khúc . Tôi rất tiếc phải nói rằng đó thực sự là một vấn đề chưa được giải quyết. Nhưng một phương pháp tôi muốn giới thiệu cho nó là thuật toán dựa trên đồ thị . Biểu đồ cắt biểu thị hình ảnh dưới dạng biểu đồ của các nút được kết nối cục bộ. Nó chia các thành phần được kết nối của đồ thị theo cách đệ quy sao cho đường viền giữa hai thành phần phụ có độ dài tối thiểu bằng cách sử dụng định lý Max-Flow-min-cut và thuật toán Ford Fulkerson .
Về cơ bản, bạn kết nối tất cả các gạch nước thành một biểu đồ. Gán trọng lượng cho các cạnh trong biểu đồ tương ứng với sự khác biệt giữa các ô nước liền kề. Tôi nghĩ trong trường hợp của bạn, tất cả các trọng số có thể là 1. Bạn sẽ phải chơi với các chế độ trọng lượng khác nhau để có được kết quả mong muốn. Ví dụ, bạn có thể phải thêm một số trọng lượng bao gồm kề.
Sau đó, tìm tất cả các thành phần được kết nối của biểu đồ. Đây là những biển / hồ rõ ràng và như vậy.
Cuối cùng, đối với mỗi thành phần được kết nối, chia nhỏ đệ quy thành phần sao cho các cạnh kết nối hai thành phần phụ mới có trọng lượng tối thiểu . Giữ phân chia đệ quy cho đến khi tất cả các thành phần phụ đạt kích thước tối thiểu (nghĩa là kích thước tối đa của biển) hoặc nếu các cạnh cắt hai thành phần có trọng lượng quá cao. Cuối cùng, nhãn tất cả các thành phần kết nối còn lại.
Những gì điều này sẽ làm trong thực tế là cắt biển với nhau tại các kênh, nhưng không vượt qua các đại dương lớn.
Đây là mã giả:
function SegmentGraphCut(Map worldMap, int minimumSeaSize, int maximumCutSize)
Graph graph = new Graph();
// First, build the graph from the world map.
foreach Cell cell in worldMap:
// The graph only contains water nodes
if not cell.IsWater():
continue;
graph.AddNode(cell);
// Connect every water node to its neighbors
foreach Cell neighbor in cell.neighbors:
if not neighbor.IsWater():
continue;
else:
// The weight of an edge between water nodes should be related
// to how "similar" the waters are. What that means is up to you.
// The point is to avoid dividing bodies of water that are "similar"
graph.AddEdge(cell, neighbor, ComputeWeight(cell, neighbor));
// Now, subdivide all of the connected components recursively:
List<Graph> components = graph.GetConnectedComponents();
// The seas will be added to this list
List<Graph> seas = new List<Graph>();
foreach Graph component in components:
GraphCutRecursive(component, minimumSeaSize, maximumCutSize, seas);
// Recursively subdivides a component using graph cut until all subcomponents are smaller
// than a minimum size, or all cuts are greater than a maximum cut size
function GraphCutRecursive(Graph component, int minimumSeaSize, int maximumCutSize, List<Graph> seas):
// If the component is too small, we're done. This corresponds to a small lake,
// or a small sea or bay
if(component.size() <= minimumSeaSize):
seas.Add(component);
return;
// Divide the component into two subgraphs with a minimum border cut between them
// probably using the Ford-Fulkerson algorithm
[Graph subpartA, Graph subpartB, List<Edge> cut] = GetMinimumCut(component);
// If the cut is too large, we're done. This corresponds to a huge, bulky ocean
// that can't be further subdivided
if (GetTotalWeight(cut) > maximumCutSize):
seas.Add(component);
return;
else:
// Subdivide each of the new subcomponents
GraphCutRecursive(subpartA, minimumSeaSize, maximumCutSize);
GraphCutRecursive(subpartB, minimumSeaSize, maximumCutSize);
EDIT : Nhân tiện, đây là thuật toán sẽ làm gì với ví dụ của bạn với kích thước biển tối thiểu được đặt thành khoảng 40, với kích thước cắt tối đa là 1, nếu tất cả các trọng số cạnh là 1:
Bằng cách chơi với các tham số, bạn có thể nhận được kết quả khác nhau. Chẳng hạn, kích thước cắt tối đa là 3, sẽ dẫn đến nhiều vịnh khác được chạm khắc từ các biển chính và biển số 1 sẽ bị chia nhỏ ở một nửa phía bắc và phía nam. Kích thước biển tối thiểu là 20 sẽ dẫn đến việc biển trung tâm cũng bị chia đôi.