Как группировать возле координат

У меня есть тезис "Проверка множественного выбора", и у меня большая проблема о том, что делать с моей проблемой. Я получил изображение (в частности, растровое изображение), так что вы можете видеть:

Обнаружение коробки с помощью EmguCV

Это изображение с обнаруженной коробкой, я опишу это:

  • Это экзаменационная работа, 1-50 наименований. каждому номеру соответствует поле (справа от номера, которое служит контейнером для ответа)
  • Это фотографии только для примера, количество обнаруженных ящиков может варьироваться. Мое приближение - это 150-200 обнаруженных ящиков.
  • Каждый обнаруженный ящик хранится в списке (MCvBOX2D), в котором хранится размер обнаруженного ящика , его центр и т. Д.
  • Я перенес эти координаты центра в новый список List(PointF) center;
  • Каждая коробка из изображения, может иметь 3-5 обнаруженных коробок. Как вы можете видеть, в каждом из полей изображения есть более одного обнаруженного ящика.
  • Я отсортировал все обнаруженные ящики в порядке возрастания, поэтому я бы знал, какие из них могут быть числами 1, 2 и т. Д.

Вот часть моего кода, который содержит сортировку блоков.

List<PointF> center = new List<PointF>();
List<PointF> centernew = new List<PointF>();
foreach (MCvBox2D box in lstRectangles)
{
   // this code transfers every center-coordinates of detected boxes 
   // to a new list which is center
    center.Add(new PointF(box.center.X, box.center.Y));
}
// and this one sorts the coordinates in ascending order.
centernew = center.OrderBy(p => p.Y).ThenBy(p => p.X).ToList(); 
  • Я закончил с сортировкой, теперь моя проблема в том, что в каждом блоке на изображении много обнаруженных блоков, я хотел бы сгруппировать отсортированный список центральных координат, чтобы я мог удалить другие обнаруженные блоки и получить только один обнаруженный блок за каждый номер.

  • Я знаю, что это трудно понять, поэтому я объясню больше.

    Допустим, мой отсортированный список обнаруженных ящиков содержит первые пять координат центра:

скажем, это координаты центра каждого из обнаруженных ящиков из первого ящика изображения.

 center[0] = [ 45.39, 47.6]
 center[1] = [ 65.39, 47.6]
 center[2] = [ 45.40, 47.10]
 center[3] = [ 65.45, 47.25]
 center[4] = [ 46.01, 47.50]

and the 2nd are:

 center[5] = [ 200.39, 47.2]
 center[6] = [ 45.39, 47.2]
 center[7] = [ 45.39, 47.3]
 center[8] = [ 45.39, 47.55]
  • Моя цель - организовать все отсортированные обнаруженные ящики внутри списка, я должен быть в состоянии сгруппировать все координаты центра, которые имеют близкое значение, с другим центром, в частности, их Y-координаты.

4 ответа

Решение
var rand = new Random();
var threshold = 1;
var points = new List<PointF>();

for (int i = 0; i < 20; i++)
{
    points.Add(new PointF((float) rand.NextDouble()*10, (float) rand.NextDouble()*10));
}

Console.WriteLine(points.Count);

for (int i = 0; i < points.Count(); i++)
{
    for (int j = i + 1; j < points.Count(); )
    {
        var pointHere = points[i];
        var pointThere = points[j];

        var vectorX = pointThere.X - pointHere.X;
        var vectorY = pointThere.Y - pointHere.Y;

        var length = Math.Sqrt(Math.Pow(vectorX, 2) + Math.Pow(vectorY, 2));

        if (length <= threshold)
        {
            points.RemoveAt(j);
        }
        else
        {
            j += 1;
        }
    }
}

Console.WriteLine(points.Count);

Вы можете рассчитать расстояние между данной точкой и любой другой точкой в ​​списке. Если расстояние меньше половины ширины бокса, вы можете быть уверены, что он является частью того же блока.

double threshold = 3.0;  // Make this whatever is appropriate

for (int i = center.Count - 1; i >= 0; --i)
    if (center.Any(p => p != center[i] && Distance(center[i], p) < threshold))
        center.Remove(center[i]);

И вы могли бы использовать это для вашего Distance() метод:

private double Distance(PointF p1, PointF p2)
{
    double deltaX = Math.Abs(p1.X - p2.X);
    double deltaY = Math.Abs(p1.Y - p2.Y);

    return Math.Sqrt((deltaX * deltaX) + (deltaY * deltaY));
}

Вы можете использовать Distinct с пользовательским IEqualityComparer ( см. MSDN).

В качестве примера определим класс:

class BoxEqualityComparer : IEqualityComparer<MCvBox2D>
{
    private static Double Tolerance = 0.01; //set your tolerance here

    public Boolean Equals(MCvBox2D b1, MCvBox2D b2)
    {
        if (CentersAreCloseEnough(b1.Center, b2.Center))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    private Boolean CentersAreCloseEnough(PointF c1, PointF c2)
    {
        return Math.Abs(c1.X - c2.X) < Tolerance && Math.Abs(c1.Y - c2.Y) < Tolerance;
    }

}

затем используйте метод в вашем коде так:

var distinctRectangles = lstRectangles.Distinct(new BoxEqualityComparer());

Вы можете реализовать CentersAreCloseEnough(PointF c1, PointF c2) как бы вы ни хотели; Вы можете использовать векторное расстояние, абсолютное расстояние в х и у и т. д.

Если вас беспокоят позиции с координатами Y, просто отсортируйте по этому номеру. Если вы хотите отсортировать оба, вы можете добавить оба X и Y и использовать это число для их сортировки. Вот пример того, что я имею в виду.

    for(int i = 0; i < points.length - 1; i++)
    {
        int temp = points[i].x + points[i].y;

        for(int j = i+1; j < points.length; i++)
        {
             int temp2 = point[j].x + points[j].y;

             if(temp2 < temp)
             {
                 Point jugglePoint = points[i];
                 points[i] = points[j];
                 points[j] = jugglePoint;
             }
        }
    }
Другие вопросы по тегам