Skip to content

Commit c73cb14

Browse files
committed
fix some rendering issues
1 parent 847497b commit c73cb14

File tree

4 files changed

+156
-52
lines changed

4 files changed

+156
-52
lines changed

.prettierrc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"singleQuote": true,
3+
"semi": false
4+
}

src/index.js

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,13 @@ const App = () => {
2020
return (
2121
<>
2222
<video
23-
className={styles.fixed}
23+
className={styles.video}
2424
autoPlay
2525
playsInline
2626
muted
2727
ref={videoRef}
28-
width="600"
29-
height="500"
30-
/>
31-
<canvas
32-
className={styles.fixed}
33-
ref={canvasRef}
34-
width="600"
35-
height="500"
3628
/>
29+
<canvas ref={canvasRef} />
3730
</>
3831
)
3932
}

src/styles.module.css

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
.fixed {
1+
.video {
22
position: fixed;
33
top: 0;
44
left: 0;
5+
width: 100%;
6+
height: 100%;
57
}

src/useBoxRenderer.js

Lines changed: 147 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,163 @@
1-
import { useEffect } from "react";
1+
import { useEffect } from 'react'
22

3-
const SCORE_DIGITS = 4;
3+
const getRetinaContext = (canvas) => {
4+
const ctx = canvas.getContext('2d')
5+
const scale = window.devicePixelRatio
6+
let width = canvas.width / scale
7+
let height = canvas.height / scale
8+
return {
9+
setWidth: (w) => {
10+
width = w
11+
canvas.style.width = w + 'px'
12+
canvas.width = w * scale
13+
},
14+
setHeight: (h) => {
15+
height = h
16+
canvas.style.height = h + 'px'
17+
canvas.height = h * scale
18+
},
19+
width: width,
20+
height: height,
21+
clearAll: () => {
22+
return ctx.clearRect(0, 0, width * scale, height * scale)
23+
},
24+
clearRect: (x, y, width, height) => {
25+
return ctx.clearRect(x * scale, y * scale, width * scale, height * scale)
26+
},
27+
setFont: (font) => {
28+
const size = parseInt(font, 10) * scale
29+
const retinaFont = font.replace(/^\d+px/, size + 'px')
30+
ctx.font = retinaFont
31+
},
32+
setTextBaseLine: (textBaseline) => {
33+
ctx.textBaseline = textBaseline
34+
},
35+
setStrokeStyle: (strokeStyle) => {
36+
ctx.strokeStyle = strokeStyle
37+
},
38+
setLineWidth: (lineWidth) => {
39+
ctx.lineWidth = lineWidth * scale
40+
},
41+
strokeRect: (x, y, width, height) => {
42+
return ctx.strokeRect(x * scale, y * scale, width * scale, height * scale)
43+
},
44+
setFillStyle: (fillStyle) => {
45+
ctx.fillStyle = fillStyle
46+
},
47+
measureText: (text) => {
48+
const metrics = ctx.measureText(text)
49+
return {
50+
width: metrics.width / scale,
51+
actualBoundingBoxLeft: metrics.actualBoundingBoxLeft / scale,
52+
actualBoundingBoxRight: metrics.actualBoundingBoxRight / scale,
53+
actualBoundingBoxAscent: metrics.actualBoundingBoxAscent / scale,
54+
actualBoundingBoxDescent: metrics.actualBoundingBoxDescent / scale,
55+
}
56+
},
57+
fillRect: (x, y, width, height) => {
58+
return ctx.fillRect(x * scale, y * scale, width * scale, height * scale)
59+
},
60+
fillText: (text, x, y) => {
61+
return ctx.fillText(text, x * scale, y * scale)
62+
},
63+
}
64+
}
465

5-
const getLabelText = prediction => {
6-
const scoreText = prediction.score.toFixed(SCORE_DIGITS);
7-
return prediction.class + ", score: " + scoreText;
8-
};
66+
const getLabelText = (prediction) => {
67+
const scoreText = (prediction.score * 100).toFixed(1)
68+
return `${prediction.label} ${scoreText}%`
69+
}
970

10-
const renderPredictions = (predictions, canvasRef) => {
11-
const ctx = canvasRef.current.getContext("2d");
12-
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
71+
const renderPredictions = (predictions, videoRef, canvasRef) => {
72+
const wantedWidth = videoRef.current.offsetWidth
73+
const wantedHeight = videoRef.current.offsetHeight
74+
const videoWidth = videoRef.current.videoWidth
75+
const videoHeight = videoRef.current.videoHeight
76+
77+
const scaleX = wantedWidth / videoWidth
78+
const scaleY = wantedHeight / videoHeight
79+
80+
const scale = Math.min(scaleX, scaleY)
81+
const xOffset = (wantedWidth - videoWidth * scale) / 2
82+
const yOffset = (wantedHeight - videoHeight * scale) / 2
83+
84+
const ctx = getRetinaContext(canvasRef.current)
85+
86+
ctx.setWidth(wantedWidth)
87+
ctx.setHeight(wantedHeight)
88+
89+
ctx.clearAll()
1390
// Font options.
14-
const font = "16px sans-serif";
15-
ctx.font = font;
16-
ctx.textBaseline = "top";
17-
predictions.forEach(prediction => {
18-
const x = prediction.bbox[0];
19-
const y = prediction.bbox[1];
20-
const width = prediction.bbox[2];
21-
const height = prediction.bbox[3];
91+
const font = `${16}px 'ibm-plex-sans', Helvetica Neue, Arial, sans-serif`
92+
ctx.setFont(font)
93+
ctx.setTextBaseLine('top')
94+
const border = 4
95+
const xPadding = 16
96+
const yPadding = 8
97+
const offset = 6
98+
const textHeight = parseInt(font, 10) // base 10
99+
100+
predictions.forEach((prediction) => {
101+
const x = prediction.bbox[0] * scale + xOffset
102+
const y = prediction.bbox[1] * scale + yOffset
103+
const width = prediction.bbox[2] * scale
104+
const height = prediction.bbox[3] * scale
105+
const predictionText = getLabelText(prediction)
22106
// Draw the bounding box.
23-
ctx.strokeStyle = "#00FFFF";
24-
ctx.lineWidth = 4;
25-
ctx.strokeRect(x, y, width, height);
107+
ctx.setStrokeStyle('#0062ff')
108+
ctx.setLineWidth(border)
109+
110+
ctx.strokeRect(
111+
Math.round(x),
112+
Math.round(y),
113+
Math.round(width),
114+
Math.round(height)
115+
)
26116
// Draw the label background.
27-
ctx.fillStyle = "#00FFFF";
28-
const textWidth = ctx.measureText(getLabelText(prediction)).width;
29-
const textHeight = parseInt(font, 10); // base 10
30-
ctx.fillRect(x, y, textWidth + 4, textHeight + 4);
31-
});
32-
33-
predictions.forEach(prediction => {
34-
const x = prediction.bbox[0];
35-
const y = prediction.bbox[1];
117+
ctx.setFillStyle('#0062ff')
118+
const textWidth = ctx.measureText(predictionText).width
119+
ctx.fillRect(
120+
Math.round(x - border / 2),
121+
Math.round(y - (textHeight + yPadding) - offset),
122+
Math.round(textWidth + xPadding),
123+
Math.round(textHeight + yPadding)
124+
)
125+
})
126+
127+
predictions.forEach((prediction) => {
128+
const x = prediction.bbox[0] * scale + xOffset
129+
const y = prediction.bbox[1] * scale + yOffset
130+
const predictionText = getLabelText(prediction)
36131
// Draw the text last to ensure it's on top.
37-
ctx.fillStyle = "#000000";
38-
ctx.fillText(getLabelText(prediction), x, y);
39-
});
40-
};
132+
ctx.setFillStyle('#ffffff')
133+
ctx.fillText(
134+
predictionText,
135+
Math.round(x - border / 2 + xPadding / 2),
136+
Math.round(y - (textHeight + yPadding) - offset + yPadding / 2)
137+
)
138+
})
139+
}
41140

42141
const detectFrame = async (model, videoRef, canvasRef) => {
43-
const predictions = await model.detect(videoRef.current);
44-
renderPredictions(predictions, canvasRef);
142+
const predictions = await model.detect(videoRef.current)
143+
renderPredictions(predictions, videoRef, canvasRef)
45144
requestAnimationFrame(() => {
46-
detectFrame(model, videoRef, canvasRef);
47-
});
48-
};
145+
detectFrame(model, videoRef, canvasRef)
146+
})
147+
}
49148

50149
const useBoxRenderer = (model, videoRef, canvasRef, shouldRender) => {
150+
if (canvasRef.current) {
151+
canvasRef.current.style.position = 'fixed'
152+
canvasRef.current.style.left = '0'
153+
canvasRef.current.style.top = '0'
154+
}
155+
51156
useEffect(() => {
52157
if (model && shouldRender) {
53-
detectFrame(model, videoRef, canvasRef);
158+
detectFrame(model, videoRef, canvasRef)
54159
}
55-
}, [canvasRef, model, shouldRender, videoRef]);
56-
};
160+
}, [canvasRef, model, shouldRender, videoRef])
161+
}
57162

58-
export default useBoxRenderer;
163+
export default useBoxRenderer

0 commit comments

Comments
 (0)