CVImageBuffer возвращается с дополнительным заполнением столбца.Как мне обрезать его? - PullRequest
3 голосов
/ 01 июля 2011

У меня есть CVImageBuffer, который возвращается с записанной высотой 640px и шириной 852px. Байт на строку - 3456. Вы заметите, что 3456 / 852px! = 4 (это что-то вроде 4.05). После некоторой проверки 864 будет шириной, которая делает bytesPerRow / width = 4.0. Таким образом, кажется, что в каждом ряду есть дополнительные 12px (дополненные справа). Я предполагаю, что это потому, что эти буферы оптимизированы для некоторого кратного, что это изображение не имеет.

Когда я отрисовываю этот буфер в OpenGl, он выглядит ужасно (см. Ниже). Я заметил, что шаблон повторяется каждые 71 пиксель, что имеет смысл, потому что если есть дополнительные 12 пикселей, то (852/12 пикселей = 71) Таким образом, дополнительные 12 пикселей, кажется, вызывают проблему.

Как мне быстро избавиться от этих лишних пикселей и затем использовать эти данные для чтения в OpenGL ES? Или, скорее, как мне читать в OpenGL ES, пропуская эти дополнительные пиксели в каждой строке?

enter image description here

Ответы [ 2 ]

4 голосов
/ 29 февраля 2012

Для изображений, которые используются вместе с высокоскоростными алгоритмами обработки изображений, достаточно добавить заполнение в конце каждой строки, чтобы изображение имело пиксельный или байтовый размер, кратный 4, 8, 16, 32. , и так далее. Это значительно упрощает оптимизацию определенных алгоритмов для скорости, особенно в сочетании с наборами команд SIMD, такими как SSE на x86 или NEON на ARM. В вашем случае отступ составляет 12 пикселей, что означает, что Apple, похоже, оптимизирует свои алгоритмы для обработки 32 пикселей на строку; 852 не делится на 32, 864 - это, следовательно, линии дополняются 12 пикселями для поддержания 32-пиксельного выравнивания. Правильными техническими терминами являются размер и размер шага или, в случае изображений, ширина и ширина шага. Ширина - это количество фактических данных в пикселях на строку, ширина шага - это реальный размер строки, включая данные в пикселях и необязательный отступ в конце строки.

Стандартный OpenGL позволяет загружать текстуры с шириной шага, превышающей фактическую ширину текстуры. Это достигается путем установки параметра glPixelStore GL_PACK_ROW_LENGTH соответственно. Обратите внимание, что этот «пропуск заполнения шага» обычно реализуется в части ЦП драйвера, поэтому эта операция не выполняется на GPU, фактически драйвер удаляет дополнительное заполнение перед загрузкой данных в GPU. Поскольку OpenGL ES предназначен для работы на встраиваемых устройствах, которые могут иметь очень ограниченные ресурсы ЦП, этот вариант был удален из OpenGL ES для упрощения разработки драйверов даже для очень слабых встроенных ЦП. Это оставляет вам четыре варианта решения вашей проблемы:

  1. Предварительная обработка текстуры для удаления заполнения с использованием цикла копирования C, который пропускает дополнительные пиксели в конце каждой строки. Эта реализация довольно медленная, но простая в реализации.

  2. Предварительная обработка текстуры, как в случае опции (1), однако используйте макросы SIMD компилятора, чтобы использовать инструкции NEON. Это будет примерно в 2 раза быстрее, чем вариант (1), но его также сложнее реализовать, и вам потребуются некоторые знания о инструкциях NEON и о том, как их использовать для достижения этой цели.

  3. Предварительная обработка текстур, как в случае варианта (2), однако используйте чистую реализацию сборки. Это будет примерно в 3 раза быстрее, чем опция (2), поэтому примерно в 6 раз быстрее, чем опция (1), но это также намного сложнее реализовать, так как вам понадобятся знания о программировании сборки ARM + инструкции NEON.

  4. Загрузите текстуру с отступом и настройте координаты текстуры для OpenGL, чтобы игнорировать пиксели заполнения. В зависимости от того, насколько сложным является ваше наложение текстур, это может быть очень легко реализовать, это быстрее, чем любой другой вариант выше, и единственным недостатком является то, что вы тратите немного больше памяти текстур на GPU.

Я очень мало знаю о программировании сборок ARM и еще меньше о инструкциях NEON, поэтому я не могу помочь вам с опциями (2) и (3). Я мог бы показать вам реализацию опции (1), однако, боюсь, она может быть слишком медленной для вашей цели. Это оставляет только последний вариант, который я использовал сам много раз в прошлом.

Мы объявляем 3 переменные: ширину, высоту и ширину шага.

GLsizei width = 852;
GLsizei height = 640;
GLsizei strideWidth = 864;

Когда вы загружаете данные текстуры (предполагая, что rawData указывает на необработанные байты изображения), вы делаете вид, что strideWidth является «реальной шириной»:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 
    strideWidth, height, 0, GL_RGB, GL_UNSIGNED_BYTE, rawData);

Координаты текстуры в OpenGL нормализованы, это означает, что нижний левый угол всегда равен (0.0f, 0.0f), а верхний правый угол всегда равен (1.0f, 1.0f), независимо от того, какой размер пикселя на самом деле имеет текстура. Эти два значения можно назвать (x, y), но чтобы не путать их с координатами вершины, они называются (s, t).

Чтобы OpenGL обрезал отступные пиксели, вам просто нужно отрегулировать все s-координаты по определенному коэффициенту, назовем его SPCF (Коэффициент вырезания отступа), который вычисляется следующим образом:

float SPCF = (float)width / strideWidth;

Таким образом, вместо координаты текстуры (0.35f, 0.6f), вы бы использовали (0.35f * SPCF, 0.6f). Конечно, вы не должны выполнять этот расчет один раз для визуализированного кадра. Вместо этого вы должны скопировать исходные координаты текстуры, отрегулировать все s-координаты один раз с помощью SPCF, а затем использовать эти скорректированные координаты при рендеринге кадров. Если вы когда-нибудь перезагрузите текстуру в будущем и SPCF изменился, повторите процесс настройки. В случае, если ширина равна strideWidth, этот алгоритм также работает, так как в этом случае SPCF равен 1.0f и, таким образом, не будет изменять s-координаты, что было бы правильно, так как нет отступа для заполнения.

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

0 голосов
/ 01 июля 2011

вы можете использовать переменную состояния GL_PACK_ROW_LENGTH в сочетании с GL_PACK_ALIGNMENT для управления длиной строк ваших данных.Вы можете посмотреть его на странице руководства, например, glPixelStorei или даже лучше, с некоторыми изображениями здесь: http://www.opengl.org/resources/features/KilgardTechniques/oglpitfall/

Я думаю, это будет что-то вроде:

glPixelStorei(GL_PACK_ROW_LENGTH, nPixelsPerRow);
glPixelStorei(GL_PACK_ALIGNMENT, nPadBytes);

Пожалуйста, обратите внимание, что я не проверял вышеуказанный код.Это просто намек.

ура

...