The function of this simple app is to indicate the object’s location in the image whenever the user clicks any of the labeled buttons.
To begin with, first, I created a simple design layout. (Bootstrap 5 was installed).
const ImageDetector = () =>{
return(
<>
<div className="row container">
<p className="text-center"><strong>SIMPLE IMAGE LABELING</strong></p>
<div className="row mt-2 mb-3">
<div className="col-4">
{/*buttons here*/}
</div>
<div className="col-8">
<img
id="image"
src={`/img/foods.jpg`}
alt="photo"
className="w-100"
/>
</div>
</div>
</div>
</>
)
}
export default ImageDetector;
I declared a variable containing an array of objects.
const objects = [
{
"object": "juice",
"x":1,
"y":1,
"w":230,
"h":250
},
{
"object": "oranges",
"x":200,
"y":230,
"w":320,
"h":230
},
{
"object": "bread",
"x":600,
"y":280,
"w":250,
"h":250
},
];
After declaration, I fetched the values from that variable, objects, and converted them into buttons.
<div className="col-4">
{objects.map(item=>(
<button
className={"btn btn-secondary col-12 my-2"}
key={item.object}
>
{item.object}
</button>
))}
</div>
To display the square or rectangle shape, I declared width and height variables to get the natural width and height of the image.
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);
useEffect(()=>{
const image = document.querySelector<HTMLImageElement>('#image');
if(image !== null){
let linkSource = image.src;
const imageCopy = document.createElement("img");
imageCopy.src = linkSource;
setWidth(imageCopy.width);
setHeight(imageCopy.height);
}
},[]);
Then, I inserted <svg/> below the <img/> and put them inside a <div/> then made its position relative. I defined the css style of the <svg/> to overlay the <img/>. The preserveAspectRatio and viewBox in <svg/> will help retain the x and y positions of rect and text elements even when the screen size changes.
<div className="col-8">
<div style={{"position":"relative"}}>
<img
id="image"
src={`/img/foods.jpg`}
alt="photo"
className="w-100"
/>
<svg
preserveAspectRatio="none"
className="image-map"
viewBox={`0,0,${width},${height}`}
<rect className="selected-object" rect>
<rect className="wrap-text" ></rect>
<text className="object-name"> </text>
</svg>
</div>
</div>
<style jsx>
{`
.image-map {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}
.selected-object{
position: absolute;
top: 0;
left: 0;
}
`}
</style>
I inserted variables to set values of the x,y,w,h in svg’s rect and text elements.
const [xPosition, setXPosition] = useState(0);
const [yPosition, setYPosition] = useState(0);
const [objWidth, setObjWidth] = useState(0);
const [objHeight, setObjHeight] = useState(0);
const [selectedItem, setSelectedItem] = useState("");
Created handleClick() so when the user clicks a button the rect svg will appear.
const handleClick = ( object:string ) =>{
objects.map(item =>{
if(item.object === object){
setXPosition(item.x);
setYPosition(item.y);
setObjWidth(item.w);
setObjHeight(item.h);
setSelectedItem(object.toUpperCase());
}
});
}
<div className="col-4">
{objects.map(item=>(
<button
className={"btn btn-secondary col-12 my-2"}
key={item.object}
onClick={() => handleClick(item.object)}
>
{item.object}
</button>
))}
</div>
Then I set the values of rect and text elements and edited my style css.
<rect className="selected-object"
x={xPosition}
y={yPosition}
width={objWidth}
height={objHeight}></rect>
<rect className="wrap-text"
x={xPosition}
y={yPosition}
width={objWidth}
height="20"></rect>
<text className="object-name"
x={xPosition}
y={yPosition+15}>{selectedItem}</text>
<style jsx>
{`
.image-map {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}
.selected-object{
position: absolute;
top: 0;
left: 0;
fill: transparent;
stroke: #ff9e0d;
stroke-width: 5px;
}
.wrap-text{
fill: #ff9e0d;
}
.object-name{
z-index:999;
}
`}
</style>
And it’s done. You can now run your simple app.