示例#1
0
        // 입력 이미지로 섬 데이터를 만든다.
        // 섬 데이터는 유니티에서 사용하게 된다.
        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);
            }
        }
示例#2
0
    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);
    }