// 입력 이미지로 섬 데이터를 만든다. // 섬 데이터는 유니티에서 사용하게 된다. static string ExecuteDetermineIsland(string sourceFileName, string startFileName) { Logger.WriteLine($"Running {nameof(ExecuteDetermineIsland)}"); // 이미지 파일을 열어봅시다~ using (var image = Image.Load <Rgba32>(sourceFileName)) { // 색상 별 픽셀 수 var pixelCountByColor = new Dictionary <Rgba32, int>(); // Min Point 별 (섬 별) 섬 색상 var islandColorByMinPoint = new Dictionary <Vector2Int, Rgba32>(); // Min Point 별 (섬 별) 섬 픽셀 수(면적) var islandPixelAreaByMinPoint = new Dictionary <Vector2Int, int>(); // 색상 별 섬 수 var islandCountByColor = new Dictionary <Rgba32, int>(); // 픽셀 수(면적) 별 섬 수 var islandCountByPixelArea = new Dictionary <int, int>(); // Min Point 별 (섬 별) Max Rect var maxRectByMinPoint = new Dictionary <uint, ulong>(); // 각 픽셀에 대해서 반복한다. for (var h = 0; h < image.Height; h++) { for (var w = 0; w < image.Width; w++) { var pixelColor = image[w, h]; if (pixelColor == Black) { // 경계선 색상(검은색)이면 할 일이 없다. } else { // (w, h) 좌표부터 검은색이 아닌 색을 검은색으로 채우면서 픽셀 수집한다. // 수집한 모든 픽셀은 points에, points의 min point는 반환값으로 받는다. var coord = new Vector2Int(w, h); var fillMinPoint = FloodFill.ExecuteFillIfNotBlack(image, coord, Black, out var pixelArea, out var points, out var originalColors); if (fillMinPoint != new Vector2Int(image.Width, image.Height)) { if (originalColors.Count > 1) { // 한 섬에 색상이 여러 가지라면 가장 많은 색상이 최종 색깔이 되도록 하자. // 주로 경계선 주변에서 경계선과 섬 색깔이 블렌딩되면서 다른 색깔이 되는 패턴이다. //var prominentColor = originalColors.Aggregate((l, r) => l.Value > r.Value ? l : r).Key; var prominentColor = originalColors.OrderByDescending(e => e.Value) .First(e => e.Key != White).Key; pixelColor = prominentColor; // foreach (var originalColor in originalColors) { // Logger.WriteLine($"{originalColor.Key} = {originalColor.Value}"); // } // throw new Exception($"Island color is not uniform! It has {originalColors.Count} colors in it! coord={coord}"); } if (originalColors.Count == 0) { throw new Exception("Island color is empty. Is this possible?"); } if (pixelColor == White) { throw new Exception("Island color is WHITE?! Fix it!"); } IncreaseCountOfDictionaryValue(pixelCountByColor, pixelColor); islandColorByMinPoint[fillMinPoint] = pixelColor; islandPixelAreaByMinPoint[fillMinPoint] = pixelArea; IncreaseCountOfDictionaryValue(islandCountByPixelArea, pixelArea); IncreaseCountOfDictionaryValue(islandCountByColor, pixelColor); var xMax = points.Max(e => e.x); var xMin = points.Min(e => e.x); var yMax = points.Max(e => e.y); var yMin = points.Min(e => e.y); var subRectW = xMax - xMin + 1; var subRectH = yMax - yMin + 1; var A = Enumerable.Range(0, subRectH).Select(e => new int[subRectW]).ToArray(); foreach (var point in points) { A[point.y - yMin][point.x - xMin] = 1; } var area = MaxSubRect.MaxRectangle(subRectH, subRectW, A, out var beginIndexR, out var endIndexR, out var beginIndexC, out var endIndexC); Logger.WriteLine( $"Sub Rect: area:{area} [({yMin + beginIndexR},{xMin + beginIndexC})-({yMin + endIndexR},{xMin + endIndexC})]"); maxRectByMinPoint[Vector2IntToUInt32(fillMinPoint)] = GetRectRange( xMin + beginIndexC, yMin + beginIndexR, xMin + endIndexC, yMin + endIndexR); } else { throw new Exception("Invalid fill min point!"); } } } } Logger.WriteLine($"Total Pixel Count: {image.Width * image.Height}"); pixelCountByColor.TryGetValue(White, out var whiteCount); Logger.WriteLine($"White Count: {whiteCount}"); if (islandColorByMinPoint.Count < 1) { throw new IslandCountException(); } var islandIndex = 1; // 0번째 island는 외곽선을 위해 예비한 값이다. foreach (var kv in islandColorByMinPoint.OrderBy(kv => Vector2IntToUInt32(kv.Key))) { Logger.WriteLine( $"Island #{islandIndex} fillMinPoint={kv.Key}, color={kv.Value}, area={islandPixelAreaByMinPoint[kv.Key]}"); islandIndex++; } var colorCountIndex = 1; // 0 번째 컬러는 외곽선을 위해 예비한 블랙 값이다. foreach (var kv in pixelCountByColor) { islandCountByColor.TryGetValue(kv.Key, out var islandCount); Logger.WriteLine( $"Color #{colorCountIndex} {kv.Key}: pixelCount={kv.Value}, islandCount={islandCount}"); colorCountIndex++; if (kv.Key == White) { throw new Exception("Palette color should not be white!"); } } var pixelAreaCountIndex = 0; foreach (var kv in islandCountByPixelArea.OrderByDescending(kv => kv.Key)) { Logger.WriteLine($"Pixel Area #{pixelAreaCountIndex} {kv.Key}: islandCount={kv.Value}"); pixelAreaCountIndex++; } var stageData = new StageData(); var islandIndex2 = 1; foreach (var kv in islandPixelAreaByMinPoint.OrderBy(kv => Vector2IntToUInt32(kv.Key))) { var p = Vector2IntToUInt32(kv.Key); stageData.islandDataByMinPoint[p] = new IslandData { index = islandIndex2, pixelArea = islandPixelAreaByMinPoint[kv.Key], rgba = Rgba32ToUInt32(islandColorByMinPoint[kv.Key]), maxRect = maxRectByMinPoint[p] }; islandIndex2++; } var outputPath = Path.ChangeExtension(startFileName, "bytes"); if (string.IsNullOrEmpty(outputPathReplaceFrom) == false && string.IsNullOrEmpty(outputPathReplaceTo) == false) { outputPath = outputPath.Replace(outputPathReplaceFrom, outputPathReplaceTo); } using (var stream = File.Create(outputPath)) { var formatter = new BinaryFormatter(); formatter.Serialize(stream, stageData); stream.Close(); } Logger.WriteLine($"{stageData.islandDataByMinPoint.Count} islands loaded."); Logger.WriteLine($"Written to {outputPath}"); return(outputPath); } }
public static void TestMaxHist() { var beginIndex = 0; var endIndex = 0; var area = 0; area = MaxSubRect.MaxHist(new int[] { 100, 0, 6, 2, 5, 4, 5, 1, 6 }, out beginIndex, out endIndex); AreEqual(area, 100); AreEqual(beginIndex, 0); AreEqual(endIndex, 1); area = MaxSubRect.MaxHist(new int[] { 6, 2, 5, 4, 5, 1, 6 }, out beginIndex, out endIndex); AreEqual(area, 12); AreEqual(beginIndex, 2); AreEqual(endIndex, 5); area = MaxSubRect.MaxHist(new int[] { 6, 2, 5, 4, 5, 1, 6, 100 }, out beginIndex, out endIndex); AreEqual(area, 100); AreEqual(beginIndex, 7); AreEqual(endIndex, 8); area = MaxSubRect.MaxHist(new int[] { 100, 100, 6, 2, 5, 4, 5, 1, 6 }, out beginIndex, out endIndex); AreEqual(area, 200); AreEqual(beginIndex, 0); AreEqual(endIndex, 2); area = MaxSubRect.MaxHist(new int[] { 100, 100, 100, 100, 200, 200, 5, 1, 6 }, out beginIndex, out endIndex); AreEqual(area, 600); AreEqual(beginIndex, 0); AreEqual(endIndex, 6); area = MaxSubRect.MaxHist(new int[] { 1 }, out beginIndex, out endIndex); AreEqual(area, 1); AreEqual(beginIndex, 0); AreEqual(endIndex, 1); area = MaxSubRect.MaxHist(new int[] { 5, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, out beginIndex, out endIndex); AreEqual(area, 10); AreEqual(beginIndex, 0); AreEqual(endIndex, 10); area = MaxSubRect.MaxHist(new int[] { 1, 1, 1, 1, 1, 5, 1, 1, 1, 1 }, out beginIndex, out endIndex); AreEqual(area, 10); AreEqual(beginIndex, 0); AreEqual(endIndex, 10); area = MaxSubRect.MaxHist(new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 5, 1 }, out beginIndex, out endIndex); AreEqual(area, 10); AreEqual(beginIndex, 0); AreEqual(endIndex, 10); area = MaxSubRect.MaxHist(new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 5 }, out beginIndex, out endIndex); AreEqual(area, 10); AreEqual(beginIndex, 0); AreEqual(endIndex, 10); area = MaxSubRect.MaxHist(new int[] { 1, 1, 5 }, out beginIndex, out endIndex); AreEqual(area, 5); AreEqual(beginIndex, 2); AreEqual(endIndex, 3); area = MaxSubRect.MaxHist(new int[] { 1, 7, 2 }, out beginIndex, out endIndex); AreEqual(area, 7); AreEqual(beginIndex, 1); AreEqual(endIndex, 2); area = MaxSubRect.MaxHist(new int[] { 1, 7, 2, 8, 3 }, out beginIndex, out endIndex); AreEqual(area, 8); AreEqual(beginIndex, 3); AreEqual(endIndex, 4); area = MaxSubRect.MaxHist(new int[] { 1, 7, 2, 8, 3, 1, 1, 1, 1, 1, 1, 1, 1 }, out beginIndex, out endIndex); AreEqual(area, 13); AreEqual(beginIndex, 0); AreEqual(endIndex, 13); area = MaxSubRect.MaxHist(new int[] { }, out beginIndex, out endIndex); AreEqual(area, 0); AreEqual(beginIndex, 0); AreEqual(endIndex, 0); area = MaxSubRect.MaxHist(new int[] { 1, 0, 1 }, out beginIndex, out endIndex); AreEqual(area, 1); AreEqual(beginIndex, 0); AreEqual(endIndex, 1); }