Я создал собственный 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.