Пример #1
0
        unsafe HashSet <int> CreateLabelColorMapping()
        {
            HashSet <int> colors = new HashSet <int>();

            for (int i = 0; i < denseMap.Length; i++)
            {
                denseMap[i]  = 0;
                strokeMap[i] = -1;
            }

            using (BitmapIterator iter = new BitmapIterator(preBrushedImage, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb))
            {
                int *data = (int *)iter.PixelData;
                for (int y = 0; y < preBrushedImage.Height; y++)
                {
                    for (int x = 0; x < preBrushedImage.Width; x++)
                    {
                        int idx   = x + y * preBrushedImage.Width;
                        int color = data[idx];
                        if (color != 0)
                        {
                            denseMap[idx]  = hardness * K;
                            strokeMap[idx] = color;
                            colors.Add(color);
                        }
                    }
                }
            }

            return(colors);
        }
        // 各ピクセルを左上端としてエッジ画像に充填できる正方形の大きさ
        // DPで求める
        unsafe int[] CalcBallSize(Bitmap mono, out int maxSize, out int minSize)
        {
            // 各ピクセルを左上端としてエッジ画像に充填できる正方形の大きさ
            // DPで求める
            maxSize = 0;
            minSize = int.MaxValue;
            int[] ballSize = new int[mono.Width * mono.Height];
            using (BitmapIterator iter = new BitmapIterator(mono, ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed))
            {
                byte *data = (byte *)iter.PixelData;
                // 右辺
                for (int y = mono.Height - 1; y >= 0; y--)
                {
                    int  pixelIdx = mono.Width - 1 + y * iter.Stride;
                    byte val      = data[pixelIdx];
                    if (val <= edgeThreshold)
                    {
                        int ballIdx = mono.Width - 1 + y * mono.Width;
                        ballSize[ballIdx] = 1;
                    }
                }
                // 下辺
                for (int x = mono.Width - 1; x >= 0; x--)
                {
                    int  pixelIdx = x + (mono.Height - 1) * iter.Stride;
                    byte val      = data[pixelIdx];
                    if (val <= edgeThreshold)
                    {
                        int ballIdx = x + (mono.Height - 1) * mono.Width;
                        ballSize[ballIdx] = 1;
                    }
                }
                // それ以外
                for (int y = mono.Height - 2; y >= 0; y--)
                {
                    for (int x = mono.Width - 2; x >= 0; x--)
                    {
                        int  idx = x + y * iter.Stride;
                        byte val = data[idx];
                        if (val <= 10)
                        {
                            int ballIdx  = x + y * mono.Width;
                            int ballIdxR = (x + 1) + y * mono.Width;
                            int ballIdxB = x + (y + 1) * mono.Width;
                            int ballIdxD = (x + 1) + (y + 1) * mono.Width;
                            ballSize[ballIdx] = 1 + Math.Min(ballSize[ballIdxR], Math.Min(ballSize[ballIdxB], ballSize[ballIdxD]));
                            maxSize           = Math.Max(maxSize, ballSize[ballIdx]);
                            minSize           = Math.Min(minSize, ballSize[ballIdx]);
                        }
                    }
                }
            }

            return(ballSize);
        }
Пример #3
0
        unsafe public static void Load(GraphicsDevice GraphicsDevice, System.Drawing.Bitmap bmp, out Texture2D texture, out Color[] texData, bool reverse = false)
        {
            if (GraphicsDevice == null || bmp == null)
            {
                texture = null;
                texData = null;
                return;
            }

            using (BitmapIterator iter = new BitmapIterator(bmp, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb))
            {
                Stopwatch sw = Stopwatch.StartNew();

                texData = new Color[bmp.Width * bmp.Height];
                byte *data   = (byte *)iter.PixelData;
                int   texIdx = 0;

                Console.WriteLine("0: " + sw.ElapsedMilliseconds + "ms");
                sw.Restart();

                for (int y = 0; y < bmp.Height; y++)
                {
                    for (int x = 0; x < bmp.Width; x++)
                    {
                        int idx = !reverse ?
                                  4 * x + y * iter.Stride :
                                  4 * (bmp.Width - 1 - x) + y * iter.Stride;
                        if (data[idx + 3] == 0)
                        {
                            texData[texIdx] = Color.Transparent;
                        }
                        else
                        {
                            texData[texIdx].R = data[idx + 2];
                            texData[texIdx].G = data[idx + 1];
                            texData[texIdx].B = data[idx + 0];
                            texData[texIdx].A = data[idx + 3];
                        }
                        texIdx++;
                    }
                }


                Console.WriteLine("1: " + sw.ElapsedMilliseconds + "ms");
                sw.Restart();

                texture = new Texture2D(GraphicsDevice, bmp.Width, bmp.Height);
                texture.SetData(texData);

                Console.WriteLine("2: " + sw.ElapsedMilliseconds + "ms");
                sw.Restart();
            }
        }
        /// AnnotationSketch:f8bd4fff-3382-4282-b7b1-44f6236f3ff9.txt
        unsafe float SqColorDist(BitmapIterator iter, int x0, int y0, int x1, int y1)
        {
            byte *data    = (byte *)iter.PixelData;
            int   offset0 = 4 * x0 + y0 * iter.Stride;
            int   offset1 = 4 * x1 + y1 * iter.Stride;
            byte  r0      = data[offset0 + 2];
            byte  g0      = data[offset0 + 1];
            byte  b0      = data[offset0 + 0];
            byte  r1      = data[offset1 + 2];
            byte  g1      = data[offset1 + 1];
            byte  b1      = data[offset1 + 0];
            float dr      = r1 - r0;
            float dg      = g1 - g0;
            float db      = b1 - b0;
            float sqDist  = dr * dr + dg * dg + db * db;

            return(sqDist);
        }
 unsafe void FlashArrayToBitmap(int[] array, int w, int h, Bitmap bmp)
 {
     using (var iter = new BitmapIterator(bmp, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb))
     {
         byte *data = (byte *)iter.PixelData;
         for (int y = 0; y < h; y++)
         {
             for (int x = 0; x < w; x++)
             {
                 int idx   = 4 * x + y * iter.Stride;
                 int arIdx = x + y * w;
                 data[idx + 0] = (byte)array[arIdx];
                 data[idx + 1] = (byte)array[arIdx];
                 data[idx + 2] = (byte)array[arIdx];
                 data[idx + 3] = (byte)255;
             }
         }
     }
 }
Пример #6
0
        unsafe public void Execute()
        {
            HashSet <int> colors = CreateLabelColorMapping();

            CalcEdgeWeights();
            for (int i = 0; i < labelMap.Length; i++)
            {
                labelMap[i] = -1;
            }

            foreach (int color in colors.Take(colors.Count - 1))
            {
                UpdateEdgeWeights();
                IntPtr grid = CreateGraph(edgeImage.Width, edgeImage.Height);
                SetEdgeWeights(grid, edgeImage.Width, edgeImage.Height, edgeHorizon, edgeVertical);
                Execute(grid, color, edgeImage.Width, edgeImage.Height, denseMap, strokeMap, labelMap);
                DeleteGraph(grid);
            }

            using (BitmapIterator brushIter = new BitmapIterator(brushedImage, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb))
                //            using (BitmapIterator preBrushIter = new BitmapIterator(preBrushedImage, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb))
                using (BitmapIterator intIter = new BitmapIterator(edgeImage, ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed))
                {
                    int *data = (int *)brushIter.PixelData;
                    //                int* predata = (int*)preBrushIter.PixelData;
                    byte *intData = (byte *)intIter.PixelData;
                    for (int y = 0; y < brushedImage.Height; y++)
                    {
                        for (int x = 0; x < brushedImage.Width; x++)
                        {
                            int idx    = x + y * brushedImage.Width;
                            int intIdx = x + y * intIter.Stride;
                            data[idx] =
                                labelMap[idx];
                            //                            intData[intIdx] >= 10 ? intData[intIdx] : labelMap[idx];
                        }
                    }
                }
        }
Пример #7
0
 unsafe void CalcEdgeWeights()
 {
     using (BitmapIterator iter = new BitmapIterator(edgeImage, ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed))
     {
         byte *data = (byte *)iter.PixelData;
         for (int y = 0; y < edgeImage.Height; y++)
         {
             for (int x = 0; x < edgeImage.Width - 1; x++)
             {
                 edgeHorizon[x + y * edgeImage.Width] =
                     1 + K * Math.Pow(data[x + y * iter.Stride] * colorScale, gamma);
             }
         }
         for (int x = 0; x < edgeImage.Width; x++)
         {
             for (int y = 0; y < edgeImage.Height - 1; y++)
             {
                 edgeVertical[x + y * edgeImage.Width] =
                     1 + K * Math.Pow(data[x + y * iter.Stride] * colorScale, gamma);
             }
         }
     }
 }
        void AddToContour(HashSet <int> uncoloredPixels, PriorityQueue <ContourPixel> contour, int[] labelMap, int x, int y, ContourPixel src, BitmapIterator orgIter, float threshold)
        {
            int idx = x + y * edgeImage.Width;

            if (uncoloredPixels.Contains(idx) && SqColorDist(orgIter, src.x, src.y, x, y) <= threshold)
            {
                contour.Push(new ContourPixel(x, y, 0));
                labelMap[idx] = labelMap[src.x + src.y * edgeImage.Width];
                uncoloredPixels.Remove(idx);
            }
        }
        /// AnnotationSketch:regiongrowing.png
        unsafe void RegionGrowing(HashSet <int> uncoloredPixels, int[] labelMap, BitmapIterator orgIter)
        {
            float threshold = sqColorDistThreshold;
            PriorityQueue <ContourPixel> contour = new PriorityQueue <ContourPixel>();

            while (uncoloredPixels.Count >= 1 && threshold <= float.MaxValue / 4)
            {
                foreach (int idx in uncoloredPixels)
                {
                    int x, y;
                    y = Math.DivRem(idx, edgeImage.Width, out x);
                    if (0 <= x - 1 && !uncoloredPixels.Contains(idx - 1) && labelMap[idx - 1] >= 1)
                    {
                        contour.Push(new ContourPixel(x - 1, y, 0));
                    }
                    if (0 <= y - 1 && !uncoloredPixels.Contains(idx - edgeImage.Width) && labelMap[idx - edgeImage.Width] >= 1)
                    {
                        contour.Push(new ContourPixel(x, y - 1, 0));
                    }
                    if (edgeImage.Width > x + 1 && !uncoloredPixels.Contains(idx + 1) && labelMap[idx + 1] >= 1)
                    {
                        contour.Push(new ContourPixel(x + 1, y, 0));
                    }
                    if (edgeImage.Height > y + 1 && !uncoloredPixels.Contains(idx + edgeImage.Width) && labelMap[idx + edgeImage.Width] >= 1)
                    {
                        contour.Push(new ContourPixel(x, y + 1, 0));
                    }
                }

                Console.WriteLine("init contour: done");

                while (contour.Count >= 1)
                {
                    ContourPixel cpt = contour.Top;
                    contour.Pop();
                    if (cpt.error >= sqColorDistThreshold)
                    {
                        break;
                    }
                    AddToContour(uncoloredPixels, contour, labelMap, cpt.x - 1, cpt.y, cpt, orgIter, threshold);
                    AddToContour(uncoloredPixels, contour, labelMap, cpt.x + 1, cpt.y, cpt, orgIter, threshold);
                    AddToContour(uncoloredPixels, contour, labelMap, cpt.x, cpt.y - 1, cpt, orgIter, threshold);
                    AddToContour(uncoloredPixels, contour, labelMap, cpt.x, cpt.y + 1, cpt, orgIter, threshold);
                }
                Console.WriteLine("region growing: done (" + uncoloredPixels.Count + ", " + threshold + ")");
                threshold *= 2;
            }

            List <byte>[] r       = new List <byte> [segmentCnt];
            List <byte>[] g       = new List <byte> [segmentCnt];
            List <byte>[] b       = new List <byte> [segmentCnt];
            int[]         c       = new int[segmentCnt];
            byte *        orgData = (byte *)orgIter.PixelData;

            for (int y = 0; y < edgeImage.Height; y++)
            {
                for (int x = 0; x < edgeImage.Width; x++)
                {
                    int label = labelMap[x + y * edgeImage.Width];
                    if (1 <= label && label < segmentCnt)
                    {
                        if (r[label] == null)
                        {
                            r[label] = new List <byte>();
                            g[label] = new List <byte>();
                            b[label] = new List <byte>();
                        }
                        r[label].Add(orgData[4 * x + y * orgIter.Stride + 0]);
                        g[label].Add(orgData[4 * x + y * orgIter.Stride + 1]);
                        b[label].Add(orgData[4 * x + y * orgIter.Stride + 2]);
                        c[label]++;
                    }
                }
            }

            for (int i = 1; i < r.Length; i++)
            {
                if (c[i] >= 1)
                {
                    r[i].Sort();
                    g[i].Sort();
                    b[i].Sort();
                }
            }

            // 隣接している似た色の領域をまとめる
            Dictionary <int, int> segmentCorrespondence = new Dictionary <int, int>();

            /*
             * int id = 0;
             * for (int y = 0; y < edgeImage.Height; y++)
             * {
             *  for (int x = 0; x < edgeImage.Width; x++)
             *  {
             *      if (x + 1 < edgeImage.Width && IsSameRegion(labelMap[id], labelMap[id + 1], r, g, b))
             *      {
             *          segmentCorrespondence[Math.Max(labelMap[id], labelMap[id + 1])] = Math.Min(labelMap[id], labelMap[id + 1]);
             *      }
             *      if (y + 1 < edgeImage.Height && IsSameRegion(labelMap[id], labelMap[id + edgeImage.Width], r, g, b))
             *      {
             *          segmentCorrespondence[Math.Max(labelMap[id], labelMap[id + edgeImage.Width])] = Math.Min(labelMap[id], labelMap[id + edgeImage.Width]);
             *      }
             *      id++;
             *  }
             * }
             * for (int i = 0; i < labelMap.Length; i++)
             *  while (segmentCorrespondence.ContainsKey(labelMap[i]))
             *      labelMap[i] = segmentCorrespondence[labelMap[i]];
             */
        }
Пример #10
0
 unsafe bool CanFill(int idx, int idxSeg, int ptX, int ptY, int srcX, int srcY, HashSet <int> filled, HashSet <int> uncoloredPixels, IplImage segmentImage, BitmapIterator orgIter, Stopwatch sw2 = null)
 {
     //            sw2.Start();
     if (!uncoloredPixels.Contains(idx))
     {
         //              sw2.Stop();
         return(false);
     }
     if (filled.Contains(idx))
     {
         //            sw2.Stop();
         return(false);
     }
     if (ptX < 0 || edgeImage.Width <= ptX)
     {
         //          sw2.Stop();
         return(false);
     }
     if (ptY < 0 || edgeImage.Height <= ptY)
     {
         //        sw2.Stop();
         return(false);
     }
     if (segmentImage.ImageDataPtr[idxSeg] != 0)
     {
         //      sw2.Stop();
         return(false);
     }
     if (SqColorDist(orgIter, srcX, srcY, ptX, ptY) >= sqColorDistThreshold)
     {
         //    sw2.Stop();
         return(false);
     }
     //            sw2.Stop();
     return(true);
 }
Пример #11
0
        /// AnnotationSketch:floodfill2.png
        unsafe public CvRect FloodFill(HashSet <int> initPoints, IplImage segmentImage, Color c, HashSet <int> uncoloredPixels, BitmapIterator orgIter, Stopwatch sw2 = null)
        {
            int           roiMinX  = int.MaxValue;
            int           roiMinY  = int.MaxValue;
            int           roiMaxX  = int.MinValue;
            int           roiMaxY  = int.MinValue;
            List <int>    curs     = initPoints.ToList();
            List <int>    nexts    = new List <int>();
            HashSet <int> filled   = new HashSet <int>(initPoints);
            byte *        partData = segmentImage.ImageDataPtr;

            while (curs.Count > 0)
            {
                for (int i = 0; i < curs.Count; i++)
                {
                    int src = curs[i];
                    int x, y;
                    y = Math.DivRem(src, edgeImage.Width, out x);
                    if (x < roiMinX)
                    {
                        roiMinX = x;
                    }
                    if (x > roiMaxX)
                    {
                        roiMaxX = x;
                    }
                    if (y < roiMinY)
                    {
                        roiMinY = y;
                    }
                    if (y > roiMaxY)
                    {
                        roiMaxY = y;
                    }
                    int offset = 4 * x + y * segmentImage.WidthStep;
                    if (CanFill(src - 1, offset - 4 + 3, x - 1, y, x, y, filled, uncoloredPixels, segmentImage, orgIter, sw2))
                    {
                        nexts.Add(src - 1);
                        filled.Add(src - 1);
                    }
                    if (CanFill(src + 1, offset + 4 + 3, x + 1, y, x, y, filled, uncoloredPixels, segmentImage, orgIter, sw2))
                    {
                        nexts.Add(src + 1);
                        filled.Add(src + 1);
                    }
                    if (CanFill(src - edgeImage.Width, offset - segmentImage.WidthStep + 3, x, y - 1, x, y, filled, uncoloredPixels, segmentImage, orgIter, sw2))
                    {
                        nexts.Add(src - segmentImage.Width);
                        filled.Add(src - segmentImage.Width);
                    }
                    if (CanFill(src + edgeImage.Width, offset + segmentImage.WidthStep + 3, x, y + 1, x, y, filled, uncoloredPixels, segmentImage, orgIter, sw2))
                    {
                        nexts.Add(src + segmentImage.Width);
                        filled.Add(src + segmentImage.Width);
                    }
                    partData[offset + 0] = c.B;
                    partData[offset + 1] = c.G;
                    partData[offset + 2] = c.R;
                    partData[offset + 3] = 255;
                }
                curs.Clear();
                FMath.Swap(ref curs, ref nexts);
            }

            return(new CvRect(
                       Math.Max(0, roiMinX - 1),
                       Math.Max(0, roiMinY - 1),
                       Math.Min(edgeImage.Width, roiMaxX - roiMinX + 2),
                       Math.Min(edgeImage.Height, roiMaxY - roiMinY + 2)));
        }
Пример #12
0
        unsafe List <IplImage> MoveBall(HashSet <int> orgUncoloredPixels, int[] labelMap, int radius, BitmapIterator edgeIter, BitmapIterator orgIter, int[] ballSizeList)
        {
            System.Diagnostics.Stopwatch sw  = System.Diagnostics.Stopwatch.StartNew();
            System.Diagnostics.Stopwatch sw2 = new Stopwatch();

            List <IplImage> segments           = new List <IplImage>();
            byte *          edgeData           = (byte *)edgeIter.PixelData;
            int             ballSize           = 1 + 2 * radius;
            HashSet <int>   uncoloredPixels    = new HashSet <int>(orgUncoloredPixels);
            Queue <int>     uncoloredPixelList = new Queue <int>(orgUncoloredPixels);

            while (uncoloredPixels.Count >= 1)
            {
                int idx = uncoloredPixelList.Dequeue();
                if (!uncoloredPixels.Contains(idx))
                {
                    continue;
                }
                uncoloredPixels.Remove(idx);

                int x, y;
                y = Math.DivRem(idx, edgeImage.Width, out x);

                // ボールが入るか
                if (ballSizeList[idx] < ballSize)
                {
                    continue;
                }

                // 彩色済みなら塗らない
                bool colored = false;
                for (int yy = y; yy < y + ballSize; yy++)
                {
                    for (int xx = x; xx < x + ballSize; xx++)
                    {
                        if (!(y == yy && x == xx) && !uncoloredPixels.Contains(xx + yy * edgeImage.Width))
                        {
                            colored = true;
                            goto COLORED_DECIDED;
                        }
                    }
                }

COLORED_DECIDED:

                if (colored)
                {
                    continue;
                }

                // 新しいセグメント
                IplImage segmentImage = new IplImage(edgeImage.Width, edgeImage.Height, BitDepth.U8, 4);
                Cv.Set(segmentImage, new CvScalar(0, 0, 0, 0));

                Random rand = new Random();
                Color  c    = Color.FromArgb(rand.Next(255) + 1, rand.Next(255) + 1, rand.Next(255) + 1);

                Cv.Rectangle(segmentImage, x + 1, y + 1, x + ballSize - 1, y + ballSize - 1, Cv.RGB(c.R, c.G, c.B));

                // 新しい色でFlood fill
                HashSet <int> initPoints = new HashSet <int>();
                for (int yy = y; yy < y + ballSize - 1; yy++)
                {
                    initPoints.Add(x + yy * edgeImage.Width);
                    initPoints.Add((x + ballSize - 1) + yy * edgeImage.Width);
                }
                for (int xx = x; xx < x + ballSize - 1; xx++)
                {
                    initPoints.Add(xx + y * edgeImage.Width);
                    initPoints.Add(xx + (y + ballSize - 1) * edgeImage.Width);
                }

                CvRect roi = FloodFill(initPoints, segmentImage, c, uncoloredPixels, orgIter, sw2);



                sw2.Start();

                Cv.SetImageROI(segmentImage, roi);
                {
                    IplConvKernel kernel = new IplConvKernel(2 * radius + 1, 2 * radius + 1, radius, radius, ElementShape.Ellipse);
                    Cv.Erode(segmentImage, segmentImage, kernel, 1);
                    Cv.Dilate(segmentImage, segmentImage, kernel, 1);
                }
                Cv.ResetImageROI(segmentImage);


                for (int yy = roi.Y; yy < roi.Bottom; yy++)
                {
                    for (int xx = roi.X; xx < roi.Right; xx++)
                    {
                        if (segmentImage.ImageDataPtr[4 * xx + yy * segmentImage.WidthStep + 3] != 0)
                        {
                            int idx2 = xx + yy * edgeImage.Width;
                            labelMap[idx2] = segmentCnt;
                            uncoloredPixels.Remove(idx2);
                            orgUncoloredPixels.Remove(idx2);
                        }
                    }
                }
                segmentCnt++;

                segments.Add(segmentImage);

                sw2.Stop();
            }
            //if (segments.Count >= 1) segments.First().Save(radius + ".png");

            //            if (sw.ElapsedMilliseconds >= 1000)
            //          {
            //            Console.WriteLine("[radius=" + radius + "]");
            //          Console.WriteLine("total= " + sw.ElapsedMilliseconds + " ms");
            //        Console.WriteLine("total= " + sw2.ElapsedMilliseconds + " ms");
            //  }
            return(segments);
        }
Пример #13
0
        unsafe public void Execute()
        {
            const int deltaRadius = 3;

            segmentCnt = 1;

            // 充填できるボールサイズを事前計算
            int maxBallSize, minBallSize;

            int[] ballSizeList = CalcBallSize(edgeImage, out maxBallSize, out minBallSize);
            int   maxRadius    = ((maxBallSize / 2) / deltaRadius - 1) * deltaRadius;
            int   minRadius    = ((minBallSize / 2) / deltaRadius + 1) * deltaRadius;
            //            FlashArrayToBitmap(ballSizeList, edgeImage.Width, edgeImage.Height, brushedImage);

            // 出力
            Dictionary <IplImage, int> segmentList = new Dictionary <IplImage, int>();

            int[] labelMap = new int[edgeImage.Width * edgeImage.Height];

            // 塗るべきピクセルを追加
            HashSet <int> uncoloredPixels = new HashSet <int>();

            using (BitmapIterator edgeIter = new BitmapIterator(edgeImage, ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed))
            {
                byte *edgeData = (byte *)edgeIter.PixelData;
                int   i        = 0;
                for (int y = 0; y < edgeImage.Height; y++)
                {
                    for (int x = 0; x < edgeImage.Width; x++)
                    {
                        if (edgeData[x + y * edgeIter.Stride] <= edgeThreshold)
                        {
                            uncoloredPixels.Add(i);
                        }
                        else
                        {
                            // エッジは取り除く
                            labelMap[i] = -1;
                        }
                        i++;
                    }
                }
            }

            // エッジの間に大雑把に色を塗っていく
            using (BitmapIterator edgeIter = new BitmapIterator(edgeImage, ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed))
                using (BitmapIterator orgIter = new BitmapIterator(orgImage, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb))
                {
                    byte *edgeData = (byte *)edgeIter.PixelData;
                    byte *orgData  = (byte *)orgIter.PixelData;

                    // ボールを徐々に小さくしながらTrappedBallSegmentation
                    for (int radius = maxRadius; radius >= Math.Max(10, minRadius); radius -= deltaRadius)
                    {
                        List <IplImage> segments = MoveBall(uncoloredPixels, labelMap, radius, edgeIter, orgIter, ballSizeList);
                        for (int i = 0; i < segments.Count; i++)
                        {
                            segmentList[segments[i]] = radius;
                        }
                    }

                    Console.WriteLine("move ball: done");

                    int cnt = labelMap.Count(val => val == 0);
                    Debug.Assert(cnt == uncoloredPixels.Count);


                    RegionGrowing(uncoloredPixels, labelMap, orgIter);

                    Dictionary <int, Color> colors = new Dictionary <int, Color>();

                    colors[-1] = Color.Black;
                    colors[0]  = Color.Black;

                    List <byte>[] r = new List <byte> [segmentList.Count];
                    List <byte>[] g = new List <byte> [segmentList.Count];
                    List <byte>[] b = new List <byte> [segmentList.Count];
                    int[]         c = new int[segmentList.Count];

                    for (int y = 0; y < edgeImage.Height; y++)
                    {
                        for (int x = 0; x < edgeImage.Width; x++)
                        {
                            int label = labelMap[x + y * edgeImage.Width];
                            if (1 <= label && label < segmentList.Count)
                            {
                                if (r[label] == null)
                                {
                                    r[label] = new List <byte>();
                                    g[label] = new List <byte>();
                                    b[label] = new List <byte>();
                                }
                                r[label].Add(orgData[4 * x + y * orgIter.Stride + 0]);
                                g[label].Add(orgData[4 * x + y * orgIter.Stride + 1]);
                                b[label].Add(orgData[4 * x + y * orgIter.Stride + 2]);
                                c[label]++;
                            }
                        }
                    }

                    Random rand = new Random();

                    for (int i = 1; i < segmentList.Count; i++)
                    {
                        if (c[i] >= 1)
                        {
                            r[i].Sort();
                            g[i].Sort();
                            b[i].Sort();
                            if (randColorMode)
                            {
                                colors[i] = Color.FromArgb(
                                    rand.Next(155) + 100,
                                    rand.Next(155) + 100,
                                    rand.Next(155) + 100);
                            }
                            else
                            {
                                colors[i] = Color.FromArgb(
                                    r[i][r[i].Count / 2],
                                    g[i][g[i].Count / 2],
                                    b[i][b[i].Count / 2]);
                            }
                        }
                    }

                    using (BitmapIterator brushedIter = new BitmapIterator(brushedImage, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb))
                    {
                        byte *brushedData = (byte *)brushedIter.PixelData;
                        for (int y = 0; y < edgeImage.Height; y++)
                        {
                            /// AnnotationSketch:83892581-23e5-484e-928a-ea6d4ca4892d.txt
                            for (int x = 0; x < edgeImage.Width; x++)
                            {
                                int label = labelMap[x + y * edgeImage.Width];
                                if (!colors.ContainsKey(label))
                                {
                                    colors[label] = Color.FromArgb(
                                        orgData[4 * x + y * orgIter.Stride + 0],
                                        orgData[4 * x + y * orgIter.Stride + 1],
                                        orgData[4 * x + y * orgIter.Stride + 2]);
                                }
                                brushedData[4 * x + y * brushedIter.Stride + 0] = colors[label].R;
                                brushedData[4 * x + y * brushedIter.Stride + 1] = colors[label].G;
                                brushedData[4 * x + y * brushedIter.Stride + 2] = colors[label].B;
                                brushedData[4 * x + y * brushedIter.Stride + 3] = 255;
                            }
                        }
                    }
                }
        }