CVImageBuffer возвращается с дополнительным заполнением столбца. Как мне его обрезать?

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

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

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

введите здесь описание изображения


person sotangochips    schedule 30.06.2011    source источник
comment
См. мой ответ на другой связанный с вами вопрос здесь: искажено из вывода данных avcapturesession с помощью avcapturesessio"> stackoverflow.com/questions/6540710/   -  person Dex    schedule 31.10.2011


Ответы (2)


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

Стандарт OpenGL позволяет загружать текстуры с шириной шага, превышающей фактическую ширину текстуры. Это достигается соответствующей настройкой параметра glPixelStore GL_PACK_ROW_LENGTH. Обратите внимание, что этот «пропуск заполнения шага» обычно реализуется в части ЦП драйвера, поэтому эта операция не выполняется на графическом процессоре, фактически драйвер удалит дополнительное дополнение перед загрузкой данных в графический процессор. Поскольку 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% медленнее. Я предполагаю, что это приемлемо и все еще намного быстрее, чем любой из других вариантов с интенсивным использованием ЦП выше.

person Mecki    schedule 29.02.2012

вы можете использовать переменную состояния 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);

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

ваше здоровье

person pokey909    schedule 30.06.2011
comment
Я бы хотел использовать эти константы, но OpenGL ES не поддерживает GL_PACK_ROW_LENGTH или GL_PACK_ALIGNMENT. - person sotangochips; 01.07.2011
comment
блин это правда. Вы можете использовать glTexSubImage2D? О, и вы, по крайней мере, можете использовать GL_PACK_ALIGNMENT. Но только до значения 8 байт, так что это тоже не поможет :-( - person pokey909; 01.07.2011
comment
Кажется, OpenGL ES версии 2.0 не поддерживает GL_PACK_ROW_LENGTH, но поддерживается версия 3.0. Существует расширение для работы с GL_PACK_ROW_LENGTH в OpenGL ES v2.0. Подробнее: stackoverflow.com/questions/18149967/ - person Nhat Dinh; 01.11.2016