creatyde-Scope
Praxinoscope Hands!
My design is of a moving hand opening then closing. I personally like hands because of their expressive nature. I took about 10 to 12 pictures of my hand against a white background in bright light then used OpenCV to extract the Scharr Edges. I had to mess around with some of the blur and thresholding to get the image of the hand’s shadow to look sharp.
Please click on the link to view the actual praxinoscope design:
Praxinoscope Hands
Here is the associated code:
// Template for KidzLabs/4M/Toysmith Animation Praxinoscope
// https://www.amazon.com/4M-3474-Animation-Praxinoscope/dp/B000P02HYC
// https://www.walmart.com/ip/Animation-Praxinoscope-Science-Kits-by-Toysmith-3474/45681503
// Developed for Processing 3.3.6 * http://processing.org
// 23 January 2018 * Golan Levin
// See information about Processing PDF export at:
// https://processing.org/reference/libraries/pdf/index.html
// PDF generated by Processing can be opened in Adobe Illustrator.
import processing.pdf.*;
// Thanks to OpenCV made by Greg Borenstein
import gab.opencv.*;
PImage img;
OpenCV opencv;
boolean bRecordingPDF = false;
float inch = 72;
float diamArtInner = inch * 1.50;
float diamArtOuter = inch * 4.80;
float diamCutInner = inch * 1.41;
float diamCutOuter = inch * 4.875;
float holeDy = inch * 0.23;
float holeDx = inch * 0.20;
float holeD = inch * 0.1;
final int nFrames = 10;
int myFrameCount = 0;
int exportFrameCount = 0;
boolean bAnimate = true;
boolean bExportFrameImages = false;
//-------------------------------------------------------
void setup() {
size(792, 612); // 11x8.5" at 72DPI
frameRate(15);
smooth();
}
//-------------------------------------------------------
void draw() {
background(240);
if (bRecordingPDF) {
beginRecord(PDF, "praxinoscope-output.pdf");
}
// Do all the drawing.
pushMatrix();
translate(width/2, height/2);
drawCutLines();
drawGuides();
drawAllFrames();
popMatrix();
if (bExportFrameImages) {
// If activated, export .PNG frames
if (exportFrameCount < nFrames) {
String filename = "frame_"
+ nf((exportFrameCount%nFrames), 3) + ".png";
saveFrame("frames/" + filename);
println("Saved: " + filename);
exportFrameCount++;
if (exportFrameCount >= nFrames) {
bExportFrameImages = false;
exportFrameCount = 0;
}
}
}
//-------------------------------------------------------
void keyPressed() {
switch (key) {
case ' ':
// Press spacebar to pause/unpause the animation.
bAnimate = !bAnimate;
break;
case 'p':
case 'P':
// Press 'p' to export a PDF for the Praxinoscope.
bRecordingPDF = true;
break;
case 'f':
case 'F':
// Press 'f' to export .png Frames (to make an animated .GIF)
myFrameCount = 0;
exportFrameCount = 0;
bExportFrameImages = true;
bAnimate = true;
break;
}
}
//-------------------------------------------------------
void drawCutLines() {
fill(0);
textAlign(CENTER, BOTTOM);
text("Praxinoscope Template", 0, 0-diamCutOuter/2-6);
stroke(0);
strokeWeight(1.0);
noFill();
if (!bRecordingPDF) {
fill(255);
}
ellipse(0, 0, diamCutOuter, diamCutOuter);
noFill();
if (!bRecordingPDF) {
fill(240);
}
ellipse(0, 0, diamCutInner, diamCutInner);
noFill();
ellipse(diamCutOuter/2 - holeDx, 0-holeDy, holeD, holeD);
line (diamCutInner/2, 0, diamCutOuter/2, 0);
}
//-------------------------------------------------------
void drawGuides() {
// This function draws the guidelines.
// Don't draw these when we're exporting the PDF.
if (!bRecordingPDF) {
noFill();
stroke(128);
strokeWeight(0.2);
ellipse(0, 0, diamArtInner, diamArtInner);
ellipse(0, 0, diamArtOuter, diamArtOuter);
for (int i=0; i<nFrames; i++) {
float angle = map(i, 0, nFrames, 0, TWO_PI);
float pxi = diamArtInner/2 * cos(angle);
float pyi = diamArtInner/2 * sin(angle);
float pxo = diamArtOuter/2 * cos(angle);
float pyo = diamArtOuter/2 * sin(angle);
stroke(128);
strokeWeight(0.2);
line (pxi, pyi, pxo, pyo);
}
// Draw the red wedge outline, highlighting the main view.
int redWedge = 7; // assuming nFrames = 10
for (int i=redWedge; i<=(redWedge+1); i++) {
float angle = map(i, 0, nFrames, 0, TWO_PI);
float pxi = diamArtInner/2 * cos(angle);
float pyi = diamArtInner/2 * sin(angle);
float pxo = diamArtOuter/2 * cos(angle);
float pyo = diamArtOuter/2 * sin(angle);
stroke(255, 0, 0);
strokeWeight(2.0);
line (pxi, pyi, pxo, pyo);
}
noFill();
stroke(255, 0, 0);
strokeWeight(2.0);
float startAngle = redWedge*TWO_PI/nFrames;
float endAngle = (redWedge+1)*TWO_PI/nFrames;
arc(0, 0, diamArtInner, diamArtInner, startAngle, endAngle);
arc(0, 0, diamArtOuter, diamArtOuter, startAngle, endAngle);
for (int i=0; i<nFrames; i++) {
float angle = map(i, 0, nFrames, 0, TWO_PI);
pushMatrix();
rotate(angle);
float originY = ((diamArtOuter + diamArtInner)/2)/2;
translate(0, 0-originY);
noFill();
stroke(128);
strokeWeight(0.2);
line (-inch/2, 0, inch/2, 0);
line (0, -inch/2, 0, inch/2);
popMatrix();
}
}
}
//-------------------------------------------------------
void drawAllFrames() {
for (int i=0; i<nFrames; i++) {
float angle = map(i, 0, nFrames, 0, TWO_PI);
float originY = ((diamArtOuter + diamArtInner)/2)/2;
pushMatrix();
rotate(angle);
translate(0, 0-originY);
scale(0.8, 0.8); // feel free to ditch this
int whichFrame = i;
if (bAnimate) {
whichFrame = (i+myFrameCount)%nFrames;
}
drawArtFrame (whichFrame);
// drawArtFrameAlternate (whichFrame);
popMatrix();
}
myFrameCount++;
}
//-------------------------------------------------------
void drawArtFrame (int whichFrame) {
// Draw the artwork for a generic frame of the Praxinoscope,
// given the framenumber (whichFrame) out of nFrames.
// NOTE #1: The "origin" for the frame is in the center of the wedge.
// NOTE #2: Remember that everything will appear upside-down!
// Draw the frame number
fill(0);
noStroke();
textAlign(CENTER, CENTER);
text (whichFrame, -1, -47);
img = loadImage("faces_small/image" + whichFrame +".png");
img.resize(75, 75);
opencv = new OpenCV(this, img);
//opencv.threshold(80);
opencv.blur(5);
//opencv.findCannyEdges(40, 75);
opencv.findScharrEdges(OpenCV.HORIZONTAL);
//opencv.findSobelEdges(1,0);
opencv.contrast(1.5);//(int)map(mouseX,0,width,-255,255));
opencv.threshold(175);
opencv.invert();
//opencv.blur(2);
image(opencv.getOutput(),-37.5,-37.5);
/**
// Draw a pulsating ellipse
noFill();
stroke(0);
strokeWeight(1);
float t = map(whichFrame, 0, nFrames, 0, 1);
float diam = map(cos(t*TWO_PI), -1, 1, 25, 50);
ellipse(0, -45, diam, diam*0.8);
// Draw some expanding boxes, centered on the local origin
int nBoxes = 3;
for (int i=0; i<nBoxes; i++) {
float F = ((whichFrame + i*nFrames)/(float)nBoxes)%nFrames;
float rs = map(F, 0, nFrames-1, 0, 35);
float rx = 0;
float ry = 0;
float rg = map(F, 0, nFrames, 0, 255);
stroke(rg);
strokeWeight(1.0);
rect(rx-rs/2, ry-rs/2, rs, rs);
}
// Draw some rotating spokes
int nSpokes = 7;
for (int i=0; i<nSpokes; i++) {
float cx = 0;
float cy = 45;
float u = 0 - map(whichFrame + i*nFrames, 0, nFrames*nSpokes, 0, 1);
float sx = cx + 15 * cos(u * TWO_PI);
float sy = cy + 15 * sin(u * TWO_PI);
stroke(0);
strokeWeight(1);
line (cx, cy, sx, sy);
}
**/
}
//-------------------------------------------------------
void drawArtFrameAlternate(int whichFrame) {
// An alternate drawing test.
// Draw a falling object.
// Draw a little splat on the frame when it hits the ground.
if (whichFrame == (nFrames-1)) {
stroke(0, 0, 0);
strokeWeight(0.5);
int nL = 10;
for (int i=0; i<nL; i++) {
float a = HALF_PI + map(i, 0, nL-1, 0, TWO_PI);
float cx = 12 * cos(a);
float cy = 10 * sin(a);
float dx = 16 * cos(a);
float dy = 13 * sin(a);
line (cx, 45+cy, dx, 45+dy);
}
}
// Draw a little box frame
fill(255);
stroke(0, 0, 0);
strokeWeight(1);
rect(-5, -50, 10, 100);
// Make the puck accelerate downward
float t = map(whichFrame, 0, nFrames-1, 0, 1);
float t2 = pow(t, 2.0);
float rh = 8 + whichFrame * 0.5; // wee stretch
float ry = map(t2, 0, 1, 0, 100-rh) - 50;
noStroke();
fill(0, 0, 0);
rect(-5, ry, 10, rh);
}