PAC 2. Tractament d’imatge

La segona PAC d’aquesta assignatura presenta una estructura molt semblant a la primera. En primer lloc es tracta el tema de l’aliasing en les imatges: perquè apareix, com es pot evitar… Després, confeccionar una taula amb 10 aplicacions que facin servir imatges i anotar les característiques més rellevants de les imatges que fan servir aquestes aplicacions (format, resolució, etc.).

Per acabar, el petit programa de Processing que se’ns demanava era la confecció d’una felicitació de Nadal interactiva amb música i efectes sonors,  objectes animats en moviment i transformacions i, finalment, algun tipus d’interacció per part de l’usuari. Com m’interessa especialment la generació procedural d’imatges, vaig investigar com podia crear flocs de neu amb codi i, després de molts intents infructuosos, vaig trobar per la xarxa que algú havia fet uns flocs de neu molt divertits repetint 6 vegades un caràcter aleatori i jugant amb l’opacitat del text. Per programar el codi del sistema de partícules vaig seguir els exemples del llibre “The Nature of Code” de Daniel Shiffman encara que hivaig aplicar força millores, com eliminar mètodes que figuren com deprecats a la versió 3.x de Processing o afegir molta més funcionalitat en el processos de creació de partícules (com per exemple la creació de flocs de neu en lloc de simples cercles, un procés
de naixement de les partícules, etc.). Pel que fa al dibuix dels flocs de neu, vaig fer servir una animació amb una doble matriu de transformació anidada en la que primer es posiciona el floc en la pantalla i després se li assigna una rotació. Cada frame, els flocs actualitzen la seva posició i angle de rotació. A més, el floc en sí mateix es dibuixa repetint una lletra aleatòria amb simetria diedral en 2D (un tema que haviem tractat a l’assignatura Matemàtiques i Física per a Multimèdia). La transparència i el petit cercle central proporcionen unes imatges senzilles però que em varen semblat interessants.

Aquest és el document del treball entregat:

pac2_pere_amengual

I aquest és del codi de l’sketch de Processing de la felicitació de Nadal:

 

import processing.sound.*; // importa llibreria de so
SoundFile cascavell; // declaració objectes so
SoundFile jingle;
PFont flocfont; // declaració objecte tipografia
PImage nadal; // declaració objectes imatges
PImage nuvol;
SistemaDeParticules ps; // declaració classe SistemadePartícules
int maxParticules =20; // nombre màxim de partícules
int nuvolX; // posició x del núvol
float momentCreacioParticulaAnterior; // declaració variables temporals
float darrerClic;

void setup() {
  fullScreen(); // activa mode pantalla completa
  frameRate(30); // frameRate fix per estandaritzar les animacions
  flocfont = createFont("Serif", 250); // creació objecte tipografia
  nadal = loadImage("nadal.png"); // creació i càrrega imatges
  nuvol = loadImage("cloud.png");
  nuvol.resize(displayWidth, displayHeight/2); // canvia les mides del núvol relatives a la pantalla
  ps = new SistemaDeParticules(new PVector(width/2, height/2)); // posició arbitrària només pel constructor
  cascavell = new SoundFile(this, "cascavells.mp3"); // creació i càrrega sons
  jingle = new SoundFile(this, "jingle.mp3");
  jingle.loop(); // un bug de Processing fa actualment només funcioni amb arxius mono
}

void draw() {
  if (millis()<6000) { // aquesta part s'executarà en els primers 6 segons
    fill(25, 37, 124, 130); // l'alpha crea un efecte de motion blur
    rect(0, 0, displayWidth, displayHeight); // substitueix al background
    fill(255); // color blanc del text
    textSize(32); // mides del text
    textAlign(CENTER,CENTER); // alineació del text
    text ("Arrossega el ratolí sobre el núvol per fer caure la neu...", displayWidth/2, displayHeight/2);
  } else {
    fill(25, 37, 124, 150); 
    rect(0, 0, displayWidth, displayHeight);
    image(nuvol, nuvolX, 0); // dibuixa el núvol a les coordenades
    fill(255, 255, 255, 160); 
    image(nadal, 0, displayHeight/5*4, displayWidth, displayHeight/5); // dibuixa el rétol
    ps.origin.set(random(nuvolX+displayWidth/5, nuvolX+displayWidth/1.2), random(displayWidth/10, displayWidth/6.6), 0); // cada frame actualitza l'origen del sistema de partícules
    if (frameCount%120==0) {
      ps.addParticle(); // cada 120 frames afegeix una partícula
    }
    ps.runSistema(); // cada frame executa el mètode runSistema(), que alhora executa el mètode run() de les partícules
  }
}

void mouseDragged() { // quan s'arrossega el ratolí s'executa aquest codi
  if (mouseY<displayHeight/2.8) { // només si s'arrossega en les coordinades del núvol
    nuvolX = mouseX-displayWidth/2;
    if (millis()-momentCreacioParticulaAnterior>100 ) { // no crearà més d'una partícula cada 100 milisegons
      momentCreacioParticulaAnterior=millis();
      ps.addParticle(); // afegeix la partícula
    }
  }
}

void mousePressed() { // quan es fa clic amb el ratolí executa aquest codi
  ps.addParticle(); // afegeix una partícula
  if ( millis()-darrerClic > 3000 && millis()>6000 ) { // torna fer a sonar el so del cascavell només si aquest està en silenci
    cascavell.play();
    darrerClic=millis();
  }
}

 

 

 

// personalització de l'exemple de sistema de partícules de D. Shiffman "The Nature of code". 

class Floc {
        PVector location; // posició
        PVector velocity; // velocitat
        PVector acceleration; // acceleració
        float lifespan; // temps de vida
        float mides; // mides
        float alpha; // transparència
        float naixement; // temps que triga en adquirir màxima opacitat
        float rotacio; // gir sobre l'eix z
        float theta; // angle de rotació
        char c; // lletra que forma el floc

        Floc(PVector l) { // constructor
                acceleration = new PVector(0, displayWidth/800*0.03); // relativa a mides pantalla
                velocity = new PVector(random(-displayWidth/800, displayWidth/800), random(displayWidth/800*-0.01, 0));
                location = l.copy(); // obté una còpia del vector l, a Processing 2.x era .get() 
                lifespan = 255.0; // valor inicial del temps de vida, també defineix transparència
                c = (char) int(random(33, 127)); // lletra que formarà el floc de neu
                mides = random(displayWidth/800*40, displayWidth/800*100); // mides relatives a pantalla
                naixement = random(200, 250);// temps del fade-in
                rotacio=0; 
                theta=random(-0.02,0.02); // angle de rotació aleatori
        }

        void run() {  // aquest mètode és cridat pel sistema de partícules
                update();
                display();
        }


        void update() { // actualitza la posició del floc i la seva transparència
                velocity.add(acceleration); // la velocitat adquereix acceleració i canvia de valor
                location.add(velocity); // la posició adquereix velocitat i canvia de valor
                lifespan -= 1.5; // controla la transparència i també ho feim servir per saber si una partícula està viva
                alpha = (lifespan > naixement) ? map (lifespan, 255, naixement, 0, 255) : map (lifespan, naixement, 0, 255, 0); // fa creixer o decreixer la transparència
        }


        void display() { // mostra el floc en pantalla 
                stroke(255, alpha); // la transparència és variable en la vida del floc
                strokeWeight(2);
                fill(255, 255, 255, alpha);
                ellipse(location.x, location.y, 12, 12); // petit cercle central
                textFont(flocfont, mides); // assigna mides tipografia del floc
                pushMatrix(); // matriu de transformació per posició i rotació del floc
                translate(location.x, location.y); 
                rotate(rotacio);
                rotacio=rotacio+theta;
                pushMatrix(); // matriu de transformació anidada per crear el floc de neu repetint la lletra 6 vegades
                translate(0, 0);
                for (int i=0; i<6; i++) {
                        textAlign(CENTER);
                        rotate(PI*1/3);
                        text(c, 0, 0);
                }
                popMatrix();
                popMatrix();
        }


        boolean isDead() {   // El floc ja no és visible (és transparent)? mètode que fa servir el sistema de partícules
                if (lifespan < 0.0) {
                        return true;
                } else {
                        return false;
                }
        }
}

 

 

 

class SistemaDeParticules {
        ArrayList<Floc> particles; // declaració de la arraylist que contrindrá objectes Floc
        PVector origin; // origen del sistema de partícules

        SistemaDeParticules(PVector location) { // constructor de la classe
                origin = location.get(); // pren la posició inicial de quan hem creat el sistema
                particles = new ArrayList<Floc>(); // creació de la arraylist
        }

        void addParticle() { // el programa principal crida aquest mètode per afegir partícules
                if (maxParticules>particles.size()) { // només afegim partícules noves si no hem superat el límit
                        particles.add(new Floc(origin)); // cada frame origin s'actualitza segons la posició actual del ratolí
                }
        }

        void runSistema() { // el programa principal crida aquest mètode cada frame
                for (int i = particles.size()-1; i >= 0; i--) { // per cada un dels elements de l'arraylist
                        Floc f = particles.get(i);   // assignem la partícula a una variable temporal
                        f.run(); // executem el mètode run per aquesta partícula
                        if (f.isDead()) { // comprovem si aquesta partícula està morta per eliminar-la
                                particles.remove(i);
                        }
                }
        }
}

 

Leave a Reply