context.putImageData of HTML <canvas> set on wrong coordinate - html

I am trying to get each average color of 100 rectangle as pictures
enter image description here
As the picture show, the pasted imageData doesn't set suitably. But the all coordinate parameter of (ctx.getImageData() and ctx.putImageData()) is same as console.log picture I attached
enter image description here
Is it bug? Or did I miss something ?
convert(){
let canvas = this.$el.querySelector('#pixel-art-canvas'),
image = this.$el.querySelector('#upload-image'),
ctx = canvas.getContext('2d'),
degree = 10,
img = new Image,
tiles = Math.pow(degree,2),
eachWidth,eachHeight;
img.src = image.src;
ctx.drawImage(img,0,0);
eachWidth= canvas.width/degree;
eachHeight= canvas.height/degree;
for(let k = 0; k < tiles; k++) {
let imgd,x,y,
rgb = {r:0,g:0,b:0},
count = 0;
x = (k % degree) * eachWidth;
y = (k / degree) * eachHeight;
imgd = ctx.getImageData(x, y, eachWidth, eachHeight);
console.log('x: ' + x + ' , y:' +y+' , w: '+eachWidth + ' , h :' +eachHeight);
for (let i=0; i < imgd.data.length; i=i+4) {
rgb.r += imgd.data[i];
rgb.g += imgd.data[i+1];
rgb.b += imgd.data[i+2];
count++;
}
rgb.r = ~~(rgb.r/count);
rgb.g = ~~(rgb.g/count);
rgb.b = ~~(rgb.b/count);
for (let j=0; j < imgd.data.length; j=j+4) {
imgd.data[j] = rgb.r;
imgd.data[j+1] = rgb.g;
imgd.data[j+2] = rgb.b;
}
ctx.putImageData(imgd, x, y, 0, 0, eachWidth, eachHeight);
console.log('x: ' + x + ' , y:' +y+' , w: '+eachWidth + ' , h :' +eachHeight);
}//end for
}

The problem is that you are incorrectly calculating the y coordinate.
You have
y = (k / degree) * eachHeight;
Which will give a fractional result for (k/degree) you need to round (floor) the value before multiplying it.
// to fix the y coord.
y = Math.floor(k / degree) * eachHeight;
// or
y = ((k / degree) | 0) * eachHeight;
Also you are incorrectly getting the mean of the colors. RGB values represent the square root of the intensity of the pixel. Thus the difference in intensity between a RGB value of 128 and 256 is not 2 times but 4 times as bright.
If you take the mean by just summing the RGB values you end up with a result that is darker than it should be.
The correct method is to get the mean of the the square of the RGB values, then convert back to logarithmic RGB to put back onto the canvas.
Change you code
const rgb = {r:0,g:0,b:0};
var count = 0;
for (let i=0; i < imgd.data.length; i=i+4) {
rgb.r += imgd.data[i] * imgd.data[i]; // Square the value
rgb.g += imgd.data[i + 1] * imgd.data[i + 1];
rgb.b += imgd.data[i + 2] * imgd.data[i + 1];
count++;
}
// Get mean and convert back to logarithmic
// Also you do not need to floor the values with ~~(val) as
// the array is of type Uint8ClampedArray which will floor and clamp the
// values for you.
// Also ~~(val) requires 2 operations, a quicker way the requires only one
// operation is (val) | 0
rgb.r = Math.sqrt(rgb.r/count);
rgb.g = Math.sqrt(rgb.g/count);
rgb.b = Math.sqrt(rgb.b/count);
for (let j=0; j < imgd.data.length; j=j+4) {
imgd.data[j] = rgb.r;
imgd.data[j+1] = rgb.g;
imgd.data[j+2] = rgb.b;
}
ctx.putImageData(imgd, x, y, 0, 0, eachWidth, eachHeight);
You can also optimise a little via.
const x = (k % degree) * eachWidth;
const y = ((k / degree) | 0) * eachHeight;
const imgd = ctx.getImageData(x, y, eachWidth, eachHeight);
const rgb = {r : 0, g : 0, b : 0};
const count = imgd.data.length / 4;
var i = 0;
while( i < imgd.data.length ) {
rgb.r += imgd.data[i] * imgd.data[i++]; // square the value
rgb.g += imgd.data[i] * imgd.data[i++];
rgb.b += imgd.data[i] * imgd.data[i++];
i ++;
}
// need to round as we are not directly adding back to the buffer.
rgb.r = Math.sqrt(rgb.r / count) | 0;
rgb.g = Math.sqrt(rgb.g / count) | 0;
rgb.b = Math.sqrt(rgb.b / count) | 0;
// get a refer to 32 bit version of same data and set all values
// the 0xFF000000 sets the alpha to 255
// shift red 2 bytes (16 bits)
// shift green 1 byte (8 bit)
// blue is in the correct place.
new Uint32Array(imgd.data.buffer).fill(
0xFF000000 + (rgb.r << 16) + (rgb.g << 8) + rgb.b
);
// putImageData use 3 arguments imgd and the x, y if you are
// copying all the data back to the canvas.
ctx.putImageData(imgd, x, y);

Related

Color rectangle one by one in a canvas

I have canvas on which there are number of rectangle drawn so that the make a grid. What I want is to color each rectangle one by one in the grid until each of them are colored black and then again select few of them to color white in the same way one by one. I have tried few methods using "for" loop and "setTimeout" but its not working out.
I have recently started javascript that's why I need help to do this.
This code only includes the making of the grid no further because that part was not working out:
var canvas;
var ctx;
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
tileW = 20;
tileH = 20;
tileRowCount = 25;
tileColCount = 40;
var tile = [];
for (c = 0; c < tileColCount; c++) {
tile[c] = [];
for (r = 0; r < tileRowCount; r++){
tile[c][r] = {
x: c * (tileW + 3),
y: r * (tileH + 3),
state: 'e'
}; //state e for empty
}
}
for (c = 0; c < tileColCount; c++)
for (r = 0; r < tileRowCount; r++) {
ctx.beginPath();
ctx.fillStyle = '#AAAAAA';
ctx.rect(tile[c][r].x, tile[c][r].y, tileW, tileH);
ctx.closePath();
ctx.fill();
}
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<canvas id="canvas" width='1000' height='600'></canvas>
<script type="text/javascript" src='data.js'></script>
</body>
</html>
Update your rendering code to use the fillRect method from the 2d context.
Replace this:
ctx.beginPath();
ctx.fillStyle = '#AAAAAA';
ctx.rect(tile[c][r].x, tile[c][r].y, tileW, tileH);
ctx.closePath();
ctx.fill();
With this:
ctx.fillStyle = "#AAAAAA";
ctx.fillRect(tile[c][r].x, tile[c][r].y, tileW, tileH);
It is not clear what you want. My guess is that you want a checkerboard. The following will do so.
The Remainder operator
The operator % returns the remainder after division. eg 3 % 2 is 1 and 4 % 2 is 0
We can use the remainder operator to let us know which color to make each square. As we count down the rows the remainder of the row count and 2 will switch between 0 and 1. For each column we want the opposite. If you add the row count to the column count and get the remainder we get the desired result. Example (r + c) will give (0 + 0) % 2 == 0, (1 + 0) % 2 == 1, (2 + 0) % 2 == 0, (3 + 0) % 2 == 1, then the next column (0 + 1) % 2 == 1, (1 + 1) % 2 == 0, (2 + 1) % 2 == 1, (3 + 1) % 2 == 0. and so on.
The % operator has higher precedence than the addition operator + thus we need to put the grouping operators ( ) around the addition.
Eg 3 + 1 % 2 will equal 4. The 1 % 2 is done first then the 3 is added while (3 + 1) % 2 will equal 0. The (3 + 1) is first then the remainder is done
The Ternary operator
We can do this most simply using the ternary operator ?. eg color = (c + r) % 2 ? "white" : "black" which is the same as if ((c + r) % 2 === 1) { color = "white" } else { color = "black" }
Example
const ctx = canvas.getContext('2d');
const tileW = 20;
const tileH = 20;
const tileRowCount = 25;
const tileColCount = 40;
const tiles = [];
for (let c = 0; c < tileColCount; c++) {
for (let r = 0; r < tileRowCount; r++) {
tiles.push({
x: c * (tileW + 3),
y: r * (tileH + 3),
color: (c + r) % 2 ? "white" : "black",
});
}
}
for (const tile of tiles) {
ctx.fillStyle = tile.color;
ctx.fillRect(tile.x, tile.y, tileW, tileH);
}
<canvas id="canvas" width='1000' height='600'></canvas>

How do I generate a visually distinct, non-random color from a single input number?

In Flash AS3, how would I write a function that will:
Take in an integer (a list index, for example)
return a visually distinct hex color based on that number (and will consistently return that same color given that same number)
The purpose is to provide a visually distinct color for each item in varying-length list of items. The most I expect to support is around 200, but I don't see the count going far above 20 or so for most.
Here's my quick and dirty:
public static function GetAColor(idx:int):uint {
var baseColors:Array = [0xff0000, 0x00ff00, 0xff0080, 0x0000ff, 0xff00ff, 0x00ffff, 0xff8000];
return Math.round(baseColors[idx % baseColors.length] / (idx + 1) * 2);
}
It does OK, but it would be nice to see a more distinct set of colors that are not so visually close to one another
You could go with generator of random values that supports seed, so you will be able return same color. As for color you could build it - by randomValue * 0xFFFFFF, where randomValue between 0 and 1. And exclude values (colors) that are close.
Second option: build palette of 200 colors with step - 0xFFFFFF / 200 and shuffle palette with predefined logic, so you will have same colors.
Third option: as for really distinct colors, you could go with big jumps in every channel. Example: 0xFF * 0.2 - 5 steps in every channel.
Fourth option: go with HSV. It's easy to understand(watch image, rotate hue from 0 to 360, change saturation and value from 0 to 100) how to manipulate parameters to get distinct color:
//Very simple demo, where I'm only rotating Hue
var step:uint = 15;
var position:uint = 0;
var colors:Array = [];
for (; position < 360; position += step) {
colors.push(HSVtoRGB(position, 100, 100));
}
//Visualisation for demo
var i:uint, len:uint = colors.length, size:uint = 40, shape:Shape, posX:uint, posY:uint;
for (i; i < len; ++i) {
shape = new Shape();
shape.graphics.beginFill(colors[i]);
shape.graphics.drawRect(0, 0, size, size);
addChild(shape);
shape.x = posX;
shape.y = posY;
posX += size;
if (posX + size >= stage.stageWidth) {
posX = 0;
posY += size;
}
}
public function HSVtoRGB(h:Number, s:Number, v:Number):uint {
var r:Number = 0;
var g:Number = 0;
var b:Number = 0;
var tempS:Number = s / 100;
var tempV:Number = v / 100;
var hi:int = Math.floor(h / 60) % 6;
var f:Number = h / 60 - Math.floor(h / 60);
var p:Number = (tempV * (1 - tempS));
var q:Number = (tempV * (1 - f * tempS));
var t:Number = (tempV * (1 - (1 - f) * tempS));
switch (hi) {
case 0:
r = tempV;
g = t;
b = p;
break;
case 1:
r = q;
g = tempV;
b = p;
break;
case 2:
r = p;
g = tempV;
b = t;
break;
case 3:
r = p;
g = q;
b = tempV;
break;
case 4:
r = t;
g = p;
b = tempV;
break;
case 5:
r = tempV;
g = p;
b = q;
break;
}
return (Math.round(r * 255) << 16 | Math.round(g * 255) << 8 | Math.round(b * 255));
}
And last one, if you want go with this task like a pro, this wiki article could be helpful for you.

Google Maps determine distance along Line

I am trying to determine the distance of a point along a given Polyline (from the start point) in Google maps (given that the user clicks on the Polyline and I get the point coordinates in the event).
So far, this is the only thing that comes to mind:
Iterate over all segments in the Polyline until I find one such that
d(line, point) ~= 0, keeping track of the distance covered so far.
Interpolate on the segment the point is on to find its distance
relative to the start of the segment.
Sadly, this seems rather complicated for something that should be straightforward to do.
Is there any easier way?
P.S.: I'm using API v3
So, after much searching I decided to implement the algorithm as described above. Turned out it isn't as bad as I thought. Should anyone ever land on this page, the full code is below:
var DistanceFromStart = function (/*latlng*/ markerPosition) {
var path = this.polyline.getPath();
var minValue = Infinity;
var minIndex = 0;
var x = markerPosition.lat();
var y = markerPosition.lng();
for (var i = 0; i < path.getLength() - 1; i++) {
var x1 = path.getAt(i).lat();
var y1 = path.getAt(i).lng();
var x2 = path.getAt(i + 1).lat();
var y2 = path.getAt(i + 1).lng();
var dist = pDistance(x, y, x1, y1, x2, y2);
if (dist < minValue) {
minIndex = i;
minValue = dist;
}
}
var gdist = google.maps.geometry.spherical.computeDistanceBetween;
var dinit = gdist(markerPosition, path.getAt(minIndex));
var dtotal = gdist(path.getAt(minIndex), path.getAt(minIndex + 1));
var distanceFromStart = 0;
for (var i = 0; i <= minIndex - 1; i++) {
distanceFromStart += gdist(path.getAt(i), path.getAt(i + 1));
}
distanceFromStart += dtotal * dinit / dtotal;
return distanceFromStart;
}
function pDistance(x, y, x1, y1, x2, y2) {
var A = x - x1;
var B = y - y1;
var C = x2 - x1;
var D = y2 - y1;
var dot = A * C + B * D;
var len_sq = C * C + D * D;
var param = dot / len_sq;
var xx, yy;
if (param < 0 || (x1 == x2 && y1 == y2)) {
xx = x1;
yy = y1;
}
else if (param > 1) {
xx = x2;
yy = y2;
}
else {
xx = x1 + param * C;
yy = y1 + param * D;
}
var dx = x - xx;
var dy = y - yy;
return Math.sqrt(dx * dx + dy * dy);
}
If you see anything to improve, do let me know.
If you get the coordinates for the start and end points, then use the haversine algorithm to calculate the distance you can easily find the distance between two points taking into consideration the curvature of the earth.
Here is the formula (you may need to convert in into the language you are using):
var R = 6371; // km
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad();
var lat1 = lat1.toRad();
var lat2 = lat2.toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
variable d is your distance.
Hope this helps

Iterating each pixel of a Bitmap image in ActionScript

Is it possible to iterate each pixel of a bitmap image? Eventually what I'm trying to achieve is that I need to get the coordinate values of each pixel of a bitmap image and change the color of those pixels according to their coordinate values. As I see it, I need to use the getPixels() method but I still did not understand exactly what I should do.
( too slow :) )
so this is the sae as above with a linear loop instead of 2 nested loops.
//creates a new BitmapData, with transparency, white 0xFFFFFF
var bd:BitmapData = new BitmapData( 100, 100, false, 0xFFFFFF );
//stores the width and height of the image
var w:int = bd.width;
var h:int = bd.height;
var i:int = w * h;
var x:int, y:int, col;
//decremental loop are said to be faster :)
while ( i-- )
{
//this is the position of each pixel in x & y
x = i % w;
y = int( i / w );
//gets the current color of the pixel ( 0xFFFFFF )
col = bd.getPixel( x, y );
//assign the 0xFF0000 ( red ) color to the pixel
bd.setPixel( x, y, 0xFF0000 );
}
addChild( new Bitmap( bd ) );//a nice red block
note that if you're using a bitmapData with an alpha channel (say if you load the image, the alpha will be turned on automatically ) you 'll have to use
bd.getPixel32( x, y );// returns a uint : 0xFF000000
//and
bd.setPixel32( x, y, UINT );// 0xFF000000
EDIT : I 've done a quick bench :
package
{
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.utils.getTimer;
public class pixels extends Sprite
{
private var bd:BitmapData = new BitmapData( 100, 100, false, 0xFFFFFF );
public function pixels()
{
var i:int, total:int = 100, t:int = 0;
t = getTimer();
i = total;
while( i-- )
{
whileLoop( bd );
}
trace( 'while:', getTimer() - t );
t = getTimer();
i = total;
while( i-- )
{
forLoop( bd );
}
trace( 'for:', getTimer() - t );
}
private function forLoop( bd:BitmapData ):void
{
var i:int, j:int;
var col:int;
for ( i = 0; i < bd.width; i++ )
{
for ( j = 0; j < bd.height; j++ )
{
col = bd.getPixel( i, j ); // +/- 790 ms
}
}
//for ( i = 0; i < bd.width; i++ ) for ( j = 0; j < bd.height; j++ ) col = bd.getPixel( i, j ); // +/-530 ms
//var w:int = bd.width;
//var h:int = bd.height;
//for ( i = 0; i < w; i++ ) for ( j = 0; j < h; j++ ) col = bd.getPixel( i, j ); // +/-250 ms
}
private function whileLoop( bd:BitmapData ):void
{
var w:int = bd.width;
var h:int = bd.height;
var i:int = w * h;
var col:int;
while ( i-- )
{
col = bd.getPixel( i % w, int( i / w ) ); // +/- 580 ms
}
//while ( i-- ) col = bd.getPixel( i % w, int( i / w ) ); // +/- 330 ms
}
}
}
for 100 * ( 100 * 100 ) getPixel, the fastest (on my machine) is the one-line for loop with local variables. ( +/- 250 ms ) then the one-line while( +/- 330 ms ) :)
storing local variables w and h for width and height makes the for loops twice faster :)
good to know
If, as you say, you are only setting the pixels based on their x and y, you need neither getPixel() nor getPixels()!
myBitmapData.lock();
for( var j:int = 0; j < myBitmapData.height; j++ )
{
for( var i:int = 0; i < myBitmapData.width; i++ )
{
var alpha:uint = 0xFF000000; // Alpha is always 100%
var red:uint = 0x00FF0000 * ( i / myBitmapData.width ); // Set red based on x
var green:uint = 0x0000FF00 * ( j / myBitmapData.height ); // Set green based on y
var newColor:uint = alpha + red + green; // Add the components
// Set the new pixel value (setPixel32() includes alpha, e.g. 0xFFFF0000 => alpha=FF, red=FF, green=00, blue=00)
myBitmapData.setPixel32( i, j, newColor );
}
}
myBitmapData.unlock();
If, however, you want to read the pixels' current value, let me join the speed competition.
In addition to earlier answers, here's much more speed increase!
Instead of numerous calls to getPixel(), you can use getPixels() to get a byteArray of the pixel data.
myBitmapData.lock();
var numPixels:int = myBitmapData.width * myBitmapData.height;
var pixels:ByteArray = myBitmapData.getPixels( new Rectangle( 0, 0, myBitmapData.width, myBitmapData.height ) );
for( var i:int = 0; i < numPixels; i++ )
{
// Read the color data
var color:uint = pixels.readUnsignedInt();
// Change it if you like
// Write it to the pixel (setPixel32() includes alpha, e.g. 0xFFFF0000 => alpha=FF, red=FF, green=00, blue=00)
var theX:int = i % myBitmapData.width;
myBitmapData.setPixel32( theX, ( i - theX ) / myBitmapData.width, color );
}
myBitmapData.unlock();
You need a BitmapData object. Then, it's a simple straight-forward nested loop :
var pix : int; //AS3 uses int even for uint types
for (var x:int = 0; x < myBitmapData.width; x++)
{
for (var y:int = 0; y < myBitmapData.height; y++)
{
// This'll get you the pixel color as RGB
pix = myBitmapData.getPixel(x,y);
// To change the color, use the setPixel method + the uint corresponding
// to the new color.
}
}

Colour scale based on custom range? in HEX?

How do I create a custom colour scale ideally in Hex? say from yellow to red, depending on the height of an object? is this a correct way to achieve this or is there a better way without having to convert it at the end?:
var r:int = 255;
var b:int = 0;
var maxHeight:int = 52;
var minHeight:int = 21;
var scale:int = 255 / (maxHeight-minHeight);
var g:int = 255 - ((object.height-minHeight) * scale);
var hexColor:uint = RGBtoHEX(r,g,b);
private function RGBtoHEX(r:int, g:int, b:int) :uint
{
return r << 16 | g << 8 | b;
}
Here is a function that allows you to find a colour value between two others based on a range of 0-1. I think it will meet your needs
private function getBetweenColourByPercent(value:Number = 0.5 /* 0-1 */, highColor:uint = 0xFFFFFF, lowColor:uint = 0x000000):uint {
var r:uint = highColor >> 16;
var g:uint = highColor >> 8 & 0xFF;
var b:uint = highColor & 0xFF;
r += ((lowColor >> 16) - r) * value;
g += ((lowColor >> 8 & 0xFF) - g) * value;
b += ((lowColor & 0xFF) - b) * value;
return (r << 16 | g << 8 | b);
}