Сжатие массива байтов в Java и распаковка в C
в настоящее время у меня есть следующий массив в программе Java,
byte[] data = new byte[800];
и я хотел бы сжать его перед отправкой на микроконтроллер по последовательному (115200 бод). Я хотел бы затем распаковать массив на микроконтроллере в C. Однако я не совсем уверен, что это лучший способ сделать. Производительность является проблемой, так как микроконтроллер-это просто arduino, поэтому он не может быть слишком интенсивным для памяти/процессора. Данные более или менее случайны (редактировать Я думаю, это не так уж случайно, см. редактирование ниже) я бы сказал, так как он представляет значение цвета rgb для каждых 16 бит.
Как лучше всего сжать эти данные? Есть идеи, сколько сжатия я могу получить?
редактировать
извините за отсутствие информации. Мне нужно, чтобы сжатие было без потерь, и я намерен отправлять только 800 байтов за раз. Моя проблема в том, что 800 байт не будут передаваться достаточно быстро со скоростью 115200 бод, которую я я использую. Я надеялся, что смогу немного уменьшить размер, чтобы увеличить скорость.
каждые два байта выглядит так:
0RRRRRGGGGGBBBBB
где r g и B бит представляют значения для цветовых каналов красный, зеленый и синий соответственно. Каждые два байта - это отдельный светодиод на сетке 20x20. Я бы предположил, что многие наборы из двух байтов будут идентичны, так как я часто назначаю одни и те же цветовые коды нескольким светодиодам. Это также может быть так, что RGB значения часто > 15, так как я обычно использую яркие цвета, когда могу (однако это может быть спорным моментом, поскольку они не все обычно > 15 сразу).
7 ответов
Если данные "более или менее случайные", то вам не удастся их сжать, я боюсь.
обновление
учитывая новую информацию, я уверен, что вам не нужно 32k цветов на вашем светодиодном дисплее. Я бы предположил, что 1024-или 256-цветовая палитра может быть достаточной. Следовательно, вы можете уйти с тривиальной схемой сжатия (просто сопоставьте каждое слово через таблицу поиска или, возможно, просто отбросьте lsbs каждого компонента), которая будет работать даже для полностью некоррелированные значения пикселей.
очень простой алгоритм сжатия/декомпрессии, который практичен в крошечных встроенных средах и легко "свернуть свой собственный", - это кодирование длины. В основном это означает замену запуска повторяющихся значений парой (count, value). Конечно, вам нужно значение sentinel (magic), чтобы ввести пару, а затем механизм, позволяющий магическому значению появляться в обычных данных (обычно escape-последовательность может использоваться для обоих заданий). В вашем примере лучше всего использовать 16 бит значения (2 байта).
но естественно все зависит от данных. Данные, которые достаточно случайны, несжимаемы по определению. Лучше всего сначала собрать некоторые примеры данных, а затем оценить параметры сжатия.
редактировать после дополнительной информации
просто для удовольствия и показать, как легко запустить кодирование длины я закодировал что-то. Боюсь, я также использовал C для сжатия, так как я не Java-парень. Чтобы сохранить вещи просто я работал полностью с 16-битными данными. Оптимизация заключалась бы в использовании 8-битного счетчика в паре (count,value). Я не пытался скомпилировать и протестировать этот код. См. также Мой комментарий на ваш вопрос о возможных преимуществах коверкая светодиодный адреса.
#define NBR_16BIT_WORDS 400
typedef unsigned short uint16_t;
// Return number of words written to dst (always
// less than or equal to NBR_16BIT_WORDS)
uint16_t compress( uint16_t *src, uint16_t *dst )
{
uint16_t *end = (src+NBR_16BIT_WORDS);
uint16_t *dst_begin = dst;
while( src < end )
{
uint16_t *temp;
uint16_t count=1;
for( temp=src+1; temp<end; temp++ )
{
if( *src == *temp )
count++;
else
break;
}
if( count < 3 )
*dst++ = *src++;
else
{
*dst++ = (*src)|0x8000;
*dst++ = count;
*src += count;
}
}
return dst-dst_begin;
}
void decompress( uint16_t *src, uint16_t *dst )
{
uint16_t *end_src = (src+NBR_16BIT_WORDS);
uint16_t *end_dst = (dst+NBR_16BIT_WORDS);
while( src<end_src && dst<end_dst )
{
data = *src++;
if( (data&0x8000) == 0 )
*dst++ = data;
else
{
data &= 0x7fff;
uint16_t count = *src++;
while( dst<end_dst && count-- )
*dst++ = data;
}
}
}
одной из первых вещей, которые нужно сделать, было бы преобразовать из RGB в YUV, или YCrCb, или что-то в этом порядке. Сделав это, вы обычно можете уйти с суб-выборкой каналов U и V (или Cr/Cb) до половины разрешения. Это довольно распространено в большинстве типов изображений (например, JPEG и MPEG, а также датчики в большинстве цифровых камер).
реалистично, начиная только с 800 байтов данных, большинство других форм сжатия будут пустой тратой времени и усилие. Вам придется приложить немало усилий, прежде чем вы многого добьетесь (и держать его достаточно быстро на Arduino не будет тривиальным для обоих).
Edit: хорошо, если вы абсолютно уверены, что не можете изменить данные вообще, все становится сложнее очень быстро. Реальный вопрос в этот момент заключается в том, с каким вкладом вы имеете дело. Другие уже упоминали о возможности чего-то в порядке предсказательного Дельта-сжатия, например, на основе предшествующие пиксели предсказывают, каким будет следующий, а затем кодируют только разницу между предсказанием и фактическим значением. Однако получение максимальной отдачи от этого обычно требует запуска результата через какой-то алгоритм на основе энтропии, такой как сжатие Шеннона-Фанно или Хаффмана. К сожалению, они обычно не самые быстрые для распаковки.
Если ваши данные-это большинство вещей, таких как диаграммы или графики, где вы можете ожидать больших площадей идентичные пиксели, кодировка длины выполнения (или конца выполнения) могут работать довольно хорошо. Это имеет то преимущество, что это действительно тривиально для распаковки.
Я сомневаюсь,что сжатие на основе LZ будет работать так хорошо. Сжатие на основе LZ работает (в общем случае) путем создания словаря строк байтов, которые были замечены, и когда/если та же строка байтов видна снова, передача кода, назначенного предыдущему экземпляру, вместо повторной передачи всей строки. Этот проблема в том, что вы не могу передать несжатые байты -- вы начинаете с отправки кодового слова, которое представляет этот байт в словаре. В вашем случае вы можете использовать (например) 10-разрядное кодовое слово. Это означает, что в первый раз, когда вы отправляете какой-либо конкретный символ, вам нужно отправить его как 10 бит, а не только 8. Вы только начинаете получать некоторое сжатие, когда можете создать более длинное (двухбайтовое,трехбайтовое и т. д.) строки в вашем словаре,и найти подходящего строка позже на входе.
Это означает, что сжатие на основе LZ обычно получает довольно плохое сжатие для первых нескольких сотен байтов или около того, а затем о перерывах даже на некоторое время, и только после того, как он работает через некоторый вход на некоторое время, он действительно начинает сжиматься хорошо. Имея дело только с 800 байтами за раз, я совсем не уверен, что вы когда-нибудь увидите много сжатия-на самом деле, работая в таких небольших блоках, не было бы особенно удивительно видеть расширение данных на довольно регулярной основе (особенно если это очень случайно).
данные более или менее случайны, я бы сказал, так как он представляет значение цвета rgb для каждых 16 бит.
Как лучше всего сжать эти данные? Есть идеи, сколько сжатия я могу получить?
В идеале вы можете сжать 800 байт цветных данных в один байт, если все изображение одного цвета. Однако, как упоминает Оли Чарльзуорт, чем более случайные данные, тем меньше вы можете их сжать. Если ваши изображения выглядят как статические телевизор, тогда действительно, удачи в получении любого сжатия из него.
определенно рассмотрим ответ Оли Чарльзуорта. На сетке 20x20 я не знаю, нужна ли вам полная цветовая палитра 32k.
кроме того, в начале вопрос, вы сказали, что вы пытаетесь запустить это на период 20 мс (50 Гц). Вам действительно нужна такая скорость для этого дисплея? На 115200 bps, вы можете передать ~11520 байт / сек-вызовите его 10KBps для запаса безопасности (например, ваш микро может иметь задержку между байтами, вы должны сделать некоторые эксперименты, чтобы увидеть, что "реальная" пропускная способность). При 50 Гц это позволяет вам только около 200 байт на пакет - вы ищете коэффициент сжатия более 75%, который может быть недостижим ни при каких обстоятельствах. Вы кажетесь довольно женатым на ваших требованиях, но может быть пришло время для неловкой беседы.
Если вы хотите пройти маршрут сжатия, вам, вероятно, просто придется попробовать несколько разных алгоритмов с "реальными" данными, как говорили другие, и попробовать разные кодировки. Держу пари, ты найдешь что-нибудь еще. время обработки путем делать математику матрицы, etc. между получением байтов по последовательной ссылке (у вас будет около 80 микросекунд между байтами) - если вы используете прерывания для чтения последовательных данных вместо опроса, вы, вероятно, можете сделать это довольно хорошо, используя двойной буфер и обрабатывая/отображая предыдущий буфер при чтении в текущий буфер.
EDIT: Возможно ли увеличить скорость последовательного порта за пределами 115200? Это USB-последовательный адаптер на Amazon говорит, что он поднимается до 1 Mbps (вероятно, на самом деле 921600 bps). В зависимости от вашего оборудования и среды вам может потребоваться беспокоиться о плохих данных, но если вы увеличите скорость, вы, вероятно, сможете добавить контрольную сумму и, возможно, даже ограниченную коррекцию ошибок.
Я не знаком с Arduino, но у меня есть 8-битная FreeScale HCS08, я езжу на 1.25 Мбит / с, хотя на шине фактически работает RS-485, а не RS-232 (485 использует дифференциальную сигнализацию для лучшей производительности шума), и у меня нет проблем с шумовыми ошибками. Вы даже можете рассмотреть адаптер USB RS-485, если вы можете подключить его к Arduino (вам понадобится аппаратное обеспечение для преобразования сигналов 485 на уровни Arduino).
EDIT 2: Вы также можете рассмотреть это USB кабель-Сио/с I2C адаптер, если у вас есть доступный интерфейс I2C или SPI, и вы можете обрабатывать проводку. Он говорит, что может перейти на 400 кГц I2C или 200 кГц SPI, что все еще недостаточно само по себе, но вы можете разделить данные между SPI / I2C и последовательной ссылкой, которая у вас уже есть.
LZ77 / 78 относительно легко писать http://en.wikipedia.org/wiki/LZ77_and_LZ78
однако, учитывая небольшой объем данных, которые вы передаете, его, вероятно, не стоит сжимать вообще.