update text detection result format

This commit is contained in:
Abhinav 2022-03-19 20:28:59 +05:30
parent a6e731b09e
commit 4d6d441798
8 changed files with 70 additions and 59 deletions

View file

@ -199,7 +199,7 @@ const PhotoFrame = ({
return false; return false;
} }
if (search.text && search.text.indexOf(item.id) === -1) { if (search.text && search.text.files.indexOf(item.id) === -1) {
return false; return false;
} }

View file

@ -34,7 +34,7 @@ import { FILE_TYPE } from 'constants/file';
import { GalleryContext } from 'pages/gallery'; import { GalleryContext } from 'pages/gallery';
import { AppContext } from 'pages/_app'; import { AppContext } from 'pages/_app';
import { Col } from 'react-bootstrap'; import { Col } from 'react-bootstrap';
import { Person, ThingClass } from 'types/machineLearning'; import { Person, ThingClass, WordGroup } from 'types/machineLearning';
import { IndexStatus } from 'types/machineLearning/ui'; import { IndexStatus } from 'types/machineLearning/ui';
import { PeopleList } from './MachineLearning/PeopleList'; import { PeopleList } from './MachineLearning/PeopleList';
@ -217,14 +217,18 @@ export default function SearchBar(props: Props) {
) )
); );
const textResult = await searchText(searchPhrase); const textResults = await searchText(searchPhrase);
options.push({
type: SuggestionType.TEXT,
value: textResult.files,
label: textResult.text,
} as Suggestion);
options.push(
...textResults.map(
(searchResult) =>
({
type: SuggestionType.TEXT,
value: searchResult,
label: searchResult.word,
} as Suggestion)
)
);
return options; return options;
}; };
@ -266,7 +270,7 @@ export default function SearchBar(props: Props) {
props.setOpen(true); props.setOpen(true);
break; break;
case SuggestionType.TEXT: case SuggestionType.TEXT:
props.setSearch({ text: selectedOption.value as number[] }); props.setSearch({ text: selectedOption.value as WordGroup });
props.setOpen(true); props.setOpen(true);
break; break;
} }

View file

@ -2,6 +2,7 @@ import {
MLSyncContext, MLSyncContext,
MLSyncFileContext, MLSyncFileContext,
DetectedText, DetectedText,
WordGroup,
} from 'types/machineLearning'; } from 'types/machineLearning';
import { imageBitmapToBlob } from 'utils/image'; import { imageBitmapToBlob } from 'utils/image';
import { isDifferentOrOld, getAllTextFromMap } from 'utils/machineLearning'; import { isDifferentOrOld, getAllTextFromMap } from 'utils/machineLearning';
@ -35,19 +36,24 @@ class TextService {
); );
const textDetections = const textDetections =
await syncContext.textDetectionService.detectText( await syncContext.textDetectionService.detectText(
await imageBitmapToBlob(imageBitmap) new File(
[await imageBitmapToBlob(imageBitmap)],
fileContext.enteFile.id.toString()
)
); );
// console.log('3 TF Memory stats: ', tf.memory());
// TODO: reenable faces filtering based on width const detectedText: DetectedText[] = textDetections.data.words.map(
const detectedText: DetectedText = { ({ bbox, confidence, text }) => ({
fileID: fileContext.enteFile.id, fileID: fileContext.enteFile.id,
detection: textDetections, detection: { bbox, confidence, word: text },
}; })
);
newMlFile.text = detectedText; newMlFile.text = detectedText;
// ?.filter((f) => console.log(
// f.box.width > syncContext.config.faceDetection.minFaceSize '[MLService] Detected text: ',
// ); fileContext.enteFile.metadata.title,
console.log('[MLService] Detected text: ', newMlFile.text); newMlFile.text
);
} }
async getAllSyncedTextMap(syncContext: MLSyncContext) { async getAllSyncedTextMap(syncContext: MLSyncContext) {
@ -59,32 +65,23 @@ class TextService {
return syncContext.allSyncedTextMap; return syncContext.allSyncedTextMap;
} }
public async getAllText() { public async clusterWords(): Promise<WordGroup[]> {
const allTextMap = await mlIDbStorage.getAllTextMap(); const allTextMap = await mlIDbStorage.getAllTextMap();
const allText = getAllTextFromMap(allTextMap); const allText = getAllTextFromMap(allTextMap);
return allText; const textCluster = new Map<string, number[]>();
allText.map((text) => {
if (!textCluster.has(text.detection.word)) {
textCluster.set(text.detection.word, []);
}
const objectsInCluster = textCluster.get(text.detection.word);
objectsInCluster.push(text.fileID);
});
return [...textCluster.entries()].map(([word, files]) => ({
word,
files,
}));
} }
// public async clusterThingClasses(
// syncContext: MLSyncContext
// ): Promise<ThingClass[]> {
// const allTextMap = await this.getAllSyncedTextMap(syncContext);
// const allText = getAllTextFromMap(allTextMap);
// const textCluster = new Map<string, number[]>();
// allObjects.map((object) => {
// if (!objectClusters.has(object.detection.class)) {
// objectClusters.set(object.detection.class, []);
// }
// const objectsInCluster = objectClusters.get(object.detection.class);
// objectsInCluster.push(object.fileID);
// });
// return [...objectClusters.entries()].map(([className, files], id) => ({
// id,
// className,
// files,
// }));
// }
// async syncThingClassesIndex(syncContext: MLSyncContext) { // async syncThingClassesIndex(syncContext: MLSyncContext) {
// const filesVersion = await mlIDbStorage.getIndexVersion('files'); // const filesVersion = await mlIDbStorage.getIndexVersion('files');
// console.log( // console.log(

View file

@ -184,11 +184,12 @@ export async function searchThing(searchPhrase: string) {
} }
export async function searchText(searchPhrase: string) { export async function searchText(searchPhrase: string) {
const texts = await textService.getAllText(); const texts = await textService.clusterWords();
const files = texts return texts
.filter((text) => .filter((text) => text.word.toLocaleLowerCase().includes(searchPhrase))
text.detection.data.text.toLocaleLowerCase().includes(searchPhrase) .map(({ word, files }) => ({
) word,
.map(({ fileID }) => fileID); files,
return { text: searchPhrase, files }; }))
.slice(0, 4);
} }

View file

@ -1,7 +1,7 @@
import { SetDialogMessage } from 'components/MessageDialog'; import { SetDialogMessage } from 'components/MessageDialog';
import { Collection } from 'types/collection'; import { Collection } from 'types/collection';
import { EnteFile } from 'types/file'; import { EnteFile } from 'types/file';
import { Person, ThingClass } from 'types/machineLearning'; import { Person, ThingClass, WordGroup } from 'types/machineLearning';
import { DateValue, Bbox } from 'types/search'; import { DateValue, Bbox } from 'types/search';
export type SelectedState = { export type SelectedState = {
@ -20,7 +20,7 @@ export type Search = {
fileIndex?: number; fileIndex?: number;
person?: Person; person?: Person;
thing?: ThingClass; thing?: ThingClass;
text?: number[]; text?: WordGroup;
}; };
export interface SearchStats { export interface SearchStats {
resultCount: number; resultCount: number;

View file

@ -205,7 +205,16 @@ export interface ThingClass {
files: Array<number>; files: Array<number>;
} }
export declare type TextDetection = Tesseract.RecognizeResult; export interface WordGroup {
word: string;
files: Array<number>;
}
export interface TextDetection {
bbox: Tesseract.Bbox;
word: string;
confidence: number;
}
export interface DetectedText { export interface DetectedText {
fileID: number; fileID: number;
@ -216,7 +225,7 @@ export interface MlFileData {
fileId: number; fileId: number;
faces?: Face[]; faces?: Face[];
things?: Thing[]; things?: Thing[];
text?: DetectedText; text?: DetectedText[];
imageSource?: ImageType; imageSource?: ImageType;
imageDimensions?: Dimensions; imageDimensions?: Dimensions;
faceDetectionMethod?: Versioned<FaceDetectionMethod>; faceDetectionMethod?: Versioned<FaceDetectionMethod>;
@ -315,7 +324,7 @@ export interface MLSyncContext {
nSyncedFaces: number; nSyncedFaces: number;
allSyncedFacesMap?: Map<number, Array<Face>>; allSyncedFacesMap?: Map<number, Array<Face>>;
allSyncedThingsMap?: Map<number, Array<Thing>>; allSyncedThingsMap?: Map<number, Array<Thing>>;
allSyncedTextMap?: Map<number, DetectedText>; allSyncedTextMap?: Map<number, Array<DetectedText>>;
tsne?: any; tsne?: any;
error?: Error; error?: Error;
@ -376,7 +385,7 @@ export interface ObjectDetectionService {
export interface TextDetectionService { export interface TextDetectionService {
method: Versioned<TextDetectionMethod>; method: Versioned<TextDetectionMethod>;
// init(): Promise<void>; // init(): Promise<void>;
detectText(image: Blob): Promise<TextDetection>; detectText(image: File): Promise<Tesseract.RecognizeResult>;
dispose(): Promise<void>; dispose(): Promise<void>;
} }

View file

@ -205,8 +205,8 @@ export function getAllThingsFromMap(allObjectsMap: Map<number, Array<Thing>>) {
return [...allObjectsMap.values()].flat(); return [...allObjectsMap.values()].flat();
} }
export function getAllTextFromMap(allTextMap: Map<number, DetectedText>) { export function getAllTextFromMap(allTextMap: Map<number, DetectedText[]>) {
return [...allTextMap.values()]; return [...allTextMap.values()].flat();
} }
export async function getLocalFile(fileId: number) { export async function getLocalFile(fileId: number) {
const localFiles = await getLocalFiles(); const localFiles = await getLocalFiles();

View file

@ -315,7 +315,7 @@ class MLIDbStorage {
console.time('getAllTextMap'); console.time('getAllTextMap');
const db = await this.db; const db = await this.db;
const allFiles = await db.getAll('files'); const allFiles = await db.getAll('files');
const allTextMap = new Map<number, DetectedText>(); const allTextMap = new Map<number, DetectedText[]>();
allFiles.forEach( allFiles.forEach(
(mlFileData) => (mlFileData) =>
mlFileData.text && mlFileData.text &&