如何从原始数据文件中读取12位无符号整数到JavaScript的UInt16Array中?

huangapple go评论78阅读模式
英文:

How to read 12bit unsigned integers from a raw data file into UInt16Array in JavaScript?

问题

以下是翻译好的内容:

我有一个包含12位无符号整数值的文件。我可以直接将文件读入UInt8Array或UInt16Array中,但这些数组的值当然是不正确的,而且长度也不正确。如何读取这些12位值并将它们保存到Uint16Array中,但值必须是正确的?

我读到可以将这些值读取为3个8位整数,然后将中间的一个拆分为两部分,但我不知道如何在JavaScript中真正做到这一点。

下面的代码使UInt16Array的长度正确,所以拆分是正确的,但数组中的值不正确:

英文:

So I have a file which contains values of 12 bit unsigned integers. I have no problem reading the file directly into a UInt8Array or UInt16Array but the values are of course incorrect and the length of those arrays is incorrect. How can I read those 12 bit values and save them into a Uint16Array but the values must be correct?

So I read that I could read the values as 3 8bit integers and then split the midle one into two parts but I dont know how to really do this in JavaScript.

The code bellow makes the correct length of UInt16Array, so the splitting is right, but the values in array are not correct:

fileReader.onload = () => {
    const arrayBuffer = fileReader.result;

    const dataView = new DataView(arrayBuffer);
    const length = Math.floor((dataView.byteLength * 8) / 12);
    const result = new Uint16Array(length);

    let byteOffset = 0;
    let bitOffset = 0;
    let currentIndex = 0;

    for (let i = 0; i < length; i++) {
        const bitsRemaining = 12 - bitOffset;
        const bitsToRead = Math.min(bitsRemaining, 16);

        let value = (dataView.getUint8(byteOffset) << 8) | dataView.getUint8(byteOffset + 1);
        value >>= (16 - bitOffset - bitsToRead);
        value &= (0b1 << bitsToRead) - 1;

        result[currentIndex] = value;

        bitOffset += bitsToRead;
        if (bitOffset === 12) {
            byteOffset += 1;
            bitOffset = 0;
        }
    
        currentIndex++;
    }
}
                  

答案1

得分: 1

以下是您提供的代码的中文翻译部分:

考虑到nibble顺序(类似于字节的endianness),可以在单个通用函数内实现4种最常见的布局:

BE0/BE1  |Hi0 Hi0|Lo0 Hi1|Lo1 Lo1|
BE0/LE1  |Hi0 Hi0|Lo0 Lo1|Hi1 Hi1|
LE0/BE1  |Lo0 Lo0|Hi0 Hi1|Lo1 Lo1|
LE0/LE1  |Lo0 Lo0|Hi0 Lo1|Hi1 Hi1|

该函数通过3字节块循环以提高速度。

function unpackUint12(arrayBuffer, nibbleOrder) {

    let u8 = new Uint8Array(arrayBuffer);
    let length = Math.floor(arrayBuffer.byteLength * 8 / 12);
    let result = new Uint16Array(length);

    switch (nibbleOrder.toUpperCase()) {
    case 'BB':
        var read = () => {
            // |Hi0 Hi0|Lo0 Hi1|Lo1 Lo1|
            let chunk = (u8[j++] << 16) | (u8[j++] << 8) | u8[j++];
            result[i++] = chunk >> 12;
            result[i++] = chunk & 0x0fff;
        };
        break;
    case 'BL':
        var read = () => {
            // |Hi0 Hi0|Lo0 Lo1|Hi1 Hi1|
            result[i++] = (u8[j++] << 4) | (u8[j] >> 4);
            result[i++] = (u8[j++] & 15) | (u8[j++] << 4);
        };
        break;
    case 'LB':
        var read = () => {
            // |Lo0 Lo0|Hi0 Hi1|Lo1 Lo1|
            result[i++] = u8[j++] | ((u8[j] & 0xf0) << 4);
            result[i++] = ((u8[j++] & 15) << 8) | u8[j++];
        };
        break;
    case 'LL':
        var read = () => {
            // |Lo0 Lo0|Hi0 Lo1|Hi1 Hi1|
            result[i++] = u8[j++] | ((u8[j] & 0xf0) << 4);
            result[i++] = (u8[j++] & 15) | (u8[j++] << 4);
        };
        break;
    default:
        throw Error('Invalid nibble order');
    }

    for (var i = 0, j = 0; j <= u8.length - 3;) {
        read();
    }
    // 读取尾部值,如果有的话
    if (u8.length % 3 == 2) {
        result[i] = (nibbleOrder[0].toUpperCase() == 'B')
            ? (u8[j] << 4) | (u8[j+1] >> 4)
            : u8[j] | ((u8[j+1] & 0xf0) << 4);
    }
    return result;
}

// 用数字从1到2048测试缓冲区,并在末尾添加3333
let test_BB = new Uint8Array([
    0b00000000,0b00010000,0b00000010,
    0b00000000,0b01000000,0b00001000,
    0b00000001,0b00000000,0b00100000,
    0b00000100,0b00000000,0b10000000,
    0b00010000,0b00000010,0b00000000,
    0b01000000,0b00001000,0b00000000,
    0b11010000,0b01010000
]).buffer;
let test_BL = new Uint8Array([
    0b00000000,0b00010010,0b00000000,
    0b00000000,0b01001000,0b00000000,
    0b00000001,0b00000000,0b00000010,
    0b00000100,0b00000000,0b00001000,
    0b00010000,0b00000000,0b00100000,
    0b01000000,0b00000000,0b10000000,
    0b11010000,0b01010000
]).buffer;
let test_LB = new Uint8Array([
    0b00000001,0b00000000,0b00000010,
    0b00000100,0b00000000,0b00001000,
    0b00010000,0b00000000,0b00100000,
    0b01000000,0b00000000,0b10000000,
    0b00000000,0b00010010,0b00000000,
    0b00000000,0b01001000,0b00000000,
    0b00000101,0b11010000
]).buffer;
let test_LL = new Uint8Array([
    0b00000001,0b00000010,0b00000000,
    0b00000100,0b00001000,0b00000000,
    0b00010000,0b00000000,0b00000010,
    0b01000000,0b00000000,0b00001000,
    0b00000000,0b00010000,0b00100000,
    0b00000000,0b01000000,0b10000000,
    0b00000101,0b11010000
]).buffer;

console.log(unpackUint12(test_BB, 'BB'));
console.log(unpackUint12(test_BL, 'BL'));
console.log(unpackUint12(test_LB, 'LB'));
console.log(unpackUint12(test_LL, 'LL'));

然而,如果nibble以更复杂的方式混合,最好编写一些专用的提取方法。

英文:

With consideration of nibble order (something similar to endianness for bytes), 4 most expected layouts can be implemented within a single generic function:

BE0/BE1  |Hi0 Hi0|Lo0 Hi1|Lo1 Lo1|
BE0/LE1  |Hi0 Hi0|Lo0 Lo1|Hi1 Hi1|
LE0/BE1  |Lo0 Lo0|Hi0 Hi1|Lo1 Lo1|
LE0/LE1  |Lo0 Lo0|Hi0 Lo1|Hi1 Hi1|

The function loops through 3-byte chunks to run faster.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

function unpackUint12 (arrayBuffer, nibbleOrder) {
let u8 = new Uint8Array(arrayBuffer);
let length = Math.floor(arrayBuffer.byteLength * 8 / 12);
let result = new Uint16Array(length);
switch (nibbleOrder.toUpperCase()) {
case &#39;BB&#39;:
var read = () =&gt; {
// |Hi0 Hi0|Lo0 Hi1|Lo1 Lo1|
let chunk = (u8[j++] &lt;&lt; 16) | (u8[j++] &lt;&lt; 8) | u8[j++];
result[i++] = chunk &gt;&gt; 12;
result[i++] = chunk &amp; 0x0fff;
};
break;
case &#39;BL&#39;:
var read = () =&gt; {
// |Hi0 Hi0|Lo0 Lo1|Hi1 Hi1|
result[i++] = (u8[j++] &lt;&lt; 4) | (u8[j] &gt;&gt; 4);
result[i++] = (u8[j++] &amp; 15) | (u8[j++] &lt;&lt; 4);
};
break;
case &#39;LB&#39;:
var read = () =&gt; {
// |Lo0 Lo0|Hi0 Hi1|Lo1 Lo1|
result[i++] = u8[j++] | ((u8[j] &amp; 0xf0) &lt;&lt; 4);
result[i++] = ((u8[j++] &amp; 15) &lt;&lt; 8) | u8[j++];
};
break;
case &#39;LL&#39;:
var read = () =&gt; {
// |Lo0 Lo0|Hi0 Lo1|Hi1 Hi1|
result[i++] = u8[j++] | ((u8[j] &amp; 0xf0) &lt;&lt; 4);
result[i++] = (u8[j++] &amp; 15) | (u8[j++] &lt;&lt; 4);
};
break;
default:
throw Error(&#39;Invalid nibble order&#39;);
}
for (var i = 0, j = 0; j &lt;= u8.length - 3;) {
read();
}
// read trailing value, if there&#39;s any
if (u8.length % 3 == 2) {
result[i] = (nibbleOrder[0].toUpperCase() == &#39;B&#39;)
? (u8[j] &lt;&lt; 4) | (u8[j+1] &gt;&gt; 4)
: u8[j] | ((u8[j+1] &amp; 0xf0) &lt;&lt; 4);
}
return result;
}
// test buffers with numbers from 1 to 2048, and trailing 3333
let test_BB = new Uint8Array([
0b00000000,0b00010000,0b00000010,
0b00000000,0b01000000,0b00001000,
0b00000001,0b00000000,0b00100000,
0b00000100,0b00000000,0b10000000,
0b00010000,0b00000010,0b00000000,
0b01000000,0b00001000,0b00000000,
0b11010000,0b01010000
]).buffer;
let test_BL = new Uint8Array([
0b00000000,0b00010010,0b00000000,
0b00000000,0b01001000,0b00000000,
0b00000001,0b00000000,0b00000010,
0b00000100,0b00000000,0b00001000,
0b00010000,0b00000000,0b00100000,
0b01000000,0b00000000,0b10000000,
0b11010000,0b01010000
]).buffer;
let test_LB = new Uint8Array([
0b00000001,0b00000000,0b00000010,
0b00000100,0b00000000,0b00001000,
0b00010000,0b00000000,0b00100000,
0b01000000,0b00000000,0b10000000,
0b00000000,0b00010010,0b00000000,
0b00000000,0b01001000,0b00000000,
0b00000101,0b11010000
]).buffer;
let test_LL = new Uint8Array([
0b00000001,0b00000010,0b00000000,
0b00000100,0b00001000,0b00000000,
0b00010000,0b00000000,0b00000010,
0b01000000,0b00000000,0b00001000,
0b00000000,0b00010000,0b00100000,
0b00000000,0b01000000,0b10000000,
0b00000101,0b11010000
]).buffer;
console.log(unpackUint12(test_BB, &#39;BB&#39;));
console.log(unpackUint12(test_BL, &#39;BL&#39;));
console.log(unpackUint12(test_LB, &#39;LB&#39;));
console.log(unpackUint12(test_LL, &#39;LL&#39;));

<!-- end snippet -->

However if nibbles are mixed in a more complicated way, it would be better to write some dedicated method for extraction.

答案2

得分: 0

我使用以下代码成功获得了我想要的结果:

fileReader.onload = () => {
    const arrayBuffer = fileReader.result;
    var dataView = new DataView(arrayBuffer)

    var numIntegers = Math.floor((dataView.byteLength * 8) / 12);
    uint16Array = new Uint16Array(numIntegers);

    var j = 0;
    for (var i = 0; i < dataView.byteLength; i += 3) {
        uint16Array[j] = dataView.getUint8(i) << 4 | dataView.getUint8(i + 1) >> 4;
        uint16Array[j + 1] = (dataView.getUint8(i + 1) & 0x0F) << 8 | dataView.getUint8(i + 2);
        j += 2;
    }
}
英文:

So i managed to get the result I wanted with the code below:

fileReader.onload = () =&gt; {
const arrayBuffer = fileReader.result;
var dataView = new DataView(arrayBuffer)
var numIntegers = Math.floor((dataView.byteLength * 8) / 12);
uint16Array = new Uint16Array(numIntegers);
var j = 0;
for (var i = 0; i &lt; dataView.byteLength; i += 3) {
uint16Array[j] = dataView.getUint8(i) &lt;&lt; 4 | dataView.getUint8(i + 1) &gt;&gt; 4;
uint16Array[j + 1] = (dataView.getUint8(i + 1) &amp; 0x0F) &lt;&lt; 8 | dataView.getUint8(i + 2);
j += 2;
}
}

huangapple
  • 本文由 发表于 2023年5月25日 21:07:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/76332631.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定