Подтвердить что ты не робот

IOS GLSL. Есть ли способ создать гистограмму изображения с использованием шейдера GLSL?

В другом месте StackOverflow задан вопрос относительно гистограммы глубины буфера - Создать текстуру гистограммы буфера глубины с помощью GLSL.

Я пишу приложение для обработки изображений iOS и заинтригован этим вопросом, но неясно, на какой ответ. Итак, можно ли создать гистограмму изображения с помощью GPU через GLSL?

4b9b3361

Ответ 1

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

Если это помогает, DirectX11 поставляется с примером FFT для вычисления шейдеров. См. Примечания к выпуску SDK в августе DX11.

Ответ 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. Я открыт для других предложений.