| @@ -2343,6 +2343,12 @@ | |||
| "resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-0.0.34.tgz", | |||
| "integrity": "sha1-6nrd907Ow9dimCegw54smt3HPQQ=" | |||
| }, | |||
| "@types/dom-mediacapture-record": { | |||
| "version": "1.0.7", | |||
| "resolved": "https://registry.npmjs.org/@types/dom-mediacapture-record/-/dom-mediacapture-record-1.0.7.tgz", | |||
| "integrity": "sha512-ddDIRTO1ajtbxaNo2o7fPJggpN54PZf1ZUJKOjto2ENMJE/9GKUvaw3ZRuQzlS/p0E+PnIcssxfoqYJ4yiXSBw==", | |||
| "dev": true | |||
| }, | |||
| "@types/glob": { | |||
| "version": "7.1.3", | |||
| "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", | |||
| @@ -4909,6 +4915,21 @@ | |||
| "timsort": "^0.3.0" | |||
| } | |||
| }, | |||
| "css-line-break": { | |||
| "version": "1.1.1", | |||
| "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-1.1.1.tgz", | |||
| "integrity": "sha512-1feNVaM4Fyzdj4mKPIQNL2n70MmuYzAXZ1aytlROFX1JsOo070OsugwGjj7nl6jnDJWHDM8zRZswkmeYVWZJQA==", | |||
| "requires": { | |||
| "base64-arraybuffer": "^0.2.0" | |||
| }, | |||
| "dependencies": { | |||
| "base64-arraybuffer": { | |||
| "version": "0.2.0", | |||
| "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz", | |||
| "integrity": "sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ==" | |||
| } | |||
| } | |||
| }, | |||
| "css-loader": { | |||
| "version": "3.5.3", | |||
| "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.5.3.tgz", | |||
| @@ -5556,6 +5577,11 @@ | |||
| "resolved": "https://registry.npmjs.org/dom-storage/-/dom-storage-2.1.0.tgz", | |||
| "integrity": "sha512-g6RpyWXzl0RR6OTElHKBl7nwnK87GUyZMYC7JWsB/IA73vpqK2K6LT39x4VepLxlSsWBFrPVLnsSR5Jyty0+2Q==" | |||
| }, | |||
| "dom-to-image": { | |||
| "version": "2.6.0", | |||
| "resolved": "https://registry.npmjs.org/dom-to-image/-/dom-to-image-2.6.0.tgz", | |||
| "integrity": "sha1-ilA2CAiMh7HCL5A0rgMuGJiVWGc=" | |||
| }, | |||
| "domain-browser": { | |||
| "version": "1.2.0", | |||
| "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", | |||
| @@ -7207,6 +7233,14 @@ | |||
| "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", | |||
| "dev": true | |||
| }, | |||
| "html2canvas": { | |||
| "version": "1.0.0-rc.7", | |||
| "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.0.0-rc.7.tgz", | |||
| "integrity": "sha512-yvPNZGejB2KOyKleZspjK/NruXVQuowu8NnV2HYG7gW7ytzl+umffbtUI62v2dCHQLDdsK6HIDtyJZ0W3neerA==", | |||
| "requires": { | |||
| "css-line-break": "1.1.1" | |||
| } | |||
| }, | |||
| "htmlparser2": { | |||
| "version": "3.10.1", | |||
| "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", | |||
| @@ -11,6 +11,11 @@ | |||
| "lint": "ng lint", | |||
| "e2e": "ng e2e" | |||
| }, | |||
| "browser": { | |||
| "fs": false, | |||
| "os": false, | |||
| "path": false | |||
| }, | |||
| "private": true, | |||
| "dependencies": { | |||
| "@angular/common": "~10.0.0", | |||
| @@ -28,10 +33,12 @@ | |||
| "@ionic-native/status-bar": "^5.0.0", | |||
| "@ionic/angular": "^5.0.0", | |||
| "cordova-res": "^0.15.2", | |||
| "dom-to-image": "^2.6.0", | |||
| "face-api.js": "^0.22.2", | |||
| "faker": "^5.1.0", | |||
| "firebase": "^8.2.6", | |||
| "hammerjs": "^2.0.8", | |||
| "html2canvas": "^1.0.0-rc.7", | |||
| "moment": "^2.29.1", | |||
| "rxjs": "~6.5.5", | |||
| "sharp": "^0.27.0", | |||
| @@ -47,6 +54,7 @@ | |||
| "@angular/compiler-cli": "~10.0.0", | |||
| "@angular/language-service": "~10.0.0", | |||
| "@ionic/angular-toolkit": "^2.3.0", | |||
| "@types/dom-mediacapture-record": "^1.0.7", | |||
| "@types/jasmine": "~3.5.0", | |||
| "@types/jasminewd2": "~2.0.3", | |||
| "@types/node": "^12.11.1", | |||
| @@ -1,9 +1,21 @@ | |||
| <ion-content> | |||
| <div class="container"> | |||
| <button class="back-button" (click)="back()"> <ion-icon name="close-outline"></ion-icon> </button> | |||
| <div class="container" #container id="container"> | |||
| <img class="glass-image" [ngStyle]="glassProperties" src="/assets/ar-accessories/glass.png" alt=""/> | |||
| <video id="playback-video" #videoElement autoPlay></video> | |||
| <button *ngIf="areNeuralNetsLoaded" (click)="getCameraStream()">Get access</button> | |||
| <button *ngIf="areNeuralNetsLoaded" (click)="stopCameraStream()">Stop stream</button> | |||
| <button *ngIf="areNeuralNetsLoaded" (click)="toggleDetection()">{{ isDetecting ? 'Stop detection' : 'Detect and draw' }}</button>} | |||
| <video id="playback-video" width="{{ width }}" height="{{ height }}" #videoElement autoPlay></video> | |||
| </div> | |||
| <img [src]="temp" *ngIf="temp" #temp class="test-img" width="100px" height="100px" alt=""> | |||
| <div class="action-buttons"> | |||
| <!-- <button *ngIf="areNeuralNetsLoaded" (click)="stopCameraStream()">Stop stream</button> --> | |||
| <!-- <button *ngIf="areNeuralNetsLoaded" (click)="toggleDetection()">{{ isDetecting ? 'Stop detection' : 'Detect and draw' }}</button> --> | |||
| <button class="camera-button" (click)="capture()"> <ion-icon name="camera"></ion-icon> </button> | |||
| <button class="skin-button" *ngIf="areNeuralNetsLoaded" (click)="toggleDetection()" | |||
| [ngClass]="{'active' : isDetecting }"> <ion-icon name="glasses"></ion-icon> </button> | |||
| </div> | |||
| </ion-content> | |||
| @@ -1,5 +1,32 @@ | |||
| @import '../colors'; | |||
| .test-img { | |||
| position: fixed; | |||
| z-index: 1; | |||
| left: 50%; | |||
| top: 50%; | |||
| } | |||
| .back-button { | |||
| position: fixed; | |||
| top: 10px; | |||
| right: 10px; | |||
| width: 40px; | |||
| height: 40px; | |||
| background: rgba(white, 0.5); | |||
| border: 0px; | |||
| border-radius: 50%; | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| z-index: 3; | |||
| ion-icon { | |||
| color: white; | |||
| font-size: 20px; | |||
| } | |||
| } | |||
| .container { | |||
| position: relative; | |||
| } | |||
| @@ -13,11 +40,6 @@ | |||
| #playback-video, #result-canvas, #three-container { | |||
| display: block; | |||
| width: 100vw; | |||
| height: 75vw; | |||
| max-width: 600px; | |||
| max-height: 450px; | |||
| } | |||
| #result-canvas, #three-container { | |||
| @@ -39,4 +61,43 @@ | |||
| .right-eye { | |||
| background-color: red; | |||
| } | |||
| .action-buttons { | |||
| position: fixed; | |||
| left: 0; | |||
| bottom: 20px; | |||
| display: flex; | |||
| align-items: center; | |||
| width: 100%; | |||
| button { | |||
| width: 50px; | |||
| height: 50px; | |||
| background-color: white; | |||
| color: $pink; | |||
| border-radius: 50%; | |||
| ion-icon { | |||
| font-size: 25px; | |||
| } | |||
| &.camera-button { | |||
| margin-left: calc(50% - 25px); | |||
| transform: scale(1.5); | |||
| } | |||
| &.skin-button { | |||
| transform: scale(1.2); | |||
| margin-left: 40px; | |||
| background-color: transparent; | |||
| border: 2px solid $pink; | |||
| &.active { | |||
| background-color: $pink; | |||
| color: white; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -1,5 +1,7 @@ | |||
| import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; | |||
| import { detectSingleFace, loadFaceExpressionModel, loadFaceLandmarkModel, loadFaceLandmarkTinyModel, loadFaceRecognitionModel, loadSsdMobilenetv1Model, loadTinyFaceDetectorModel, TinyFaceDetectorOptions } from 'face-api.js'; | |||
| import domtoimage from 'dom-to-image'; | |||
| import { Location } from '@angular/common'; | |||
| @Component({ | |||
| selector: 'app-ar-fan-cam', | |||
| @@ -8,6 +10,7 @@ import { detectSingleFace, loadFaceExpressionModel, loadFaceLandmarkModel, loadF | |||
| }) | |||
| export class ArFanCamPage implements OnInit { | |||
| @ViewChild('videoElement', null) videoElement: ElementRef<HTMLVideoElement>; | |||
| mediaStream: MediaStream|null = null; | |||
| glassProperties: { | |||
| @@ -22,7 +25,14 @@ export class ArFanCamPage implements OnInit { | |||
| width = 360; | |||
| height = 270; | |||
| constructor() { } | |||
| temp: any; | |||
| stream: any; | |||
| constructor( | |||
| private location: Location | |||
| ) { } | |||
| loadNeuralNetModels = async () => { | |||
| await loadTinyFaceDetectorModel('/assets/weights'); | |||
| @@ -37,25 +47,47 @@ export class ArFanCamPage implements OnInit { | |||
| this.areNeuralNetsLoaded = true; | |||
| } | |||
| back() { | |||
| this.location.back(); | |||
| this.stopCameraStream(); | |||
| } | |||
| getCameraStream = async () => { | |||
| const stream = await window.navigator.mediaDevices.getUserMedia({ | |||
| video: { | |||
| facingMode: 'user', | |||
| width: this.width, | |||
| height: this.height | |||
| }, | |||
| }); | |||
| this.mediaStream = stream; | |||
| this.videoElement.nativeElement.srcObject = stream; | |||
| }).then((stream) => { | |||
| this.mediaStream = stream; | |||
| this.videoElement.nativeElement.srcObject = stream; | |||
| }).catch(err => alert(JSON.stringify(err))); | |||
| }; | |||
| stopCameraStream = async () => { | |||
| if (this.mediaStream) { | |||
| this.mediaStream.getVideoTracks().forEach(track => track.stop()); | |||
| this.mediaStream.getVideoTracks().forEach(track => { | |||
| track.stop(); | |||
| }); | |||
| this.mediaStream = null; | |||
| this.stream = null; | |||
| } | |||
| } | |||
| async capture() { | |||
| let element: HTMLElement = document.querySelector('#container'); | |||
| await domtoimage.toPng(element).then((dataUrl) => { | |||
| this.temp = dataUrl; | |||
| console.log(dataUrl); | |||
| }) | |||
| .catch((error) => { | |||
| console.error('oops, something went wrong!', error); | |||
| }); | |||
| } | |||
| detectAndDrawFace = async () => { | |||
| const tinyFaceDetectorOptions = new TinyFaceDetectorOptions(); | |||
| @@ -87,14 +119,19 @@ export class ArFanCamPage implements OnInit { | |||
| } | |||
| } | |||
| ngOnInit() { | |||
| ngOnInit() { | |||
| } | |||
| ngAfterViewInit() { | |||
| this.loadNeuralNetModels(); | |||
| const feedWidth = window.innerWidth > 600 ? 600 : window.innerWidth; | |||
| const feedHeight = feedWidth * 0.75; | |||
| const feedWidth = window.innerWidth; | |||
| const feedHeight = window.innerHeight; | |||
| this.width = feedWidth; | |||
| this.height = feedHeight; | |||
| this.getCameraStream().then(() => this.detectAndDrawFace(), (err) => console.log(err)); | |||
| } | |||
| } | |||
| @@ -87,7 +87,13 @@ export class FanZonePage implements OnInit { | |||
| } | |||
| ngAfterViewInit() { | |||
| this.showSocialLogin = true; | |||
| if (localStorage.googleUserData) { | |||
| this.googleUserData = JSON.parse(localStorage.googleUserData); | |||
| this.showSocialLogin = false; | |||
| } else { | |||
| this.googleUserData = {}; | |||
| this.showSocialLogin = true; | |||
| } | |||
| } | |||
| generateEmojiStream() { | |||
| @@ -153,6 +159,9 @@ export class FanZonePage implements OnInit { | |||
| profileImage: data.user.photoURL, | |||
| credentials: data.credential | |||
| }; | |||
| localStorage.googleUserData = JSON.stringify(this.googleUserData); | |||
| this.showSocialLogin = false; | |||
| }).catch(err => { | |||
| alert(err.message); | |||