How to use PNGJS library to create png from rgb matrix? - javascript

Im having trouble figuring out to create a PNG file (encoding) from the documentation here https://github.com/niegowski/node-pngjs . The documentation gives an example on manipulating an existing PNG. In order to sanity check I've been trying to give every pixel the same RGB value. I think this issue post is relevant as well https://github.com/niegowski/node-pngjs/issues/20 . Heres a code Ive tried
var pic = new PNG({
width: cols,
height: rows
});
console.log(pic.width, pic.height);
var writeStream = fs.createWriteStream('C:\\Users\\yako\\desktop\\out.png');
pic.pack().pipe(writeStream);
writeStream.on('parsed', function() {
console.log('parsed');
});
writeStream.on('finish', function() {
fs.createReadStream('C:\\Users\\yako\\desktop\\out.png')
.pipe(new PNG({
filterType: -1
}))
.on('parsed', function() {
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
var idx = (this.width * y + x) << 2;
this.data[idx] = 255;
this.data[idx+1] = 218;
this.data[idx+2] = 185;
this.data[idx+3] = 0.5;
}
}
this.pack().pipe(fs.createWriteStream('C:\\Users\\yako\\desktop\\newOut.png'));
});
});
writeStream.on('error', function (err) {
console.error(err);
});

Simple:
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; // red
png.data[idx+1] = 218; // green
png.data[idx+2] = 185; // blue
png.data[idx+3] = 128; // alpha (0 is transparent)
}
}
png.pack().pipe(fs.createWriteStream('newOut.png'));

Related

FabricJS: big grid objects is blurry

I want to draw a large grid with fabricjs, but it is too blurry. I use fabricJS canvas.toSVG() export to a svg file, and I open the svg file in browser, it's view good. so I think this is most possible likely a bug for edge.
The test code is:
var data = [];
var temp = [0,0,-0.012,-0.012,-0.012,-0.012,-0.012,-0.012,0,0,-0.012,-0.012,0,0.049,0.073,0.049,0.049,0.037,-0.012,-0.012,-0.024,-0.049,-0.024,-0.049,-0.061,-0.012,-0.061,-0.086,0.061,0.146,0.354,0.403,-0.647,-1.88,-1.672,-0.757,-0.33,-0.098,0.024,0.012,0.073,0.122,0.098,0.146,0.183,0.171,0.207,0.232];
for (var i = 0; i < 200; i++) {
data = data.concat(temp);
}
// case 1 : blurry
var canvas1 = new fabric.Canvas("ecg1");
var width = 8000;
var height = 400;
canvas1.setWidth(width);
canvas1.setHeight(height);
var bg1 = getBackgroundPath(width, height);
var ecg1 = getEcgPath(data);
canvas1.add(bg1);
canvas1.add(ecg1);
canvas1.renderAll(); // blurry
// case 2 : ok
var canvas2 = new fabric.Canvas("ecg2");
var data2 = data.slice(0, 3000);
width = 1000;
height = 400;
canvas2.setWidth(width);
canvas2.setHeight(height);
var bg2 = getBackgroundPath(width, height);
var ecg2 = getEcgPath(data2);
canvas2.add(bg2);
canvas2.add(ecg2);
canvas2.renderAll();
// case 3 : blurry
var canvas3 = new fabric.Canvas("ecg3");
canvas3.setWidth(width);
canvas3.setHeight(height);
canvas3.add(new fabric.Group([bg2, ecg2]));
canvas3.renderAll();
function getBackgroundPath(width, height) {
var grid = [];
for (var y = 0; y <= height; y += 10) {
grid.push("M");
grid.push("0");
grid.push(y);
grid.push("H");
grid.push(width);
}
for (var x = 0; x <= width; x += 10) {
grid.push("M");
grid.push(x);
grid.push("0");
grid.push("V");
grid.push(height);
}
return new fabric.Path(grid.join(" "), {
top : 0,
left : 0,
stroke: 'pink',
strokeWidth: 1
});
}
function getEcgPath(data) {
var xm = 2;
var ym = 100;
var max = Math.max.apply(Math, data);
var path = [];
for (var i = 0; i < data.length; i++) {
path.push("L");
path.push(i * xm);
path.push((max - data[i]) * ym);
}
path[0] = "M";
return new fabric.Path(path.join(" "), {
top : 0,
left : 0,
fill : '',
stroke: 'black',
strokeWidth: 1
});
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<canvas id="ecg1"></canvas>
<canvas id="ecg2"></canvas>
<canvas id="ecg3"></canvas>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.3/fabric.min.js" ></script>
</body>
</html>
the result is
Please have further reading here:
http://fabricjs.com/fabric-object-caching
This question is actually easily solvable disabling caching for the ECG line.
If your application needs to draw just a big path or an handfull of them, caching is not necessary.
How caching works?
Caching draw the object on a separate canvas and then reuse that canvas every frame when you move it.
This is way more performing than having to execute multiple fill and stroke operation each frame for many objects, especially when objects are complex.
To avoid performances pitfall the canvas used for caching is limited to 2 mega pixels by default ( you can push it up changing fabric.perfLimitSizeTotal ) and whatever is the mega pixel size, the largest size is limited to 4096 by default ( if you do not need to support ie11 you can make it bigger changing fabric.maxCacheSideLimit ).
In your case the path is smoothly handled by the cpu.
For the grid we need some more code.
var data = [];
var temp = [0,0,-0.012,-0.012,-0.012,-0.012,-0.012,-0.012,0,0,-0.012,-0.012,0,0.049,0.073,0.049,0.049,0.037,-0.012,-0.012,-0.024,-0.049,-0.024,-0.049,-0.061,-0.012,-0.061,-0.086,0.061,0.146,0.354,0.403,-0.647,-1.88,-1.672,-0.757,-0.33,-0.098,0.024,0.012,0.073,0.122,0.098,0.146,0.183,0.171,0.207,0.232];
for (var i = 0; i < 200; i++) {
data = data.concat(temp);
}
// case 1 : blurry
var canvas1 = new fabric.Canvas("ecg1");
var width = 8000;
var height = 400;
canvas1.setWidth(width);
canvas1.setHeight(height);
var bg1 = getBackgroundPath(width, height);
var ecg1 = getEcgPath(data);
canvas1.add(bg1);
canvas1.add(ecg1);
canvas1.renderAll(); // blurry
function getBackgroundPath(width, height) {
var grid = [];
for (var y = 0; y <= height; y += 10) {
grid.push("M");
grid.push("0");
grid.push(y);
grid.push("H");
grid.push(width);
}
for (var x = 0; x <= width; x += 10) {
grid.push("M");
grid.push(x);
grid.push("0");
grid.push("V");
grid.push(height);
}
return new fabric.Path(grid.join(" "), {
top : 0,
left : 0,
stroke: 'pink',
strokeWidth: 1
});
}
function getEcgPath(data) {
var xm = 2;
var ym = 100;
var max = Math.max.apply(Math, data);
var path = [];
for (var i = 0; i < data.length; i++) {
path.push("L");
path.push(i * xm);
path.push((max - data[i]) * ym);
}
path[0] = "M";
return new fabric.Path(path.join(" "), {
top : 0,
left : 0,
fill : '',
stroke: 'black',
strokeWidth: 1,
objectCaching: false,
});
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<canvas id="ecg1"></canvas>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.3/fabric.min.js" ></script>
</body>
</html>
#AndreaBogazzi
Thanks your answer. After I use fabric.Object.prototype.objectCaching = false, it's shows good.
In my application, the draw grid code is as follows.
var data = [];
var temp = [0,0,-0.012,-0.012,-0.012,-0.012,-0.012,-0.012,0,0,-0.012,-0.012,0,0.049,0.073,0.049,0.049,0.037,-0.012,-0.012,-0.024,-0.049,-0.024,-0.049,-0.061,-0.012,-0.061,-0.086,0.061,0.146,0.354,0.403,-0.647,-1.88,-1.672,-0.757,-0.33,-0.098,0.024,0.012,0.073,0.122,0.098,0.146,0.183,0.171,0.207,0.232];
for (var i = 0; i < 200; i++) {
data = data.concat(temp);
}
// case 1 : blurry
var canvas1 = new fabric.Canvas("ecg1");
var width = 8000;
var height = 400;
canvas1.setWidth(width);
canvas1.setHeight(height);
var bg11 = getBackgroundDot(width, height);
var bg12 = getBackgroundPath(width, height);
var ecg1 = getEcgPath(data);
canvas1.add(bg11);
canvas1.add(bg12);
canvas1.add(ecg1);
canvas1.renderAll(); // blurry
function getBackgroundDot() {
var dotLineWidth = 2;
var dot = [];
dot.push("M 0 0");
for (var y = 0; y <= height; y += 10) {
if (y % 50 == 0) {
continue;
}
dot.push("M");
dot.push("0");
dot.push(y);
dot.push("H");
dot.push(width);
}
var sda = [0, 10];
for (var i = 1; i < 5; i++) {
sda.push(dotLineWidth);
sda.push(8);
}
return new fabric.Path(dot.join(" "), {
top : 0,
left : 0,
stroke: 'pink',
strokeWidth: dotLineWidth,
strokeDashArray: sda
});
}
function getBackgroundPath(width, height) {
var grid = [];
for (var y = 0; y <= height; y += 50) {
grid.push("M");
grid.push("0");
grid.push(y);
grid.push("H");
grid.push(width);
}
for (var x = 0; x <= width; x += 50) {
grid.push("M");
grid.push(x);
grid.push("0");
grid.push("V");
grid.push(height);
}
return new fabric.Path(grid.join(" "), {
top : 0,
left : 0,
stroke: 'pink',
strokeWidth: 1
});
}
function getEcgPath(data) {
var xm = 2;
var ym = 100;
var max = Math.max.apply(Math, data);
var path = [];
for (var i = 0; i < data.length; i++) {
path.push("L");
path.push(i * xm);
path.push((max - data[i]) * ym);
}
path[0] = "M";
return new fabric.Path(path.join(" "), {
top : 0,
left : 0,
fill : '',
stroke: 'black',
strokeWidth: 1
});
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<canvas id="ecg1"></canvas>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.3/fabric.min.js" ></script>
</body>
</html>

Scrollable board for HTML5 GAME should use SVG's or Canvas

I wish to create a simple webgame that involves a tiled board. I have a collection of svg's for the background of each square (i.e one for grass, one for stone, one for dirt etc). I also have svg's for items that will be displayed on the layer above the background (such as trees, wood, sword).
I have an in memory database of what the background for each square is and if and which item it contains.
I wish to be able to:
* Zoom in or out
* Scroll left or right
* Scolll up or down
* Have items displayed above the background for that square
Only needs to work in recent versions of modern browsers
What is the best approach for this:
1. Have a canvas object. Get the current zoom, top most XY, canvas width and canvas height. Loop though the squares in the in memory database and print the corresponding SVG's in the correct locations. Each time it is scrolled or zoomed reprint the entire board.
2. Have a div. Get the current zoom, top most XY, canvas width and canvas height. Loop though the squares in the in memory database and create SVG's in the correct locations.
Each time the board scrolls add new SVGs as they become visible, delete SVGs as they move of the board. Translate all the existing SVGs by the appropriate amount.
Each time the board zooms enlarge or shrink all the existing SVGs based on the new zoom level.
3. Some third approach that I am unaware of.
The example below uses two modules svg to load images (Any image format will work) & board which handles panning, zooming & rendering. It also provides an onClick event which will give you an object that describes the tile that has been clicked on.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
background-color: black;
}
canvas {
display: block;
margin: 30px auto 0px auto;
border: solid 1px white;
border-radius: 10px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="application/javascript">
var svg = function() {
"use strict";
var svgImages = [];
var areImagesLoaded = true;
var isPageLoaded = false;
addEventListener("load",function() { isPageLoaded = true; isFinished(); });
var exports = {
onload: function() {},
request: null,
get: null
};
function isFinished() {
if (isPageLoaded && areImagesLoaded) {
exports.onload();
}
}
function onSvgError() {
this.isDone = true;
console.warn("SVG " + this.src + " failed to load.");
}
function onSvgLoaded() {
this.isDone = true;
for (var id in svgImages) {
if (!svgImages[id].isDone) {
return;
}
}
areImagesLoaded = true;
isFinished();
}
function request(id,path) {
if (svgImages[id]) {
return;
}
areImagesLoaded = false;
var img = document.createElement("img");
img.onerror = onSvgError;
img.onload = onSvgLoaded;
img.isDone = false;
img.id = id;
img.src = path;
svgImages[id] = img;
}
function get(id) {
return svgImages[id];
}
exports.request = request;
exports.get = get;
return exports;
}();
var board = function() {
"use strict";
var canvasWidth = 0;
var canvasHeight = 0;
var canvas = null;
var ctx = null;
var frameRequested = false;
var tileWidth = 0;
var tileHeight = 0;
var tileTypes = [];
var boardWidth = 0;
var boardHeight = 0;
var board = [];
var hasInitialized = false;
var camera = {
x: 0.0,
y: 0.0,
zoom: 1.0
};
function mapToBoard(x,y) {
var invZoom = 1.0 / camera.zoom;
return [
(x - (canvasWidth >> 1)) * invZoom - camera.x,
(y - (canvasHeight >> 1)) * invZoom - camera.y
];
}
var isMouseDragging = false;
var mouseStartX = 0;
var mouseStartY = 0;
var mouseLastX = 0;
var mouseLastY = 0;
var tileEvent = {
background: "",
foreground: "",
x: 0,
y: 0
};
function onTileSelected(e) {
}
function onMouseDown(e) {
isMouseDragging = true;
var bounds = canvas.getBoundingClientRect();
mouseStartX = mouseLastX = e.clientX - bounds.left;
mouseStartY = mouseLastY = e.clientY - bounds.top;
}
function onMouseUp(e) {
isMouseDragging = false;
var bounds = canvas.getBoundingClientRect()
var x = e.clientX - bounds.left - mouseStartX;
var y = e.clientY - bounds.top - mouseStartY;
var l = Math.sqrt(x * x + y * y);
if (l < 2.0) {
[x,y] = mapToBoard(e.clientX - bounds.left,e.clientY - bounds.top);
if (x > 0 && y > 0 && x < boardWidth * tileWidth && y < boardHeight * tileHeight) {
x = (x / tileWidth) | 0;
y = (y / tileHeight) | 0;
var tile = board[x + y * boardWidth];
tileEvent.background = tile.background;
tileEvent.foreground = tile.foreground;
tileEvent.x = x;
tileEvent.y = y;
} else {
tileEvent.background = "";
tileEvent.foreground = "";
tileEvent.x = -1;
tileEvent.y = -1;
}
onTileSelected(tileEvent);
}
}
function onMouseMove(e) {
if (hasInitialized && isMouseDragging) {
var bounds = canvas.getBoundingClientRect();
var x = e.clientX - bounds.left;
var y = e.clientY - bounds.top;
camera.x += (x - mouseLastX) / camera.zoom;
camera.y += (y - mouseLastY) / camera.zoom;
mouseLastX = x;
mouseLastY = y;
requestDraw();
}
}
function onWheel(e) {
if (Math.sign(e.deltaY) === -1) {
camera.zoom *= 1.1;
} else {
camera.zoom *= 0.9;
}
requestDraw();
}
function draw() {
ctx.fillStyle = "gray";
ctx.fillRect(-canvasWidth >> 1,-canvasHeight >> 1,canvasWidth,canvasHeight);
var _tileWidth = tileWidth * camera.zoom;
var _tileHeight = tileHeight * camera.zoom;
var _ox = camera.x * camera.zoom;
var _oy = camera.y * camera.zoom;
var _x = _ox;
var _y = _oy;
for (var x = 0; x <boardWidth; ++x) {
for (var y = 0; y < boardHeight; ++y) {
var index = x + y * boardWidth;
var tile = board[index];
var background = tileTypes[tile.background];
var foreground = tileTypes[tile.foreground];
if (background) {
ctx.drawImage(
background,
_x,
_y,
_tileWidth,
_tileHeight
);
}
if (foreground) {
ctx.drawImage(
foreground,
_x,
_y,
_tileWidth,
_tileHeight
);
}
_y += _tileHeight;
}
_y = _oy;
_x += _tileWidth;
}
frameRequested = false;
}
function requestDraw() {
if (!frameRequested) {
frameRequested = true;
requestAnimationFrame(draw);
}
}
return {
BACKGROUND: 0,
FOREGROUND: 1,
set canvas(canvasID) {
if (!hasInitialized) {
canvas = document.getElementById(canvasID);
canvas.onmousedown = onMouseDown;
canvas.onmouseup = onMouseUp;
canvas.onmousemove = onMouseMove;
canvas.onwheel = onWheel;
ctx = canvas.getContext("2d");
}
},
set canvasWidth(w) {
if (!hasInitialized && canvas) {
canvasWidth = canvas.width = w;
}
},
set canvasHeight(h) {
if (!hasInitialized && canvas) {
canvasHeight = canvas.height = h;
}
},
set tileWidth(w) {
if (!hasInitialized) {
tileWidth = w;
}
},
set tileHeight(h) {
if (!hasInitialized) {
tileHeight = h;
}
},
set width(w) {
if (!hasInitialized) {
boardWidth = w;
}
},
set height(h) {
if (!hasInitialized) {
boardHeight = h;
}
},
set onTileSelected(callback) {
onTileSelected = callback;
},
get width() {
return boardWidth;
},
get height() {
return boardHeight;
},
get onTileSelected() {
return onTileSelected;
},
defineTileTypes: function(types) {
if (types.length % 2 !== 0) {
return;
}
for (var i = 0; i < types.length; i += 2) {
var id = types[i];
var img = types[i + 1];
tileTypes[id] = img;
}
},
init: function() {
camera.x = -(boardWidth >> 1) * tileWidth;
camera.y = -(boardHeight >> 1) * tileHeight;
ctx.translate(canvasWidth >> 1,canvasHeight >> 1);
board.length = boardWidth * boardHeight;
for (var i = 0; i < board.length; ++i) {
board[i] = {
background: "",
foreground: ""
};
}
hasInitialized = true;
requestAnimationFrame(draw);
},
set: function(type,id,x,y) {
if (hasInitialized
&& tileTypes[id]
&& x > -1
&& x < boardWidth
&& y > -1
&& y < boardHeight) {
var index = x + y * boardWidth;
if (type === this.BACKGROUND) {
board[index].background = id;
} else {
board[index].foreground = id;
}
requestDraw();
}
}
};
}();
void function() {
"use strict";
svg.request("grass","https://i.stack.imgur.com/CkvU7.png");
svg.request("water","https://i.stack.imgur.com/an6a5.png");
svg.onload = function() {
board.canvas = "canvas";
board.canvasWidth = 180;
board.canvasHeight = 160;
board.tileWidth = 25;
board.tileHeight = 25;
board.width = 20;
board.height = 20;
board.defineTileTypes([
"GRASS",svg.get("grass"),
"WATER",svg.get("water")
]);
board.init();
for (var x = 0; x < board.width; ++x) {
for (var y = 0; y < board.height; ++y) {
board.set(board.BACKGROUND,"WATER",x,y);
if (Math.random() > 0.2) {
board.set(board.BACKGROUND,"GRASS",x,y);
} else {
board.set(board.BACKGROUND,"WATER",x,y);
}
}
}
}
board.onTileSelected = function(e) {
if (e.background === "GRASS") {
board.set(board.BACKGROUND,"WATER",e.x,e.y);
} else {
board.set(board.BACKGROUND,"GRASS",e.x,e.y);
}
}
}();
</script>
</body>
</html>

Rotating a 1d RGBA array

I'm working with a 1D pixel RGBA array that looks like this:
pixelArray =[0,0,0,255,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255];
What this pixelArray corresponds to when drawn is 2 black pixels and 4 white pixels:
BB
WW
WW
My goal is to rotate the order of the pixels inside the array so the picture when drawn will look like
BWW or WWB
BWW WWB
Which means I need to convert the pixelArray to
rotatedPixelArray = [0,0,0,255,255,255,255,255,255,255,255,255,0,0,0,255,255,255,255,255,255,255,255,255]
The above example is just that, an example. The actual rgba could represent any image and could have a length of 1 million +.
I've tried various algorithms like this, and converting to a 2d array and then rotating and flattening (which does work) but i'd like to avoid this because speed/memory is an issue.
Maybe this could be helpful in p5.js for 90 degrees rotation (left and right) (the previous suggested solution seems to have a little 'asymmetric' error):
function rotateRight(img){
var w = img.width;
var h = img.height;
var index, indexr;
var img2 = createImage(w, h);
img.loadPixels();
img2.loadPixels();
indexr = 0;
for (let x = 0; x < w; x++) {
for(let y = h - 1; y >= 0; y--) {
index = (x+y*w)*4;
img2.pixels[indexr] = img.pixels[index];
img2.pixels[indexr + 1] = img.pixels[index+1];
img2.pixels[indexr + 2] = img.pixels[index+2];
img2.pixels[indexr + 3] = img.pixels[index+3];
indexr += 4;
}
}
img.updatePixels();
img2.updatePixels();
return img2;
}
function rotateLeft(img){
var w = img.width;
var h = img.height;
var index, indexr;
var img2 = createImage(w, h);
img.loadPixels();
img2.loadPixels();
indexr = 0;
for (let x = w - 1; x >= 0; x--) {
for(let y = 0; y < h; y++) {
index = (x+y*w)*4;
img2.pixels[indexr] = img.pixels[index];
img2.pixels[indexr + 1] = img.pixels[index+1];
img2.pixels[indexr + 2] = img.pixels[index+2];
img2.pixels[indexr + 3] = img.pixels[index+3];
indexr += 4;
}
}
img.updatePixels();
img2.updatePixels();
return img2;
}
So I figured it out, in my case it needed to be rotated left or right. The code I used is as followed:
function rotatePixelArray(pixelArray,w,h) {
var rotatedArray = [];
for (var x=0;x<w;x++) {
for(var y=0;y<h;y++) {
index = (x+y*w)*4;
rotatedArray.push(pixelArray[index]);
rotatedArray.push(pixelArray[index+1]);
rotatedArray.push(pixelArray[index+2]);
rotatedArray.push(pixelArray[index+3]);
}
}
return rotatedArray;
}
To rotate it back you can pass in switched w,h variables.

Method to remove small regions from binary image : Javascript

I have an image like this:
(just an binary image of ones and zeros)
... and I'D like to get something like this:
Is there an efficient & fast way to remove small regions from the image (1/0-matrix) using pure javascript?
This is what my matrix (generated from the picture on above) looks like:
100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110
111111111111111111111010001111111111111111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110001101110
111111111111111111111111111111110011111111111111111111111111111111111111111111111111111111111111111111111111111110000011111111111111111111111111111111111111111111111111111111111111111111110100000000110
111111111111111111111111111000000000111111111111111111111111111111111111111111111111111111111111111111111111110000000000011111111111111111111111111111111111111111111111111111111111111111111000000000000
111111111111111111111111110000000000001111111111111111111111111111111111111111111111111111111111111111111111100000000000001111111111111111111111111111111111111111111111111111111111111111111000000000000
111111111111111111111111100000000000000111111111111111111111111111111111111111111111111111111111111111111111000000000000000111111111111111111111111111111111111111111111111111111111111111111000000000000
111111111111111111111111000000000000000011111111111111111111111111111111110000111111111111111111111111111110000000000000000011111111111111111111111111111110000000011111111111111111111111111000000000000
111111111111111111111110000000000010000011111111111111111111111111111110000000000111111111111111111111111100000000000000000011111111111111111111111111110000000000000111111111111111111111111000000000000
111111111111111111111100000000000000000001111111111111111111111111110000000000000001111111111111111111111100000000001000000001111111111111111111111111100000000000000011111111111111111111111000000000000
111111111111111111111100000010100001000001111111111111111111111111100000000000000000111111111111111111111100000000010001000001111111111111111111111111000001000000000001111111111111111111111100000000000
111111111111111111111100000000100110000001111111111111111111111111100000000000000000111111111111111111111000000000000010000001111111111111111111111111000000000000000000111111111111111111110100000000000
110111111111111111111100000001000000000001111111111111111111111111000000000000000000011111111111111111111000000000011000000001111111111111111111111110000000000000000000011111111111111111111100000000000
110111111111111111111100000000000010001000111111111111111111111111000000000010000000011111111111111111111000000000100000000000111111111111111111111110000000000000000000011111111111111111111100000000000
110111111111111111111000000000100000000000111111111111111111111110000000000000100000001111111111111111111000000100001000000000111111111111111111111110000000000000000000011111111111111111111100000000000
110111111111111111111100001000010011000000111111111111111111111110000000001000000010001111111111111111111000000000000000000000111111111111111111111110000000000000000000001111111111111111111000000010100
110111111111111111111100000000000000000000111111111111111111111110000000000000000000001111111111111111111000000000000000000000111111111111111111111100000000000000000000101111111111111111111000000000010
110111111111111111111100000000000000000000111111111111111111111110000000000000011000001111111111111111111000000000000000000000111111111111111111111100000000000000000000001111111111111111111100000000010
110111111111111111111100000000000000000000111111111111111111111110000000000000000000001111111111111111111000000000000000000000111111111111111111111100000000000000000000001110111111111111111100000000000
110111111111111111111100000000000000000000111111111111111111111110000000000000000000001111111111111111111000000000000000000000111111111111111111111110000000000000000000011111111111111111111100000000010
110111111111111111111100000000000000000000111111111111111111111110000000000000000000001111111111111111111100000000000000000000111111111111111111111101000000000000000000001111111111111111111000000001000
110111111111111111111100000000000000000000111111111111111111111110000000000000000000001111111111111111111000000000000000000000111111111111111111111110000000000010000000001111111111111111111100000000000
110111111111111111111100000000000000010000111111111111111111111110000000000000000000001111111111111111111000000000000000000000111111111111111111111100000000000000000000001111111111111111111110000000010
110111111111111111111000000000000000000000011111111111111111111110000000000000000000000111111111111111111000000000000000000000111111111111111111111110000000000000000000001111111111111111111100000000000
110111111111111111111100000000000000000000111111111111111111111110000000000000000000000111111111111111111000000000000000000000111111111111111111111100000000001000000000001111111111111111111100000000000
110111111111111111111100000000000000000000111111111111111111111110000000000000000000001111111111111111111000000000000000000000111111111111111111111101000000000000000000011101111111111111111100000000000
110111111111111111111100000000000000000000111111111111111111111110000000000000000000001111111111111111111100000000000000000000111111111111111111111101100000000010000000001111111111111111111100000000000
110111111111111111111100000000000000000000111111111111111111111110000000000000000000001111111111111111111100000000000000000000111111111111111111111100000000000000000000001111111111111111111100000000000
110111111111111111111100000000000000000000011111111111111111111110000000000000000000000111111111111111111100000000000000000000111111111111111111111100000000011000000000011111111111111111111100000000000
110111111111111111111100000000000000000000011111111111111111111110100000000000000000000111111111111111111100000000000000000000111111111111111111111100000010000001010000111110111111111111111100000000000
110111111111111111111100000000000000000000011111111111111111111110000000000000000000001111111111111111111100000000000000000000111111111111111111111101000000000000000000101111111111111111111100000000000
110111111111111111111100000000000000000000011111111111111111111110000000000000000000001111111111111111111100000000000000000000111111111111111111111100000000100000000000001111111111111111111110000000000
110111111111111111111100000000000000000000011111111111111111111110000000000000000000000111111111111111111100000000000000000000011111111111111111111110000010000000000000001111111111111111111100000000000
110111111111111111111100000000000000000000011111111111111111111100000000000000000000000111111111111111111100000000000000000000011111111111111111111100001000000000000000001111111111111111111100000000010
110111111111111111111100000000000000000000011111111111111111111100000000000000000000001111111111111111111100000000000000000000011111111111111111111110000100000000000000101111111111111111111110000000010
110111111111111111111100000000000000000000011111111111111111111110000000000000000000001111111111111111111100000000000000000000011111111111111111111100000000000000000000001111111111111111111100000000010
110111111111111111111100000000000000000000011111111111111111111110000000000000000000001111111111111111111100000000000000000000011111111111111111111100001000000000000000101111111111111111111100000000000
110111111111111111111100000000000000000000011111111111111111111110000000000000000000001111111111111111111100000000000000000000011111111111111111111100000000000000000000001111111111111111111100000000000
110111111111111111111100000000000000000000011111111111111111111110000000000000000000001111111111111111111100000000000000000000011111111111111111111110000000000000000000001110111111111111111100000000000
110111111111111111111100000000000000000000011111111111111111111110000000000000000000001111111111111111111100000000000000000000011111111111111111111100000000000000000000011111111111111111111100000000000
110111111111111111111100000000100001010000011111111111111111111110000000000000000000001111111111111111111100000000000000000000011111111111111111111100000000000000000000011111111111111111111100000000000
110111111111111111111100000000000000000000001111111111111111111110000001000000000000001111111111111111111100000000000010010000011111111111111111111110000000000000000000011111111111111111111110000000000
110111111111111111111100000000001000000000001111111111111111111110000000000000000000001111111111111111111100000000000000000000011111111111111111111100000000000000000000011111111111111111111100000000000
110111111111111111111100001000000000000000001111111111111111111110000000000000000000001111111111111111111100000000000000000000011111111111111111111110000000000000000000011111111111111111111110000000000
110111111111111111111100000000000000000000001111111111111111111110000000000000000000001111111111111111111100000000000000000000001111111111111111111100000000000000000000011111111111111111111110000000000
110111111111111111111100000000000000000000001111111111111111111110000000000000000000001111111111111111111100000000000000000000011111111111111111111100000000000000000000011111111111111111111100000000000
110111111111111111111100000000000000000000001111111111111111111110000000000000000000001111111111111111111000000000000000000010001111111111111111111100000000000000000000011111111111111111111100000000010
110111111111111111111100000000000000000000000111111111111111111110000000000000000000001111111111111111111100000000000000000000001111111111111111111100000000000000000000011111111111111111111100000000000
110111111111111111111100000000000000000000001111111111111111111110000000000000000000001111111111111111111100000000000000000000001111111111111111111100000000000000000000011111111111111111111100000000000
110111111111111111111100001000000000000000001111111111111111111110000000000000000000011111111111111111111100000000000000000000001111111111111111111100000000000000000000011111111111111111111100000000100
110111111111111111111100000000000000000000001111111111111111111110000000000000000000001111111111111111111100000000000000000000001111111111111111111100000000000000000000011110111111111111111101000000000
110111111111111111111110000000000000000000001111111111111111111110000000000000000000011111111111111111111100000000000000000000011111111111111111111100000000000000000000011111111111111111111100000000000
110111111111111111111110000000000000000000001111111111111111111110000000000000000000011111111111111111111100000000100000000000001111111111111111111100000000000000000000011111111111111111111100000000000
110111111111111111111110000000000000000000001111111111111111111110000000000000000000011111111111111111111100000000000000000000001111111111111111111100000000000000000000011111111111111111111110000000000
110111111111111111111110000000000000000000000111111111111111111110000000000000000000011111111111111111111100000000000000000000001111111111111111111110000000000000000000011111111111111111111100000000000
110111111111111111111110000000000000000000000111111111111111111110000000000000000000011111111111111111111100000000000000000000011111111111111111111110000000000000000000011111111111111111111110000000000
110111111111111111111110000000000000000000000111111111111111111110000000000000000000011111111111111111111100000000000000000000001111111111111111111100000000000000000000011111111111111111111100000000000
110111111111111111111110000000100000000000000111111111111111111100000000000000000000011111111111111111111100000000000000000000001111111111111111111100000000000000000000011111111111111111111110000000000
110111111111111111111110000000000000000000000111111111111111111110000000000000000000011111111111111111111100000000000000000000011111111111111111111100000000000000000000011110111111111111111100000000000
110111111111111111111110000000000000000000001111111111111111111110000000000000000000011111111111111111111100000000000000000000011111111111111111111100000000000000000000011111111111111111111110000000000
110111111111111111111110000000000000000000000111111111111111111110000000000000000000011111111111111111111100000000000000000000011111111111111111111100000000000000000000011111111111111111111100000000000
110111111111111111111110000000000000000000000111111111111111111110000000000000000000011111111111111111111100000000000000000000001111111111111111111100000000000000000000011111111111111111111100000000010
110111111111111111111110000000110001011000000111111111111111111110000000001000000000011111111111111111111100000000000000000000001111111111111111111100000000000000000000011111111111111111111100000000000
110111111111111111111110000000000000000000000111111111111111111110000000000000000000011111111111111111111100000000000010000000001111111111111111111100000000000000000000011111111111111111111110000000000
110111111111111111111111000000110001011000000111111111111111111110000001000000000000011111111111111111111100000000000000011010001111111111111111111100000000000000000000011111111111111111111110000000000
111111111111111111111111000000000010100000001111111111111111111110000000000100000000011111111111111111111110000000000000000100011111111111111111111100000000000000000000011111111111111111111110000000000
111111111111111111111111000000000100000000001111111111111111111110000000000110000000011111111111111111111110000000000100000000011111111111111111111100000000000000000000011111111111111111111100000000000
111111111111111111111111000000000000000000001111111111111111111110000000011000010000011111111111111111111110000000000000000000011111111111111111111100000000000000000000011111111111111111111110000000010
111111111111111111111111100000000000001000011111111111111111111110000000000001101100011111111111111111111111000000000000000000111111111111111111111110000000000000000000111111111111111111111110000000000
111111111111111111111111110000000000000000011111111111111111111110000000100001000000111111111111111111111111100000000000000001111111111111111111111110000000000000000000111111111111111111111100000001000
111111111111111111111111110000000000000000111111111111111111111111000000000000000000111111111111111111111111110000000000000001111111111111111111111111000000000000000001111111111111111111111100000000000
111111111111111111111111111100000000000011111111111111111111111111000000000001000000111111111111111111111111111000000000000111111111111111111111111111000000001000001001111111111111111111111110000000000
111111111111111111111111111110000000001111111111111111111111111111100000000000000001111111111111111111111111111110000000011111111111111111111111111111100000000000000011111111111111111111111110000000000
111111111111111111111111111111111111111111111111111111111111111111110000000000000011111111111111111111111111111111111111111111111111111111111111111111110000000000000111111111111111111111111110000000000
111111111111111111111111111111111111111111111111111111111111111111111000000000000111111111111111111111111111111111111111111111111111111111111111111111111000000001001111111111111111111111111110000000000
111111111111111111111111111111111111111111111111111111111111111111111100000000001111111111111111111111111111111111111111111111111111111111111111111111111111100011111111111111111111111111111110000000000
111111111111111011111111111111111111111111111111111111111111111111111111000001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100000000010
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000011110
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110
100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
This question is an edit to the JAVA based question here.
This is another picture symbolizing my idea!
I don't know if it fit your needs but a median filter might be a good starting point.
Note: This example uses node-imagemagick (plus NodeJs) and requires ImageMagick CLI.
Install node-imagemagick from command line with:
npm install imagemagick
Then apply a median -median filter to image.png with radius = 5 and save the filtered image with the following code:
var path = require('path');
var im = require('imagemagick');
var radius = 5;
im.convert([path.join(__dirname +'/image.png'), '-median', radius , path.join(__dirname +'/image'+radius+'.png')],
function(err, stdout){
if (err) throw err;
});
Radius Value
I tried differente values for "radius", a number between 6-8 looks like the most appropriate.
var path = require('path');
var im = require('imagemagick');
var radiuses = [2,4,6,8];
radiuses.forEach((radius) => {
im.convert([path.join(__dirname +'/image.png'), '-median', radius, path.join(__dirname +'/image'+radius+'.png')],
function(err, stdout){
if (err) throw err;
});
});
Result #1: Link
Result #2: Link
Plain JS
const medianFilter = (image, radius) => {
let width = image.width;
let height = image.height;
var input = image.getImageData(0, 0, image.width, image.height).data;
var output = input;
for (var x = 0; x < width; x++) {
for (var y = 0; y < height; y++) {
var index = (x + y * width) * 4; //Get a pixel index in a 1D array
var bufferRed = [];
var bufferGreen = [];
var bufferYellow = [];
var bufferAlpha = [];
for (var cx = 0; cx < radius; cx++) {
for (var cy = 0; cy < radius; cy++) {
if (x + cx < width && y + cy < height) {
var idx = (x + cx + (y + cy) * width) * 4; //Get a pixel index inside a radius*radius square
bufferRed.push(input[idx]);
bufferGreen.push(input[idx + 1]);
bufferYellow.push(input[idx + 2]);
bufferAlpha.push(input[idx + 3]);
}
}
}
output[index] = median(bufferRed.sort());
output[index + 1] = median(bufferGreen.sort());
output[index + 2] = median(bufferYellow.sort());
output[index + 3] = median(bufferAlpha.sort());
}
}
return output;
}
Full Example Without Node
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<canvas id="src" width="1305" height="569"></canvas>
<canvas id="dest" width="1305" height="569"></canvas>
<script type="text/javascript">
var ctx = document.getElementById('src').getContext("2d");
var img = new Image();
img.onload = function() {
ctx.drawImage(img, 0, 0);
ctx.width = img.width;
ctx.height = img.height;
var output = medianFilter(ctx, 7);
var ctx2 = document.getElementById('dest').getContext("2d");
var imageData = new ImageData(output, img.width, img.height);
ctx2.putImageData(imageData, 0, 0);
};
img.src = 'image.png';
function median(numbers) {
var median = 0,
numsLen = numbers.length;
numbers.sort();
if (
numsLen % 2 === 0
) {
median = (numbers[numsLen / 2 - 1] + numbers[numsLen / 2]) / 2;
} else {
median = numbers[(numsLen - 1) / 2];
}
return median;
}
const medianFilter = (image, radius) => {
let width = image.width;
let height = image.height;
var input = image.getImageData(0, 0, image.width, image.height).data;
var output = input;
for (var x = 0; x < width; x++) {
for (var y = 0; y < height; y++) {
var index = (x + y * width) * 4;
var bufferRed = [];
var bufferGreen = [];
var bufferYellow = [];
var bufferAlpha = [];
for (var cx = 0; cx < radius; cx++) {
for (var cy = 0; cy < radius; cy++) {
if (x + cx < width && y + cy < height) {
var idx = (x + cx + (y + cy) * width) * 4;
bufferRed.push(input[idx]);
bufferGreen.push(input[idx + 1]);
bufferYellow.push(input[idx + 2]);
bufferAlpha.push(input[idx + 3]);
}
}
}
output[index] = median(bufferRed.sort());
output[index + 1] = median(bufferGreen.sort());
output[index + 2] = median(bufferYellow.sort());
output[index + 3] = median(bufferAlpha.sort());
}
}
return output;
}
</script>
</body>
</html>
Disclaimer: Median Function, What are the mean and median filters?

Canvas image zooming using Nearest Neighbor Algorithm

I'm using nearest neighbor algorithm to zoom the image on canvas. But, when I move the scaling bar higher, the image have white line that create a square array
Original Image
After I move the scale bar
The zoom is work but the problem is only the white lines.
For the source code I will provide in bottom
1.html
<!DOCTYPE HTML>
<html>
<head>
<title>Prototype PC</title>
</head>
<body>
<canvas id='canvas1'></canvas>
<hr>
<button id='read'>READ IMAGE</button>
<hr>
Scale <input type='range' value='1' min='1' max='5' step='0.25' id='scale'>
<br><button id='default2'>Default Scalling</button>
<hr/>
</body>
<style>
body{
background : rgba(255,255,255,1);
}
</style>
<script src='imagine.js'></script>
<script>
var canvas = document.getElementById('canvas1')
var obj = new pc(canvas)
obj.image2canvas("565043_553561101348179_1714194038_a.jpg")
var tes = new Array()
document.getElementById('read').addEventListener('click',function(){
tes = obj.image2read()
})
document.getElementById('scale').addEventListener('change',function(){
var scaleval = this.value
var xpos = 0
var ypos = 0
var xnow = 0
var ynow = 0
var objW = obj.width
var objH = obj.height
tesbackup = new Array()
for(var c=0; c<tes.length; c++){
temp = new Array()
for(var d=0; d<4; d++){
temp.push(255)
}
tesbackup.push(temp)
}
//end of copy
for(var i=0; i<tes.length; i++){
xpos = obj.i2x(i)
ypos = obj.i2y(i)
xnow = Math.round(xpos) * scaleval)
ynow = Math.round(ypos) * scaleval)
if (xnow < objW && ynow < objH) {
for (var j=0; j<scaleval; j++) {
for (var k=0; k<scaleval; k++) {
var idxnow = obj.xy2i(xnow,ynow)
tesbackup[idxnow][0] = tes[i][0]
tesbackup[idxnow][1] = tes[i][1]
tesbackup[idxnow][2] = tes[i][2]
}
}
}
}
obj.array2canvas(tesbackup)
})
</script>
and, for imagine.js
function info(text){
console.info(text)
}
function pc(canvas){
this.canvas = canvas
this.context = this.canvas.getContext('2d')
this.width = 0
this.height = 0
this.imgsrc = ""
this.image2read = function(){
this.originalLakeImageData = this.context.getImageData(0,0, this.width, this.height)
this.resultArr = new Array()
this.tempArr = new Array()
this.tempCount = 0
for(var i=0; i<this.originalLakeImageData.data.length; i++){
this.tempCount++
this.tempArr.push(this.originalLakeImageData.data[i])
if(this.tempCount == 4){
this.resultArr.push(this.tempArr)
this.tempArr = []
this.tempCount = 0
}
}
info('image2read Success ('+this.imgsrc+') : '+this.width+'x'+this.height)
return this.resultArr
}
this.image2canvas = function(imgsrc){
var imageObj = new Image()
var parent = this
imageObj.onload = function() {
parent.canvas.width = imageObj.width
parent.canvas.height = imageObj.height
parent.context.drawImage(imageObj, 0, 0)
parent.width = imageObj.width
parent.height = imageObj.height
info('image2canvas Success ('+imgsrc+')')
}
imageObj.src = imgsrc
this.imgsrc = imgsrc
}
this.array2canvas = function(arr){
this.imageData = this.context.getImageData(0,0, this.width, this.height)
if(this.imageData.data.length != arr.length*4) {
return false
}
for(var i = 0; i < arr.length; i++){
this.imageData.data[(i*4)] = arr[i][0]
this.imageData.data[(i*4)+1] = arr[i][1]
this.imageData.data[(i*4)+2] = arr[i][2]
this.imageData.data[(i*4)+3] = arr[i][3]
}
this.context.clearRect(0, 0, this.width, this.height)
this.context.putImageData(this.imageData, 0, 0)
info('Array2Canvas Success ('+this.imgsrc+')')
}
this.i2x = function(i){
return (i % this.width)
}
this.i2y = function(i){
return ((i - (i % this.width))/ this.width)
}
this.xy2i = function(x,y){
return (y * this.width) + (x)
}
}
Thanks in advance for a solution of this problem
Rounding out pixels
Nearest pixel will result in some zoomed pixels being larger than otheres
It is a problem with the value of scaleval. It has a step of 0.25 and when you calculate each zoomed pixels address you use (and I am guessing as your code has syntax errors) Math.round(xpos * scaleval) but then you draw the pixel using only the fractional size eg 2.75 not the integer size eg 3.0
The size of each pixel is var xSize = Math.round((xpos + 1) * scaleval)-Math.round(xpos * scaleval) same for y. That way when the pixel zoom is not an integer value every so many zoomed pixels will be one pixel wider and higher.
The following is a fix of your code but as you had a number of syntax errors and bugs I have had to guess some of your intentions.
xpos = obj.i2x(i)
ypos = obj.i2y(i)
xnow = Math.round(xpos * scaleval)
ynow = Math.round(ypos * scaleval)
// pixel width and height
var pw = Math.round((xpos + 1) * scaleval) - xnow;
var ph = Math.round((ypos + 1) * scaleval) - ynow;
if (xnow < objW && ynow < objH) {
for (var y = 0; y < ph; y++) {
for (var x =0; x < pw; x++) {
var idxnow = obj.xy2i(xnow + x, ynow + y)
tesbackup[idxnow][0] = tes[i][0]
tesbackup[idxnow][1] = tes[i][1]
tesbackup[idxnow][2] = tes[i][2]
}
}
}
}
But you are not really doing a nearest neighbor algorithm. For that you iterate each of the destination pixels finding the nearest pixel and using its colour. That allows you to easily apply a transform to the zoom but still get every pixel and not skip pixels due to rounding errors.
Nearest neighbor
Example of using nearest neighbor lookup for a scale rotated and translated image
var scaleFac = 2.3; // scale 1> zoom in
var panX = 10; // scaled image pan
var panY = 10;
var ang = 1;
var w = ctx.canvas.width; // source image
var h = ctx.canvas.height;
var wd = ctx1.canvas.width; // destination image
var hd = ctx1.canvas.height;
// use 32bit ints as we are not interested in the channels
var src = ctx.getImageData(0, 0, w, h);
var data = new Uint32Array(src.data.buffer);// source
var dest = ctx1.createImageData(wd, hd);
var zoomData = new Uint32Array(dest.data.buffer);// destination
var xdx = Math.cos(ang) * scaleFac; // xAxis vector x
var xdy = Math.sin(ang) * scaleFac; // xAxis vector y
var ind = 0;
var xx,yy;
for(var y = 0; y < hd; y ++){
for(var x = 0; x < wd; x ++){
// transform point
xx = (x * xdx - y * xdy + panX);
yy = (x * xdy + y * xdx + panY);
// is the lookup pixel in bounds
if(xx >= 0 && xx < w && yy >= 0 && yy < h){
// use the nearest pixel to set the new pixel
zoomData[ind++] = data[(xx | 0) + (yy | 0) * w]; // set the pixel
}else{
zoomData[ind++] = 0; // pixels outside bound are transparent
}
}
}
ctx1.putImageData(dest, 0, 0); // put the pixels onto the destination canvas

Categories

Resources