Как эффективно вычислить среднее "направление" пикселей в изображении в оттенках серого?

поэтому я понял, что могу преобразовать изображение в оттенки серого, как это:

public static Bitmap GrayScale(this Image img)
{
    var bmp = new Bitmap(img.Width, img.Height);
    using(var g = Graphics.FromImage(bmp))
    {
        var colorMatrix = new ColorMatrix(
            new[]
                {
                    new[] {.30f, .30f, .30f, 0, 0},
                    new[] {.59f, .59f, .59f, 0, 0},
                    new[] {.11f, .11f, .11f, 0, 0},
                    new[] {0, 0, 0, 1.0f, 0},
                    new[] {0, 0, 0, 0, 1.0f}
                });

        using(var attrs = new ImageAttributes())
        {
            attrs.SetColorMatrix(colorMatrix);
            g.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height),
                0, 0, img.Width, img.Height, GraphicsUnit.Pixel, attrs);
        }
    }
    return bmp;
}

теперь я хочу вычислить среднее "направление" пикселей.

Я имею в виду, что я хочу посмотреть, скажем, область 3x3, а затем, если левая сторона темнее, чем правая сторона, то направление будет справа, если нижняя темнее, чем верхняя, то направление будет вверх, если нижняя левая темнее, чем верхняя правая, то направление будет вверх-направо. (Подумайте о маленьких векторных стрелках над каждой областью 3x3). Возможно, лучшим примером является рисование градиента оттенков серого в photoshop, и вы хотите вычислить, под каким углом они его нарисовали.

Я делал такие вещи, как этот MatLab, но это было много лет назад. Я полагаю, что мог бы использовать матрицу, подобную ColorMatrix чтобы вычислить это, но я не совсем уверен, как. Похоже на эта функция может быть, я хочу; могу ли я преобразовать его в оттенки серого (как указано выше), а затем сделать что-то с матрица оттенков серого для вычисления этих направлений?

IIRC, то, что я хочу, очень похоже на детекция.

после того, как я вычислю эти векторы направления, я просто собираюсь обойти их и вычислить среднее направление изображения.

конечная цель-я хочу повернуть изображения так, чтобы их среднее направление всегда было вверх; таким образом, если у меня есть два одинаковых изображения, кроме одного (90,180 или 270 градусов), они будут ориентированы точно так же (меня не волнует, если человек оказывается вверх ногами).


* snip* удаление какой-то спам. Вы можете просмотреть изменения, которые хотите прочитать остальные мои попытки.

4 ответов


вычисление среднего значения углов, как правило, плохая идея:

...
        sum += Math.Atan2(yi, xi);
    }
}
double avg = sum / (img.Width * img.Height);

среднее значение набора углов не имеет четкого значения: например, среднее значение одного угла, указывающего вверх, и одного угла, указывающего вниз, является углом, указывающим вправо. Это то, что вы хотите? Предполагая, что "вверх" равно +PI, то среднее между двумя углами почти указывая вверх, будет угол, указывающий вниз, если один угол PI - [некоторое небольшое значение], другой-PI+[некоторое небольшое значение]. Возможно, это не то, чего ты хочешь. Кроме того, вы полностью игнорируете силу края - большинство пикселей в ваших реальных изображениях вообще не являются краями, поэтому направление градиента-это в основном шум.

Если вы хотите вычислить что-то вроде "среднего направления", вам нужно добавить векторы вместо углов, затем вычислить Atan2 после цикла. Проблема в том, что векторная сумма ничего не говорит вам об объектах внутри изображения, поскольку градиенты, указывающие в противоположных направлениях, отменяют друг друга. Он только что-то говорит о разнице в яркости между первой/последней строки и первого и последнего столбца изображения. Возможно, это не то, чего ты хочешь.

Я думаю, что самый простой способ ориентировать изображения-создать гистограмму угла: создать массив с (например) 360 ячейками для 360° градиентных направлений. Затем вычислите угол градиента и величину для каждого пикселя. Добавьте каждую величину градиента в прямоугольник. Это не даст вам ни одного угла, но угол-гистограмму, который затем может использоваться для ориентации двух изображений друг к другу с помощью простой циклической корреляции.

вот доказательство концепции реализации Mathematica, которую я собрал вместе, чтобы увидеть, будет ли это работать:

angleHistogram[src_] :=
 (
  Lx = GaussianFilter[ImageData[src], 2, {0, 1}];
  Ly = GaussianFilter[ImageData[src], 2, {1, 0}];
  angleAndOrientation = 
   MapThread[{Round[ArcTan[#1, #2]*180/\[Pi]], 
      Sqrt[#1^2 + #2^2]} &, {Lx, Ly}, 2];
  angleAndOrientationFlat = Flatten[angleAndOrientation, 1];
  bins = BinLists[angleAndOrientationFlat , 1, 5];
  histogram = 
   Total /@ Flatten[bins[[All, All, All, 2]], {{1}, {2, 3}}];
  maxIndex = Position[histogram, Max[histogram]][[1, 1]];
  Labeled[
   Show[
    ListLinePlot[histogram, PlotRange -> All],
    Graphics[{Red, Point[{maxIndex, histogram[[maxIndex]]}]}]
    ], "Maximum at " <> ToString[maxIndex] <> "\[Degree]"]
  )

результаты с образцами изображений:

enter image description here

угловые гистограммы также показывают, почему средний угол не может работать: гистограмма по существу представляет собой один острый пик, другие углы примерно одинаковы. Среднее значение этой гистограммы всегда будет будет преобладать равномерный "фоновый шум". Вот почему у вас почти одинаковый угол (около 180°) для каждого из "реальных живых" изображений с вашим текущим алгоритмом.

изображение дерева имеет один доминирующий угол (горизонт), поэтому в этом случае вы можете использовать режим гистограммы (наиболее частый угол). Но это не будет работать для каждого изображения:

enter image description here

здесь у вас есть два пика. Циклическая корреляция по-прежнему должна ориентировать два изображения на каждое других, но просто использовать режим, вероятно, недостаточно.

также обратите внимание, что пик в угловой гистограмме не "вверх": на изображении дерева выше пик в угловой гистограмме, вероятно, является горизонтом. Значит, он указывает вверх. На изображении Лены это вертикальная белая полоса на заднем плане - так что она указывает вправо. Просто ориентировать изображения используя самый частый угол будет не повернуть каждое изображение с правой стороны, указывая вверх.

enter image description here

Это изображение имеет еще больше пиков: использование режима (или, возможно, любого одного угла) было бы ненадежным для ориентации этого изображения. Но гистограмма угла в целом все равно должна дать вам надежную ориентацию.

Примечание: я не обрабатывал изображения, я не пробовал градиентные операторы в разных масштабах, я не обрабатывал полученную гистограмму. В реальном приложении, вы бы настроить все эти вещи, чтобы получить наилучший алгоритм для большого набора тестовых изображений. Это просто быстрый тест, чтобы увидеть, может ли идея работать вообще.

добавить: чтобы ориентировать два изображения с помощью этой гистограммы, вы бы

  1. Нормализуйте все гистограммы, чтобы область под гистограммой была одинаковой для каждого изображения (даже если некоторые из них ярче, темнее или размытее)
  2. возьмите гистограммы изображений и сравните их для каждого интересующего вас вращения в:

например, в C#:

for (int rotationAngle = 0; rotationAngle < 360; rotationAngle++)
{
   int difference = 0;
   for (int i = 0; i < 360; i++)
      difference += Math.Abs(histogram1[i] - histogram2[(i+rotationAngle) % 360]);
   if (difference < bestDifferenceSoFar)
   {
      bestDifferenceSoFar = difference;
      foundRotation = rotationAngle;
   }
}

(вы можете ускорить это с помощью FFT, если длина гистограммы равна двум. Но код будет намного сложнее, и для 256 бункеров это может не иметь большого значения)


Ну я могу дать тебе другой способ сделать это. Хотя не будет красиво, но надеюсь, что это сработает для вас.

вероятно, ваши вычисления в порядке. Просто градиент один раз в среднем оказывается в другом среднем значении, отличном от того, что вы ожидаете. Поэтому я подозреваю, что, глядя на изображение, вы чувствуете, что в нем должен быть другой средний угол. Поэтому;

  • преобразование изображения в двоичный файл.
  • найти строки с помощью преобразования Хоу
  • принять самая длинная линия и вычислить ее угол. Это должно дать вам наиболее заметный угол.
  • вам может понадобиться предварительная / пост обработка, чтобы получить строки правильно.

и как еще один подход. Попробуй!--15-->суть это в основном реализация наиболее широко используются в распознавании сцены. Я нахожу ваши изображения реальными сцены и поэтому я бы предложил принять этот подход. Этот метод даст вам вектор, который вы сравниваете с разные векторы ориентации одного и того же изображения. Это очень хорошо знакомая техника и обязательно должна быть применима в вашем случае.


рассмотрите возможность использования градиента вашего изображения для вычисления нужного направления:en.wikipedia.org/wiki/Image_gradient


вам нужно свернуть изображение с двумя гауссовыми производными ядрами (одно в X и одно в Y). Это на самом деле Lx и Ly в ответе выше.

вычесть заранее средняя интенсивность пикселя перед вычислением подвел продукта между сдвижное окно (фрагмент исходного изображения) и первого порядка производная функции Гаусса.

см., например, это руководство: http://bmia.bmt.tue.nl/people/bromeny/MICCAI2008/Materials/05%20Gaussian%20derivatives%20MMA6.pdf

выберите оптимальный коэффициент сглаживания sigma >= 1.

чтобы вычислить гауссовы ядра, дифференцируйте один раз 2D-Гауссову функцию (известную из нормального распределения) с 1D-переменной '(x-0)^2', замененной (x^2 + y^2). Вы можете нарисовать его в 2D, например, в MS Excel.

удачи!

Михаил