Сравнение двух строк изображения base64 и удаления совпадений?

62
2

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

var bitmap = new Bitmap(1024, 720);

string oldBase = "";

using (var stream = new MemoryStream())
using (var graphics = Graphics.FromImage(bitmap))
{
graphics.CopyFromScreen(bounds.X, bounds.Y, 0, 0, bounds.Size);
bitmap.Save(stream, ImageFormat.Jpeg);
string newBase = Convert.ToBase64String(stream.ToArray());

// ! Do compare/replace stuff here with newBase and oldBase !

// Store the old image as a base64 string.
oldBase = newBase;
}

Используя что-то подобное, я мог бы сравнить строки base64 и заменить любые совпадения. Соответствующий текст можно заменить чем-то вроде:

[количество замененных символов]

Таким образом, на стороне клиента я знаю, где заменить старые данные и добавить новое. Опять же, я не уверен, будет ли это работать, так что любые мысли по этому поводу будут очень оценены. :) Если это возможно, не могли бы вы указать мне в правильном направлении? Благодарю.

спросил(а) 2021-01-25T14:04:20+03:00 4 месяца, 4 недели назад
1
Решение
63

Вы можете сделать это, сравнив биты битмапа напрямую. Посмотрите на Bitmap.LockBits, который даст вам указатель BitmapData из которого вы сможете получить данные о пикселях. Затем вы можете сравнить пиксели для каждой строки сканирования и закодировать их в любом формате, который вы хотите использовать для транспорта.

Обратите внимание, что длина строки сканирования в байтах всегда кратная 4. Поэтому, если вы не используете 32-битный цвет, вы должны учитывать отступы, которые могут быть в конце строки сканирования. Это свойство Stride для структуры BitmapData.

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

<start marker>
// for each scan line
<scan line marker><scan line number>
<pixel position><number of pixels><pixel data>
<pixel position><number of pixels><pixel data>
...
// next scan line
<scan line marker><scan line number>
...
<end marker>

каждая <pixel position><number of pixels><pixel data> - это запуск измененных пикселей. Если в строке сканирования нет измененных пикселей, вы можете не отправлять ее. Или вы можете просто отправить маркер и номер строки сканирования, а затем следующую строку сканирования.

Для поля <pixel position> и для поля <number of pixels> будет достаточно двух байтов. Таким образом, у вас есть накладные расходы на четыре байта для каждого блока. Оптимизация, которая может вас заинтересовать, после того, как вы выполняете самую простую версию, заключалась бы в объединении блоков измененных/неизменных пикселей, если есть небольшие прогоны. Например, если у вас есть uucucuc, где u - неизменный пиксель, а c - измененный пиксель, вы, вероятно, захотите закодировать cucuc как один из пяти измененных пикселей. Это уменьшит объем данных, которые вы должны передать.

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

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

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

ответил(а) 2021-01-25T14:04:20+03:00 4 месяца, 4 недели назад
63

В настоящее время мы работаем над чем-то очень похожим - в основном, то, что вы пытаетесь реализовать, - это видеокодек (очень простое движение jpeg). Есть несколько простых подходов и некоторые очень сложные.


Самый простой подход - сравнить последовательные кадры и отправить только различия. Вы можете попытаться сравнить цветовые различия между кадрами в пространстве RGB или пространстве YCbCr и отправить только пиксели, которые изменились с некоторыми метаданными. Более сложным решением является сравнение изображений после преобразования DCT, но до энтропийного кодирования. Это даст вам лучшее сравнение и устранит некоторые уродливые артефакты. Проверьте больше информации о JPEG, Motion JPEG, H.264 - вы можете использовать некоторые методы, используемые этими кодеками или просто используя существующий кодек, если это возможно.

ответил(а) 2021-01-25T14:04:20+03:00 4 месяца, 4 недели назад
44

Это не работает для JPEG. Вам нужно использовать BMP или, возможно, несжатый TIFF.

Я думаю, если бы это был я, я бы использовал BMP, просматривал пиксели для изменений и строил PNG, где все, кроме изменений, было прозрачным.

Во-первых, это уменьшит размер вашей передачи, потому что сжатие PNG неплохое, особенно для повторения пикселей.

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

ответил(а) 2021-01-25T14:04:20+03:00 4 месяца, 4 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема