aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2024-08-20 21:30:32 -0400
committerbobzel <zzzman@gmail.com>2024-08-20 21:30:32 -0400
commitcdb21e036ff65d63991a53798133407be1d5755f (patch)
tree6df221d6855aea45fb7e63870b2e56c1a880b267 /src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
parent5196009ec6bcb673fd2a4519c54442df218841f7 (diff)
fixed error handling of images too big to load. cleaned up facecollectionbox. changed metadata field naming to match conventions.
Diffstat (limited to 'src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx')
-rw-r--r--src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx88
1 files changed, 35 insertions, 53 deletions
diff --git a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
index 50b91e8fe..d5a2809dc 100644
--- a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
@@ -11,17 +11,20 @@ import { Doc, DocListCast } from '../../../../fields/Doc';
import { DocData } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { List } from '../../../../fields/List';
-import { ImageCast, StrCast } from '../../../../fields/Types';
+import { listSpec } from '../../../../fields/Schema';
+import { Cast, ImageCast, StrCast } from '../../../../fields/Types';
import { DocumentType } from '../../../documents/DocumentTypes';
import { Docs } from '../../../documents/Documents';
import { DragManager } from '../../../util/DragManager';
import { SnappingManager } from '../../../util/SnappingManager';
+import { undoable } from '../../../util/UndoManager';
import { ViewBoxBaseComponent } from '../../DocComponent';
import { ObservableReactComponent } from '../../ObservableReactComponent';
import { DocumentView } from '../../nodes/DocumentView';
import { FieldView, FieldViewProps } from '../../nodes/FieldView';
import './FaceCollectionBox.scss';
import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
+import { FaceRecognitionHandler } from '../../search/FaceRecognitionHandler';
interface FaceDocumentProps {
faceDoc: Doc;
@@ -32,17 +35,15 @@ interface FaceDocumentProps {
*/
@observer
export class FaceDocumentItem extends ObservableReactComponent<FaceDocumentProps> {
- private ref: React.RefObject<HTMLDivElement>;
- @observable _displayImages: boolean = true;
private _dropDisposer?: DragManager.DragDropDisposer;
- private _inputRef = React.createRef<HTMLInputElement>();
constructor(props: FaceDocumentProps) {
super(props);
makeObservable(this);
- this.ref = React.createRef();
}
+ @observable _displayImages: boolean = true;
+
protected createDropTarget = (ele: HTMLDivElement) => {
this._dropDisposer?.();
ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this._props.faceDoc));
@@ -54,20 +55,20 @@ export class FaceDocumentItem extends ObservableReactComponent<FaceDocumentProps
const filteredDocs = docDragData.droppedDocuments.filter(doc => doc.type === DocumentType.IMG);
filteredDocs.forEach(doc => {
// If the current Face Document has no items, and the doc has more than one face descriptor, don't let the user add the document first.
- if ((this._props.faceDoc[DocData].faceDescriptors as List<List<number>>).length === 0 && (doc[DocData].faces as List<List<number>>).length > 1) {
+ if ((this._props.faceDoc[DocData].face_descriptors as List<List<number>>).length === 0 && (doc[DocData][FaceRecognitionHandler.FacesField(doc)] as List<List<number>>).length > 1) {
alert('Cannot add a document with multiple faces as the first item!');
} else {
// Loop through the documents' face descriptors.
// Choose the face with the smallest distance to add.
- const float32Array = (this._props.faceDoc[DocData].faceDescriptors as List<List<number>>).map(faceDescriptor => new Float32Array(Array.from(faceDescriptor)));
- const labeledFaceDescriptor = new faceapi.LabeledFaceDescriptors(StrCast(this._props.faceDoc[DocData].label), float32Array);
+ const float32Array = (this._props.faceDoc[DocData].face_descriptors as List<List<number>>).map(faceDescriptor => new Float32Array(Array.from(faceDescriptor)));
+ const labeledFaceDescriptor = new faceapi.LabeledFaceDescriptors(StrCast(this._props.faceDoc[DocData].face_label), float32Array);
const faceDescriptors: faceapi.LabeledFaceDescriptors[] = [labeledFaceDescriptor];
const faceMatcher = new FaceMatcher(faceDescriptors, 1);
let cur_lowest_distance = 1;
let cur_matching_face = new List<number>();
- (doc[DocData].faces as List<List<number>>).forEach(face => {
+ (doc[DocData][FaceRecognitionHandler.FacesField(doc)] as List<List<number>>).forEach(face => {
// If the face has the current lowest distance, mark it as such
// Once that lowest distance is found, add the face descriptor to the faceDoc, and add the associated doc
const convered_32_array: Float32Array = new Float32Array(Array.from(face));
@@ -79,16 +80,15 @@ export class FaceDocumentItem extends ObservableReactComponent<FaceDocumentProps
}
});
- if (doc[DocData][`FACE DESCRIPTOR - ${this._props.faceDoc[DocData].label}`]) {
- doc[DocData][`FACE DESCRIPTOR - ${this._props.faceDoc[DocData].label}`] = new List<List<number>>([...(doc[DocData][`FACE DESCRIPTOR - ${this._props.faceDoc[DocData].label}`] as List<List<number>>), cur_matching_face]);
+ const faceFieldKey = FaceRecognitionHandler.FaceField(doc, this._props.faceDoc);
+ if (doc[DocData][faceFieldKey]) {
+ Cast(doc[DocData][faceFieldKey], listSpec('number'), null).push(cur_matching_face as unknown as number); // items are lists of numbers, not numbers, but type system can't handle that
} else {
- doc[DocData][`FACE DESCRIPTOR - ${this._props.faceDoc[DocData].label}`] = new List<List<number>>([cur_matching_face]);
+ doc[DocData][faceFieldKey] = new List<List<number>>([cur_matching_face]);
}
- this._props.faceDoc[DocData].associatedDocs = new List<Doc>([...DocListCast(this._props.faceDoc[DocData].associatedDocs), doc]);
- this._props.faceDoc[DocData].faceDescriptors = new List<List<number>>([...(this._props.faceDoc[DocData].faceDescriptors as List<List<number>>), cur_matching_face]);
-
- //const match = faceMatcher.findBestMatch(cur_descriptor);
+ Doc.AddDocToList(this._props.faceDoc[DocData], 'face_docList', doc);
+ Cast(this._props.faceDoc[DocData].face_descriptors, listSpec('number'), null).push(cur_matching_face as unknown as number); // items are lists of numbers, not numbers, but type system can't handle that
}
});
return false;
@@ -107,12 +107,11 @@ export class FaceDocumentItem extends ObservableReactComponent<FaceDocumentProps
/**
* Deletes a Face Document.
*/
- @action
- deleteFaceDocument = () => {
+ deleteFaceDocument = undoable(() => {
if (Doc.ActiveDashboard) {
- Doc.ActiveDashboard[DocData].faceDocuments = new List<Doc>(DocListCast(Doc.ActiveDashboard[DocData].faceDocuments).filter(doc => doc !== this._props.faceDoc));
+ Doc.RemoveDocFromList(Doc.ActiveDashboard[DocData], 'faceDocuments', this._props.faceDoc);
}
- };
+ }, 'remove face');
/**
* Deletes a document from a Face Document's associated docs list.
@@ -120,60 +119,44 @@ export class FaceDocumentItem extends ObservableReactComponent<FaceDocumentProps
*/
@action
deleteAssociatedDoc = (doc: Doc) => {
- this._props.faceDoc[DocData].faceDescriptors = new List<List<number>>(
- (this._props.faceDoc[DocData].faceDescriptors as List<List<number>>).filter(fd => !(doc[DocData][`FACE DESCRIPTOR - ${this._props.faceDoc[DocData].label}`] as List<List<number>>).includes(fd))
+ this._props.faceDoc[DocData].face_descriptors = new List<List<number>>(
+ (this._props.faceDoc[DocData].face_descriptors as List<List<number>>).filter(fd => !(doc[DocData][FaceRecognitionHandler.FaceField(doc, this._props.faceDoc)] as List<List<number>>).includes(fd))
);
- doc[DocData][`FACE DESCRIPTOR - ${this._props.faceDoc[DocData].label}`] = new List<List<number>>();
- this._props.faceDoc[DocData].associatedDocs = new List<Doc>(DocListCast(this._props.faceDoc[DocData].associatedDocs).filter(associatedDoc => associatedDoc !== doc));
+ doc[DocData][FaceRecognitionHandler.FaceField(doc, this._props.faceDoc)] = new List<List<number>>();
+ Doc.RemoveDocFromList(this._props.faceDoc[DocData], 'face_docList', doc);
};
render() {
return (
<div className="face-document-item" ref={ele => this.createDropTarget(ele!)}>
<div className="face-collection-buttons">
- <IconButton tooltip={'Delete Face From Collection'} onPointerDown={this.deleteFaceDocument} icon={'x'} style={{ width: '4px' }} size={Size.XSMALL} />
+ <IconButton tooltip="Delete Face From Collection" onPointerDown={this.deleteFaceDocument} icon={'x'} style={{ width: '4px' }} size={Size.XSMALL} />
</div>
<div className="face-document-top">
- <h1>{StrCast(this._props.faceDoc[DocData].label)}</h1>
+ <h1>{StrCast(this._props.faceDoc[DocData].face_label)}</h1>
</div>
<IconButton
- tooltip={'See image information'}
+ tooltip="See image information"
onPointerDown={() => this.onDisplayClick()}
- icon={this._displayImages ? <FontAwesomeIcon icon="caret-up" /> : <FontAwesomeIcon icon="caret-down" />}
+ icon={<FontAwesomeIcon icon={this._displayImages ? 'caret-up' : 'caret-down'} />}
color={MarqueeOptionsMenu.Instance.userColor}
style={{ width: '19px' }}
/>
{this._displayImages ? (
<div className="face-document-image-container">
- {DocListCast(this._props.faceDoc[DocData].associatedDocs).map(doc => {
+ {DocListCast(this._props.faceDoc[DocData].face_docList).map(doc => {
const [name, type] = ImageCast(doc[Doc.LayoutFieldKey(doc)]).url.href.split('.');
return (
<div className="image-wrapper" key={Utils.GenerateGuid()}>
- <img
- onClick={async () => {
- await DocumentView.showDocument(doc, { willZoomCentered: true });
- }}
- style={{ maxWidth: '60px', margin: '10px' }}
- src={`${name}_o.${type}`}
- />
+ <img onClick={() => DocumentView.showDocument(doc, { willZoomCentered: true })} style={{ maxWidth: '60px', margin: '10px' }} src={`${name}_o.${type}`} />
<div className="remove-item">
- <IconButton
- tooltip={'Remove Doc From Face Collection'}
- onPointerDown={() => {
- this.deleteAssociatedDoc(doc);
- }}
- icon={'x'}
- style={{ width: '4px' }}
- size={Size.XSMALL}
- />
+ <IconButton tooltip={'Remove Doc From Face Collection'} onPointerDown={() => this.deleteAssociatedDoc(doc)} icon={'x'} style={{ width: '4px' }} size={Size.XSMALL} />
</div>
</div>
);
})}
</div>
- ) : (
- <div></div>
- )}
+ ) : null}
</div>
);
}
@@ -190,9 +173,8 @@ export class FaceCollectionBox extends ViewBoxBaseComponent<FieldViewProps>() {
@computed get currentDocs() {
if (Doc.ActiveDashboard) {
return DocListCast(Doc.ActiveDashboard[DocData].faceDocuments);
- } else {
- return [];
}
+ return [];
}
constructor(props: FieldViewProps) {
@@ -204,9 +186,9 @@ export class FaceCollectionBox extends ViewBoxBaseComponent<FieldViewProps>() {
render() {
return (
<div className="searchBox-container" style={{ pointerEvents: 'all', color: SnappingManager.userColor, background: SnappingManager.userBackgroundColor }}>
- {this.currentDocs.map(doc => {
- return <FaceDocumentItem key={doc[Id]} faceDoc={doc} />;
- })}
+ {this.currentDocs.map(doc => (
+ <FaceDocumentItem key={doc[Id]} faceDoc={doc} />
+ ))}
</div>
);
}