Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
F
face
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Иван Кубота
face
Commits
18f23a1c
Commit
18f23a1c
authored
Jun 08, 2018
by
vincent
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fixed memory leaks + accept Tensors and HTMLCanvasElement as inputs
parent
3ca0f4af
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
104 additions
and
26 deletions
+104
-26
FaceDetectionResult.ts
src/faceDetectionNet/FaceDetectionResult.ts
+36
-0
index.ts
src/faceDetectionNet/index.ts
+59
-24
utils.ts
src/utils.ts
+9
-2
No files found.
src/faceDetectionNet/FaceDetectionResult.ts
0 → 100644
View file @
18f23a1c
import
{
FaceDetectionNet
}
from
'./types'
;
export
class
FaceDetectionResult
{
private
score
:
number
private
top
:
number
private
left
:
number
private
bottom
:
number
private
right
:
number
constructor
(
score
:
number
,
top
:
number
,
left
:
number
,
bottom
:
number
,
right
:
number
)
{
this
.
score
=
score
this
.
top
=
Math
.
max
(
0
,
top
),
this
.
left
=
Math
.
max
(
0
,
left
),
this
.
bottom
=
Math
.
min
(
1.0
,
bottom
),
this
.
right
=
Math
.
min
(
1.0
,
right
)
}
public
forSize
(
width
:
number
,
height
:
number
):
FaceDetectionNet
.
Detection
{
return
{
score
:
this
.
score
,
box
:
{
top
:
this
.
top
*
height
,
left
:
this
.
left
*
width
,
bottom
:
this
.
bottom
*
height
,
right
:
this
.
right
*
width
}
}
}
}
\ No newline at end of file
src/faceDetectionNet/index.ts
View file @
18f23a1c
...
@@ -2,12 +2,12 @@ import * as tf from '@tensorflow/tfjs-core';
...
@@ -2,12 +2,12 @@ import * as tf from '@tensorflow/tfjs-core';
import
{
isFloat
}
from
'../utils'
;
import
{
isFloat
}
from
'../utils'
;
import
{
extractParams
}
from
'./extractParams'
;
import
{
extractParams
}
from
'./extractParams'
;
import
{
FaceDetectionResult
}
from
'./FaceDetectionResult'
;
import
{
mobileNetV1
}
from
'./mobileNetV1'
;
import
{
mobileNetV1
}
from
'./mobileNetV1'
;
import
{
resizeLayer
}
from
'./resizeLayer'
;
import
{
predictionLayer
}
from
'./predictionLayer'
;
import
{
outputLayer
}
from
'./outputLayer'
;
import
{
nonMaxSuppression
}
from
'./nonMaxSuppression'
;
import
{
nonMaxSuppression
}
from
'./nonMaxSuppression'
;
import
{
FaceDetectionNet
}
from
'./types'
;
import
{
outputLayer
}
from
'./outputLayer'
;
import
{
predictionLayer
}
from
'./predictionLayer'
;
import
{
resizeLayer
}
from
'./resizeLayer'
;
function
fromData
(
input
:
number
[]):
tf
.
Tensor4D
{
function
fromData
(
input
:
number
[]):
tf
.
Tensor4D
{
const
pxPerChannel
=
input
.
length
/
3
const
pxPerChannel
=
input
.
length
/
3
...
@@ -21,6 +21,7 @@ function fromData(input: number[]): tf.Tensor4D {
...
@@ -21,6 +21,7 @@ function fromData(input: number[]): tf.Tensor4D {
}
}
function
fromImageData
(
input
:
ImageData
[])
{
function
fromImageData
(
input
:
ImageData
[])
{
return
tf
.
tidy
(()
=>
{
const
idx
=
input
.
findIndex
(
data
=>
!
(
data
instanceof
ImageData
))
const
idx
=
input
.
findIndex
(
data
=>
!
(
data
instanceof
ImageData
))
if
(
idx
!==
-
1
)
{
if
(
idx
!==
-
1
)
{
throw
new
Error
(
`expected input at index
${
idx
}
to be instanceof ImageData`
)
throw
new
Error
(
`expected input at index
${
idx
}
to be instanceof ImageData`
)
...
@@ -31,9 +32,12 @@ function fromImageData(input: ImageData[]) {
...
@@ -31,9 +32,12 @@ function fromImageData(input: ImageData[]) {
.
map
(
data
=>
tf
.
expandDims
(
data
,
0
))
as
tf
.
Tensor4D
[]
.
map
(
data
=>
tf
.
expandDims
(
data
,
0
))
as
tf
.
Tensor4D
[]
return
tf
.
cast
(
tf
.
concat
(
imgTensors
,
0
),
'float32'
)
return
tf
.
cast
(
tf
.
concat
(
imgTensors
,
0
),
'float32'
)
})
}
}
function
padToSquare
(
imgTensor
:
tf
.
Tensor4D
):
tf
.
Tensor4D
{
function
padToSquare
(
imgTensor
:
tf
.
Tensor4D
):
tf
.
Tensor4D
{
return
tf
.
tidy
(()
=>
{
const
[
_
,
height
,
width
]
=
imgTensor
.
shape
const
[
_
,
height
,
width
]
=
imgTensor
.
shape
if
(
height
===
width
)
{
if
(
height
===
width
)
{
return
imgTensor
return
imgTensor
...
@@ -45,10 +49,25 @@ function padToSquare(imgTensor: tf.Tensor4D): tf.Tensor4D {
...
@@ -45,10 +49,25 @@ function padToSquare(imgTensor: tf.Tensor4D): tf.Tensor4D {
}
}
const
pad
=
tf
.
fill
([
1
,
width
-
height
,
width
,
3
],
0
)
as
tf
.
Tensor4D
const
pad
=
tf
.
fill
([
1
,
width
-
height
,
width
,
3
],
0
)
as
tf
.
Tensor4D
return
tf
.
concat
([
imgTensor
,
pad
],
1
)
return
tf
.
concat
([
imgTensor
,
pad
],
1
)
})
}
}
function
getImgTensor
(
input
:
ImageData
|
ImageData
[]
|
number
[])
{
function
getImgTensor
(
input
:
tf
.
Tensor
|
HTMLCanvasElement
|
ImageData
|
ImageData
[]
|
number
[])
{
return
tf
.
tidy
(()
=>
{
return
tf
.
tidy
(()
=>
{
if
(
input
instanceof
HTMLCanvasElement
)
{
return
tf
.
cast
(
tf
.
expandDims
(
tf
.
fromPixels
(
input
),
0
),
'float32'
)
as
tf
.
Tensor4D
}
if
(
input
instanceof
tf
.
Tensor
)
{
const
rank
=
input
.
shape
.
length
if
(
rank
!==
3
&&
rank
!==
4
)
{
throw
new
Error
(
'input tensor must be of rank 3 or 4'
)
}
return
tf
.
cast
(
rank
===
3
?
tf
.
expandDims
(
input
,
0
)
:
input
,
'float32'
)
as
tf
.
Tensor4D
}
const
imgDataArray
=
input
instanceof
ImageData
const
imgDataArray
=
input
instanceof
ImageData
?
[
input
]
?
[
input
]
...
@@ -58,11 +77,9 @@ function getImgTensor(input: ImageData|ImageData[]|number[]) {
...
@@ -58,11 +77,9 @@ function getImgTensor(input: ImageData|ImageData[]|number[]) {
:
null
:
null
)
)
return
padToSquare
(
return
imgDataArray
!==
null
imgDataArray
!==
null
?
fromImageData
(
imgDataArray
)
?
fromImageData
(
imgDataArray
)
:
fromData
(
input
as
number
[])
:
fromData
(
input
as
number
[])
)
})
})
}
}
...
@@ -85,31 +102,47 @@ export function faceDetectionNet(weights: Float32Array) {
...
@@ -85,31 +102,47 @@ export function faceDetectionNet(weights: Float32Array) {
})
})
}
}
function
forward
(
input
:
ImageData
|
ImageData
[]
|
number
[])
{
function
forward
(
input
:
tf
.
Tensor
|
ImageData
|
ImageData
[]
|
number
[])
{
return
tf
.
tidy
(
return
tf
.
tidy
(
()
=>
forwardTensor
(
padToSquare
(
getImgTensor
(
input
)))
()
=>
forwardTensor
(
padToSquare
(
getImgTensor
(
input
)))
)
)
}
}
async
function
locateFaces
(
async
function
locateFaces
(
input
:
ImageData
|
ImageData
[]
|
number
[],
input
:
tf
.
Tensor
|
HTMLCanvasElement
|
ImageData
|
ImageData
[]
|
number
[],
minConfidence
:
number
=
0.8
,
minConfidence
:
number
=
0.8
,
maxResults
:
number
=
100
,
maxResults
:
number
=
100
,
):
Promise
<
FaceDetection
Net
.
Detection
[]
>
{
):
Promise
<
FaceDetection
Result
[]
>
{
const
imgTensor
=
getImgTensor
(
input
)
const
[
_
,
height
,
width
]
=
imgTensor
.
shape
let
paddedHeightRelative
=
1
,
paddedWidthRelative
=
1
const
{
const
{
boxes
:
_boxes
,
boxes
:
_boxes
,
scores
:
_scores
scores
:
_scores
}
=
forwardTensor
(
imgTensor
)
}
=
tf
.
tidy
(()
=>
{
let
imgTensor
=
getImgTensor
(
input
)
const
[
_
,
height
,
width
]
=
imgTensor
.
shape
imgTensor
=
padToSquare
(
imgTensor
)
paddedHeightRelative
=
imgTensor
.
shape
[
1
]
/
height
paddedWidthRelative
=
imgTensor
.
shape
[
2
]
/
width
return
forwardTensor
(
imgTensor
)
})
// TODO batches
// TODO batches
const
boxes
=
_boxes
[
0
]
const
boxes
=
_boxes
[
0
]
const
scores
=
_scores
[
0
]
const
scores
=
_scores
[
0
]
for
(
let
i
=
1
;
i
<
_boxes
.
length
;
i
++
)
{
_boxes
[
i
].
dispose
()
_scores
[
i
].
dispose
()
}
// TODO find a better way to filter by minConfidence
// TODO find a better way to filter by minConfidence
//const ts = Date.now()
const
scoresData
=
Array
.
from
(
await
scores
.
data
())
const
scoresData
=
Array
.
from
(
await
scores
.
data
())
//console.log('await data:', (Date.now() - ts))
const
iouThreshold
=
0.5
const
iouThreshold
=
0.5
const
indices
=
nonMaxSuppression
(
const
indices
=
nonMaxSuppression
(
...
@@ -120,17 +153,19 @@ export function faceDetectionNet(weights: Float32Array) {
...
@@ -120,17 +153,19 @@ export function faceDetectionNet(weights: Float32Array) {
minConfidence
minConfidence
)
)
return
indices
const
results
=
indices
.
map
(
idx
=>
({
.
map
(
idx
=>
new
FaceDetectionResult
(
score
:
scoresData
[
idx
],
scoresData
[
idx
],
box
:
{
boxes
.
get
(
idx
,
0
)
*
paddedHeightRelative
,
top
:
Math
.
max
(
0
,
height
*
boxes
.
get
(
idx
,
0
)),
boxes
.
get
(
idx
,
1
)
*
paddedWidthRelative
,
left
:
Math
.
max
(
0
,
width
*
boxes
.
get
(
idx
,
1
)),
boxes
.
get
(
idx
,
2
)
*
paddedHeightRelative
,
bottom
:
Math
.
min
(
height
,
height
*
boxes
.
get
(
idx
,
2
)),
boxes
.
get
(
idx
,
3
)
*
paddedWidthRelative
right
:
Math
.
min
(
width
,
width
*
boxes
.
get
(
idx
,
3
))
))
}
}))
boxes
.
dispose
()
scores
.
dispose
()
return
results
}
}
return
{
return
{
...
...
src/utils.ts
View file @
18f23a1c
...
@@ -15,6 +15,13 @@ function getContext2dOrThrow(canvas: HTMLCanvasElement): CanvasRenderingContext2
...
@@ -15,6 +15,13 @@ function getContext2dOrThrow(canvas: HTMLCanvasElement): CanvasRenderingContext2
return
ctx
return
ctx
}
}
function
getMediaDimensions
(
media
:
HTMLImageElement
|
HTMLVideoElement
)
{
if
(
media
instanceof
HTMLVideoElement
)
{
return
{
width
:
media
.
videoWidth
,
height
:
media
.
videoHeight
}
}
return
media
}
export
function
isFloat
(
num
:
number
)
{
export
function
isFloat
(
num
:
number
)
{
return
num
%
1
!==
0
return
num
%
1
!==
0
}
}
...
@@ -43,7 +50,7 @@ export function drawMediaToCanvas(
...
@@ -43,7 +50,7 @@ export function drawMediaToCanvas(
throw
new
Error
(
'drawMediaToCanvas - expected media to be of type: HTMLImageElement | HTMLVideoElement'
)
throw
new
Error
(
'drawMediaToCanvas - expected media to be of type: HTMLImageElement | HTMLVideoElement'
)
}
}
const
{
width
,
height
}
=
dims
||
media
const
{
width
,
height
}
=
dims
||
getMediaDimensions
(
media
)
canvas
.
width
=
width
canvas
.
width
=
width
canvas
.
height
=
height
canvas
.
height
=
height
...
@@ -59,7 +66,7 @@ export function mediaToImageData(media: HTMLImageElement | HTMLVideoElement, dim
...
@@ -59,7 +66,7 @@ export function mediaToImageData(media: HTMLImageElement | HTMLVideoElement, dim
const
ctx
=
drawMediaToCanvas
(
document
.
createElement
(
'canvas'
),
media
)
const
ctx
=
drawMediaToCanvas
(
document
.
createElement
(
'canvas'
),
media
)
const
{
width
,
height
}
=
dims
||
media
const
{
width
,
height
}
=
dims
||
getMediaDimensions
(
media
)
return
ctx
.
getImageData
(
0
,
0
,
width
,
height
)
return
ctx
.
getImageData
(
0
,
0
,
width
,
height
)
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment