Как обнаружить углы в бинарных изображениях с помощью OpenGL?


13

У меня есть двоичные изображения 160x120, такие как:

исходное изображение

Я хотел бы обнаружить углы этих белых пятен. Они ранее закрыты математической морфологией, поэтому не должно быть внутренних углов. В этом конкретном случае я хотел бы 16 углов, как:

пример обнаружения углов

Моей первой попыткой было использование некоторых функций OpenCV, таких как goodFeaturesToTrack или FAST, но они особенно медленные (плюс FAST очень нестабилен). Моя идея состояла бы в том, чтобы сделать такое вычисление на GPU, поскольку мой исходный образ исходит из него. Я искал в Интернете идеи о том, как писать такие шейдеры (я использую OpenGL ES 2.0), но не нашел ничего конкретного. Есть идеи, как я мог запустить такой алгоритм?


2
БЫСТРО медленный? :)
эндолит

1
да, смешно, верно? на самом деле, это быстрее, чем предыдущие алгоритмы, такие как SURF или SIFT, но он менее точен, довольно нестабилен от одного изображения к другому и все еще недостаточно быстр, чтобы его можно было выполнить на процессоре
Стефан Пешар

Насколько важно точно определять их на каждом кадре? Как быстро двигаются прямоугольники? Можно ли определять углы в большинстве кадров и интерполировать их в кадрах, где отсутствует алгоритм?
Юстис

@justis хорошо, способ, которым я делаю это прямо сейчас (через использование функций cvFindContours () и cvApproxPoly ()) OpenCV, не очень стабилен во времени, поэтому я фильтрую результат с помощью фильтра нижних частот, что приводит к запаздыванию. Как вы думаете, я могу получить более стабильный результат с интерполяцией?
Стефан Пешар

Ответы:


3

Какого размера изображения вы работаете? На какой частоте кадров? На каком железе? БЫСТРО, довольно быстро в моем опыте.

Я также видел, что FAST используется в качестве детектора ROI, при этом goodFeaturesToTrack запускается на идентифицированных областях ROI, чтобы обеспечить лучшую стабильность без потери gFTT для всего изображения.

«Харрис» угловой детектор также потенциально очень быстро , как она состоит из очень простых операций (без SQRT () на пиксель, например!) - не так стабильна , как gFTT, но , возможно , в большей степени , чем быстро.

(С точки зрения реализации GPU, Googling, gpu cornerкажется, представляет довольно много ссылок, но я понятия не имею, насколько они могут быть подходящими - я склонен внедрять в FPGA.)


Мои изображения имеют размер 160x120, предположительно со скоростью 30 кадров в секунду, на iPhone, но, конечно, у приложения есть намного больше дел :-) Я видел приложение, реализующее FAST довольно быстро на таком устройстве, но это была только демонстрационная версия делаю это ... Вот почему я ищу решения на основе GPU.
Стефан Пешар

15

Мне просто довелось реализовать что-то подобное в OpenGL ES 2.0 с использованием определения угла Харриса, и, хотя я еще не закончил, я подумал, что поделюсь реализацией на основе шейдеров, которая у меня есть. Я сделал это как часть iOS-среды с открытым исходным кодом , так что вы можете проверить код, если вам интересно, как работает какой-то конкретный шаг.

Для этого я использую следующие шаги:

  • Уменьшите изображение до его значений яркости, используя точечное произведение значений RGB с вектором (0,2125, 0,7154, 0,0721).
  • Вычислите производные X и Y, вычитая значения красного канала из пикселей слева и справа и выше и ниже текущего пикселя. Затем я сохраняю производную х в квадрате в красном канале, производную Y в квадрате в зеленом канале и произведение производных Х и Y в синем канале. Для этого фрагментный шейдер выглядит следующим образом:

    precision highp float;
    
    varying vec2 textureCoordinate;
    varying vec2 leftTextureCoordinate;
    varying vec2 rightTextureCoordinate;
    
    varying vec2 topTextureCoordinate; 
    varying vec2 bottomTextureCoordinate;
    
    uniform sampler2D inputImageTexture;
    
    void main()
    {
     float topIntensity = texture2D(inputImageTexture, topTextureCoordinate).r;
     float bottomIntensity = texture2D(inputImageTexture, bottomTextureCoordinate).r;
     float leftIntensity = texture2D(inputImageTexture, leftTextureCoordinate).r;
     float rightIntensity = texture2D(inputImageTexture, rightTextureCoordinate).r;
    
     float verticalDerivative = abs(-topIntensity + bottomIntensity);
     float horizontalDerivative = abs(-leftIntensity + rightIntensity);
    
     gl_FragColor = vec4(horizontalDerivative * horizontalDerivative, verticalDerivative * verticalDerivative, verticalDerivative * horizontalDerivative, 1.0);
    }
    

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

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

  • Выполните фактический расчет определения угла Харриса, используя размытые входные производные значения. В данном случае я на самом деле использую расчет, описанный Элисон Ноубл в ее докторской диссертации. диссертация "Описания поверхностей изображений". Шейдер, который обрабатывает это, выглядит следующим образом:

    varying highp vec2 textureCoordinate;
    
    uniform sampler2D inputImageTexture;
    
    const mediump float harrisConstant = 0.04;
    
    void main()
    {
     mediump vec3 derivativeElements = texture2D(inputImageTexture, textureCoordinate).rgb;
    
     mediump float derivativeSum = derivativeElements.x + derivativeElements.y;
    
     // This is the Noble variant on the Harris detector, from 
     // Alison Noble, "Descriptions of Image Surfaces", PhD thesis, Department of Engineering Science, Oxford University 1989, p45.     
     mediump float harrisIntensity = (derivativeElements.x * derivativeElements.y - (derivativeElements.z * derivativeElements.z)) / (derivativeSum);
    
     // Original Harris detector
     //     highp float harrisIntensity = derivativeElements.x * derivativeElements.y - (derivativeElements.z * derivativeElements.z) - harrisConstant * derivativeSum * derivativeSum;
    
     gl_FragColor = vec4(vec3(harrisIntensity * 10.0), 1.0);
    }
    
  • Выполните локальное не максимальное подавление и примените порог, чтобы выделить проходящие пиксели. Я использую следующий фрагментный шейдер для выборки восьми пикселей в окрестности центрального пикселя и определения, является ли он максимальным в этой группе:

    uniform sampler2D inputImageTexture;
    
    varying highp vec2 textureCoordinate;
    varying highp vec2 leftTextureCoordinate;
    varying highp vec2 rightTextureCoordinate;
    
    varying highp vec2 topTextureCoordinate;
    varying highp vec2 topLeftTextureCoordinate;
    varying highp vec2 topRightTextureCoordinate;
    
    varying highp vec2 bottomTextureCoordinate;
    varying highp vec2 bottomLeftTextureCoordinate;
    varying highp vec2 bottomRightTextureCoordinate;
    
    void main()
    {
        lowp float bottomColor = texture2D(inputImageTexture, bottomTextureCoordinate).r;
        lowp float bottomLeftColor = texture2D(inputImageTexture, bottomLeftTextureCoordinate).r;
        lowp float bottomRightColor = texture2D(inputImageTexture, bottomRightTextureCoordinate).r;
        lowp vec4 centerColor = texture2D(inputImageTexture, textureCoordinate);
        lowp float leftColor = texture2D(inputImageTexture, leftTextureCoordinate).r;
        lowp float rightColor = texture2D(inputImageTexture, rightTextureCoordinate).r;
        lowp float topColor = texture2D(inputImageTexture, topTextureCoordinate).r;
        lowp float topRightColor = texture2D(inputImageTexture, topRightTextureCoordinate).r;
        lowp float topLeftColor = texture2D(inputImageTexture, topLeftTextureCoordinate).r;
    
        // Use a tiebreaker for pixels to the left and immediately above this one
        lowp float multiplier = 1.0 - step(centerColor.r, topColor);
        multiplier = multiplier * 1.0 - step(centerColor.r, topLeftColor);
        multiplier = multiplier * 1.0 - step(centerColor.r, leftColor);
        multiplier = multiplier * 1.0 - step(centerColor.r, bottomLeftColor);
    
        lowp float maxValue = max(centerColor.r, bottomColor);
        maxValue = max(maxValue, bottomRightColor);
        maxValue = max(maxValue, rightColor);
        maxValue = max(maxValue, topRightColor);
    
        gl_FragColor = vec4((centerColor.rgb * step(maxValue, centerColor.r) * multiplier), 1.0);
    }
    

Этот процесс генерирует карту углов из ваших объектов, которая выглядит следующим образом:

Угловая карта

Следующие точки определены как углы на основе не максимального подавления и порогового значения:

Выявленные углы

С правильными пороговыми значениями, установленными для этого фильтра, он может идентифицировать все 16 углов в этом изображении, хотя он склонен размещать углы на пиксель или около того внутри фактических краев объекта.

На iPhone 4 это определение угла может быть выполнено со скоростью 20 кадров в секунду на кадрах видео 640x480, поступающих с камеры, а iPhone 4S может легко обрабатывать видео такого размера со скоростью более 60 кадров в секунду. Это должно быть намного быстрее, чем обработка с привязкой к процессору для такой задачи, как сейчас, хотя сейчас процесс считывания точек привязан к процессору и немного медленнее, чем должен быть.

Если вы хотите увидеть это в действии, вы можете взять код для моей платформы и запустить пример FilterShowcase, который поставляется вместе с ним. Пример обнаружения углов Harris запускается в режиме реального времени с камеры устройства, хотя, как я уже упоминал, считывание обратных угловых точек в настоящее время происходит на процессоре, что действительно замедляет это. Для этого я перехожу к процессу на основе графического процессора.


1
Очень хорошо! Я слежу за вашими рамками на github, это кажется действительно интересным, поздравляю!
Стефан Пешар

У вас есть где-нибудь пример того, как вернуть координаты угла обратно в процессор? Существует ли какой-нибудь интеллектуальный способ графического процессора, или он требует повторного считывания и последующего зацикливания на процессоре через возвращенное растровое изображение в поисках отмеченных пикселей?
Квазимондо

@Quasimondo - я работал над использованием пирамид гистограммы для извлечения точек: tevs.eu/files/vmv06.pdf , чтобы избежать связанной с ЦП итерации по пикселям для определения угла. В последнее время немного отвлекся, так что еще не закончил, но я бы хотел в ближайшее время.
Брэд Ларсон

Привет @BradLarson, я знаю, что это очень старая тема, и спасибо за ваш ответ. Я только что проверил KGPUImageHarrisCornerDetection.m в платформе GPUImage. Чтобы извлечь угловое местоположение из изображения, вы использовали glReadPixels для считывания изображения в буфер, а затем зациклили его в буфере для сохранения точек с colotByte> 0 в массиве. Есть ли способ сделать все это в GPU, где нам не нужно читать изображения в буфере и цикле?
Сахил

1
@SahilBajaj - Одна из техник, которые я видел (и еще не успел реализовать), - это использование пирамид гистограммы для быстрого выделения точек из разреженных изображений, подобных этой. Это значительно ускорило бы это.
Брэд Ларсон

3

«Надежные» детекторы углов, такие как Shi-Tomasi и Moravec, как известно, работают медленно. проверьте их здесь - http://en.wikipedia.org/wiki/Corner_detection FAST, вероятно, является единственным достаточно хорошим легким угловым детектором. Вы можете улучшить FAST, выполнив не максимальное подавление - выбрав выход FAST с наилучшей оценкой «угловатости» (есть несколько интуитивно понятных способов его расчета, включая Shi-Tomasi и Moravec в качестве оценки угловатости). У вас также есть выбор из нескольких детекторов FAST - от FAST-5 до FAST-12 и FAST_ER (последний, вероятно, слишком велик для мобильных устройств) Другой способ - сгенерировать FAST - получить генератор кода FAST с сайта автора и обучить его на множестве вероятных изображений. http://www.edwardrosten.com/work/fast.html


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.