@@ -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); | |||