2016-08-15 2 views
0

Ich habe mit dem Modul jpeg-js und Node JS Buffer gearbeitet und versucht, ein kleines Befehlszeilenprogramm zu erstellen, das die decodierten JPEG-Pufferdaten modifiziert und ein Muster aus X-Anzahl von umgekehrten Scanlinien und X-Anzahl von normalen Scanlinien erzeugt Speichern eines neuen JPEG. Mit anderen Worten, ich versuche, Teile des Bildes umzudrehen, aber nicht das gesamte Bild selbst (viele Module, die so etwas tun, aber nicht den spezifischen Anwendungsfall, den ich habe).Wie reverse ich eine Scanline mit dem jpeg-js-Modul/Knoten JS-Puffer?

Um die umgekehrten/normalen Linienmuster zu erstellen, habe ich Zeile für Zeile gelesen/geschrieben und ein Stück dieser Linie in einer Variablen gespeichert, dann am Ende der Scanlinie beginnend und inkrementell um 4 Stück nach unten gegangen Bytes (die Zuweisung für einen RGBA-Wert), bis ich am Anfang der Zeile bin. Code für das Programm:

'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); 

Leider ist der umgekehrte Scanline-Code, den ich geschrieben habe, nicht richtig. Leider konnte ich nur den roten Kanal meines Testbilds erfolgreich umkehren (siehe unten links), wobei die blauen und grünen Kanäle gerade zu verschwommenen Unschärfen werden. Das Farbschema sollte in etwa dem richtigen Bild entsprechen.

current color output ideal color output

Was mache ich hier falsch?

Antwort

2

Für umgekehrte Zeilen haben Sie Schichten von 4 Bytes (4 Bytes = 1 Pixel) gespeichert, dann schreiben Sie den ersten Wert des Pixels (rot) richtig. Aber in der nächsten Iteration überschreiben Sie die Scheibe curr mit currLine, Rest der Kanäle bekommt falsche Werte.

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 
} 
  • Iteration 0: val == true, track2 == 4, eingestellt curr zum nächsten Bildpunkt, roten Kanal schreiben.
  • Iteration 1: val == true, track2 == 1, (val && track2 === 4) == false, curr auf currLine setzen, grünen Kanal schreiben.

Sie können track2 === 4 Zweig bewegen, dies zu vermeiden:

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 
} 

Fest Code sollte wie folgt aussehen:

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

Statt Array-Indizes von Tracking, können Sie Dienstprogramm Bibliothek wie lodash, sollte es die Dinge einfacher machen:

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'); 
Verwandte Themen