PHOTOBOOTH

THE WEB IS YOUR

 

SMILE!

$whoami

Diana Rodríguez

Google Developer Expert

Auth0 Ambassador 

Microsoft MVP

 

Python Developer Advocate @ Vonage

 

🏡  https://superdi.dev

🐦  @cotufa82

 

$whoami-NOT

So what does it do?

 

  • Capture stream from a webcam

  • Snap Pic with countdown (5seg)

  • Auto mode

  • Drawer for captures

  • Click to Download

  • SMS Sharing

TOOLS 🧰

  • Vonage Video API 
  • Vonage Communications API (SMS)
  • Azure App Service
  • Azure Cognitive Services

TALK CODE TO ME

import Vue from 'vue'
import App from './App.vue'
import vuetify from './plugins/vuetify';

Vue.config.productionTip = false

console.log(process.env)

new Vue({
  vuetify,
  render: h => h(App)
}).$mount('#app')

main.js


data: () => ({
    //
    opentok_api_key: ENV.OPENTOK_API_KEY ? ENV.OPENTOK_API_KEY : "",
    opentok_session_id: ENV.OPENTOK_SESSION_ID ? ENV.OPENTOK_SESSION_ID : "",
    opentok_token: ENV.OPENTOK_TOKEN ? ENV.OPENTOK_TOKEN : "",
    azure_face_api_subscription_key: ENV.AZURE_FACE_API_SUBSCRIPTION_KEY
      ? ENV.AZURE_FACE_API_SUBSCRIPTION_KEY
      : "",
    azure_face_api_endpoint: ENV.AZURE_FACE_API_ENDPOINT
      ? ENV.AZURE_FACE_API_ENDPOINT
      : "",
   ………….
  }),
<div id="publisher">
    <v-overlay :absolute="true" :value="counter != 6">
        <div style="font-size:150px;">{{ counter }}</div>
    </v-overlay>
</div>

async initializeSession() {
      var session = OT.initSession(this.opentok_api_key,
        this.opentok_session_id);
      this.publisher = OT.initPublisher("publisher",{insertMode: "append",
       width: 400,height: 300
      },
      handleError
      );

      // Connect to the session
      session.connect(this.opentok_token, error => {
        // If the connection is successful, initialize a publisher and publish to the session
        if (error) {
          handleError(error);
        } else {
          session.publish(this.publisher, handleError);
        }
      });
    }

<v-card-actions>
                <v-btn @click="analyze()" v-if="manual == true" text class="analyze">
                  <v-img :src="require('./assets/snap.png')" contain height="50" />
                </v-btn>
</v-card-actions>

analyze() {
      let imageData = this.publisher.getImgData();
      let blob = this.dataURItoBlob("data:image/png;base64," + imageData);
      //Evaluates image emotion in azure cognitive face service
      const xhr = new XMLHttpRequest();
        xhr.open(
          "POST",
          `${this.azure_face_api_endpoint}face/v1.0/detect?returnFaceId=true&returnFaceLandmarks=false&returnFaceAttributes=emotion`
        );
        xhr.onreadystatechange = () => {
            if (xhr.readyState === 4) {
              let response = xhr.response[0];
            if (response !== null && response !== undefined) {
                ............
               //Emotion is present, so we evaluate happiness factor (between 0 and 1) if happiness is > 0.5 we take the snap
                    if (
                      response.faceAttributes.emotion.happiness !== null &&
                      response.faceAttributes.emotion.happiness !== undefined
                    ) {
                      if (response.faceAttributes.emotion.happiness >= 0.5) {
                        //take the snap and put it in image array
                        this.images.push({
                          id: this.images.length + 1,
                          dataurl: "data:image/png;base64," + image
                        });
                      } else {
                        this.snackbar = true;
                        this.snackbar_message =
                          "Smiling is a requirement. Smile and we take your photo";
                      }
                    }
                  }
     	..............
        };
        xhr.responseType = "json";
        xhr.setRequestHeader("Content-Type", "application/octet-stream");
        xhr.setRequestHeader(
          "Ocp-Apim-Subscription-Key",
          this.azure_face_api_subscription_key
        );
        xhr.send(blob);
      }, 6000);
    }
## App.vue

 
<v-btn
    @click="downloadImages()"
    style="background-color:inherit !important; padding: 0 !important;"
>
   
# MANY LINES AFTER....
      
   forceFileDownload(index, place) {
      let imgs = null;
      if (place == undefined) imgs = this.images;
      else imgs = this.filteredImages;
      let image_file = this.dataURItoBlob(imgs[index - 1].dataurl);
      const url = window.URL.createObjectURL(image_file);
      const link = document.createElement("a");
      link.href = url;
      link.setAttribute("download", "snap_" + index + ".png"); //or any other extension
      link.click();
    },
    downloadImages() {
      for (let i = 0; i < this.images.length; i++) {
        this.forceFileDownload(this.images[i].id);
      }
      for (let f = 0; f < this.filteredImages.length; f++) {
        this.forceFileDownload(this.filteredImages[f].id, "filtered");
      }
    },
# re module is for regular expression ops
import nexmo, re, base64
from flask import Flask, request, jsonify, send_from_directory
from flask_cors import CORS
from dotenv import load_dotenv
from os import environ, makedirs
from os.path import join, dirname, abspath, exists
from datetime import datetime

app = Flask(__name__, static_url_path="", static_folder="dist")
CORS(app)

# 10mb size is allowed
app.config["MAX_CONTENT_LENGTH"] = 10 * 1024 * 1024

# Get env vars from file
envpath = join(dirname(__file__), "./.env")
load_dotenv(envpath)

# Needed for uploading files
basedir = abspath(dirname(__file__))

client = nexmo.Client(
    key=environ.get("NEXMO_API_KEY"), secret=environ.get("NEXMO_API_SECRET")
)

# Verify if snaps directory exists, if not then create it
if not exists("{}/dist/snaps".format(basedir)):
    makedirs("{}/dist/snaps".format(basedir))


@app.route("/", methods=["GET"])
def home():
    return send_from_directory("./dist/", "index.html")


@app.route("/send-mms", methods=["POST"])
def send_mms():
    params = request.get_json() or request.form or request.args
    if "phone" and "image" in params:
        # Get the base64 image
        image = re.sub(r"^data:image\/png;base64,", "", params["image"])
        image = bytes(image, "utf-8")
        phone = params["phone"]
        # Get the current timestamp
        filename_prefix = datetime.utcnow().isoformat()
        filename = "{phone}-{prefix}.png".format(phone=phone, prefix=filename_prefix)
        # Save image
        imagedir = "{}/dist/snaps".format(basedir)
        # Create the binary file in the snaps directory
        imagefile = open(
            "{imagedir}/{filename}".format(imagedir=imagedir, filename=filename), "wb"
        )
        # Write the bytes
        imagefile.write(base64.decodebytes(image))
        # Close file
        imagefile.close()
        # Send sms
        response = client.send_message(
            {
                "from": environ.get("NEXMO_NUMBER"),
                "to": phone,
                "text": "Opentok-Nexmo, Your snap is ready: {site_url}/snaps/{filename}".format(
                    site_url=environ.get("SITE_URL"), filename=filename
                ),
            }
        )
        if response["messages"][0]["status"] == "0":
            return jsonify({"status": "success", "message": "All OK"}), 200
        else:
            return (
                jsonify(
                    {
                        "status": "error",
                        "message": "Message failed with error: "
                        + response["messages"][0]["error-text"],
                    }
                ),
                200,
            )
    else:
        return (
            jsonify(
                {
                    "status": "error",
                    "message": "Required params (phone, image) not provided",
                }
            ),
            200,
        )

# re module is for regular expression ops
import nexmo, re, base64
from flask import Flask, request, jsonify, send_from_directory
from flask_cors import CORS
from dotenv import load_dotenv
from os import environ, makedirs
from os.path import join, dirname, abspath, exists
from datetime import datetime

app = Flask(__name__, static_url_path="", static_folder="dist")
CORS(app)

# 10mb size is allowed
app.config["MAX_CONTENT_LENGTH"] = 10 * 1024 * 1024

# Get env vars from file
envpath = join(dirname(__file__), "./.env")
load_dotenv(envpath)

# Needed for uploading files
basedir = abspath(dirname(__file__))

client = nexmo.Client(
    key=environ.get("NEXMO_API_KEY"), secret=environ.get("NEXMO_API_SECRET")
)

# Verify if snaps directory exists, if not then create it
if not exists("{}/dist/snaps".format(basedir)):
    makedirs("{}/dist/snaps".format(basedir))

@app.route("/", methods=["GET"])
def home():
    return send_from_directory("./dist/", "index.html")


@app.route("/send-mms", methods=["POST"])
def send_mms():
    params = request.get_json() or request.form or request.args
    if "phone" and "image" in params:
        # Get the base64 image
        image = re.sub(r"^data:image\/png;base64,", "", params["image"])
        image = bytes(image, "utf-8")
        phone = params["phone"]
        # Get the current timestamp
        filename_prefix = datetime.utcnow().isoformat()
        filename = "{phone}-{prefix}.png".format(phone=phone, prefix=filename_prefix)
        # Save image
        imagedir = "{}/dist/snaps".format(basedir)
        # Create the binary file in the snaps directory
        imagefile = open(
            "{imagedir}/{filename}".format(imagedir=imagedir, filename=filename), "wb"
        )
        # Write the bytes
        imagefile.write(base64.decodebytes(image))
        # Close file
        imagefile.close()
        # Send sms
        response = client.send_message(
            {
                "from": environ.get("NEXMO_NUMBER"),
                "to": phone,
                "text": "Opentok-Nexmo, Your snap is ready: {site_url}/snaps/{filename}".format(
                    site_url=environ.get("SITE_URL"), filename=filename
                ),
            }
        )
        if response["messages"][0]["status"] == "0":
            return jsonify({"status": "success", "message": "All OK"}), 200
        else:
            return (
                jsonify(
                    {
                        "status": "error",
                        "message": "Message failed with error: "
                        + response["messages"][0]["error-text"],
                    }
                ),
                200,
            )
    else:
        return (
            jsonify(
                {
                    "status": "error",
                    "message": "Required params (phone, image) not provided",
                }
            ),
            200,
        )

DEPLOYMENT

DEMO!

https://opentok-nexmo.azurewebsites.net

https://github.com/opentok-community/opentok-photobooth

MUCHAS GRACIAS!!

@cotufa82

https://superdi.dev

https://slides.com/superdiana/smile

Smile!

By Super Diana

Smile!

A photobooth built with Vonage communication and video APIs!

  • 594