Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revamp Bite Selection Visualization #108

Merged
merged 5 commits into from
Dec 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 24 additions & 17 deletions feedingwebapp/src/Pages/Home/MealStates/BiteSelection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ const BiteSelection = (props) => {
let textFontSize = isPortrait ? '2.5vh' : '2vw'
// Indicator of how to arrange screen elements based on orientation
let dimension = isPortrait ? 'column' : 'row'
// Whether to scale all the masks equally or not
const scaleMasksEqually = false

/**
* Create a local state variable to store the detected masks, the
Expand Down Expand Up @@ -284,17 +286,6 @@ const BiteSelection = (props) => {
if (actionStatus.actionStatus === ROS_ACTION_STATUS_SUCCEED) {
// If we have a result and there are detected items
if (actionResult && actionResult.detected_items && actionResult.detected_items.length > 0) {
// Get the size of the largest mask
let [maxWidth, maxHeight] = [0, 0]
for (let detected_item of actionResult.detected_items) {
if (detected_item.roi.width > maxWidth) {
maxWidth = detected_item.roi.width
}
if (detected_item.roi.height > maxHeight) {
maxHeight = detected_item.roi.height
}
}

// Get the allotted space per mask
let parentWidth, parentHeight
if (maskButtonParentRef.current) {
Expand All @@ -320,10 +311,26 @@ const BiteSelection = (props) => {
* Determine how much to scale the masks so that the largest mask fits
* into the alloted space.
*/
let widthScaleFactor = allottedSpaceWidth / maxWidth
let heightScaleFactor = allottedSpaceHeight / maxHeight
let maskScaleFactor = Math.min(widthScaleFactor, heightScaleFactor)
// maskScaleFactor = Math.min(maskScaleFactor, 1.0)
// Get the size of the largest mask
let [maxWidth, maxHeight] = [0, 0]
if (scaleMasksEqually) {
for (let detected_item of actionResult.detected_items) {
if (detected_item.roi.width > maxWidth) {
maxWidth = detected_item.roi.width
}
if (detected_item.roi.height > maxHeight) {
maxHeight = detected_item.roi.height
}
}
}
// Create a list to contain the scale factors for each mask
let maskScaleFactors = []
for (let detected_item of actionResult.detected_items) {
let widthScaleFactor = allottedSpaceWidth / (scaleMasksEqually ? maxWidth : detected_item.roi.width)
let heightScaleFactor = allottedSpaceHeight / (scaleMasksEqually ? maxHeight : detected_item.roi.height)
let maskScaleFactor = Math.min(widthScaleFactor, heightScaleFactor)
maskScaleFactors.push(maskScaleFactor)
}

return (
<View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center', width: '100%', height: '100%' }}>
Expand All @@ -334,7 +341,7 @@ const BiteSelection = (props) => {
buttonSize={buttonSize}
maskSrc={'data:image/jpeg;base64,' + detected_item.mask.data}
invertMask={true}
maskScaleFactor={maskScaleFactor}
maskScaleFactor={maskScaleFactors[i]}
maskBoundingBox={detected_item.roi}
onClick={foodItemClicked}
value={i.toString()}
Expand All @@ -345,7 +352,7 @@ const BiteSelection = (props) => {
)
}
}
}, [actionStatus, actionResult, foodItemClicked, isPortrait, windowSize, margin])
}, [actionStatus, actionResult, foodItemClicked, isPortrait, windowSize, margin, scaleMasksEqually])

/** Get the button for continue without acquiring bite
*
Expand Down
45 changes: 44 additions & 1 deletion feedingwebapp/src/buttons/MaskButton.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// React imports
import React from 'react'
import React, { useCallback, useEffect, useRef } from 'react'
import Button from 'react-bootstrap/Button'
// PropTypes is used to validate that the used props are in fact passed to this Component
import PropTypes from 'prop-types'
import { useWindowSize } from '../helpers'

/**
* A component that renders an image and a mask on top of it, all within a
Expand All @@ -28,6 +29,9 @@ import PropTypes from 'prop-types'
*
*/
function MaskButton(props) {
// Get a reference for the canvas
const canvasRef = useRef(null)

// Get the properties
let buttonSize = props.buttonSize
let imgSrc = props.imgSrc
Expand All @@ -38,6 +42,34 @@ function MaskButton(props) {
let onClick = props.onClick
let value = props.value

// Draw a red filled circle on the middle of the canvas
const drawCircle = useCallback(() => {
// Get the canvas
const canvas = canvasRef.current
// Get the context
const ctx = canvas.getContext('2d')
// Clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height)
// Draw a red filled circle
let radius = 5
ctx.beginPath()
ctx.arc(canvas.width / 2, canvas.height / 2, radius, 0, 2 * Math.PI)
ctx.fillStyle = 'red'
ctx.fill()
}, [])

// Draw a red filled circle on the middle of the canvas
useEffect(() => {
// Dummy log necessary to have useEffect re-run when the props change
console.log('props.maskBoundingBox', props.maskBoundingBox)

// Draw the circle
drawCircle()
}, [drawCircle, props.maskBoundingBox])

// Redraw the circle when the window size changes
useWindowSize(drawCircle)

return (
<Button
style={{ backgroundColor: invertMask ? 'white' : 'black', borderColor: invertMask ? 'black' : 'white', borderWidth: 'medium' }}
Expand Down Expand Up @@ -88,6 +120,17 @@ function MaskButton(props) {
pointerEvents: 'none'
}}
/>
<canvas
ref={canvasRef}
width={maskBoundingBox.width * maskScaleFactor}
height={maskBoundingBox.height * maskScaleFactor}
style={{
position: 'absolute',
top: '0px',
display: 'block',
pointerEvents: 'none'
}}
/>
</div>
</div>
</Button>
Expand Down