From 2e6e166bd74b91c5fe02f559df27f6192674a57b Mon Sep 17 00:00:00 2001 From: Ashish Chaudhary Date: Wed, 14 Aug 2019 18:19:12 +0530 Subject: [PATCH 1/2] added 8bit image encodings --- src/utils/processing.js | 22 +++++++++++++ src/viz/Image.js | 73 +++++++++++++++++++++-------------------- 2 files changed, 59 insertions(+), 36 deletions(-) diff --git a/src/utils/processing.js b/src/utils/processing.js index fc2294cf..b03ec4f4 100644 --- a/src/utils/processing.js +++ b/src/utils/processing.js @@ -140,3 +140,25 @@ export const imageDataToCanvas = imageData => { context.putImageData(imageData, 0, 0); return canvas; }; + +export const populateImageDataFromImageMsg = ( + message, + offset, + rgbaOrder, + imageData, +) => { + const { data: rawData, height, step } = message; + const typedArray = Uint8Array.from(rawData); + // endianness is required for > 8bit encodings + const encodedDataView = new DataView(typedArray.buffer); + + let j = 0; + for (let i = 0; i < step * height; i += offset) { + for (let k = 0; k < rgbaOrder.length; k++) { + imageData.data[j++] = encodedDataView.getUint8(i + rgbaOrder[k]); + } + if (rgbaOrder.length === 3) { + imageData.data[j++] = 255; + } + } +}; diff --git a/src/viz/Image.js b/src/viz/Image.js index 99ad00b4..4bd34065 100644 --- a/src/viz/Image.js +++ b/src/viz/Image.js @@ -1,5 +1,6 @@ import Core from '../core'; import { DEFAULT_OPTIONS_IMAGE, MESSAGE_TYPE_IMAGE } from '../utils/constants'; +import { populateImageDataFromImageMsg } from '../utils/processing'; class Image extends Core { constructor(ros, topicName, options = DEFAULT_OPTIONS_IMAGE) { @@ -18,61 +19,61 @@ class Image extends Core { } applyImageData(message) { - const { - data, - encoding, - height, - is_bigendian: isBigEndian, - step, - width, - } = message; + const { encoding, height, width } = message; const ctx = this.object.getContext('2d'); const shadowCtx = this.shadowObject.getContext('2d'); const imgData = shadowCtx.createImageData(width, height); - const encodeToUInt8 = Uint8Array.from(data); - const encodedDataView = new DataView(encodeToUInt8.buffer); - switch (encoding) { + // not using encoding.find(bayer) because js switch statement + // is much faster + case 'bayer_rggb8': + case 'bayer_bggr8': + case 'bayer_gbrg8': + case 'bayer_grbg8': + case 'bayer_rggb16': + case 'bayer_bggr16': + case 'bayer_gbrg16': + case 'bayer_grbg16': + case '8UC1': + case '8SC1': case 'mono8': { - let j = 0; - for (let i = 0; i < step * height; i++) { - imgData.data[j++] = encodedDataView.getUint8(i, !isBigEndian); - imgData.data[j++] = encodedDataView.getUint8(i, !isBigEndian); - imgData.data[j++] = encodedDataView.getUint8(i, !isBigEndian); - imgData.data[j++] = 255; - } + populateImageDataFromImageMsg(message, 1, [0, 0, 0], imgData); break; } + case '8UC3': + case '8SC3': case 'bgr8': { - const offset = 3; - - let j = 0; - for (let i = 0; i < step * height; i += offset) { - imgData.data[j++] = encodedDataView.getUint8(i + 2, !isBigEndian); - imgData.data[j++] = encodedDataView.getUint8(i + 0, !isBigEndian); - imgData.data[j++] = encodedDataView.getUint8(i + 1, !isBigEndian); - imgData.data[j++] = 255; - } + populateImageDataFromImageMsg(message, 3, [2, 1, 0], imgData); + break; + } + case '8UC4': + case '8SC4': + case 'bgra8': { + populateImageDataFromImageMsg(message, 4, [2, 1, 0, 3], imgData); break; } case 'rgb8': { - const offset = 3; - - let j = 0; - for (let i = 0; i < step * height; i += offset) { - imgData.data[j++] = encodedDataView.getUint8(i + 0, !isBigEndian); - imgData.data[j++] = encodedDataView.getUint8(i + 1, !isBigEndian); - imgData.data[j++] = encodedDataView.getUint8(i + 2, !isBigEndian); - imgData.data[j++] = 255; - } + populateImageDataFromImageMsg(message, 3, [0, 1, 2], imgData); + break; + } + case 'rgba8': { + populateImageDataFromImageMsg(message, 4, [0, 1, 2, 3], imgData); break; } default: break; } + ctx.clearRect(0, 0, this.object.width, this.object.height); + shadowCtx.clearRect( + 0, + 0, + this.shadowObject.width, + this.shadowObject.height, + ); + shadowCtx.putImageData(imgData, 0, 0); ctx.drawImage( this.shadowObject, From d1264678d06332a7310332fe5d691e49006c73ea Mon Sep 17 00:00:00 2001 From: Ashish Chaudhary Date: Wed, 14 Aug 2019 21:17:04 +0530 Subject: [PATCH 2/2] image scaling on resize is now 1:1 with rviz --- src/viz/Image.js | 48 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/src/viz/Image.js b/src/viz/Image.js index 4bd34065..141b9d29 100644 --- a/src/viz/Image.js +++ b/src/viz/Image.js @@ -10,8 +10,6 @@ class Image extends Core { }); this.object = document.createElement('canvas'); this.shadowObject = document.createElement('canvas'); - this.ratioX = 1; - this.ratioY = 1; this.updateOptions({ ...DEFAULT_OPTIONS_IMAGE, ...options, @@ -21,6 +19,7 @@ class Image extends Core { applyImageData(message) { const { encoding, height, width } = message; + const aspectRatio = width / height; const ctx = this.object.getContext('2d'); const shadowCtx = this.shadowObject.getContext('2d'); const imgData = shadowCtx.createImageData(width, height); @@ -67,28 +66,49 @@ class Image extends Core { } ctx.clearRect(0, 0, this.object.width, this.object.height); + ctx.fillStyle = '#000'; + ctx.fillRect(0, 0, this.object.width, this.object.height); shadowCtx.clearRect( 0, 0, this.shadowObject.width, this.shadowObject.height, ); - shadowCtx.putImageData(imgData, 0, 0); - ctx.drawImage( - this.shadowObject, - 0, - 0, - width * this.ratioX, - height * this.ratioY, - ); + + const scaledImageHeight = this.object.width / aspectRatio; + const scaledImageWidth = this.object.height * aspectRatio; + + if (aspectRatio >= this.object.width / this.object.height) { + ctx.drawImage( + this.shadowObject, + 0, + 0, + width, + height, + 0, + (this.object.height - scaledImageHeight) / 2, + this.object.width, + scaledImageHeight, + ); + } else { + ctx.drawImage( + this.shadowObject, + 0, + 0, + width, + height, + (this.object.width - scaledImageWidth) / 2, + 0, + scaledImageWidth, + this.object.height, + ); + } } updateDimensions(width, height) { this.object.width = width; this.object.height = height; - this.ratioX = width / this.shadowObject.width; - this.ratioY = height / this.shadowObject.height; } updateOptions(options) { @@ -100,12 +120,8 @@ class Image extends Core { update(message) { const { height, width } = message; - this.shadowObject.width = width; this.shadowObject.height = height; - this.ratioX = this.object.width / width; - this.ratioY = this.object.height / height; - this.applyImageData(message); }