Skip to content

Commit eb4684b

Browse files
Merge pull request justadudewhohacks#70 from justadudewhohacks/justadudewhohacks#66
fixed image size overflowing in extractFaceTensors and extractFaces
2 parents 147f090 + 11e7da3 commit eb4684b

File tree

6 files changed

+194
-2
lines changed

6 files changed

+194
-2
lines changed

.npmignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ node_modules
22
.rpt2_cache
33

44
examples
5-
test
65
proto
76
weights
7+
weights_uncompressed
8+
test
89
tools

src/Rect.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ export class Rect implements IRect {
1818
this.height = height
1919
}
2020

21+
public get right() {
22+
return this.x + this.width
23+
}
24+
25+
public get bottom() {
26+
return this.y + this.height
27+
}
28+
2129
public toSquare(): Rect {
2230
let { x, y, width, height } = this
2331
const diff = Math.abs(width - height)
@@ -45,4 +53,17 @@ export class Rect implements IRect {
4553
Math.floor(this.height)
4654
)
4755
}
56+
57+
public clipAtImageBorders(imgWidth: number, imgHeight: number): Rect {
58+
const { x, y, right, bottom } = this
59+
const clippedX = Math.max(x, 0)
60+
const clippedY = Math.max(y, 0)
61+
62+
const newWidth = right - clippedX
63+
const newHeight = bottom - clippedY
64+
const clippedWidth = Math.min(newWidth, imgWidth - clippedX)
65+
const clippedHeight = Math.min(newHeight, imgHeight - clippedY)
66+
67+
return (new Rect(clippedX, clippedY, clippedWidth, clippedHeight)).floor()
68+
}
4869
}

src/extractFaceTensors.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@ export async function extractFaceTensors(
3636

3737
const boxes = detections.map(
3838
det => det instanceof FaceDetection
39-
? det.forSize(imgWidth, imgHeight).getBox().floor()
39+
? det.forSize(imgWidth, imgHeight).getBox()
4040
: det
4141
)
42+
.map(box => box.clipAtImageBorders(imgWidth, imgHeight))
43+
4244
const faceTensors = boxes.map(({ x, y, width, height }) =>
4345
tf.slice(imgTensor, [0, y, x, 0], [1, height, width, numChannels])
4446
)

src/extractFaces.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ export async function extractFaces(
4242
? det.forSize(canvas.width, canvas.height).getBox().floor()
4343
: det
4444
)
45+
.map(box => box.clipAtImageBorders(canvas.width, canvas.height))
46+
4547
return boxes.map(({ x, y, width, height }) => {
4648
const faceImg = createCanvas({ width, height })
4749
getContext2dOrThrow(faceImg)

test/tests/extractFaceTensors.test.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { extractFaceTensors, Rect } from '../../src';
2+
import { bufferToImage } from '../../src/utils';
3+
4+
describe('extractFaceTensors', () => {
5+
6+
let imgEl: HTMLImageElement
7+
8+
beforeAll(async () => {
9+
const img = await (await fetch('base/test/images/face1.png')).blob()
10+
imgEl = await bufferToImage(img)
11+
})
12+
13+
describe('extracts tensors', () => {
14+
15+
it('single box', async () => {
16+
const rect = new Rect(0, 0, 50, 60)
17+
const tensors = await extractFaceTensors(imgEl, [rect])
18+
19+
expect(tensors.length).toEqual(1)
20+
expect(tensors[0].shape).toEqual([1, 60, 50, 3])
21+
tensors[0].dispose()
22+
})
23+
24+
it('multiple boxes', async () => {
25+
const rects = [
26+
new Rect(0, 0, 50, 60),
27+
new Rect(50, 50, 70, 80),
28+
]
29+
const tensors = await extractFaceTensors(imgEl, rects)
30+
31+
expect(tensors.length).toEqual(2)
32+
expect(tensors[0].shape).toEqual([1, 60, 50, 3])
33+
expect(tensors[1].shape).toEqual([1, 80, 70, 3])
34+
tensors[0].dispose()
35+
tensors[1].dispose()
36+
})
37+
38+
})
39+
40+
describe('box out of image borders', () => {
41+
42+
it('clips upper left corner', async () => {
43+
const rect = new Rect(-10, -10, 110, 110)
44+
const tensors = await extractFaceTensors(imgEl, [rect])
45+
46+
expect(tensors[0].shape).toEqual([1, 100, 100, 3])
47+
tensors[0].dispose()
48+
})
49+
50+
it('clips bottom right corner', async () => {
51+
const rect = new Rect(imgEl.width - 100, imgEl.height - 100, 110, 110)
52+
const tensors = await extractFaceTensors(imgEl, [rect])
53+
54+
expect(tensors[0].shape).toEqual([1, 100, 100, 3])
55+
tensors[0].dispose()
56+
})
57+
58+
it('clips upper left and bottom right corners', async () => {
59+
const rect = new Rect(-10, -10, imgEl.width + 20, imgEl.height + 20)
60+
const tensors = await extractFaceTensors(imgEl, [rect])
61+
62+
expect(tensors[0].shape).toEqual([1, imgEl.height, imgEl.width, 3])
63+
tensors[0].dispose()
64+
})
65+
66+
})
67+
68+
})

test/tests/extractFaces.test.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { extractFaces, Rect } from '../../src';
2+
import { bufferToImage, createCanvasFromMedia } from '../../src/utils';
3+
4+
describe('extractFaces', () => {
5+
6+
let imgEl: HTMLImageElement, canvasEl: HTMLCanvasElement
7+
8+
beforeAll(async () => {
9+
const img = await (await fetch('base/test/images/face1.png')).blob()
10+
imgEl = await bufferToImage(img)
11+
canvasEl = createCanvasFromMedia(imgEl)
12+
})
13+
14+
describe('extracts canvases', () => {
15+
16+
it('HTMLImageElement, single box', async () => {
17+
const rect = new Rect(0, 0, 50, 60)
18+
const canvases = await extractFaces(imgEl, [rect])
19+
20+
expect(canvases.length).toEqual(1)
21+
expect(canvases[0] instanceof HTMLCanvasElement).toBe(true)
22+
expect(canvases[0].width).toEqual(50)
23+
expect(canvases[0].height).toEqual(60)
24+
})
25+
26+
it('HTMLImageElement, multiple boxes', async () => {
27+
const rects = [
28+
new Rect(0, 0, 50, 60),
29+
new Rect(50, 50, 70, 80),
30+
]
31+
const canvases = await extractFaces(imgEl, rects)
32+
33+
expect(canvases.length).toEqual(2)
34+
expect(canvases[0] instanceof HTMLCanvasElement).toBe(true)
35+
expect(canvases[0].width).toEqual(50)
36+
expect(canvases[0].height).toEqual(60)
37+
expect(canvases[1] instanceof HTMLCanvasElement).toBe(true)
38+
expect(canvases[1].width).toEqual(70)
39+
expect(canvases[1].height).toEqual(80)
40+
})
41+
42+
it('HTMLCanvasElement, single box', async () => {
43+
const rect = new Rect(0, 0, 50, 60)
44+
const canvases = await extractFaces(canvasEl, [rect])
45+
46+
expect(canvases.length).toEqual(1)
47+
expect(canvases[0] instanceof HTMLCanvasElement).toBe(true)
48+
expect(canvases[0].width).toEqual(50)
49+
expect(canvases[0].height).toEqual(60)
50+
})
51+
52+
it('HTMLCanvasElement, multiple boxes', async () => {
53+
const rects = [
54+
new Rect(0, 0, 50, 60),
55+
new Rect(50, 50, 70, 80),
56+
]
57+
const canvases = await extractFaces(canvasEl, rects)
58+
59+
expect(canvases.length).toEqual(2)
60+
expect(canvases[0] instanceof HTMLCanvasElement).toBe(true)
61+
expect(canvases[0].width).toEqual(50)
62+
expect(canvases[0].height).toEqual(60)
63+
expect(canvases[1] instanceof HTMLCanvasElement).toBe(true)
64+
expect(canvases[1].width).toEqual(70)
65+
expect(canvases[1].height).toEqual(80)
66+
})
67+
68+
})
69+
70+
describe('box out of image borders', () => {
71+
72+
it('clips upper left corner', async () => {
73+
const rect = new Rect(-10, -10, 110, 110)
74+
const canvases = await extractFaces(imgEl, [rect])
75+
76+
expect(canvases[0].width).toEqual(100)
77+
expect(canvases[0].height).toEqual(100)
78+
})
79+
80+
it('clips bottom right corner', async () => {
81+
const rect = new Rect(imgEl.width - 100, imgEl.height - 100, 110, 110)
82+
const canvases = await extractFaces(imgEl, [rect])
83+
84+
expect(canvases[0].width).toEqual(100)
85+
expect(canvases[0].height).toEqual(100)
86+
})
87+
88+
it('clips upper left and bottom right corners', async () => {
89+
const rect = new Rect(-10, -10, imgEl.width + 20, imgEl.height + 20)
90+
const canvases = await extractFaces(imgEl, [rect])
91+
92+
expect(canvases[0].width).toEqual(imgEl.width)
93+
expect(canvases[0].height).toEqual(imgEl.height)
94+
})
95+
96+
})
97+
98+
})

0 commit comments

Comments
 (0)