Spaces:
Running
Running
File size: 3,896 Bytes
cbd23a5 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
import { createContext, useContext, useState, ReactNode, useCallback } from 'react';
import { ClassificationResult } from './api';
export interface ImageState {
// Current image
file: File | null;
preview: string | null;
processedImage: string | null;
// Classification results
classificationResults: ClassificationResult[] | null;
// View (corrected takes priority)
predictedView: string | null;
correctedView: string | null;
// Session ID for feedback
sessionId: string;
}
interface ImageContextType extends ImageState {
// Actions
setFile: (file: File | null, preview: string | null) => void;
setClassificationResults: (results: ClassificationResult[] | null, processedImage?: string | null) => void;
setCorrectedView: (view: string | null) => void;
resetState: () => void;
// Computed
currentView: string | null; // Returns correctedView if set, otherwise topPrediction
isViewGAEligible: boolean;
}
// GA-eligible views (match keys from prompt_fetal_view.json)
export const GA_ELIGIBLE_VIEWS = [
"brain", // Head Circumference
"abdomen", // Abdominal Circumference
"femur", // Femur Length
];
// Display names for GA biometry types
export const GA_BIOMETRY_LABELS: Record<string, string> = {
"brain": "Head Circumference (HC)",
"abdomen": "Abdominal Circumference (AC)",
"femur": "Femur Length (FL)",
};
const generateSessionId = () => {
return Math.random().toString(36).substring(2, 10);
};
const initialState: ImageState = {
file: null,
preview: null,
processedImage: null,
classificationResults: null,
predictedView: null,
correctedView: null,
sessionId: generateSessionId(),
};
const ImageContext = createContext<ImageContextType | null>(null);
export function ImageProvider({ children }: { children: ReactNode }) {
const [state, setState] = useState<ImageState>(initialState);
const setFile = useCallback((file: File | null, preview: string | null) => {
setState(prev => ({
...prev,
file,
preview,
processedImage: null,
classificationResults: null,
predictedView: null,
correctedView: null,
}));
}, []);
const setClassificationResults = useCallback((
results: ClassificationResult[] | null,
processedImage: string | null = null
) => {
setState(prev => ({
...prev,
classificationResults: results,
processedImage: processedImage || prev.processedImage,
predictedView: results && results.length > 0 ? results[0].label : null,
}));
}, []);
const setCorrectedView = useCallback((view: string | null) => {
setState(prev => ({
...prev,
correctedView: view,
}));
}, []);
const resetState = useCallback(() => {
setState({
...initialState,
sessionId: generateSessionId(),
});
}, []);
// Computed: current view (corrected takes priority)
const currentView = state.correctedView || state.predictedView;
// Computed: is view eligible for GA estimation
const isViewGAEligible = currentView ? GA_ELIGIBLE_VIEWS.includes(currentView) : false;
return (
<ImageContext.Provider
value={{
...state,
setFile,
setClassificationResults,
setCorrectedView,
resetState,
currentView,
isViewGAEligible,
}}
>
{children}
</ImageContext.Provider>
);
}
export function useImageContext() {
const context = useContext(ImageContext);
if (!context) {
throw new Error('useImageContext must be used within an ImageProvider');
}
return context;
}
|