Как я могу найти самую яркую точку в CIImage (возможно, в металле)? - PullRequest
3 голосов
/ 13 июня 2019

Я создал собственный CIKernel в Metal.Это полезно, потому что это близко к реальному времени.Я избегаю любых cgcontext или cicontext, которые могут отставать в режиме реального времени.По сути, мое ядро ​​выполняет преобразование Хафа, но я не могу понять, как читать белые точки из буфера изображения.

Вот kernel.metal:

#include <CoreImage/CoreImage.h>

extern "C" {
    namespace coreimage {

        float4 hough(sampler src) {

            // Math

            // More Math

            // eventually:

            if (luminance > 0.8) {
                uint2 position = src.coord()
                // Somehow add this to an array because I need to know the x,y pair
            }

            return float4(luminance, luminance, luminance, 1.0);
        }
    }
}

IЯ в порядке, если эта часть может быть извлечена в другое ядро ​​или функцию.Предостережение CIKernel - это тип возвращаемого значения - float4, представляющий новый цвет пикселя.В идеале, вместо фильтра image -> image я бы хотел заключить сделку image -> array.Например, уменьшить вместо карты.У меня плохое предчувствие, это потребует от меня его рендеринга и обработки на процессоре.

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

РЕДАКТИРОВАНИЕ ЗАКЛЮЧИТЕЛЬНОГО РЕШЕНИЯ:

В соответствии с предложениями ответа я делаю большие расчеты для каждого пикселя в графическом процессоре и некоторые математические вычисления в процессоре.Я разработал 2 дополнительных ядра, которые работают как встроенные редукционные ядра.Одно ядро ​​возвращает изображение высотой 1 пиксель с самыми высокими значениями в каждом столбце, а другое ядро ​​возвращает изображение высотой 1 пиксель нормализованной координаты Y с самым высоким значением:

    /// Returns the maximum value in each column.
    ///
    /// - Parameter src: a sampler for the input texture
    /// - Returns: maximum value in for column
    float4 maxValueForColumn(sampler src) {

        const float2 size = float2(src.extent().z, src.extent().w);

        /// Destination pixel coordinate, normalized
        const float2 pos = src.coord();

        float maxV = 0;

        for (float y = 0; y < size.y; y++) {
            float v = src.sample(float2(pos.x, y / size.y)).x;
            if (v > maxV) {
                maxV = v;
            }
        }

        return float4(maxV, maxV, maxV, 1.0);
    }

    /// Returns the normalized coordinate of the maximum value in each column.
    ///
    /// - Parameter src: a sampler for the input texture
    /// - Returns: normalized y-coordinate of the maximum value in for column
    float4 maxCoordForColumn(sampler src) {

        const float2 size = float2(src.extent().z, src.extent().w);

        /// Destination pixel coordinate, normalized
        const float2 pos = src.coord();

        float maxV = 0;
        float maxY = 0;

        for (float y = 0; y < size.y; y++) {
            float v = src.sample(float2(pos.x, y / size.y)).x;
            if (v > maxV) {
                maxY = y / size.y;
                maxV = v;
            }
        }

        return float4(maxY, maxY, maxY, 1.0);
    }

Это не будетукажите каждый пиксель, где яркость больше 0,8, но для моих целей он возвращает достаточно: самое высокое значение в каждом столбце и его местоположение.

Pro: копирование только (2 * ширина изображения) байтов вПроцессор вместо каждого пикселя экономит тонны времени (несколько мс).

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

СЛЕДУЙТЕ ЗА:

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

Этот неотвеченный вопрос описывает проблему.

Редактировать: Этот ответ на вопрос предоставляет очень удобную функцию металла.Когда вы вызываете его для значения металла (, например, 0,5 ) и возвращаете его, вы получите правильное значение (, например, 128 ) для CPU.

1 Ответ

1 голос
/ 13 июня 2019

Проверьте фильтры в CICategoryReduction (например, CIAreaAverage).Они возвращают изображения высотой всего несколько пикселей, содержащие результат сокращения.Но вам все равно нужно отрендерить их, чтобы можно было прочитать значения в вашей функции Swift.

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

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

Вы могли бы сделать гибридный подход, где вы выполняете сложную математику для каждого пикселя на GPU с Core Image, а затем выполняете анализ на CPU, используя Accelerate.Вы даже можете интегрировать часть процессора в ваш конвейер Core Image, используя CIImageProcessorKernel.

...