Можно ли отобразить байтовый массив png / jpeg в элемент без создания BLOB-объекта и URL.createObjectURL? - PullRequest
1 голос
/ 10 апреля 2019

Я пишу интерфейсное приложение с Angular и grpc-web stream.Цель состоит в том, чтобы визуализировать полезную нагрузку изображения из потока со скоростью 60-100 кадров в секунду.Ранее он был реализован в WPF, но теперь мне нужно переключиться на веб-интерфейс.Я заметил, что хотя двоичное изображение было передано в браузер, мне все еще нужно создать URL-адрес большого двоичного объекта для загрузки.

Моя текущая стратегия:

URL.revokeObjectURL(this.ImageSrc);
const blob = new Blob([res.getImage_asU8()], {type: res.getType()});
this.ImageSrc = URL.createObjectURL(blob);

Однако,Есть две основные проблемы с производительностью 1. Загрузка процессора очень высока.Я профилировал его с помощью CDT и обнаружил, что new Blob() составляет больше всего времени сценария.2. Несмотря на то, что объект уже находится в памяти, после того, как я создал URL-адрес большого двоичного объекта, элемент img, казалось, снова загружал большой двоичный объект из памяти.Из сетевого мониторинга CDT я обнаружил, что для загрузки каждого двоичного объекта в памяти требуется 10-30 мс. (ОБНОВЛЕНИЕ: возможно, из-за того, что CDT замедляет производительность)

Помимо создания URL-адреса для большого двоичного объекта, я также попытался использовать base64 для рендеринга изображения.Изображение имеет размер 800 * 600, монохромный png, и, как и ожидалось, base64 в этом случае не поможет.

Интересно, могу ли я напрямую визуализировать двоичное изображение, переданное потоком, в элемент (img, canvas илиsvg) без создания большого двоичного объекта или повторной загрузки большого двоичного объекта из памяти.Спасибо!

Обновление 10 апреля

Я сделал тестовый проект для сравнения скорости обновления изображения.Я хотел сравнить три варианта использования, но я не знаю, как эффективно декодировать двоичный файл png / jpeg в RGBA в JavaScript браузера, поэтому я пропустил это.

Сравнение нескольких изображений

<img> + createObjectURL: img + blob

<canvas> + putImageData: canvas

Таким образом, когда 10 изображений передаются в потоковом режиме параллельно, решение «картинка + блоб» кажется быстрее, чем метод на основе холста.

Сравнение одного изображения

<img> + createObjectURL: img + blob

<canvas> + putImageData: server-side + canvas

Удивительно, но IMG можно идти в ногу с такой скоростью.Холст кажется слишком тяжелым, чтобы обновлять его с такой скоростью.

Заключение

<img> + createObjectURL должно соответствовать большинству ситуаций с высоким FPS.** Не доверяйте времени в CDT, потому что CDT замедлит выполнение **.Здесь я не очень тщательно сравниваю, пожалуйста, дайте мне знать, если я не правильно использую элемент canvas.Кроме того, использование кучи для <img> + createObjectURL намного ниже, чем при использовании canvas, если URL.revokeObjectURL вызывается во времени.

Ответы [ 2 ]

0 голосов
/ 10 апреля 2019

Может быть некоторые работы могут быть выгружены веб-работнику?

Например, createImageBitmap может создать ImageBitmap из BLOB-объекта, а ImageBitmap реализует интерфейс Transferable, что означает, что он может передаваться между веб-работником и основным потоком.

Итак, возможно, стоит поискать веб-работника, создающего ImageBitmap, который затем передается в основной поток и отображается на холсте?

0 голосов
/ 10 апреля 2019

Это действительно зависит от того, откуда ваши изображения.

Простейшей идеей здесь было бы отправить его в виде видеопотока.

Если это невозможно, то вы можете рассмотреть возможность отправки необработанных растровых данных как Uint8 [R, G, B, A, R, G, B, A], которые вы сможете отправить непосредственно на холст. , если у вас есть фиксированная ширина и высота.

const width = 300;
const height = 150;
const service = new Worker(getWorkerURL());
const ctx = canvas.getContext('2d');

service.onmessage = e => {
  const img = new ImageData(e.data, 300, 150);
  ctx.putImageData(img,0,0);
};





// fake service
// a simple Worker that will send a message with an Uint8ClampedArray of random data

function getWorkerURL() {
  const content = `
function sendData() {
  const arr = new Uint8ClampedArray(300*150*4);
  for(let i = 0; i<arr.length; i++) {
    if(i%4 === 3) arr[i] = 255;
    arr[i] = Math.random() * 255;
  }
  postMessage(arr, [arr.buffer]);
  if(self.requestAnimationFrame)
    self.requestAnimationFrame(sendData);
  else setTimeout(sendData, 16);
}
sendData();`;
  const blob = new Blob([content], {type: 'application/javascript'});
  return URL.createObjectURL(blob);
}
<canvas id="canvas"></canvas>

Если это также невозможно, и вам необходимо отправлять двоично-закодированные изображения, то нет лучшего способа, чем пройти через элемент <img>, даже если это действительно подразумевает множество асинхронных операций, таких как сетевые. Конечно, вы можете попытаться самостоятельно проанализировать двоичный файл из полученного ArrayBuffer и декодировать необработанные значения пикселей, но моя интуиция говорит, что вам потребуется много работы, чтобы конкурировать со встроенными реализациями.

...