Сжатие байтового массива в Java и распаковка в C - PullRequest
6 голосов
/ 12 ноября 2010

У меня в настоящее время есть следующий массив в программе Java,

byte[] data = new byte[800];

и я хотел бы сжать его перед отправкой на микроконтроллер через последовательный порт (115200 бод). Я хотел бы затем распаковать массив на микроконтроллере в C. Однако я не совсем уверен, что лучший способ сделать это. Производительность является проблемой, так как микроконтроллер является просто Arduino, поэтому он не может быть слишком интенсивным памятью / процессором. Данные более или менее случайны ( edit Я думаю, что это не так уж случайно, см. Редактирование ниже). Я бы сказал, так как они представляют значение цвета rgb для каждых 16 битов.

Как лучше всего сжать эти данные? Есть идеи, какое сжатие я мог бы получить?

редактировать

Извините за отсутствие информации. Мне нужно, чтобы сжатие было без потерь, и я намерен отправлять только 800 байтов за раз. Моя проблема в том, что 800 байтов не будут передаваться достаточно быстро со скоростью 115200 бод, которую я использую. Я надеялся, что смогу немного уменьшить размер, чтобы улучшить скорость.

Каждые два байта выглядят так:

0RRRRRGGGGGBBBBB

Где биты R G и B представляют значения для цветовых каналов красного, зеленого и синего соответственно. Каждые два байта - это отдельный светодиод на сетке 20x20. Я полагаю, что многие наборы из двух байтов будут идентичны, поскольку я часто назначаю одинаковые цветовые коды для нескольких светодиодов. Также может быть так, что значения RGB часто> 15, поскольку я обычно использую яркие цвета, когда могу (однако, это может быть спорным вопросом, поскольку они не все обычно> 15 одновременно).

Ответы [ 7 ]

6 голосов
/ 12 ноября 2010

Если данные «более или менее случайные», то, боюсь, вам не повезет сжимать их.

UPDATE

Учитывая новую информацию, держу пари, что вам не нужно 32 тыс. Цветов на вашем светодиодном дисплее. Я полагаю, что может быть достаточно 1024- или 256-цветовой палитры. Следовательно, вы могли бы обойтись без тривиальной схемы сжатия (просто отобразить каждое слово через справочную таблицу или, возможно, просто отбросить lsbs каждого компонента), которая работала бы даже для полностью некоррелированных значений пикселей.

2 голосов
/ 12 ноября 2010

Первое, что нужно сделать, - это конвертировать из RGB в YUV, или YCrCb, или что-то в этом порядке. Сделав это, вы обычно можете избежать частичной дискретизации каналов U и V (или Cr / Cb) до половины разрешения. Это довольно часто встречается в большинстве типов изображений (например, JPEG и MPEG оба делают это, как и датчики в большинстве цифровых камер).

Реально, начиная с 800 байтов данных, большинство других форм сжатия будут пустой тратой времени и усилий. Тебе придется проделать немало работы, прежде чем ты достигнешь многого (и поддерживать его достаточно быстро на Arduino тоже не будет тривиально).

Редактировать: хорошо, если вы абсолютно уверены, что вообще не можете изменить данные, все очень быстро усложняется. На данный момент реальный вопрос в том, с каким вкладом вы имеете дело. Другие уже упоминали о возможности чего-то порядка прогностического дельта-сжатия - например, основываясь на предыдущих пикселях, прогнозируют, каким будет следующий, и затем кодируют только разницу между прогнозом и фактическим значением. Однако для получения максимальной отдачи от этого обычно требуется выполнить результат с помощью какого-либо алгоритма, основанного на энтропии, такого как сжатие Шеннона-Фанно или Хаффмана. Однако, к сожалению, это не самый быстрый способ распаковать.

Если ваши данные в большинстве своем похожи на диаграммы или графики, где вы можете ожидать, что у вас будут большие области идентичных пикселей, кодирование по длине (или концу) может работать довольно хорошо. Это имеет то преимущество, что на самом деле очень просто распаковать.

Я сомневаюсь, что сжатие на основе LZ будет работать так хорошо, хотя. Сжатие на основе LZ работает (в общем) путем создания словаря строк байтов, которые были замечены, и когда / если снова видна та же строка байтов, передавая код, назначенный предыдущему экземпляру, вместо повторной передачи всего строка. Проблема в том, что вы не можете передавать несжатые байты - вы начинаете с отправки кодового слова, которое представляет этот байт в словаре. В вашем случае вы можете использовать (например) 10-битное кодовое слово. Это означает, что в первый раз, когда вы отправляете какой-либо конкретный символ, вам нужно отправить его как 10 битов, а не только 8. Вы начинаете получать некоторое сжатие только тогда, когда вы можете создать более длинный (двухбайтовый, трехбайтовый и т. Д.) Строки в вашем словаре, и , найдут соответствующую строку позже во входных данных.

Это означает, что сжатие на основе LZ обычно получает довольно слабое сжатие для первых нескольких сотен байтов или около того, затем даже без перерыва на некоторое время, и только после того, как оно какое-то время проходит по некоторому вводу, оно действительно начинает хорошо сжиматься , Имея дело только с 800 байтами за раз, я вовсе не уверен, что вы когда-нибудь увидите сильное сжатие - фактически, работая в таких маленьких блоках, не было бы особенно удивительно видеть, как данные расширяются на довольно регулярное основание (особенно если оно очень случайное).

2 голосов
/ 12 ноября 2010

Действительно простой алгоритм сжатия / декомпрессии, который практичен в крошечных встроенных средах и легко "катится самостоятельно", - это кодирование по длине прогона.В основном это означает замену серии повторяющихся значений парой (количество, значение).Конечно, вам необходимо ввести магическое (магическое) значение для введения пары, а затем механизм, позволяющий отображать магическое значение в обычных данных (обычно для обеих заданий можно использовать escape-последовательность).В вашем примере может быть лучше использовать 16-битные значения (2 байта).

Но, естественно, все зависит от данных.Достаточно случайные данные несжимаемы по определению.Лучше всего сначала собрать некоторые примеры данных, а затем оценить параметры сжатия.

Редактировать после публикации дополнительной информации

Просто для удовольствия и показать, как легко пробежать длинукодировка это я что-то закодировал.Боюсь, я тоже использовал C для сжатия, так как я не Java-парень.Для простоты я работал полностью с 16-битными данными.Оптимизация заключалась бы в использовании 8-битного счетчика в паре (количество, значение).Я не пытался скомпилировать или протестировать этот код.См. Также мой комментарий к вашему вопросу о возможных преимуществах искажения адресов светодиодов.

#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;
        }
    }
}
2 голосов
/ 12 ноября 2010

Используйте сжатие miniLZO. Java-версия C-версия

1 голос
/ 12 ноября 2010

Определенно рассмотрите ответ Оли Чарльзуорт. На сетке 20x20, я не знаю, нужна ли вам полная 32-цветовая палитра.

Кроме того, в своем предыдущем вопросе вы сказали, что пытаетесь запустить его в течение 20 мс (50 Гц). Вам действительно нужно столько скорости для этого дисплея? При скорости 115200 бит / с вы можете передавать ~ 11520 байт / с - для обеспечения безопасности назовите его 10 Кбит / с (например, у вашего микро может быть задержка между байтами, вам следует провести несколько экспериментов, чтобы увидеть, какова «реальная» пропускная способность). При частоте 50 Гц это позволяет использовать только около 200 байтов на пакет - вам нужна степень сжатия более 75%, которая может быть недоступна ни при каких обстоятельствах. Вы, кажется, довольно женаты для своих требований, но это может быть время для неловкой беседы.

Если вы хотите пойти по пути сжатия, вам, вероятно, нужно будет просто попробовать несколько разных алгоритмов с «реальными» данными, как уже говорили другие, и попробовать разные кодировки. Могу поспорить, что вы можете найти дополнительное время обработки, выполняя математическую математику и т. Д. Между полученными байтами по последовательному каналу (между байтами у вас будет около 80 микросекунд) - если вы используете прерывания для чтения последовательных данных вместо опроса, вы вероятно, может быть очень хорошо, используя двойной буфер и обрабатывая / отображая предыдущий буфер при чтении в текущий буфер.

EDIT: Можно ли увеличить скорость последовательного порта выше 115200? Этот USB-последовательный адаптер в Amazon говорит, что он разгоняется до 1 Мбит / с (вероятно, на самом деле 921600 бит / с). В зависимости от вашего оборудования и среды вам, возможно, придется беспокоиться о плохих данных, но если вы увеличите скорость достаточно, вы можете добавить контрольную сумму и, возможно, даже ограниченную коррекцию ошибок.

Я не знаком с Arduino, но у меня есть 8-битный FreeScale HCS08, который я использую со скоростью 1,25 Мбит / с, хотя шина на самом деле работает по RS-485, а не по RS-232 (485 использует дифференциальную сигнализацию для лучшего шумовые характеристики), и у меня нет проблем с ошибками шума. Вы могли бы даже подумать об адаптере USB RS-485, если вы можете подключить его к Arduino (вам потребуется преобразовательное оборудование, чтобы изменить сигналы 485 до уровней Arduino).

РЕДАКТИРОВАТЬ 2: Вы можете также рассмотреть этот адаптер USB-SPI / I2C , если у вас есть доступный интерфейс I2C или SPI, и вы можете справиться с проводкой. В нем говорится, что он может использовать 400 кГц I2C или 200 кГц SPI, что само по себе недостаточно, но вы можете разделить данные между SPI / I2C и последовательным каналом, который у вас уже есть.

1 голос
/ 12 ноября 2010

Я бы сказал, что данные более или менее случайные, поскольку они представляют значение цвета rgb для каждых 16 битов.

Как лучше всего сжать эти данные? Есть идеи, какое сжатие я мог бы получить?

В идеале вы можете сжать 800 байт данных цвета в один байт, если все изображение одного цвета. Однако, как упоминает Оли Чарльзуорт, чем больше случайных данных, тем меньше их можно сжать. Если ваши изображения выглядят как статичные на телевизоре, то, действительно, удачи в любом сжатии.

0 голосов
/ 12 ноября 2010

LZ77 / 78 относительно легко написать http://en.wikipedia.org/wiki/LZ77_and_LZ78

Однако, учитывая небольшой объем передаваемых данных, его, вероятно, вообще не стоит сжимать.

...