Я пишу приложение для обработки изображений iOS и заинтригован этим вопросом, но неясно, на какой ответ. Итак, можно ли создать гистограмму изображения с помощью GPU через GLSL?
Ответ 2
Да, есть, хотя это немного сложнее в iOS, чем вы думаете. Это красная гистограмма, созданная и полностью построенная на GPU, работающая против живой видеопотока:
![GPU red histogram]()
Предложение Tommy в вопросе, который вы указываете, является отличной отправной точкой, равно как этот документ Scheuermann и Hensley. Что предлагает использовать рассеяние для создания гистограммы для цветовых каналов в изображении. Рассеяние - это процесс, в котором вы передаете сетку точек в ваш вершинный шейдер, а затем этот шейдер читает цвет в этой точке. Затем значение желаемого цветового канала в этой точке записывается как координата X (с 0 для координат Y и Z). Затем ваш шейдер фрагмента вытаскивает полупрозрачную точку с 1 пикселем в этой координате в вашей цели.
Эта цель представляет собой изображение шириной в 1 пиксель, ширину в 256 пикселей, причем каждое положение ширины представляет собой один бит цвета. Выписывая точку с низким альфа-каналом (или низкими значениями RGB), а затем используя аддитивное смешение, вы можете накапливать более высокое значение для каждого бина на основе количества раз, которое имеет определенное значение цвета в изображении. Эти пиксели гистограммы затем могут быть прочитаны для последующей обработки.
Основная проблема с этим в шейдерах iOS заключается в том, что, несмотря на отчеты об обратном, Apple четко заявляет, что текстура, читаемая в вершинном шейдере, не будет работать IOS. Я пробовал это со всеми моими устройствами iOS 5.0, и ни один из них не смог выполнить чтение текстуры в вершинном шейдере (экран просто черным, без ошибок GL).
Чтобы обойти это, я обнаружил, что могу читать исходные пиксели моего входного изображения (через glReadPixels()
или более быстрые текстурные кеши) и передавать эти байты в виде данных вершин с типом GL_UNSIGNED_BYTE. Следующий код выполняет следующее:
glReadPixels(0, 0, inputTextureSize.width, inputTextureSize.height, GL_RGBA, GL_UNSIGNED_BYTE, vertexSamplingCoordinates);
[self setFilterFBO];
[filterProgram use];
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ONE, GL_ONE);
glEnable(GL_BLEND);
glVertexAttribPointer(filterPositionAttribute, 4, GL_UNSIGNED_BYTE, 0, (_downsamplingFactor - 1) * 4, vertexSamplingCoordinates);
glDrawArrays(GL_POINTS, 0, inputTextureSize.width * inputTextureSize.height / (CGFloat)_downsamplingFactor);
glDisable(GL_BLEND);
В приведенном выше коде вы заметите, что я использую шаг, чтобы отображать только часть пикселей изображения. Это связано с тем, что самый низкий уровень непрозрачности или оттенка серого, который вы можете записать, составляет 1/256, а это означает, что каждый бит становится максимальным, а 255 пикселей на этом изображении имеют это значение цвета. Поэтому мне пришлось уменьшить количество обработанных пикселей, чтобы привести диапазон гистограммы в это ограниченное окно. Я ищу способ расширить этот динамический диапазон.
Шейдеры, используемые для этого, следующие: начиная с вершинного шейдера:
attribute vec4 position;
void main()
{
gl_Position = vec4(-1.0 + (position.x * 0.0078125), 0.0, 0.0, 1.0);
gl_PointSize = 1.0;
}
и заканчивая фрагментарным шейдером:
uniform highp float scalingFactor;
void main()
{
gl_FragColor = vec4(scalingFactor);
}
Рабочую реализацию этого можно найти в моей открытой исходной структуре GPUImage. Возьмите и запустите пример FilterShowcase, чтобы просмотреть анализ гистограммы и построить для себя.
В этой реализации есть некоторые проблемы с производительностью, но это был единственный способ, которым я мог думать об этом на GPU на iOS. Я открыт для других предложений.