it-swarm.com.ru

Как конвертировать 16-битный RGB565 в 24-битный RGB888?

Я получил 16-битное изображение rgb565 (в частности, дамп кадрового буфера Android) и хотел бы преобразовать его в 24-битный rgb888 для просмотра на обычном мониторе.

Вопрос в том, как преобразовать 5- или 6-битный канал в 8-битный? Очевидный ответ - сдвинуть его. Я начал с написания этого:

puts("P6 320 480 255");
uint16_t buf;
while (read(0, &buf, sizeof buf)) {
    unsigned char red = (buf & 0xf800) >> 11;
    unsigned char green = (buf & 0x07e0) >> 5;
    unsigned char blue = buf & 0x001f;
    putchar(red << 3);
    putchar(green << 2);
    putchar(blue << 3);
}

Однако у меня нет одного свойства, которое мне бы хотелось, чтобы 0xffff отображался в 0xffffff вместо 0xf8fcf8. Мне нужно каким-то образом расширить значение, но я не уверен, как это должно работать.

Android SDK поставляется с инструментом под названием ddms (Dalvik Debug Monitor), который делает снимки экрана. Насколько я могу судить по читая код , он реализует ту же логику; все же его скриншоты выходят по-другому, и белый становится белым.

Вот необработанный кадровый буфер , интеллектуальное преобразование по ddms и тупое преобразование по вышеуказанному алгоритму. Обратите внимание, что последний немного темнее и зеленее.

(Кстати, это преобразование реализовано в ffmpeg , но оно просто выполняет тупое преобразование, указанное выше, оставляя младшие биты равными нулю.)

Я думаю, у меня есть два вопроса:

  • Какой самый разумный способ конвертировать rgb565 в rgb888?
  • Как DDMS конвертирует свои скриншоты?
21
Josh Lee

Вы можете сдвинуться и затем или с самыми значимыми битами; то есть.

Red 10101 becomes 10101000 | 101 => 10101101
    12345         12345---   123    12345123

Это свойство, которое вы ищете, но это не самое линейное отображение значений из одного пространства в другое. Это быстро, хотя. :)

Ответ Клетуса более полный и, вероятно, лучше. :)

16
Jakob Borg

Вы хотите отобразить каждый из них из 5/6 битного пространства в 8 битное пространство.

  • 5 бит = 32 значения
  • 6 бит = 64 значения
  • 8 бит = 256 значений

Код, который вы используете, использует наивный подход: x5 * 256/32 = x8, где 256/32 = 8, а умножение на 8 - сдвиг влево 3, но, как вы говорите, это не обязательно заполняет новое числовое пространство " правильно". От 5 до 8 для максимального значения составляет от 31 до 255, и в этом заключается ваша подсказка к решению.

x8 = 255/31 * x5
x8 = 255/63 * x6

где x5, x6 и x8 являются 5, 6 и 8-битными значениями соответственно.

Теперь возникает вопрос о том, как лучше всего это реализовать. Это включает в себя деление, и с целочисленным делением вы потеряете любой остаток (в основном, округление вниз), поэтому лучшее решение, вероятно, состоит в том, чтобы сделать арифметику с плавающей запятой, а затем округлить половину до целого числа.

Это можно значительно ускорить, просто используя эту формулу для генерации таблицы поиска для каждого из 5- и 6-битных преобразований.

21
cletus

Мои несколько центов:

Если вам нужно точное отображение, но быстрый алгоритм, вы можете рассмотреть это:

R8 = ( R5 * 527 + 23 ) >> 6;
G8 = ( G6 * 259 + 33 ) >> 6;
B8 = ( B5 * 527 + 23 ) >> 6;

Он использует только: MUL, ADD и SHR -> так что это довольно быстро! С другой стороны, он совместим на 100% с отображением с плавающей запятой при правильном округлении:

// R8 = (int) floor( R5 * 255.0 / 31.0 + 0.5);
// G8 = (int) floor( G6 * 255.0 / 63.0 + 0.5);
// B8 = (int) floor( R5 * 255.0 / 31.0 + 0.5);

Некоторые дополнительные центы: Если вы заинтересованы в конвертации 888 в 565, это тоже очень хорошо работает:

R5 = ( R8 * 249 + 1014 ) >> 11;
G6 = ( G8 * 253 +  505 ) >> 10;
B5 = ( B8 * 249 + 1014 ) >> 11;

Константы были найдены с помощью поиска методом грубой силы с некоторыми ранними отклонениями, чтобы немного ускорить процесс.

20
Anonymous

iOS vImage Conversion

iOS Accelerate Framework документирует следующий алгоритм для функции vImageConvert_RGB565toARGB8888:

Pixel8 alpha = alpha
Pixel8 red   = (5bitRedChannel   * 255 + 15) / 31
Pixel8 green = (6bitGreenChannel * 255 + 31) / 63
Pixel8 blue  = (5bitBlueChannel  * 255 + 15) / 31

Для одноразового преобразования это будет достаточно быстро, но если вы хотите обработать много кадров, вы хотите использовать что-то вроде преобразования iOS vImage или реализовать это самостоятельно, используя NEON intrinsics .

Из ARMs Community Forum Tutorial

Сначала мы рассмотрим преобразование RGB565 в RGB888. Мы предполагаем, что в регистре q0 имеется восемь 16-битных пикселей, и мы хотели бы разделить красные, зеленые и синие цвета на 8-битные элементы по трем регистрам от d2 до d4.

 vshr.u8      q1, q0, #3      @ shift red elements right by three bits,
                                @  discarding the green bits at the bottom of
                                @  the red 8-bit elements.
vshrn.i16    d2, q1, #5      @ shift red elements right and narrow,
                                @  discarding the blue and green bits.
vshrn.i16    d3, q0, #5      @ shift green elements right and narrow,
                                @  discarding the blue bits and some red bits
                                @  due to narrowing.
vshl.i8      d3, d3, #2      @ shift green elements left, discarding the
                                @  remaining red bits, and placing green bits
                                @  in the correct place.
vshl.i16  q0, q0, #3      @ shift blue elements left to most-significant
                                @  bits of 8-bit color channel.
vmovn.i16    d4, q0          @ remove remaining red and green bits by
                                @  narrowing to 8 bits.

Эффекты каждой инструкции описаны в комментариях выше, но в итоге, операция, выполняемая на каждом канале: Удалите данные о цвете для соседних каналов, используя сдвиги, чтобы выдвинуть биты с любого конца элемента . Используйте второе смещение, чтобы расположить данные о цвете в старших значащих битах каждого элемента, и сузьте, чтобы уменьшить размер элемента с 16 до восьми бит.

Обратите внимание на использование размеров элементов в этой последовательности для адресации 8- и 16-битных элементов, чтобы выполнить некоторые операции маскирования.

Небольшая проблема

Вы можете заметить, что если вы используете приведенный выше код для преобразования в формат RGB888, ваши белые цвета не совсем белые. Это связано с тем, что для каждого канала младшие два или три бита равны нулю, а не единице; белый цвет, представленный в RGB565 как (0x1F, 0x3F, 0x1F), становится (0xF8, 0xFC, 0xF8) в RGB888. Это можно исправить, используя shift со вставкой, чтобы поместить некоторые старшие биты в младшие биты.

Для конкретного примера для Android я нашел преобразование YUV-в-RGB , написанное на встроенных элементах.

5
Cameron Lowell Palmer

Попробуй это:

red5 = (buf & 0xF800) >> 11;
red8 = (red5 << 3) | (red5 >> 2);

Это отобразит все нули во все нули, все 1 во все 1 и все промежуточное во все промежуточное. Вы можете сделать его более эффективным, сдвинув биты на место за один шаг:

redmask = (buf & 0xF800);
rgb888 = (redmask << 8) | ((redmask<<3)&0x070000) | /* green, blue */

Сделайте так же для зеленого и синего (для 6 битов сдвиньте влево 2 и вправо 4 соответственно в верхнем методе).

4
Rex Kerr

Есть ошибка jleedev !!!

unsigned char green = (buf & 0x07c0) >> 5;
unsigned char blue = buf & 0x003f;

хороший код

unsigned char green = (buf & 0x07e0) >> 5;
unsigned char blue = buf & 0x001f;

Cheers, Энди

3
Andy

Общее решение состоит в том, чтобы рассматривать числа как двоичные дроби - таким образом, 6-битное число 63/63 совпадает с 8-битным числом 255/255. Вы можете вычислить это с помощью математики с плавающей запятой, а затем вычислить таблицу подстановки, как предлагают другие авторы. Это также имеет преимущество в том, что он более интуитивно понятен, чем решения с битовой обработкой. :)

2
Nick Johnson

Я использовал следующее и получил хорошие результаты. Оказалось, что моя камера Logitek была 16-битной RGB555, и использование следующего для конвертации в 24-битную RGB888 позволило мне сохранить в формате JPEG с использованием меньших животных ijg: Спасибо за подсказку, найденную здесь на stackoverflow.

// Convert a 16 bit inbuf array to a 24 bit outbuf array
BOOL JpegFile::ByteConvert(BYTE* inbuf, BYTE* outbuf, UINT width, UINT height)
{     UINT row_cnt, pix_cnt;     
      ULONG off1 = 0, off2 = 0;
      BYTE  tbi1, tbi2, R5, G5, B5, R8, G8, B8;

      if (inbuf==NULL)
          return FALSE;

      for (row_cnt = 0; row_cnt <= height; row_cnt++) 
      {     off1 = row_cnt * width * 2;
            off2 = row_cnt * width * 3;
            for(pix_cnt=0; pix_cnt < width; pix_cnt++)
            {    tbi1 = inbuf[off1 + (pix_cnt * 2)];
                 tbi2 = inbuf[off1 + (pix_cnt * 2) + 1];
                 B5 = tbi1 & 0x1F;
                 G5 = (((tbi1 & 0xE0) >> 5) | ((tbi2 & 0x03) << 3)) & 0x1F;
                 R5 = (tbi2 >> 2) & 0x1F;
                 R8 = ( R5 * 527 + 23 ) >> 6;
                 G8 = ( G5 * 527 + 23 ) >> 6;
                 B8 = ( B5 * 527 + 23 ) >> 6;
                 outbuf[off2 + (pix_cnt * 3)] = R8;
                 outbuf[off2 + (pix_cnt * 3) + 1] = G8;
                 outbuf[off2 + (pix_cnt * 3) + 2] = B8;
            }
       }
       return TRUE;
}        
0
bravo