Разделить изображение из нескольких цифр на отдельные изображения, имеющие только одну цифру - PullRequest
0 голосов
/ 26 сентября 2018

Я пытаюсь разделить изображение рукописных цифр на отдельные.

Предположим, у меня есть это изображение:

Image to split

Я сделал простую логику, которая могла бы работать, но она сработает и столкнулась с проблемой:

private static void SplitImages()
{
    //We're going to use this code once.. to split our own images into seperate images.. can we do this somehow?
    Bitmap testSplitImage = (Bitmap)Bitmap.FromFile("TestSplitImage.jpg");
    int[][] imagePixels = new int[testSplitImage.Width][];
    for(int i=0;i<imagePixels.Length;i++)
    {
        imagePixels[i] = new int[testSplitImage.Height];
    }

    for(int i=0;i<imagePixels.Length;i++)
    {
        for(int j=0;j<imagePixels[i].Length;j++)
        {
            Color c = testSplitImage.GetPixel(i, j);
            imagePixels[i][j] = (c.R + c.G + c.B) / 3;
        }
    }

    //let's start by getting the first height vector... and count how many of them is white..dunno..
    int startColNumber = 0;
    int endColNumber = 0;
    bool isStart = false;
    int imageNumber = 1;

    for(int i=0;i<imagePixels.Length;i++)
    {
        int whiteNumbers = 0;
        for(int j=0;j<imagePixels[i].Length;j++)
        {
            if (imagePixels[i][j] > 200)
            {
                //consider it white or not really relevant
                whiteNumbers++;
            }
        }
        if (whiteNumbers > testSplitImage.Height*95.0/100.0)
        {
            //let's consider that if a height vector has more than 95% white pixels.. it means that we can start checking for an image
            //now if we started checking for the image.. we need to stop
            if (isStart)
            {
                //consider the end of image.. so the end column should be here or we make it +1 at least
                endColNumber = i + 1;
                isStart = false;
            }
        }
        else
        {
            if (!isStart)
            {
                isStart = true; //we will start checking for the image one row before that maybe?
                startColNumber = i == 0 ? i : i - 1;
            }
        }
        if (endColNumber > 0)
        {
            //we got a start and an end.. let's create a new image out of those pixels..hopefully this will work
            Bitmap splittedImage = new Bitmap(endColNumber - startColNumber + 1, testSplitImage.Height);
            int col = 0;
            for(int k=startColNumber;k<=endColNumber;k++)
            {
                for (int l=0;l<testSplitImage.Height;l++)
                {
                    int c = imagePixels[k][l];
                    splittedImage.SetPixel(col, l, Color.FromArgb(c, c, c));
                }
                col++;
            }
            splittedImage.Save($"Image{imageNumber++}.jpg");
            endColNumber = 0;
        }
        whiteNumbers = 0;
    }
}

Я получил хорошие результаты:

21 9

Я также получил три ноля:

0-1 0-2 0-3

Однако я получил это как одно изображение также:

37

Это один образец изображения, которое нужно разделить (в основном из 4000 изображений), и это один из лучших и самых простых изображений.Мне интересно, есть ли способ улучшить мою логику, или я должен отказаться от этого и найти другой?

1 Ответ

0 голосов
/ 26 сентября 2018

Этот код работает только с монохромными (2 цветными, черно-белыми) изображениями.

 public static class Processor
    {
        public static byte[] ToArray(this Bitmap bmp) // bitmap to byte array using lockbits
        {
            Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
            BitmapData data = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
            IntPtr ptr = data.Scan0;
            int numBytes = data.Stride * bmp.Height;
            byte[] bytes = new byte[numBytes];
            System.Runtime.InteropServices.Marshal.Copy(ptr, bytes, 0, numBytes);
            bmp.UnlockBits(data);
            return bytes;
        }


        public static int GetPixel(this byte[] array, int bpr, int x, int y) //find out if the given pixel is 0 or 1
        {
            int num = y * bpr + x / 8;
            return (array[num] >> 7- x%8) & 1;
        }

        public static List<Point> getDrawingPoints(this Point start, byte[] array, int width, int height) // get one 0 point (black point) and find all adjacent black points by traveling neighbors  
        {
            List<Point> points = new List<Point>();
            points.Add(start);
            int BytePerRow = array.Length / bmp.Height;
            int counter = 0;
            do
            {
                for (int i = Math.Max(0, points[counter].X - 1); i <= Math.Min(width - 1, points[counter].X + 1); i++)
                    for (int j = Math.Max(0, points[counter].Y - 1); j <= Math.Min(height - 1, points[counter].Y + 1); j++)
                        if (array.GetPixel(BytePerRow, i, j) == 0 && !points.Any(p => p.X == i && p.Y == j))
                            points.Add(new Point(i, j));
                counter++;
            } while (counter < points.Count);
            return points;
        }
        public static Bitmap ToBitmap(this List<Point> points) // convert points to bitmap
        {

            int startX = points.OrderBy(p => p.X).First().X,
                endX = points.OrderByDescending(p => p.X).First().X,
                startY = points.OrderBy(p => p.Y).First().Y,
                endY = points.OrderByDescending(p => p.Y).First().Y;
            Bitmap bmp = new Bitmap(endX - startX + 1, endY - startY + 1);
            Graphics g = Graphics.FromImage(bmp);
            g.FillRectangle(new SolidBrush(Color.White), new Rectangle(0, 0, endX - startX - 1, endY - startY - 1));
            for (int i = startY; i <= endY; i++)
                for (int j = startX; j <= endX; j++)
                    if (points.Any(p => p.X == j && p.Y == i)) bmp.SetPixel(j - startX, i - startY, Color.Black); 
            return bmp;
        }
    }

И используйте его для получения всех чисел внутри основного изображения:

 List<Point> processed = new List<Point>();
 Bitmap bmp = ((Bitmap)Bitmap.FromFile(SourceBitmapPath));
 byte[] array = bmp.ToArray();
 int BytePerRow = array.Length / bmp.Height;
 int imgIndex = 1;
 for (int i = 0; i < bmp.Width; i++)
     for (int j = 0; j < bmp.Height; j++)
     {
          if (array.GetPixel(BytePerRow, i, j) == 0 && !processed.Any(p => p.X == i && p.Y == j))
          {
               List<Point> points = new Point(i, j).getDrawingPoints(array, bmp.Width, bmp.Height);
               processed.AddRange(points);
               Bitmap result = points.ToBitmap();
               result.Save($"{imgIndex++}.bmp");
           }

      }

Я использую рисование и монохромный формат BMP «Сохранить как» для генерации исходного изображения.

Я также протестировал его с этим изображением: enter image description here

, что привело кследующие три изображения: enter image description here

enter image description here

enter image description here

...