User:Neoksaal/Projects/Interference pattern/Worknote: Interference Pattern/Code to work 1

From neoksaal wiki
Jump to navigation Jump to search

delayed video

example

let capture; // → variable to put video stream in: *createCapture(VIDEO)*
let pastFrames = []; // → array to put frames in, saving live images every frame to create the past data set
const FRAME_RATE = 10;
const DELAY_SECONDS = 60;
let delayFrames = DELAY_SECONDS * FRAME_RATE; // → how many frams in total to save

function setup() {
  createCanvas(640, 480);
  frameRate(FRAME_RATE); // → why is it here not in draw()

  capture = createCapture(VIDEO);
  capture.size(640, 480); // → output the video feed as same as the canvas size
  capture.hide(); // → to hide the automatic feed display so i can show the delayed feed manually on function draw()
}

function draw() {
  // Store the current frame in the array
  pastFrames.push(capture.get()); // → javascript push() adds something to the end of an array, and get() is p5js' and has nothing to do with array

  if (pastFrames.length > delayFrames) {
    pastFrames.shift(); // → javascript shift() removes the first item of an array.
// → if too many images are saved in the array, this removes the oldest one from the front--to make space for new ones
  }

  if (pastFrames.length === delayFrames) { // → 
    image(pastFrames[0], 0, 0, 640, 480); // → [0] refers to the first(oldest) frame in array, since we keep a maximum of delayFrames with shift()function.
// → so [0] is holding the frame from exactly 60 seconds ago
// → image() draws that frame into canvas at x y position 0,0
  }
}

function


this creates a <video></video> element in html that "captures" the audio/video stream from the webcam and microphone. CreateCapture() returns a new p5.MediaElement object. Videos are shown by default. They can be hidden by calling capture.hide() and drawn to the canvas using image().
If VIDEO is passed, as in createCapture(VIDEO), only video will be captured. If AUDIO is passed, as in createCapture(AUDIO), only audio will be captured. A constraints object can also be passed to customize the stream.


Sets the number of frames to draw per second.
Calling frameRate() with one numeric argument, as in frameRate(30), attempts to draw 30 frames per second (FPS). The target frame rate may not be achieved depending on the sketch's processing needs. Most computers default to a frame rate of 60 FPS. Frame rates of 24 FPS and above are fast enough for smooth animations.

Calling frameRate() without an argument returns the current frame rate. The value returned is an approximation.

→ this is confusing, why frameRate() is in setup() not draw()? i thought whatever is in setup() only runs once
↳ canvas size setting makes sense, i guess in setup() you configure global settings here and frameRate is one of them.


The version of get() with four parameters interprets them as coordinates and dimensions. It returns a subsection of the canvas as a p5.Image object. The first two parameters are the coordinates for the upper-left corner of the subsection. The last two parameters are the width and height of the subsection.
Use p5.Image.get() to work directly with p5.Image objects.

capture.get() takes a picture from webcam. makes a new image from what webcam sees at the moment. get() is a p5js function that copies the current webcam image.
capture.get()은 웹캠의 이미지를 가져와서 p5.Image 오브젝트로 저장하는 것


↳ so it can be written differently like this :
function draw() {
let nowCopy = capture.get();
frames.push(nowCopy);




logic

  1. frames from webcam is captured in realtime
  2. frames are stored in array, keeping the latest 600 frames (10frames x 60 seconds)
  3. once 600 frames are accumulated, the code displays the frame from exactly 60 seconds ago




variation 1 - two channel display

lets display both past and present feed juxtaposed on screen


let capture;
let currentFrame; 
let pastFrames = [];
const FRAME_RATE = 10;
const DELAY_SECONDS = 60;
let delayFrames = DELAY_SECONDS * FRAME_RATE;

function setup() {
  createCanvas(640, 480);
  frameRate(FRAME_RATE);
  capture = createCapture(VIDEO);
  capture.size(640, 480);
  capture.hide();
}

function draw() {
  currentFrame = capture.get();

  pastFrames.push(currentFrame);

  if (pastFrames.length > delayFrames) {
    pastFrames.shift();
  }

  image(currentFrame, 0, 0, 320, 240);      
  if (pastFrames.length === delayFrames) {
    image(pastFrames[0], 320, 0, 320, 240);     
  }
}






ascii video

example

const density = "Ñ@#W$9876543210?!abc;:+=-,._          ";

let video;
let asciiDiv;

        function setup() {
          asciiDiv = createDiv();
          noCanvas();
          video = createCapture(VIDEO);
          video.size(64, 48);
        }

        function draw() {
          video.loadPixels();
          let asciiImage = "";
          for (let j = 0; j < video.height; j++) {
            for (let i = 0; i < video.width; i++) {
              const pixelIndex = (i + j * video.width) * 4;
              const r = video.pixels[pixelIndex + 0];
              const g = video.pixels[pixelIndex + 1];
              const b = video.pixels[pixelIndex + 2];
              const avg = (r + g + b) / 3;
              const len = density.length;
              const charIndex = floor(map(avg, 0, 255, 0, len));
              const c = density.charAt(charIndex);
              if (c == " ") asciiImage += "&nbsp;";
              else asciiImage += c;
            }
            asciiImage += '<br/>';
          }
          asciiDiv.html(asciiImage);
        }



function

const density = "Ñ@#W$9876543210?!abc;:+=-,._          "; // → string of characters, total of 39
// → later it map brightness from the left--0(darkest) to right--255(brightest) of the string

let video;
let asciiDiv;

        function setup() {
          asciiDiv = createDiv();
          noCanvas(); // → disabling the p5js default canvas, it'll displat output in a seperate html div
          video = createCapture(VIDEO);
          video.size(64, 48); // → 64 x 48 = 3072 pixels ..will it be enough for me/.
        }

Creates a div element.
elements are commonly used as containers for other elements.
The parameter html is optional. It accepts a string that sets the inner HTML of the new div element.


Removes the default canvas.
By default, a 100×100 pixels canvas is created without needing to call createCanvas(). noCanvas() removes the default canvas for sketches that don't need it.


        function draw() {
          video.loadPixels(); // → .loadPixels() has to be called before accessing video.pixels[] array. this function updates the pixel array with current frame
          let asciiImage = ""; // → here "" means an empty string, at first string has nothing in it

          // ↓ a loop that access each pixels using x=i, y=j coordinates
          // ↓ this 64 x 48 pixel grid has 48 rows of j, and 64 columns of i
          for (let j = 0; j < video.height; j++) {
            for (let i = 0; i < video.width; i++) {
             // → this is to go over every grid and read its brightness
             // → this goes over 64 columns left to right in each row, and then moves to the next row downward--starting from top left corner to bottom right corner

              // ↓ processing the pixel index, each pixels has 4 values--RGBA, hence times 4
              const pixelIndex = (i + j * video.width) * 4; 
              // ↳ total number of pixels here are 64 x 48 = 3072, each pixel has 4 number of values, so 3072 x 4 = 12288 values

              // ↓ extracting RBG value from pixel number
              const r = video.pixels[pixelIndex + 0];
              const g = video.pixels[pixelIndex + 1];
              const b = video.pixels[pixelIndex + 2];
              const avg = (r + g + b) / 3; // → average brightness, but why leave out Alpha?
              // ↳ Alpha, which is opacity, has nothing to do with detecting brightness in this case. makes sense.

              // ↓ this is to pick out the according character from the density string, by using average value.
              const len = density.length; // → .length shows how many characters in density string ""
              const charIndex = floor(map(avg, 0, 255, 0, len)); // → same as Math.floor() in javascript, which removes decimal
              const c = density.charAt(charIndex); // → charAt(n) retrieves the character at according index from the string
              // ↳ c returns the character selected from the density string
              if (c == " ") asciiImage += "&nbsp;"; // → if the selected character from density string is a space, then replace it with $nbsp to display properly in html
              else asciiImage += c; // → += means append the value
            }
            asciiImage += '<br/>'; // → when the inner loop(one row of pixels--one line of characters) is done, add a linebreak and start over from the outer loop(moving on to the next row)
          }
          asciiDiv.html(asciiImage); // → put the strings into the div
        }

Loads the current value of each pixel on the canvas into the pixels array.
loadPixels() must be called before reading from or writing to pixels.


An array containing the color of each pixel on the canvas.
Colors are stored as numbers representing red, green, blue, and alpha (RGBA) values. pixels is a one-dimensional array for performance reasons.


The length data property of a String value contains the length of the string in UTF-16 code units.


Re-maps a number from one range to another.
For example, calling map(2, 0, 10, 0, 100) returns 20. The first three arguments set the original value to 2 and the original range from 0 to 10. The last two arguments set the target range from 0 to 100. 20's position in the target range [0, 100] is proportional to 2's position in the original range [0, 10].
The sixth parameter, within Bounds, is optional. By default, map() can return values outside of the target range. For example, map(11, 0, 10, 0, 100) returns 110. Passing true as the sixth parameter constrains the remapped value to the target range. For example, map(11, 0, 10, 0, 100, true) returns 100.


The charAt() method returns the character at the specified index in a string.
The index of the first character is 0, the second character is 1, and so on.

Sets the inner HTML of the element, replacing any existing HTML.
The second parameter, append, is optional. If true is passed, as in myElement.html('hi', true), the HTML is appended instead of replacing existing HTML.

If no arguments are passed, as in myElement.html(), the element's inner HTML is returned.




logic

  1. no need for default p5js canvas, output is rendered as div element in html
  2. (this was the most obvious but somehow still a shocking discovery) the actual output is a 1d string, with line breaks to create a visual of a 2d grid
    1. first go over the image from top left pixel and scan each row from left to right, and then move downward and do it over row by row
    2. read r g b values of each scanned pixels and calculate the average value to determine brightness
    3. determine the total number of characters in density scale string
    4. map the cacluated brightness to corresponding character in density scale string
    5. if there's a space in density scale characters, make sure html can read it correctly
    6. append those mapped characters to the ascii string
    7. line break when one inner loop(each row) is done



still don't understand how exactly loadPixels() read r g b values but let's just leave it to that for now.