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

Как конвертировать 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), который берет захват экрана. Насколько я могу судить из читает код, он реализует ту же логику; но его скриншоты выделяются разными, а белый - белым.

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

(Кстати, это преобразование реализовано в ffmpeg, но оно просто выполняет немое преобразование, перечисленное выше, оставляя LSB на всех ноль.)

У меня есть два вопроса:

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

Ответ 1

Вы можете сдвинуть, а затем или с помощью наиболее значимых бит; то есть.

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

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

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

Ответ 2

Вы хотите сопоставить каждое из них с 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 бит-преобразований.

Ответ 3

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

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

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;

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

Ответ 4

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

Сначала мы рассмотрим преобразование 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. Это можно исправить с помощью сдвига со вставкой, чтобы поместить некоторые из наиболее значимых бит в младшие бит.

Для конкретного Android-приложения я обнаружил преобразование YUV-RGB, написанное в intrinsics.

Ответ 5

Попробуйте следующее:

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 соответственно в верхнем методе).

Ответ 6

Произошла ошибка jleedev!!!

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

хороший код

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

Cheers, Andy

Ответ 7

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

Ответ 8

Я использовал следующее и получил хорошие результаты. Оказалось, что моя камера 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;
}