I'm trying print image date using ESC POS commands in Javascript, but until now just crap is printing out.
Command ESC *
I'm using Javascript, trying print direct to bluethoo a image in bit64string.
I tried this example with ESC #, ESC * command in Javscript
It only prints me characters, but not the image
Thank in advance
// let image = context.getImageData(0, 0, width, height)
getImageData(image, Width, Height){
var dimensions = Width * Height
var dots = new Uint8Array(dimensions)
var index = 0
var threshold = 127;
for (var y = 0; y < Height; y++)
{
for (var x = 0; x < Width; x++)
{
var color = this.getPixelI(image, x, y)
//let luminance
let luminance = (0.2126* color[0] + 0.7152 * color[1] + 0.0722 * color[2]);
dots[index] = luminance < threshold
index++;
}
}
return dots
}
getPixelI(imgData, x, y) {
var i = y * (imgData.width * 4) + x * 4;
var d = imgData.data;
return [d[i],d[i+1],d[i+2], d[i+3]] // returns array [R,G,B,A]
}
Related
Different results are obtained with the same code on a computer(windows 10) and a smartphone(android).I'm working in P5.JS with used loadPixels(). Below is an example code and screenshots. I will also leave a link to OpenProcessing so that you can test the program:
https://openprocessing.org/sketch/1703228
function setup() {
createCanvas(300, 300);
randomSeed(1);
for (let x2=0; x2<width; x2 +=100) {
for (let y2=0; y2<height; y2 += 100) {
fill(random(200),random(55),random(155));
rect(x2,y2,100,100);
}
}
///////
loadPixels();
background(255);
for (let y1=0; y1<height; y1+=100) {
for (let x1=0; x1<width; x1+=100) {
let poz=(x1+y1*width)*4;
let r=pixels[poz];
let g=pixels[poz+1];
let b=pixels[poz+2];
fill(r,g,b);
rect(x1,y1,100,100);
}
}
//////////
}
Computer picture
Smartphone picture
p5js pixels array is different from the original Java's processing pixels array. Very different.
It stores all canvas pixels in 1d array, 4 slots for each pixel:
[pix1R, pix1G, pix1B, pix1A, pix2R, pix2G, pix2B, pix2A...] And also the pixel density mathers.
So your issue is with pixel density that are different from one device to another.
Try differents values to pixelDensity() in the code below. With 1 you get the result that you are getting in PC, with 3 you get the result you get with mobile.
function setup() {
createCanvas(300, 300);
//change here!!
pixelDensity(3);
randomSeed(1);
for (let x2 = 0; x2 < width; x2 += 100) {
for (let y2 = 0; y2 < height; y2 += 100) {
fill(random(200), random(55), random(155));
rect(x2, y2, 100, 100);
}
}
///////
loadPixels();
background(255);
for (let y1 = 0; y1 < height; y1 += 100) {
for (let x1 = 0; x1 < width; x1 += 100) {
let poz = (x1 + y1 * width) * 4;
let r = pixels[poz];
let g = pixels[poz + 1];
let b = pixels[poz + 2];
fill(r, g, b);
rect(x1, y1, 100, 100);
}
}
//////////
}
To make them consistent you need to account for different pixelsDensity in your code.
the following code shows how to account for density using pixels in a determined area, in you case that would be the entire canvas.
To work any given area (a loaded image for instance) you can adapt this snippet:
(here i'm setting the color of the area, but you can get the idea;)
//the area data
const area_x = 35;
const area_y = 48;
const width_of_area = 180;
const height_of_area = 200;
//the pixel density
const d = pixelDensity();
loadPixels();
// those 2 first loops goes trough every pixel in the area
for (let x = area_x; x < width_of_area; x++) {
for (let y = area_y; y < height_of_area; y++) {
//here we go trough the pixels array to get each value of a pixel minding the density.
for (let i = 0; i < d; i++) {
for (let j = 0; j < d; j++) {
// calculate the index of the 1d array for every pixel
// 4 values in the array for each pixel
// y times density times #of pixels
// x idem
index = 4 * ((y * d + j) * width * d + (x * d + i));
// numbers for rgb color
pixels[index] = 255;
pixels[index + 1] = 30;
pixels[index + 2] = 200;
pixels[index + 3] = 255;
}
}
}
}
updatePixels();
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.
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
I need to find out what are the N dominant colors of an image.
I've written a function inspired from this : http://bubble.ro/How_to_create_the_histogram_of_an_image_using_PHP.html
The picture is drawn inside of a hidden canvas that has the same surface as the picture.
Here is my code :
lwf.image.getDominantColors = function(args) {
// args.imageData is the ImageData object got from the canvas 2d context.
// canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height)
if(!args.imageData) alert('lwf::image::getDominantColors() : no ImageData provided.');
// args.sampling defines the percentage of pixels to be tested (0.01 for example).
var sampling = undefined != args.sampling && 0 > args.sampling && args.sampling <= 1
? args.sampling
: lwf.image.DFLT_PX_SAMPLING_RATIO; // Default value
var w = args.imageData.width,
h = args.imageData.height,
total = w * h,
mat = args.imageData.data;
// Interval allows me to skip pixels on X and Y axis to make the computation faster.
// The top-left pixel of each interval * interval square is tested.
var interval = Math.round(Math.sqrt(1 / sampling));
var histogram = new Array();
var x = 0, y = 0, // Coordinates of the tested pixel.
i = 0, j = 0; // Use to get the number of tested pixels.
for(i = 0, x = 0; x < w; ++i, x += interval) {
for(j = 0, y = 0; y < h; ++j, y += interval) {
var start = (y * w + x) << 2;
var r = mat[start ],
g = mat[start + 1],
b = mat[start + 2];
var value = Math.round((r + g + b) / 3);
// TODO
}
}
var tested = i * j;
// TODO
return histogram;
}; // lwf::image::getDominantColors()
I don't know how to complete this so that it returns an equivalent of a PHP array indicating, foreach color that was found, a value representing its presence.
And another question : what does the following expression represent ?
var value = Math.round((r + g + b) / 3);
It's strange because if we have r = 200, g = 100, b = 100 and r = 100, g = 100, b = 200, both will have the same value but for two different colors. There's no explaination about this in the tutorial i found.
Does anyone have an idea ? Thanks in advance :)
Im trying to get a hit test/overlap test to work in the below code which creates 200 circles in a random position on a canvas. I am trying to store the positions of the circles in an array and then checking that array each time another circle is created in the for loop, if the randomly created x and y is too close to a already created circle it should keep getting a new random x and y until it isnt too close to an already created circle.
I just cant get it too work in the while loop.
Any help please...
Thanks
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", canvasDraw);
function canvasDraw () {
var c = document.getElementById("canvas");
var w = window.innerWidth;
var h = window.innerHeight;
c.width = w;
c.height = h;
var ctx = c.getContext("2d");
ctx.clearRect(0,0,c.width, c.height);
var abc = 0;
var colours = new Array ("rgb(0,100,0)", "rgb(51,102,255)");
var positions = new Array();
function hitTest(x, y) {
for(var p in positions) {
pp = p.split(",");
pp[0] = parseInt(pp[0]);
pp[1] = parseInt(pp[1]);
if(((x > (pp[0] - 24)) && (x < (pp[0] + 24))) && ((y > (pp[1] - 24)) && (y < (pp[1] + 24)))) {
return true;
}
}
return false;
}
//Loop 200 times for 200 circles
for (i=0; i<200; i++) {
var x = Math.floor(Math.random()*c.width);
var y = Math.floor(Math.random()*c.height);
while(hitTest(x, y) == true){
var x = Math.floor(Math.random()*c.width);
var y = Math.floor(Math.random()*c.height);
}
var pos = x.toString() + "," + y.toString();
positions.push(pos);
var radius = 10;
var r = radius.toString();
var b = colours[Math.floor(Math.random()*colours.length)];
circle(ctx,x,y, radius, b);
}
}
function circle (ctx, x, y, radius, b) {
ctx.fillStyle = b;
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
}
</script>
Some things before beginning:
Don't create arrays with new Array(), unless you specify the initial length. Use [];
Don't iterate through arrays using for...in: use a standard for with a counter. This is more a good practice;
Converting numbers into strings and converting back to numbers is useless and expensive. Use a small array to store both values;
Don't use "magic numbers", i.e. number with a precise value but hard to recognize immediately. Use named "constants" or put a comment near each of them telling what they mean, for future maintenance.
Ok, let's see the code.
if(((x > (pp[0] - 24)) && (x < (pp[0] + 24))) && ((y > (pp[1] - 24)) && (y < (pp[1] + 24))))
Honestly, what is this? I'd call it a cranky and obscure snippet. Recall what you've learnt at school:
var dx = pp[0] - x, dy = pp[1] - y;
if (dx * dx + dy * dy < 400) return true;
Isn't it much clearer?
Let's see the whole function:
function canvasDraw () {
var c = document.getElementById("canvas");
var w = window.innerWidth;
var h = window.innerHeight;
c.width = w;
c.height = h;
var ctx = c.getContext("2d");
ctx.clearRect(0,0,c.width, c.height);
// Lolwut?
// var abc = 0;
var colours = ["rgb(0,100,0)", "rgb(51,102,255)"];
var positions = [];
function hitTest(x, y) {
for (var j = 0; j < positions.length; j++) {
var pp = positions[j];
var dx = pp[0] - x, dy = pp[1] - y;
if (dx * dx + dy * dy < 400) return true;
}
return false;
}
// You declare the radius once and for all
var radius = 10;
// Declare the local scoped variables. You forgot i
var x, y, i;
for (i=0; i<200; i++) {
// How about a do...while instead of a while?
do {
var x = Math.floor(Math.random()*c.width);
var y = Math.floor(Math.random()*c.height);
// Testing with === is faster, always do it if you know the type
// I'll let it here, but if the type is boolean, you can avoid testing
// at all, as in while (hitTest(x, y));
} while (hitTest(x, y) === true);
positions.push([x, y]);
// This instruction is useless
// var r = radius.toString();
var b = colours[Math.floor(Math.random()*colours.length)];
circle(ctx,x,y, radius, b);
}
}
BEWARE, though, that depending on your canvas size, there could be no more room for another circle, so it could end in an infinite loop. Try to put 200 circles with a radius of 10 inside a 40x40 box...
There should be another test to do, and that could be complicated.