如何使用jpeg-js模块/节点JS缓冲区反转扫描线?

时间:2016-08-15 01:15:39

标签: node.js image buffer jpeg

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

要创建反转/正常线条图案,我一直在逐行读取/写入,并将该线条的切片保存到变量,然后从扫描线的末尾开始,逐步向下切片4个字节(RGBA值的alloc),直到我在行的开头。该计划的代码:

'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);
唉,我写的反向扫描线代码不正确。不幸的是,我只能成功地反转测试图像的红色通道(见左下图),蓝色和绿色通道只是变成模糊的模糊。配色方案应该看起来像右图。

current color output ideal color output

我在这里做错了什么?

1 个答案:

答案 0 :(得分:2)

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

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,将curr设置为currLine,写入绿色频道。

您可以移动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');

flipped image

您可以使用像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');