Skip to content

Commit 43b00f4

Browse files
authored
Revamp Bite Selection Visualization (#108)
* Remove dependency on web video server * Unsubscribe before unload * Display mask middle and resize masks * Re-render canvas on resize --------- Co-authored-by: Amal Nanavati <amaln@cs.washington.edu>
1 parent 95d06eb commit 43b00f4

File tree

2 files changed

+68
-18
lines changed

2 files changed

+68
-18
lines changed

feedingwebapp/src/Pages/Home/MealStates/BiteSelection.jsx

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ const BiteSelection = (props) => {
5252
let textFontSize = isPortrait ? '2.5vh' : '2vw'
5353
// Indicator of how to arrange screen elements based on orientation
5454
let dimension = isPortrait ? 'column' : 'row'
55+
// Whether to scale all the masks equally or not
56+
const scaleMasksEqually = false
5557

5658
/**
5759
* Create a local state variable to store the detected masks, the
@@ -284,17 +286,6 @@ const BiteSelection = (props) => {
284286
if (actionStatus.actionStatus === ROS_ACTION_STATUS_SUCCEED) {
285287
// If we have a result and there are detected items
286288
if (actionResult && actionResult.detected_items && actionResult.detected_items.length > 0) {
287-
// Get the size of the largest mask
288-
let [maxWidth, maxHeight] = [0, 0]
289-
for (let detected_item of actionResult.detected_items) {
290-
if (detected_item.roi.width > maxWidth) {
291-
maxWidth = detected_item.roi.width
292-
}
293-
if (detected_item.roi.height > maxHeight) {
294-
maxHeight = detected_item.roi.height
295-
}
296-
}
297-
298289
// Get the allotted space per mask
299290
let parentWidth, parentHeight
300291
if (maskButtonParentRef.current) {
@@ -320,10 +311,26 @@ const BiteSelection = (props) => {
320311
* Determine how much to scale the masks so that the largest mask fits
321312
* into the alloted space.
322313
*/
323-
let widthScaleFactor = allottedSpaceWidth / maxWidth
324-
let heightScaleFactor = allottedSpaceHeight / maxHeight
325-
let maskScaleFactor = Math.min(widthScaleFactor, heightScaleFactor)
326-
// maskScaleFactor = Math.min(maskScaleFactor, 1.0)
314+
// Get the size of the largest mask
315+
let [maxWidth, maxHeight] = [0, 0]
316+
if (scaleMasksEqually) {
317+
for (let detected_item of actionResult.detected_items) {
318+
if (detected_item.roi.width > maxWidth) {
319+
maxWidth = detected_item.roi.width
320+
}
321+
if (detected_item.roi.height > maxHeight) {
322+
maxHeight = detected_item.roi.height
323+
}
324+
}
325+
}
326+
// Create a list to contain the scale factors for each mask
327+
let maskScaleFactors = []
328+
for (let detected_item of actionResult.detected_items) {
329+
let widthScaleFactor = allottedSpaceWidth / (scaleMasksEqually ? maxWidth : detected_item.roi.width)
330+
let heightScaleFactor = allottedSpaceHeight / (scaleMasksEqually ? maxHeight : detected_item.roi.height)
331+
let maskScaleFactor = Math.min(widthScaleFactor, heightScaleFactor)
332+
maskScaleFactors.push(maskScaleFactor)
333+
}
327334

328335
return (
329336
<View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center', width: '100%', height: '100%' }}>
@@ -334,7 +341,7 @@ const BiteSelection = (props) => {
334341
buttonSize={buttonSize}
335342
maskSrc={'data:image/jpeg;base64,' + detected_item.mask.data}
336343
invertMask={true}
337-
maskScaleFactor={maskScaleFactor}
344+
maskScaleFactor={maskScaleFactors[i]}
338345
maskBoundingBox={detected_item.roi}
339346
onClick={foodItemClicked}
340347
value={i.toString()}
@@ -345,7 +352,7 @@ const BiteSelection = (props) => {
345352
)
346353
}
347354
}
348-
}, [actionStatus, actionResult, foodItemClicked, isPortrait, windowSize, margin])
355+
}, [actionStatus, actionResult, foodItemClicked, isPortrait, windowSize, margin, scaleMasksEqually])
349356

350357
/** Get the button for continue without acquiring bite
351358
*

feedingwebapp/src/buttons/MaskButton.jsx

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
// React imports
2-
import React from 'react'
2+
import React, { useCallback, useEffect, useRef } from 'react'
33
import Button from 'react-bootstrap/Button'
44
// PropTypes is used to validate that the used props are in fact passed to this Component
55
import PropTypes from 'prop-types'
6+
import { useWindowSize } from '../helpers'
67

78
/**
89
* A component that renders an image and a mask on top of it, all within a
@@ -28,6 +29,9 @@ import PropTypes from 'prop-types'
2829
*
2930
*/
3031
function MaskButton(props) {
32+
// Get a reference for the canvas
33+
const canvasRef = useRef(null)
34+
3135
// Get the properties
3236
let buttonSize = props.buttonSize
3337
let imgSrc = props.imgSrc
@@ -38,6 +42,34 @@ function MaskButton(props) {
3842
let onClick = props.onClick
3943
let value = props.value
4044

45+
// Draw a red filled circle on the middle of the canvas
46+
const drawCircle = useCallback(() => {
47+
// Get the canvas
48+
const canvas = canvasRef.current
49+
// Get the context
50+
const ctx = canvas.getContext('2d')
51+
// Clear the canvas
52+
ctx.clearRect(0, 0, canvas.width, canvas.height)
53+
// Draw a red filled circle
54+
let radius = 5
55+
ctx.beginPath()
56+
ctx.arc(canvas.width / 2, canvas.height / 2, radius, 0, 2 * Math.PI)
57+
ctx.fillStyle = 'red'
58+
ctx.fill()
59+
}, [])
60+
61+
// Draw a red filled circle on the middle of the canvas
62+
useEffect(() => {
63+
// Dummy log necessary to have useEffect re-run when the props change
64+
console.log('props.maskBoundingBox', props.maskBoundingBox)
65+
66+
// Draw the circle
67+
drawCircle()
68+
}, [drawCircle, props.maskBoundingBox])
69+
70+
// Redraw the circle when the window size changes
71+
useWindowSize(drawCircle)
72+
4173
return (
4274
<Button
4375
style={{ backgroundColor: invertMask ? 'white' : 'black', borderColor: invertMask ? 'black' : 'white', borderWidth: 'medium' }}
@@ -88,6 +120,17 @@ function MaskButton(props) {
88120
pointerEvents: 'none'
89121
}}
90122
/>
123+
<canvas
124+
ref={canvasRef}
125+
width={maskBoundingBox.width * maskScaleFactor}
126+
height={maskBoundingBox.height * maskScaleFactor}
127+
style={{
128+
position: 'absolute',
129+
top: '0px',
130+
display: 'block',
131+
pointerEvents: 'none'
132+
}}
133+
/>
91134
</div>
92135
</div>
93136
</Button>

0 commit comments

Comments
 (0)