How to distort an image by noise in p5.js - javascript

I'm trying to produce a function that starts with source image, generates noise, and then uses the noise to distort the image.
I start with creating the noise, and turning it into a vector field, Then I remap the coordinates, and pull the pixels out of the image at the correct coordinates.
Finally I re-combine the extracted pixels into an image.
So far my code is as follows:
function distort(sourceImage){
let vectorField = [];
var amount = 100;
var scale = 0.01;
for (x = 0; x < sourceImage.width; x++){
let row = [];
for (y = 0; y < sourceImage.height; y++){
let vector = createVector(amount*(noise(scale*x,scale*y)-0.5), 4*amount*(noise(100+scale*x,scale*y)-0.5))
row.push(vector);
}
vectorField.push(row);
}
var result = [];
sourceImage.loadPixels();
for (i = 0; i < sourceImage.width; i++){ //sourceImage.width
for (j = 0; j < sourceImage.height; j += 4){ //sourceImage.height
var res = vectorField[i][j];
//console.log(res);
var ii = constrain(floor(i + res.x), 0, sourceImage.width - 1);
var jj = constrain(floor(j + res.y), 0, sourceImage.height - 1);
//console.log(ii, jj);
result[i * sourceImage.width + j] = color(sourceImage.pixels[ii * sourceImage.width + jj], sourceImage.pixels[ii * sourceImage.width + jj + 1], sourceImage.pixels[ii * sourceImage.width + jj + 2], sourceImage.pixels[ii * sourceImage.width + jj + 3]);
}
}
//console.log(result)
//console.log(sourceImage.pixels[0 + sourceImage.width * 0])
for (n=0; n<sourceImage.width; n++) {
for(m=0; m<sourceImage.height; m++){
index = (n * sourceImage.width + m) * 4;
if (index >= 4194300){
index = 4194300;
}
sourceImage.pixels[index] = red(result[index]);
sourceImage.pixels[index + 1] = green(result[index]);
sourceImage.pixels[index + 2] = blue(result[index]);
sourceImage.pixels[index + 3] = alpha(result[index]);
}
}
sourceImage.updatePixels();
image(sourceImage, 0, 0, size, size);
}
Except that as a result, I'm getting 4 panels of noise across the top 4th of the canvas. The noise notably includes a lot of pixels that I know weren't in the source image, too (namely blue pixels; the image I'm trying to distort is red and white). The noise is sort of identifiable as having started as the source image, but distorted and with the aforementioned artefacts.
For comparison:

You do not process the vector field completely, you have to read each vector from the field. Actually you read just each 4th element of the vector
for (j = 0; j < sourceImage.height; j += 4)
for (j = 0; j < sourceImage.height; j++)
Further the computation of the source index is wrong. Note the control variable for the row (jj) has to be multiplied by the height. The index of the pixel in the array has to be multiplied by 4, because each pixel consists of 4 color channels:
ii * sourceImage.width + jj
(jj * sourceImage.width + ii) * 4
The computation of the target index is wrong, too:
index = (n * sourceImage.width + m) * 4;
index = (m * sourceImage.width + n) * 4;
Note, result contains 1 element for each pixel, byut sourceImage.pixels contains 4 elements for each pixel. Thus the index which reads from result and the index which access the target are different:
let result_i = m * sourceImage.width + n;
let target_i = result_i * 4;
For instance:
let result = [];
for (let j = 0; j < sourceImage.height; j++) {
for (let i = 0; i < sourceImage.width; i++) {
let res = vectorField[i][j];
let ii = constrain(floor(i + res.x), 0, sourceImage.width - 1);
let jj = constrain(floor(j + res.y), 0, sourceImage.height - 1);
let source_i = (jj * sourceImage.width + ii) * 4;
let col = color(
sourceImage.pixels[source_i],
sourceImage.pixels[source_i + 1],
sourceImage.pixels[source_i + 2],
sourceImage.pixels[source_i + 3]);
result.push(col);
}
}
for(let m = 0; m < sourceImage.height; m++) {
for (let n = 0; n < sourceImage.width; n++) {
let result_i = m * sourceImage.width + n;
let target_i = result_i * 4;
let col = result[result_i];
sourceImage.pixels[target_i] = red(col);
sourceImage.pixels[target_i + 1] = green(col);
sourceImage.pixels[target_i + 2] = blue(col);
sourceImage.pixels[target_i + 3] = alpha(col);
}
}

Related

Array length is showing incorrect

I'm confused about why this is not working: when I print the array lengths for two exact arrays, holding graphics objects, they are different. I'm using Javascript. Can someone pls help? Thanks.
Here's the code:
for (var i = 0; i < 4; i++){
for (var j = 0; j < 4; j++){
var tile = new Circle(30);
tile.setPosition(i * 3 * tile.getRadius() + tile.getRadius() * 2, j * 3 * tile.getRadius() + tile.getRadius() * 2);
tile.setColor(colors[Randomizer.nextInt(0, colors.length - 1)]);
add(tile);
} tiles.push(tile);
}
println(tiles.length);
for (var r = 0; r < rows; r++){
for (var c = 0; c < cols; c++){
var black = new Circle(30);
black.setPosition(r * 3 * black.getRadius() + black.getRadius() * 2, c * 3 * black.getRadius() + black.getRadius() * 2);
black.setColor(Color.black);
black.isFilled = false;
after.push(black);
}
}
println(after.length);

Find the position of an element in matrix

You have a matrix N*N. You move from trough the matrix by spiral. Now that you know how many steps you have taken, you want to know your position in the matrix.
Input/Output
[input] integer size
The size of the room.
[input] integer steps
The number of steps you have taken(1-based).
[output] an integer array
Array of length two, describing your position in the room(0-based).
Example
For size = 5 and steps = 20, the output should be [2, 3].
Your path can be shown as the following figure:
[ 1, 2, 3, 4, 5],
[16, 17, 18, 19, 6],
[15, x, x, 20, 7],
[14, x, x, x, 8],
[13, 12, 11, 10, 9]
The 20th step brought you to the second line and third column (0-based), so the answer is [2, 3].
I build a solution that can build such matrix for me
const initMatrix = size => {
const matrix = [];
for (var i = 0; i < size; i++) {
matrix[i] = new Array(size);
}
return matrix;
};
const blindfolded = (size, steps) => {
const matrix = initMatrix(size);
let nc = size;
let num = 1;
for (let z = 0; z < nc; z++) {
for (let i = z; i < nc; i++) {
matrix[z][i] = num;
num++;
}
for (let i = z + 1; i < nc; i++) {
matrix[i][nc - 1] = num;
num++;
}
for (let i = nc - 2; i >= z; i--) {
matrix[nc - 1][i] = num;
num++;
}
for (let i = nc - 2; i >= z + 1; i--) {
matrix[i][z] = num;
num++;
}
nc--;
}
for (let i = 0; i < matrix.length; i++) {
for (let j = 0; j < matrix[i].length; j++) {
console.log(matrix[i][j]);
}
}
};
blindfolded(7, 1);
But probably there should be another more optimal algorithm
This can be done in constant time. The spiral looks like this:
To find number of coils for a given steps, one need to
ceil(sqrt(steps)/2 - 0.5) - 1 (1)
Side length on i-th level is 2*i+1, we start count from zero.
Suppose steps = 64 and center is (5,5). Applying (1) one can find that number of full levels is three, so we are at (8,8+1) = (8,9) (bold black dot), and we did 49 steps for it. Then, side length on 4-th level is 2*4+1=9, so subtract 7 vertically, subtract 8, and we don't have any more steps to use, so finally we are at (1,1).
Somehow this is not surprising, because we went through square 8x8.
function calc() {
var steps = window.steps.value;
if (steps <= 0) return "must be positive";
var ci = 5, cj = 5;
if (steps == 1) return ci + ", " + cj;
var level = Math.ceil(Math.sqrt(steps)/2 - 0.5) - 1;
var ri = ci + level;
var rj = cj + level + 1;
var sideFull = 2 * level + 1;
steps -= sideFull * sideFull;
var side = sideFull + 2;
{
var verUp = Math.min(steps, side - 2);
steps -= verUp;
ri -= verUp;
}
{
var horLeft = Math.min(steps, side - 1);
steps -= horLeft;
rj -= horLeft;
}
{
var verDown = Math.min(steps, side - 1);
steps += verDown;
ri -= verDown;
}
{
var horRight = Math.min(steps, side - 1);
rj + horRight;
}
return ri + ", " + rj;
}
Center at (5,5), and steps = <input id="steps" />. <button onclick="answer.value = calc();" >Calc</button><br>
Answer: <input id="answer" disabled="disabled" />

Where is the error in InitializeGrid? It's returning wrong indices

Ok, so I'm trying to code a Rectangle with multiple triangle strips joined together. according to:
http://www.corehtml5.com/trianglestripfundamentals.php
You need to take care of the triangles wrapping around when you have more than one row. However using the suggested algorithm in my code example I'm getting incorrect indice results.
Here is my example, with outputs.
I've tried copy/pasting the suggested algorithm but it doesn't seem to be returning correct results.
// Create the Index Points for the buffer array.
var rows=2;
var cols=3;
var grid = rows*cols;
var offset;
var pos = [];
var index = 0;
var mpOffset = 1;
for (var row = 0; row <= rows; ++row)
{
offsetY = row * (mpOffset / rows);
for (var col = 0; col <= cols; ++col)
{
offsetX = col * (mpOffset / cols);
pos[index+0] = (offsetX);
pos[index+1] = (offsetY);
index+=2;
}
}
log.info("pos="+JSON.stringify(pos)); // <-- Correct working good.
log.info("pos.length="+pos.length);
function initializeGrid(cols,rows)
{
var trianglestrip = [];
var RCvertices=2*cols*(rows-1);
var TSvertices=2*cols*(rows-1)+2*(rows-2);
var numVertices=TSvertices;
var j=0;
for(var i = 1; i <= RCvertices; i += 2)
{
trianglestrip[ j ] = (1 +i)/2;
trianglestrip[ j +1 ] = (cols*2 + i + 1) / 2;
if( trianglestrip[ j +1 ] % cols == 0)
{
if( trianglestrip[ j +1 ] != cols && trianglestrip[ j +1 ] != cols*rows )
{
trianglestrip[ j +2 ] = trianglestrip[ j +1 ];
trianglestrip[ j +3 ] = (1 + i + 2) / 2;
j += 2;
}
}
j += 2;
}
return trianglestrip;
}
var triStrip = initializeGrid(cols,rows);
log.info("triStrip="+JSON.stringify(triStrip)); // <-- Bad Not working.
log.info("triStrip.length="+triStrip.length);
// Generating the actual Point strip.
var actualStrip = [];
for (var i = 0 ; i < triStrip.length; ++i)
{
actualStrip.push(pos[(triStrip[i]-1)*2+0]);
actualStrip.push(pos[(triStrip[i]-1)*2+1]);
}
log.info("actualStrip="+JSON.stringify(actualStrip));
log.info("actualStrip.length="+actualStrip.length);
Indices should be:
1, 5, 2, 6, 3, 7, 4, 8, 8, 5, 5, 9, 6, 10, 7, 11, 8, 12
I ended up re-creating the function to calculate the triangle strip indices. Have not fully tested it but it can re-create the 3x2 grid in the example from the website.
Here is the code:
// This calculates the triangle points in a rectangular triangle strip.
// Used for 3D Webgl texture mapping.
// Copyright Joshua Langley 2019.
var rows=2;
var cols=3;
var grid = rows*cols;
var offset;
var pos = [];
var index = 0;
var mpOffset = 1;
var offsetX, offsetY;
for (var row = 0; row <= rows; ++row)
{
offsetY = row * (mpOffset / rows);
for (var col = 0; col <= cols; ++col)
{
offsetX = col * (mpOffset / cols);
pos[index+0] = (offsetX);
pos[index+1] = (offsetY);
index+=2;
}
}
log.info("pos="+JSON.stringify(pos));
log.info("pos.length="+pos.length);
var rows=rows+1,cols=cols+1; // Important this counting Points not Squares.
var grid = rows*cols;
var offset;
var indices = [];
var indice = 0;
var offset;
var doublePoints = false;
var tPoint, bPoint;
for (var row = 0; row < rows; ++row)
{
for (var col = 0; col < (cols-1); ++col)
{
offset = row * rows + col;
tPoint = offset+1;
bPoint = offset+cols+1;
if (bPoint > grid)
continue;
indices.push(tPoint);
indices.push(bPoint);
if (offset > 0 && (bPoint+1) < grid && (offset+1) % cols == 0)
{
indices.push(bPoint);
indices.push(tPoint+1);
}
}
}
log.info("indices="+JSON.stringify(indices)); // Expected Result
log.info("indices.length="+indices.length);
var actualStrip = [];
for (var i = 0 ; i < indices.length; ++i)
{
actualStrip.push(pos[(indices[i]-1)*2+0]);
actualStrip.push(pos[(indices[i]-1)*2+1]);
}
log.info("actualStrip="+JSON.stringify(actualStrip));
log.info("actualStrip.length="+actualStrip.length);

javascript canvas: draw moving average line with curves

So basically, I want to draw a curved average line over a certain amount of points of a time-series line chart. Like this:
I want it to span the entire length of the chart but I can't figure out how to calculate the start and end points because the average would (I think) be a point in the middle of each section. Looking at a stock chart with moving average you can see what I want to acheive:
I calculate the averages first by splitting the data array up into chunks based on a period of time. So if I start with:
[
{ time: 1, value: 2 },
{ time: 2, value: 4 },
{ time: 3, value: 5 },
{ time: 4, value: 7 },
]
I get to:
var averages = [
{
x: 1.5,
y: 3,
},
{
x: 3.5 (the average time)
y: 6 (the average value)
},
]
This is what I've tried where I end up with an incomplete line, one that doesnt start at the beginning of the chart and doesnt stop at the end, but stars and ends inside the chart at the first average time:
ctx.moveTo((averages[0].x), averages[0].y);
for(var i = 0; i < averages.length-1; i ++)
{
var x_mid = (averages[i].x + averages[i+1].x) / 2;
var y_mid = (averages[i].y + averages[i+1].y) / 2;
var cp_x1 = (x_mid + averages[i].x) / 2;
var cp_x2 = (x_mid + averages[i+1].x) / 2;
ctx.quadraticCurveTo(cp_x1, averages[i].y ,x_mid, y_mid);
ctx.quadraticCurveTo(cp_x2, averages[i+1].y ,averages[i+1].x, averages[i+1].y);
}
ctx.stroke();
How would you do this?
To get a moving mean you need to just get the mean of n points either side of the current sample.
For example
// array of data points
const movingMean = []; // the resulting means
const data = [12,345,123,53,134,...,219]; // data with index representing x axis
const sampleSize = 5;
for(var i = sampleSize; i < data.length - sampleSize; i++){
var total = 0;
for(var j = i- sampleSize; j < i + sampleSize; j++){
total += data[j];
}
movingMean[i] = total / (sampleSize * 2);
}
This method does not pull the mean forward giving the most accurate mean for each data point.
The problem with this method is that you do not get a mean for the first n and last n samples, where n is the number of samples either side of the mean.
You can do an alternative that will pull the mean forward a little but by applying a weighted mean you can reduce the bias a little
for(var i = sampleSize; i < data.length + Math.floor(sampleSize / 4); i++){
var total = 0;
var count = 0;
for(var j = sampleSize; j > 0; j --){
var index = i - (sampleSize - j);
if(index < data.length){
total += data[index] * j; // linear weighting
count += j;
}
}
movingMean[i-Math.floor(sampleSize / 4)] = total / count;
}
This method keeps that mean closer to the current sample end.
The example show a random data set and the two types of means plotted over it. Click to get a new plot. The red line is the moving mean and the blue is the weighted mean. Note how the blue line tends to follow the data a little slow.
The green line is a weighted mean that has a sample range 4 times greater than the other two.
// helper functions
const doFor = (count, callback) => {var i = 0; while (i < count) { callback(i ++) } };
const setOf = (count, callback) => {var a = [],i = 0; while (i < count) { a.push(callback(i ++)) } return a };
const rand = (min, max = min + (min = 0)) => Math.random() * (max - min) + min;
const randG = (dis, min, max) => {var r = 0; doFor(dis,()=>r+=rand(min,max)); return r / dis};
function getMinMax(data){
var min = data[0];
var max = data[0];
doFor(data.length - 1, i => {
min = Math.min(min,data[i+1]);
max = Math.max(max,data[i+1]);
});
var range = max-min;
return {min,max,range};
}
function plotData(data,minMax){
ctx.beginPath();
for(var i = 0; i < data.length; i++){
if(data[i] !== undefined){
var y = (data[i] - minMax.min) / minMax.range;
y = y *(ctx.canvas.height - 2) + 1;
ctx.lineTo(i/2,y);
}
}
ctx.stroke();
}
function getMovingMean(data,sampleSize){
const movingMean = []; // the resulting means
for(var i = sampleSize; i < data.length - sampleSize; i++){
var total = 0;
for(var j = i- sampleSize; j < i + sampleSize; j++){
total += data[j];
}
movingMean[i] = total / (sampleSize * 2);
}
return movingMean[i];
}
function getMovingMean(data,sampleSize){
const movingMean = []; // the resulting means
for(var i = sampleSize; i < data.length - sampleSize; i++){
var total = 0;
for(var j = i- sampleSize; j < i + sampleSize; j++){
total += data[j];
}
movingMean[i] = total / (sampleSize * 2);
}
return movingMean;
}
function getWeightedMean(data,sampleSize){
const weightedMean = [];
for(var i = sampleSize; i < data.length+Math.floor(sampleSize/4); i++){
var total = 0;
var count = 0;
for(var j = sampleSize; j > 0; j --){
var index = i - (sampleSize - j);
if(index < data.length){
total += data[index] * j; // linear weighting
count += j;
}
}
weightedMean[i-Math.floor(sampleSize/4)] = total / count;
}
return weightedMean;
}
const dataSize = 1000;
const sampleSize = 50;
canvas.width = dataSize/2;
canvas.height = 200;
const ctx = canvas.getContext("2d");
function displayData(){
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
var dataPoint = 100;
var distribution = Math.floor(rand(1,8));
var movement = rand(2,20);
const data = setOf(dataSize,i => dataPoint += randG(distribution, -movement, movement));
const movingMean = getMovingMean(data, sampleSize);
const weightedMean = getWeightedMean(data, sampleSize*2);
const weightedMean1 = getWeightedMean(data, sampleSize*8);
var minMax = getMinMax(data);
ctx.strokeStyle = "#ccc";
plotData(data,minMax);
ctx.strokeStyle = "#F50";
plotData(movingMean,minMax);
ctx.strokeStyle = "#08F";
plotData(weightedMean,minMax);
ctx.strokeStyle = "#4C0";
plotData(weightedMean1,minMax);
}
displayData();
document.onclick = displayData;
body { font-family : arial; }
.red { color : #F50; }
.blue { color : #0AF; }
.green { color : #4C0; }
canvas { position : absolute; top : 0px; left :130px; }
<canvas id="canvas"></canvas>
<div class="red">Moving mean</div>
<div class="blue">Weighted mean</div>
<div class="green">Wide weighted mean</div>
<div>Click for another sample</div>

Array loop in javascript

I try to develop a simple program that prints all numbers in between 1 and 100 that divide by 3 without any residual and calculate the total sum
I did it with for loop:
var sum = 0;
for (var i = 3; i <= 100; i = i + 3) {
document.write("<br/>" + i);
sum = sum + i;
}
document.write("<br/>sum = " + sum); //1683
But I failed when I wanted to do it with array:
var numbers = [];
var sum = 0;
for (var i = 0; i <= 100; i = i + 3) {
numbers[i - 1] = i;
}
for (var index = 0; index < 100; index++) {
document.write("<br/>" + numbers[index]);
sum = sum + i;
}
document.write("<br/>sum = " + sum);
Use it like this,
Array indexes should start from 0, that is why I have introduced another variable j=0
var numbers = [];
var sum = 0;
for (var i = 0, j = 0; i <= 100; i = i + 3, ++j) {
numbers[j] = i;
}
Update
First Issue:
In your code, ie. below code of yours,
for (var i = 0; i <= 100; i = i + 3) {
numbers[i - 1] = i;
}
In the first iteration,
i = 0;
numbers[0-1] = i // i.e numbers[-1] = 0;
and in your second loop, you are starting the index from 0
for (var index = 0; index < 100; index++) {
Second issue:
Also, if you don't use a sequential counter to fill the Array, you will end with undefined values for the ones you did not fill.
If you notice, the output after the loop, it says numbers.length = 99 which is wrong it will not have that many items in it.
Third Issue:
In below code, even if you introduce a sequential counter, this is still wrong
for (var i = 0; i <= 100; i = i + 3) {
numbers[i - 1] = i;
}
because i should start with 3 instead of 0, otherwise you will end up with 34 elements in the array because numbers[0] will be 0;
Fourth Issue:
In this code,
for (var index = 0; index < 100; index++) {
document.write("<br/>" + numbers[index]);
sum = sum + i;
}
You don't actually have to loop it till 100, you already have the numbers array filled, so you just need to use numbers.length, like this
var len = numbers.length;
for (var index = 0; index < len; index++) {
document.write("<br/>" + numbers[index]);
sum = sum + i;
}
A better way to write this
var numbers = [];
for (var i = 3, j=0; i <= 100; i = i + 3, j++) {
numbers[j] = i;
}
var sum = numbers.reduce((a, b) => a+b);
console.log(sum);
The line var sum = numbers.reduce((a, b) => a+b); uses Array.reduce() method.
adding number to array
var numbers = [];
for(var i = 3; i <= 100; i = i +3){
numbers.push(i);
}
summation and printing values
var sum = 0;
for (var i = 0; i < numbers.length; i++) {
document.write("<br/>" + numbers[i]);
sum = sum + numbers[i];
}
document.write("<br/>sum = " + sum); //1683
There are few issues in your code.
for (var i = 0; i <= 100; i = i + 3) {
numbers[i - 1] = i;
}
1: array is 0 based. so first insertion into the array goes for a toss.
2: the number array created will have skipping index like 3, 6 ,9
for (var index = 0; index < 100; index++) {
document.write("<br/>" + numbers[index]);
sum = sum + i;
}
3: Here you are iterating index till 100 , you should iterate it till the length of the numbers array only.
when index is 1,2
number[index] will become undefined.
4: sum = sum + i (i ??????)
You should try like this or you can also use push()
var numbers = [];
var sum = 0;
for (var i = 0,j=0; i <= 100; i = i + 3, j= j+1) {
numbers[j] = i; // array is 0 based.
}
for (var index = 0; index < numbers.length; index++) {
document.write("<br/>" + numbers[index]);
sum = sum + numbers[index];
}
document.write("<br/>sum = " + sum);
Indexes in an array begin with zero.
for (var i = 0; i <= 100; i = i + 3) {
numbers[i - 1] = i; // In the first iteration, there will be numbers[-1] = i;
}
You have several issues i suppose.
var numbers = [];
var sum = 0;
for (var i = 0; i <= 100; i = i + 3) {
numbers.push(i);
}
for (var index = 0; index < numbers.length; index++) {
document.write("<br/>" + numbers[index]);
sum = sum + i;
}
document.write("<br/>sum = " + sum);
Also for array you can use:
for (var i in array) {
console.log(array[i]);
}
And I'm pretty sure, that array of number sequence is absolutely useless, if there is no other information in it.
Try this
var numbers = [];
var sum = 0;
for (var i = 0; i <= 100; i = i + 3) {
numbers[(i-3)/3] = i;
}
for (var index = 0; index < numbers.length; index++) {
document.write("<br/>" + numbers[index]);
sum = sum + numbers[index];
}
document.write("<br/>sum = " + sum);
Here is the fiddle i tried
https://jsfiddle.net/4ncgnd7c/
This should work using a single loop
var numbers = [];
var sum = 0;
for (var i = 3; i <= 100; i = i + 3) {
numbers[i] = i;
document.write("<br/>" + i);
sum = sum + i;
}
document.write("<br/>sum = " + sum);

Categories