Моделирование передачи изображения по шумному каналу

Следуя этого вопроса , Я попытался переписать некоторые основные методы, чтобы избежать использования String для бит-операций, и в итоге я использовал BitStream. Код почти в 8 раз быстрее, чем раньше, но он по-прежнему выглядит довольно медленно, поскольку он обрабатывает 15 изображений в минуту (0,25 FPS) .

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

Я говорю о isError и addError, которые нуждаются для каждого бита (в основном шумный канал описывается вероятностью ошибки на уровне бит, что означает, что каждый бит должен обрабатываться индивидуально).

Класс BitStream относится к org.icepdf.core.io. Не стесняйтесь предлагать лучшую альтернативу, если хотите!

TestLoop

for (int i = 0; i < 15; i++) {
    byte[] input = Files.readAllBytes(new File("D:\\testFrame.jpg").toPath());
    ByteArrayInputStream bis = new ByteArrayInputStream(input);
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    // Repetition coder
    RepetitionCoder repCoder = RepetitionFactory.createRepetitionCoder(5);
    repCoder.encode(new BitStream(bis), new BitStream(bos));
    bis = new ByteArrayInputStream(bos.toByteArray());
    bos.reset();
    NoisyChannel channel = new NoisyChannel(ErrorFactory.createError(10, -3, 0));
    channel.transfer(new BitStream(bis), new BitStream(bos));
    bis = new ByteArrayInputStream(bos.toByteArray());
    bos.reset();
    // Repetition decoder
    RepetitionDecoder repDecoder = RepetitionFactory.createRepetitionDecoder(5);
    repDecoder.decode(new BitStream(bis), new BitStream(bos));
    // Write
    Files.write(outputFile.toPath(), bos.toByteArray());
}

RepetitionCoder.encode

public void encode(BitStream in, BitStream out) {
    try {
        while (in.available() > 0) {
            // Read one bit
            int currentBit = in.getBits(1);
            // Output that bit repetitions times
            for (int i = 0; i < repetitions; i++) {
                out.putBit(currentBit);
            }
        }
    } catch (IOException ex) {
        LOG.log(Level.SEVERE, ex.getMessage(), ex);
    }
}

NoisyChannel.transfer

public void transfer(BitStream input, BitStream output) throws IOException {
    while (input.available() > 0) {
        // Read one bit
        int bit = input.getBits(1);
        // Apply error
        int errorBit = errorModel.addError(bit);
        // Output the altered bit
        output.putBit(errorBit);
    }
}

SingleError.addError

protected int addError(int source) {
    if (isError()) {
        // Flip the bit
        return (source == 0) ? 1 : 0;
    }
    return source;
}

protected boolean isError() {
    for (int i = 0; i < Math.abs(exponent); i++) {
        if (Math.random() >= 0.1) {
            return false;
        }
    }
    return !(coefficient > 0 && Math.random() >= coefficient / 10);
}

RepetitionDecoder.decode

public void decode(BitStream in, BitStream out) throws IOException {
    while (in.available() > 0) {
        int zeroes = 0;
        // Read repetitions times and count zeroes
        for (int i = 0; i < repetitions; i++) {
            if (in.getBits(1) == 0) {
                zeroes += 1;
            }
        }
        // Output 0 if zeroes > repetitions/2, 1 otherwise
        out.putBit((zeroes > repetitions / 2) ? 0 : 1);
    }
}

Есть ли у вас какие-либо идеи по этому поводу? Я думаю, что может быть способ преобразования моего кода для работы с byte вместо отдельных битов, но я не очень хорошо разбираюсь маски и побитовые операции.

EDIT: теперь работаем с byte.

SingleError.addError

protected boolean isError() {        
    return random.nextFloat() < errorProbability;
}

protected byte addError(byte source) {
    byte errorMask = 0;        
    for (int j = 0; j < 8; j++) {
        errorMask |= (isError()) ? 1 : 0;
        errorMask <<= 1;            
    }
    errorMask >>= 1;
    return (byte) (source ^ errorMask);
}

NoisyChannel.transfer

public void transfer(ByteArrayInputStream input, ByteArrayOutputStream output) throws IOException {
    while (input.available() > 0) {
        byte in = (byte) input.read();
        byte error = errorModel.addError(in);
        output.write(error);
    }
}
10 голосов | спросил StepTNT 17 ThuEurope/Moscow2015-12-17T00:58:19+03:00Europe/Moscow12bEurope/MoscowThu, 17 Dec 2015 00:58:19 +0300 2015, 00:58:19

2 ответа


2

Что такое isError()?

protected boolean isError() {
    for (int i = 0; i < Math.abs(exponent); i++) {
        if (Math.random() >= 0.1) {
            return false;
        }
    }
    return !(coefficient > 0 && Math.random() >= coefficient / 10);
}

Похоже, вы должны иметь возможность вычислить вероятность ошибки бит один раз, при инициализации. Тогда весь этот метод станет return Math.random() < effective_probability;.

Вы также можете заменить использование Math.random() своим собственным выделенным экземпляром java.util.Random и используйте nextInt() , или nextFloat() , Либо должно быть несколько быстрее, чем версия с двойным генератором.

В самом деле, вы можете пропустить эту логическую логику в пользу быстрого побитового xor, например

protected int getNoiseBit() {
  // Return 1 or 0.
}

protected int addError( final int input ) {
  return input ^ getNoiseBit();
}

Edit:

Подход xor естественно масштабируется для многобитовых значений, например:

// Generates 32 random bits (one int), of which each is 1 with p="probability"
protected int getErrorInt() {
  int bit = 1;
  int result = 0;

  for ( int i = 0; i < 32; i++ ) {
    if ( rng.nextFloat() < probability ) {
      result = result | bit;
    }
    bit = bit << 1;
  }

  return result;
}

// Accepts an int containing 1 to 32 bits of data and adds a probabilistic error.    
protected int addError( final int input ) {
  return input ^ getErrorInt();
}
ответил JimmyB 19 SatEurope/Moscow2015-12-19T23:23:58+03:00Europe/Moscow12bEurope/MoscowSat, 19 Dec 2015 23:23:58 +0300 2015, 23:23:58
4

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

Ваша идея повысить надежность - это передавать каждый бит r раз подряд. Эта схема имеет несколько недостатков:

  • Это неэффективно. Очевидно, r ≥ 2, что означает, что, по крайней мере, вы удваиваете время передачи. Но удвоение данных может позволить приемнику обнаружить , что произошла ошибка. Если вы хотите, чтобы получатель автоматически исправлял любые обнаруженные ошибки без запроса повторной передачи, вам нужно как минимум r ≥ 3, причем r является нечетным, так что получатель может пойти с большинством голосов за каждый бит. Тройная или пятикратная передача времени - очень крутая цена для оплаты. (В вашем декодере, если r четный, тогда привязки смещены в сторону 1, поэтому вы не хотите использовать даже r .)
  • Передача каждого бит последовательно r раз сродни снижению скорости передачи данных в расчете на коэффициент r , что является таким же простым трюком, который обычно выполнялся аппаратным обеспечением. (Ну, не совсем так, так как тактовые частоты остаются неизменными, а механизмы сигнализации кадров не замедляются). Например, Ethernet и Wi-Fi могут автосогласовать скорость вниз или вы можете настроить скорость вручную.
  • Если вы обычно передаете изображение r раз, то передается такое же количество бит, но получатель может оптимистично попытаться отобразить первую полную копию, которую он получает, а затем подтвердить и внести исправления когда он получает последующие копии. Это может привести к более низкой задержке и лучшему опыту пользователя.
  • Один сбой в аналоговой среде, вероятно, уничтожит несколько последовательных бит. Если биты чередуются временно, тогда вы не будете помещать свои яйца в одну и ту же корзину.

Итак, что делать?

Прежде всего, вы должны решить, интересуетесь ли вы обнаружением ошибок или исправлением ошибок. Обнаружение ошибок означает добавление достаточной избыточности, чтобы позволить получателю узнать, что произошла какая-то ошибка; получатель может либо сообщить об отказе, либо запросить повторную передачу. Коррекция ошибок означает добавление достаточной избыточности, чтобы позволить получателю автоматически исправлять ошибки, если ошибок не так много.

Простым механизмом обнаружения ошибок является добавление бит четности . Например, вы можете передавать данные группами по 7 бит, а затем вставлять восьмой бит так, чтобы сумма всех восьми бит была четной. (Это называется «четным паритетом»). Это позволит вам обнаруживать до 1 бит-флип каждые 7 бит при стоимости 14% накладных расходов. Вы можете настроить параметры по мере необходимости.

Еще один механизм обнаружения ошибок - передать контрольную сумму для файла или, может быть, контрольную сумму для каждый кибибайт данных. CRC - это общий класс алгоритмов контрольной суммы, но вы также можете использовать что-то вроде SHA-2 .

Если вы хотите исправить ошибку, то выберите схему из списка . Исправление ошибок Рида Соломона - общая схема. Вы можете настроить параметр t , чтобы выдержать любую пропорциюкоторые вы ожидаете встретить, и все еще сможете полностью восстановить данные.

Имейте в виду, что протоколы низкого уровня, такие как модемы , Ethernet , IP и TCP , как правило, встроен механизм сырой контрольной суммы - уже, так что бы вы ни реализовали, это был бы дополнительный уровень страхования.

ответил 200_success 18 FriEurope/Moscow2015-12-18T13:02:03+03:00Europe/Moscow12bEurope/MoscowFri, 18 Dec 2015 13:02:03 +0300 2015, 13:02:03

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132