Show face boxes
Signed-off-by: James Ketrenos <james_git@ketrenos.com>
This commit is contained in:
parent
a9549d29a9
commit
5d38cb4787
@ -56,6 +56,27 @@ div {
|
||||
border: 0.25rem solid transparent;
|
||||
}
|
||||
|
||||
.Image .FaceBox {
|
||||
border: 1px solid red;
|
||||
border-radius: 0.25rem;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.Image .FaceBox:hover {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0px 0px 5px black;
|
||||
}
|
||||
|
||||
.Image {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background-size: contain !important;
|
||||
background-repeat: no-repeat no-repeat !important;
|
||||
background-position: 50% 50% !important;
|
||||
}
|
||||
|
||||
.Face:hover .Image {
|
||||
border: 0.25rem solid yellow;
|
||||
}
|
||||
@ -80,9 +101,6 @@ div {
|
||||
box-sizing: border-box;
|
||||
width: 8rem;
|
||||
height: 8rem;
|
||||
background-size: contain !important;
|
||||
background-repeat: no-repeat no-repeat !important;;
|
||||
background-position: 50% 50% !important;;
|
||||
}
|
||||
|
||||
.Cluster {
|
||||
|
@ -1,11 +1,91 @@
|
||||
|
||||
import React, { useState, useMemo, useEffect } from 'react';
|
||||
import React, { useState, useMemo, useEffect, useRef } from 'react';
|
||||
import { useApi } from './useApi';
|
||||
import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
|
||||
import './App.css';
|
||||
|
||||
const makeFaceBoxes = (photo: any, dimensions: any): any => {
|
||||
const faces: FaceData[] = photo.faces;
|
||||
|
||||
let width: number, height: number, offsetLeft = 0, offsetTop = 0;
|
||||
|
||||
/* If photo is wider than viewport, it will be 100% width and < 100% height */
|
||||
if (photo.width / photo.height > dimensions.width / dimensions.height) {
|
||||
width = dimensions.width;
|
||||
height = dimensions.height * photo.height / photo.width *
|
||||
dimensions.width / dimensions.height;
|
||||
offsetLeft = 0;
|
||||
offsetTop = (dimensions.height - height) * 0.5;
|
||||
} else {
|
||||
width = dimensions.width * photo.width / photo.height *
|
||||
dimensions.height / dimensions.width;
|
||||
height = dimensions.height;
|
||||
offsetLeft = (dimensions.width - width) * 0.5;
|
||||
offsetTop = 0;
|
||||
}
|
||||
|
||||
return faces.map((face: FaceData) => (
|
||||
<div className="FaceBox"
|
||||
key={face.faceId}
|
||||
style={{
|
||||
left: offsetLeft + Math.floor(face.left * width) + "px",
|
||||
top: offsetTop + Math.floor(face.top * height) + "px",
|
||||
width: Math.floor((face.right - face.left) * width) + "px",
|
||||
height: Math.floor((face.bottom - face.top) * height) + "px"
|
||||
}}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
/*
|
||||
function debounce(fn: any, ms: number) {
|
||||
let timer: any;
|
||||
return () => {
|
||||
if (timer) clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
timer = null
|
||||
fn.apply(this as typeof Photo, arguments)
|
||||
}, ms)
|
||||
};
|
||||
};
|
||||
*/
|
||||
|
||||
const Photo = ({ photoId }: any) => {
|
||||
const [image, setImage] = useState<any>(undefined);
|
||||
const ref = useRef(null);
|
||||
const [dimensions, setDimensions] = React.useState({
|
||||
height: window.innerHeight,
|
||||
width: window.innerWidth
|
||||
})
|
||||
|
||||
const faces = useMemo(() => {
|
||||
if (image === undefined) {
|
||||
return <></>;
|
||||
}
|
||||
return makeFaceBoxes(image, dimensions);
|
||||
}, [image, dimensions]);
|
||||
|
||||
useEffect(() : any => {
|
||||
if (!ref || !ref.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const el: Element = ref.current as Element;
|
||||
|
||||
const handleResize = () => {
|
||||
setDimensions({
|
||||
height: el.clientHeight,
|
||||
width: el.clientWidth
|
||||
})
|
||||
};
|
||||
|
||||
const debouncedHandleResize = handleResize;//debounce(handleResize, 250);
|
||||
debouncedHandleResize();
|
||||
window.addEventListener('resize', debouncedHandleResize);
|
||||
return () => {
|
||||
window.removeEventListener('resize', debouncedHandleResize)
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (photoId === 0) {
|
||||
@ -14,8 +94,8 @@ const Photo = ({ photoId }: any) => {
|
||||
const fetchImageData = async (image: number) => {
|
||||
console.log(`Loading photo ${image}`);
|
||||
const res = await window.fetch(`../api/v1/photos/${image}`);
|
||||
const data = await res.json();
|
||||
setImage(data[0]);
|
||||
const photo = await res.json();
|
||||
setImage(photo);
|
||||
};
|
||||
|
||||
fetchImageData(photoId);
|
||||
@ -26,9 +106,11 @@ const Photo = ({ photoId }: any) => {
|
||||
}
|
||||
|
||||
return (<div className="Image"
|
||||
ref={ref}
|
||||
style={{
|
||||
background: `url(${image.path}thumbs/scaled/${image.filename})`
|
||||
}}/>);
|
||||
background: `url(../${image.path}thumbs/scaled/${image.filename})`
|
||||
}}>{ faces }</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Face = ({ faceId, onClick, title }: any) => {
|
||||
@ -181,7 +263,11 @@ type FaceData = {
|
||||
displayName: string,
|
||||
identityId: number,
|
||||
distance: number,
|
||||
descriptors: any[]
|
||||
descriptors: any[],
|
||||
top: number
|
||||
right: number,
|
||||
bottom: number,
|
||||
left: number,
|
||||
};
|
||||
|
||||
type Identity = {
|
||||
|
@ -1084,24 +1084,62 @@ console.log("Trying path as: " + path);
|
||||
});
|
||||
|
||||
router.get("/:id", async (req, res) => {
|
||||
console.log(`GET ${req.url}`);
|
||||
|
||||
const id = parseInt(req.params.id);
|
||||
try {
|
||||
const results = await photoDB.sequelize.query(
|
||||
let results;
|
||||
|
||||
results = await photoDB.sequelize.query(
|
||||
`
|
||||
SELECT photos.*,albums.path AS path,
|
||||
faces.identityId,faces.top,faces.left,faces.right,faces.bottom
|
||||
SELECT photos.*,albums.path AS path
|
||||
FROM photos
|
||||
INNER JOIN albums ON albums.id=photos.albumId
|
||||
INNER JOIN faces ON faces.photoId=photos.id
|
||||
WHERE photos.id=:id
|
||||
`, {
|
||||
replacements: { id }
|
||||
replacements: { id },
|
||||
type: photoDB.Sequelize.QueryTypes.SELECT
|
||||
}
|
||||
);
|
||||
if (results.length === 0) {
|
||||
return res.status(404);
|
||||
}
|
||||
return res.status(200).json(results[0]);
|
||||
const photo = results[0];
|
||||
results = await photoDB.sequelize.query(
|
||||
`
|
||||
SELECT faces.* FROM faces
|
||||
WHERE faces.photoId=:id
|
||||
`, {
|
||||
replacements: { id },
|
||||
type: photoDB.Sequelize.QueryTypes.SELECT
|
||||
}
|
||||
);
|
||||
photo.faces = results;
|
||||
/* For each face, look up the Identity and clean up any
|
||||
* fields we don't want to return vai the rest API */
|
||||
await Promise.map(photo.faces, async (face) => {
|
||||
face.faceId = face.id;
|
||||
delete face.id;
|
||||
delete face.descriptorId;
|
||||
delete face.lastComparedId;
|
||||
delete face.photoId;
|
||||
delete face.scanVersion;
|
||||
if (face.identityId) {
|
||||
const results = await photoDB.sequelize.query(
|
||||
`
|
||||
SELECT displayName,firstName,lastName,middleName FROM identities
|
||||
WHERE id=:id
|
||||
`, {
|
||||
replacements: { id: face.identityId },
|
||||
type: photoDB.Sequelize.QueryTypes.SELECT
|
||||
}
|
||||
);
|
||||
face.identity = results[0];
|
||||
}
|
||||
delete face.identityId;
|
||||
});
|
||||
|
||||
return res.status(200).json(photo);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(404).json({message: `Error connecting to DB for ${id}.`})
|
||||
|
Loading…
x
Reference in New Issue
Block a user