如何使用 jpeg-js module/node JS 缓冲区反转扫描线?

How do I reverse a scanline using the jpeg-js module/node JS buffer?

我一直在摆弄 jpeg-js 模块和 Node JS 缓冲区,并尝试创建一个小型命令行程序来修改解码的 JPEG 缓冲区数据并创建 X 条反向扫描线的模式和 X 条正常扫描线,然后再保存新的 JPEG。换句话说,我希望翻转图像的一部分,而不是整个图像本身(当然,有很多模块可以做这样的事情,但不是我的特定用例)。

为了创建 reversed/normal 线条模式,我逐行 reading/writing 并将该行的一部分保存到变量中,然后从扫描线的末尾开始并逐步进行向下移动 4 个字节(RGBA 值的分配),直到我在行的开头。程序代码:

'use strict';
const fs = require('fs');
const jpeg = require('jpeg-js');
const getPixels = require('get-pixels');

let a = fs.readFileSync('./IMG_0006_2.jpg');
let d = Buffer.allocUnsafe(a.width * a.height * 4);
let c = jpeg.decode(a);

let val = false; // track whether normal or reversed scanlines
let lineWidth = b.width * 4;
let lineCount = 0;
let track = 0;
let track2 = 0;
let track3 = 0;
let curr, currLine; // storage for writing/reading scnalines, respectively
let limit = {
    one: Math.floor(Math.random() * 141),
    two: Math.floor(Math.random() * 151),
    three: Math.floor(Math.random() * 121)
};
if (limit.one < 30) {
    limit.one = 30;
}
if (limit.two < 40) {
    limit.two = 40;
}
if (limit.two < 20) {
    limit.two = 20;
}
let calc = {};
calc.floor = 0;
calc.ceil = 0 + lineWidth;

d.forEach(function(item, i) {
    if (i % lineWidth === 0) {
        lineCount++;
        /*  // alternate scanline type, currently disabled to figure out how to succesfully reverse image
        if (lineCount > 1 && lineCount % limit.one === 0) {
            // val = !val;
        }
        */
        if (lineCount === 1) {
            val = !val; // setting alt scanline check to true initially
        } else if (calc.floor + lineWidth < b.data.length - 1) {
            calc.floor += lineWidth;
            calc.ceil += lineWidth;
        }
        currLine = c.data.slice(calc.floor, calc.ceil); // current line
        track = val ? lineWidth : 0; // tracking variable for reading from scanline
        track2 = val ? 4 : 0; // tracking variable for writing from scanline
    }
    //check if reversed and writing variable has written 4 bytes for RGBA
        //if so, set writing source to 4 bytes at end of line and read from there incrementally
    if (val && track2 === 4) {
        track2 = 0; // reset writing count
        curr = currLine.slice(track - 4, track); // store 4 previous bytes as writing source
        if (lineCount === 1 && lineWidth - track < 30) console.log(curr); //debug
    } else {
        curr = currLine; //set normal scanline
    }

    d[i] = curr[track2];
    
    // check if there is no match between data source and decoded image
    if (d[i] !== curr[track2]) {
        if (track3 < 50) {
            console.log(i);
        }
        track3++;
    }
    track2++; //update tracking variable
    track = val ? track - 1 : track + 1; //update tracking variable



});


var rawImageData = {
    data: d,
    width: b.width,
    height: b.height
};
console.log(b.data.length);
console.log('errors\t', track3);
var jpegImageData = jpeg.encode(rawImageData, 100);

fs.writeFile('foo2223.jpg', jpegImageData.data);

唉,我写的反扫描线代码不对啊。不幸的是,我只能成功地反转我的测试图像的红色通道(见左下方),蓝色和绿色通道只是变成了模糊的模糊。配色方案应该看起来像正确的图像。

我做错了什么?

对于反转线,您存储了 4 个字节的切片(4 个字节 = 1 个像素),然后正确写入像素(红色)的第一个值。 但是在下一次迭代中,您用 currLine 覆盖切片 curr,其余通道得到错误的值。

if (val && track2 === 4) {
    track2 = 0; // reset writing count
    curr = currLine.slice(track - 4, track); // store 4 previous bytes as writing source
    if (lineCount === 1 && lineWidth - track < 30) console.log(curr); //debug
} else {
    curr = currLine; //set normal scanline
}
  • 迭代0:val == truetrack2 == 4,设置curr为下一个像素,写入红色通道。
  • 迭代1:val == truetrack2 == 1(val && track2 === 4) == false,设置currcurrLine,写入绿色通道。

您可以移动 track2 === 4 分支来避免这种情况:

if (val) {
  if (track2 === 4) {
    track2 = 0; // reset writing count
    curr = currLine.slice(track - 4, track); // store 4 previous bytes as writing source
    if (lineCount === 1 && lineWidth - track < 30) console.log(curr); //debug
  }
} else {
  curr = currLine; //set normal scanline
}

固定代码应如下所示:

function flipAlt(input, output) {
  const fs = require('fs');
  const jpeg = require('jpeg-js');

  let a = fs.readFileSync(input);
  let b = jpeg.decode(a);
  let d = Buffer.allocUnsafe(b.width * b.height * 4);

  let val = false; // track whether normal or reversed scanlines
  let lineWidth = b.width * 4;
  let lineCount = 0;
  let track = 0;
  let track2 = 0;
  let track3 = 0;
  let curr, currLine; // storage for writing/reading scnalines, respectively
  let limit = {
    one: Math.floor(Math.random() * 141),
    two: Math.floor(Math.random() * 151),
    three: Math.floor(Math.random() * 121)
  };
  if (limit.one < 30) {
    limit.one = 30;
  }
  if (limit.two < 40) {
    limit.two = 40;
  }
  if (limit.two < 20) {
    limit.two = 20;
  }
  let calc = {};
  calc.floor = 0;
  calc.ceil = 0 + lineWidth;

  d.forEach(function(item, i) {
    if (i % lineWidth === 0) {
      lineCount++;
      if (lineCount > 1) {
        val = !val;
      }
      if (lineCount === 1) {
        val = !val; // setting alt scanline check to true initially
      } else if (calc.floor + lineWidth < b.data.length - 1) {
        calc.floor += lineWidth;
        calc.ceil += lineWidth;
      }
      currLine = b.data.slice(calc.floor, calc.ceil); // current line
      track = val ? lineWidth : 0; // tracking variable for reading from scanline
      track2 = val ? 4 : 0; // tracking variable for writing from scanline
    }
    //check if reversed and writing variable has written 4 bytes for RGBA
    //if so, set writing source to 4 bytes at end of line and read from there incrementally
    if (val) {
      if (track2 === 4) {
        track2 = 0; // reset writing count
        curr = currLine.slice(track - 4, track); // store 4 previous bytes as writing source
        if (lineCount === 1 && lineWidth - track < 30) console.log(curr); //debug
      }
    } else {
      curr = currLine; //set normal scanline
    }

    d[i] = curr[track2];

    // check if there is no match between data source and decoded image
    if (d[i] !== curr[track2]) {
      if (track3 < 50) {
        console.log(i);
      }
      track3++;
    }
    track2++; //update tracking variable
    track = val ? track - 1 : track + 1; //update tracking variable

  });

  var rawImageData = {
    data: d,
    width: b.width,
    height: b.height
  };
  console.log(b.data.length);
  console.log('errors\t', track3);
  var jpegImageData = jpeg.encode(rawImageData, 100);

  fs.writeFile(output, jpegImageData.data);
}

flipAlt('input.jpg', 'output.jpg');

除了跟踪数组索引,您可以使用 lodash 之类的实用程序库,它应该会使事情变得更容易:

function flipAlt(input, output) {
  const fs = require('fs');
  const jpeg = require('jpeg-js');
  const _ = require('lodash');

  const image = jpeg.decode(fs.readFileSync(input));
  const lines = _.chunk(image.data, image.width*4);
  const flipped = _.flatten(lines.map((line, index) => {
    if (index % 2 != 0) {
      return line;
    }
    const pixels = _.chunk(line, 4);
    return _.flatten(pixels.reverse());
  }));

  const imageData = jpeg.encode({
    width: image.width,
    height: image.height,
    data: new Buffer(flipped)
  }, 100).data;

  fs.writeFile(output, imageData);
}

flipAlt('input.jpg', 'output.jpg');