I have a matrix of values representing an 8-bit grayscale image (values range from 0 to 255). I would like to display this image on the web. How can I do so? The image is coming from a C program, would this require converting the image in the C program?
I ended up using the pngjs library to do the job thanks to stdob's suggestion https://www.npmjs.com/package/pngjs. Heres the code I used:
var fs = require('fs'),
PNG = require('pngjs').PNG;
var png = new PNG({
width: 100,
height: 100,
filterType: -1
});
for (var y = 0; y < png.height; y++) {
for (var x = 0; x < png.width; x++) {
var idx = (png.width * y + x) << 2;
png.data[idx ] = 255;
png.data[idx+1] = 218;
png.data[idx+2] = 185;
png.data[idx+3] = 255;
}
}
png.pack().pipe(fs.createWriteStream('newOut.png'));
Related
I have tried just about everything but for some reason I can't get P5.JS to export my animated image into a gif or MP4 file. Any help with this?
I'm not sure if it's my browser too, because whenever I try and run my animation, the screen is blank.
var Offset = 100;
var minSize = 1;
var maxSize = 60;
var colors = ["#D2B634", "#E43D3B", "#BB0ACF", "#7B2C0C"];
var rectSize = 10;
let angle = 0;
function setup() {
frameRate(4);
}
function draw() {
createCanvas(500, 500);
rectMode(CENTER);
background("#62AEA7");
noStroke();
var circleCountx = (500 - 100) / Spacing;
var circleCounty = (500 - 100) / Spacing;
for (let c = 0; c < 10; c++) {
for (let i = 0; i < circleCountx; i++) {
for (let j = 0; j < circleCounty; j++) {
let colorpicker = int(random(colors.length));
fill(colors[colorpicker]);
let size = random(minSize, maxSize);
ellipse(Offset + Spacing * i, Offset + Spacing * j, size, size);
rect(
Offset + Spacing * i,
Offset + Spacing * j,
size - rectSize,
size - rectSize
);
}
}
}
}
Ccapture.js is great for that, here's a video explaining how to use it (not mine). You can later change WebM file types to mp4s and others online. Also as others said, move createCanvas(); into setup(){} and get rid of it in draw(){}.
I am running the below code and everytime it fires it returns four values.
var imgd = ctx.getImageData(2, 2, 1,1);
var pix = imgd.data;
console.log(pix);
for (var i = 0; n = pix.length, i < n; i += 4) {
if (pix[i] == 0) {
alert(i);
}
}
I am sending in the data for position x = 2 and y = 2. Width and height = 1 pixel each.
I expected the value returned to be a single value. Why does it return 4 values in the console when I click a blaxk part of the image?
It returns 0, 255, 255, 255
For each pixel in an ImageData object there's four pieces of information, the RGBA values:
R - The color red (from 0-255)
G - The color green (from 0-255)
B - The color blue (from 0-255)
A - The alpha channel (from 0-255; 0 is transparent and 255 is fully visible)
The color/alpha information is held in an array, and is stored in the data property of the ImageData object.
Source: http://www.w3schools.com/tags/canvas_getimagedata.asp
If you'd like to convert RGB to Hex values: (255,255,255 to #ffffff)
var imgd = ctx.getImageData(2, 2, 1,1);
var pix = imgd.data;
console.log(pix);
for (var i = 0; n = pix.length, i < n; i += 4) {
var red = pix[i];
var green = pix[i+1];
var blue = pix[i+2];
var alpha = pix[i+3];
var color = rgb2hex(red,green,blue);
console.log("color: "+color);
}
function rgb2hex(red, green, blue) {
var rgb = blue | (green << 8) | (red << 16);
return '#' + (0x1000000 + rgb).toString(16).slice(1)
}
Example: JS Bin
Another good resource: HTML5 Canvas Image Data Tutorial
I am working on a web audio stochastic oscillator and am having trouble with the scriptProcessorNode. My algorithm uses a random walk to determine dynamic breakpoints in the waveform and then interpolates between them.
As the breakpoints move on the x axis I thought the frequency of the oscillating waveform would change, but there is just a filtering effect, and the frequency seems to just be determined by the scriptProcessorNode buffer size, which must be a power of 2 between 256 and 16384.
How do you change the frequency of a scriptProcessorNode oscillator?
Here is my synthesis code:
scriptNode.onaudioprocess = function(audioProcessingEvent) {
walk(); //use random walk to generate new x/y position for each breakpoint
var outputBuffer = audioProcessingEvent.outputBuffer;
var lastPoint = 0;
var index = 0;
// linearly interpolate between the new breakpoint positions
for(var i = 0; i < breakpoint.length-1; i++) {
var y = breakpoint[lastPoint].y;
for(var channel = 0; channel <= 0;channel++) {
var outputData = outputBuffer.getChannelData(channel);
if(i != 0){
if(y >= breakpoint[i].y) {
while(y >= breakpoint[i].y) {
y = (breakpoint[i].m*index)+breakpoint[i].b;// y = m(x)+b
outputData[index] = y;
index++;
}
} else if(y <= breakpoint[i].y) {
while(y <= breakpoint[i].y) {
y = (breakpoint[i].m*index)+breakpoint[i].b;
outputData[index] = y;
index++;
}
}
}
}
lastPoint = i;
}
}
And here is a link to a working example: http://andrewbernste.in/bernie/gendy011.html
This is all based on Iannis Xenakis' GENDY stochastic synthesis program.
Thanks!
I solved the problem by using an index variable outside of my scriptNode.onaudioprocess function to write the waveform to the scriptNode buffer. That way the frequency at which the waveform is written to the buffer is not tied to the size of the buffer.
Here is the final code:
var index = 0;
var freq = 0.8;
scriptNode.onaudioprocess = function(audioProcessingEvent){
var outputBuffer = audioProcessingEvent.outputBuffer;
var outputData = outputBuffer.getChannelData(0);
for(var j = 0; j < outputData.length;j++){
// linearly interpolate between the new breakpoint positions
// get the interp point by comparing index to the x distance
var lerp = (index - breakpoint[point].x) / (breakpoint[point+1].x - breakpoint[point].x)
y = nx.interp(lerp,breakpoint[point].y,breakpoint[point+1].y);
if(point < breakpoint.length && index >= breakpoint[point+1].x) {
point++;
}
outputData[j] = y;
index+=freq;
if(index >= breakpoint[breakpoint.length-1].x){
index = 0;
point = 0;
walk();
}
}
}
I'm trying to decode the dataset from this source: http://yann.lecun.com/exdb/mnist/
There is a description of the "very simple" IDX file type in the bottom, but I cannot figure it out.
What I'm trying to achieve is something like:
var imagesFileBuffer = fs.readFileSync(__dirname + '/train-images-idx3-ubyte');
var labelFileBuffer = fs.readFileSync(__dirname + '/train-labels-idx1-ubyte');
var pixelValues = {};
Do magic
pixelValues are now like:
// {
// "0": [0,0,200,190,79,0... for all 784 pixels ... ],
// "4": [0,0,200,190,79,0... for all 784 pixels ... ],
etc for all image entries in the dataset. I've tried to figure out the structure of the binary files, but failed.
I realized there would be duplicate keys in my structure of the pixelValues object, so I made an array of objects of it instaed. The following code will create the structure I'm after:
var dataFileBuffer = fs.readFileSync(__dirname + '/train-images-idx3-ubyte');
var labelFileBuffer = fs.readFileSync(__dirname + '/train-labels-idx1-ubyte');
var pixelValues = [];
// It would be nice with a checker instead of a hard coded 60000 limit here
for (var image = 0; image <= 59999; image++) {
var pixels = [];
for (var x = 0; x <= 27; x++) {
for (var y = 0; y <= 27; y++) {
pixels.push(dataFileBuffer[(image * 28 * 28) + (x + (y * 28)) + 15]);
}
}
var imageData = {};
imageData[JSON.stringify(labelFileBuffer[image + 8])] = pixels;
pixelValues.push(imageData);
}
The structure of pixelValues is now something like this:
[
{5: [28,0,0,0,0,0,0,0,0,0...]},
{0: [0,0,0,0,0,0,0,0,0,0...]},
...
]
There are 28x28=784 pixel values, all varying from 0 to 255.
To render the pixels, use my for loops like I did above, rendering the first pixel in the upper left corner, then working towards the right.
Just a small improvement:
for (var image = 0; image <= 59999; image++) {
with 60000 there is an "entry" with null's at the very end of your pixelValues.
EDIT:
I got a little obsessed with details because I wanted to convert the MNIST dataset back to real and separate image files. So I have found more mistakes in your code.
it is definitely +16 because you have to skip the 16 Bytes of header data. This little mistake is reflected in your answer where the first pixel value of the first digit (with is a 5) is '28'. Which is actually the value that tells how many columns the image has - not the first pixel of the image.
Your nested for loops has to be turned inside-out to get you the right pixel order - asuming you will "rebuild" your image from the upper left corner down to the lower right corner. With your code the image will be flipped along the axis that goes from the upper left to the lower right corner.
So your code should be:
var dataFileBuffer = fs.readFileSync(__dirname + '/train-images-idx3-ubyte');
var labelFileBuffer = fs.readFileSync(__dirname + '/train-labels-idx1-ubyte');
var pixelValues = [];
// It would be nice with a checker instead of a hard coded 60000 limit here
for (var image = 0; image <= 59999; image++) {
var pixels = [];
for (var y = 0; y <= 27; y++) {
for (var x = 0; x <= 27; x++) {
pixels.push(dataFileBuffer[(image * 28 * 28) + (x + (y * 28)) + 16]);
}
}
var imageData = {};
imageData[JSON.stringify(labelFileBuffer[image + 8])] = pixels;
pixelValues.push(imageData);
}
Those little details wouldn't be an issue if you stay consistent and use those extracted data to -for example- train neural networks, because you will do the same with the testing dataset. But if you want to take that MNIST trained neural network and try to verify it with real life hand written digits, you will get bad results because the real images are not flipped.
Hopefully this helps someone out, I have added the ability to save the images to a png. Please note you will need to have an images directory
var fs = require('fs');
const {createCanvas} = require('canvas');
function readMNIST(start, end)
{
var dataFileBuffer = fs.readFileSync(__dirname + '\\test_images_10k.idx3-ubyte');
var labelFileBuffer = fs.readFileSync(__dirname + '\\test_labels_10k.idx1-ubyte');
var pixelValues = [];
for (var image = start; image < end; image++)
{
var pixels = [];
for (var y = 0; y <= 27; y++)
{
for (var x = 0; x <= 27; x++)
{
pixels.push(dataFileBuffer[(image * 28 * 28) + (x + (y * 28)) + 16]);
}
}
var imageData = {};
imageData["index"] = image;
imageData["label"] = labelFileBuffer[image + 8];
imageData["pixels"] = pixels;
pixelValues.push(imageData);
}
return pixelValues;
}
function saveMNIST(start, end)
{
const canvas = createCanvas(28, 28);
const ctx = canvas.getContext('2d');
var pixelValues = readMNIST(start, end);
pixelValues.forEach(function(image)
{
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var y = 0; y <= 27; y++)
{
for (var x = 0; x <= 27; x++)
{
var pixel = image.pixels[x + (y * 28)];
var colour = 255 - pixel;
ctx.fillStyle = `rgb(${colour}, ${colour}, ${colour})`;
ctx.fillRect(x, y, 1, 1);
}
}
const buffer = canvas.toBuffer('image/png')
fs.writeFileSync(__dirname + `\\images\\image${image.index}-${image.label}.png`, buffer)
})
}
saveMNIST(0, 5);
Are there any solution of converting any image to the 2d matrix of color values in node.js?
Before considering GD or similar, look at Node.js packages like get-pixels that do the job without any external dependencies.
Building on their default example:
const getPixels = require('get-pixels');
const src = `test.jpg`;
getPixels(src, function(err, pixels) {
if(err) {
console.log("Bad image path");
return;
}
for (let y = 0; y < pixels.shape[1]; y++) {
for (let x = 0; x < pixels.shape[0]; x++) {
const r = pixels.get(x, y, 0);
const g = pixels.get(x, y, 1);
const b = pixels.get(x, y, 2);
const a = pixels.get(x, y, 3);
const rgba = `color: rgba(${r}, ${g}, ${b}, ${a});`;
console.log(rgba);
}
}
});
This post provides more advanced examples.
You can use GD bindings library for the various image manipulations in node.js. To get the color of some pixel GD has getPixel method.