Skip to content

Commit

Permalink
got sldier working
Browse files Browse the repository at this point in the history
  • Loading branch information
rmdocherty committed Jul 26, 2024
1 parent adfcfbe commit 564f5e8
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 21 deletions.
18 changes: 13 additions & 5 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ const App = () => {
selectedConf: [selectedConf, setSelectedConf],
errVF: [errVF, setErrVF],
targetL: [targetL, setTargetL],
accurateFractions: [, setAccurateFractions],
pfB: [, setPfB],
accurateFractions: [accurateFractions, setAccurateFractions],
analysisInfo: [, setAnalysisInfo],
menuState: [menuState, setMenuState],
errorState: [errorState, setErrorState],
Expand Down Expand Up @@ -124,23 +125,30 @@ const App = () => {
const resp = await fetch(REPR_ENDPOINT, { method: 'POST', body: formData });
const obj = await resp.json();

const absErr: number = obj["abs_err"]

setMenuState('conf_result');
setShowFullResults(true);
setAnalysisInfo({
integralRange: obj["cls"],
z: 1,
stdModel: obj["std_model"],
percentageErr: obj["percent_err"],
absError: obj["abs_err"],
absError: absErr,
lForDefaultErr: obj["l"],
vf: 1
vf: 1,
pf: obj['pf_1d'],
cumSumSum: obj["cum_sum_sum"]
})

const vals = imageInfo?.phaseVals!
const phaseFrac = accurateFractions![vals[selectedPhase - 1]]
setPfB([phaseFrac - absErr, phaseFrac + absErr])

if (obj["cls"] > IR_LIMIT_PX) { setShowWarning("cls") }
const minSide = Math.min(imageInfo?.width!, imageInfo?.height!)
if (obj["l"] < minSide) { setShowWarning("over") }

console.log(obj["pf_1d"])

setTargetL(obj["l"]);
} catch (e) {
const error = e as Error;
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/components/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ const Result = () => {
selectedPhase: [selectedPhase,],
selectedConf: [selectedConf, setSelectedConf],
errVF: [errVF, setErrVF],
pfB: [pfB,],
accurateFractions: [accurateFractions,],
menuState: [, setMenuState],
showInfo: [, setShowInfo],
Expand Down Expand Up @@ -224,8 +225,10 @@ const Result = () => {
refs.map((r, i) => restyleAccordionHeaders(r, (i == 0), headerHex));
}, [selectedPhase])


const absErrFromPFB = (pfB![1] - pfB![0]) / 2
const beforeBoldText = `The phase fraction in the segmented image is ${phaseFrac.toFixed(3)}. Assuming perfect segmentation, the model proposed by Dahari et al. suggests that `
const boldText = `we can be ${selectedConf}% confident of the true phase fraction being within ${perErr?.toFixed(1)}% of this value (i.e. ${phaseFrac.toFixed(roundTo)}±${(absErr).toFixed(roundTo)})`
const boldText = `we can be ${selectedConf.toFixed(1)}% confident of the true phase fraction being within ${perErr?.toFixed(1)}% of this value (i.e. ${phaseFrac.toFixed(roundTo)}±${(absErrFromPFB).toFixed(roundTo)})`
const copyText = beforeBoldText + boldText

const copyBtn = () => { navigator.clipboard.writeText(copyText) }
Expand Down
114 changes: 100 additions & 14 deletions frontend/src/components/NormalSlider.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { useEffect, useRef, useState } from "react";
import { useEffect, useRef, useState, useContext } from "react";
import AppContext, { rgbaToHex, colours } from "./interfaces";
import { getPhaseFraction } from "./imageLogic";

import InputGroup from "react-bootstrap/InputGroup";
import Form from "react-bootstrap/Form";

const CANVAS_WIDTH = 600;
const CANVAS_HEIGHT = 350;
Expand All @@ -23,6 +28,17 @@ const normalDist = (x: number, mu: number, sigma: number) => {
return (1 / (sigma * Math.sqrt(2 * Math.PI))) * Math.exp(-0.5 * Math.pow(((x - mu) / sigma), 2))
};

const getPredictionInterval = (conf: number, pf1D: Array<number>, cumSumSum: Array<number>) => {
const halfConfLevel = (1 + conf) / 2;

const ltConf = cumSumSum.map(x => x > ((1 - halfConfLevel)));
const gtConf = cumSumSum.map(x => x > halfConfLevel);

const ltConfIdx = ltConf.findIndex((x) => x)
const gtConfIdx = gtConf.findIndex((x) => x)
return [pf1D[ltConfIdx], pf1D[gtConfIdx]]
}

const indices = [...Array(CANVAS_WIDTH).keys()];

const tmpMu = 0.4;
Expand All @@ -47,8 +63,6 @@ interface NormalParams {
start_pf: number,
end_pf: number,
max_y: number,
conf_lb: number,
conf_ub: number,
}

const LIGHT_GREY = "#838383d9"
Expand All @@ -57,25 +71,49 @@ const DARK_GREY = "#363636f0"

const xAxisStyle: DrawStyle = { fillColour: LIGHT_GREY, lineColour: LIGHT_GREY, lineWidth: 3, toFill: false, lineCap: null, lineDash: null }
const yAxisStyle: DrawStyle = { fillColour: LIGHT_GREY, lineColour: LIGHT_GREY, lineWidth: 3, toFill: false, lineCap: null, lineDash: [4, 10] }
const curveStyle: DrawStyle = { fillColour: 'red', lineColour: 'red', lineWidth: 3, toFill: false, lineCap: null, lineDash: null }
const curveStyle: DrawStyle = { fillColour: 'red', lineColour: 'red', lineWidth: 4, toFill: false, lineCap: null, lineDash: null }
const shadeStyle: DrawStyle = { fillColour: TRANS_RED, lineColour: TRANS_RED, lineWidth: 3, toFill: true, lineCap: null, lineDash: null }

const NormalSlider = () => {
const {
imageInfo: [imageInfo,],
analysisInfo: [analysisInfo,],
selectedPhase: [selectedPhase,],
selectedConf: [selectedConf, setSelectedConf],
errVF: [errVF, setErrVF],
pfB: [pfB, setPfB],
accurateFractions: [accurateFractions,],
showFullResults: [showFullResults, setShowFullResults],
} = useContext(AppContext)!

const canvasRef = useRef<HTMLCanvasElement>(null);
const [params, setParams] = useState<NormalParams>({
mu: tmpMu,
sigma: tmpSigma,
start_pf: tmpStart,
end_pf: tmpEnd,
max_y: tmpMax,
conf_lb: tmpLB,
conf_ub: tmpUB
});

const c = colours[selectedPhase];
const headerHex = rgbaToHex(c[0], c[1], c[2], c[3]);
const shadedHex = rgbaToHex(c[0], c[1], c[2], 120);

const vals = imageInfo?.phaseVals!
const phaseFrac = (accurateFractions != null) ?
accurateFractions[vals[selectedPhase - 1]]
: getPhaseFraction(
imageInfo?.previewData.data!,
vals[selectedPhase - 1]
);


const getShadedPoints = (newLB: number, newUB: number) => {
const pxLBx = xDataToPx(newLB, params.start_pf, params.end_pf, CANVAS_WIDTH);
const pxUBx = xDataToPx(newUB, params.start_pf, params.end_pf, CANVAS_WIDTH);

console.log(pxLBx, pxUBx, newLB, newUB)

const nNew = pxUBx - pxLBx;
const inds = [...Array(nNew).keys()].map((x) => (x + pxLBx));
const xData = inds.map((x) => xPxToData((x), params.start_pf, params.end_pf, CANVAS_WIDTH));
Expand All @@ -88,6 +126,12 @@ const NormalSlider = () => {
return { xPoints: xPadded, yPoints: yPadded };
}

const clearCanv = () => {
const canv = canvasRef.current!;
const ctx = canv.getContext('2d')!;
ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
}

const drawPoints = (xPoints: Array<number>, yPoints: Array<number>, style: DrawStyle) => {
const canv = canvasRef.current!;
const ctx = canv.getContext('2d')!;
Expand Down Expand Up @@ -124,31 +168,73 @@ const NormalSlider = () => {
ctx.font = "24px Noto Sans";
ctx.fillStyle = DARK_GREY;
for (let i = 0; i < dataVals.length; i++) {
const offset = (i == 1) ? 24 : -8
const val = dataVals[i].toFixed(3)
ctx.fillText(val, xPositions[i] - 30, H_GAUSS + 24);
ctx.fillText(val, xPositions[i] - 30, H_GAUSS + offset);
}
}

useEffect(() => {
// generate, draw
const redraw = () => {
if (pfB == null) { return }
const xData = indices.map((x) => xPxToData((x), params.start_pf, params.end_pf, CANVAS_WIDTH));
const yData = xData.map((xd) => normalDist(xd, params.mu, params.sigma));
const maxY = normalDist(params.mu, params.mu, params.sigma);

const yPoints = yData.map((yd) => yDataToPx(yd, 0, maxY + 0.2, H_GAUSS));
const sp = getShadedPoints(params.conf_lb, params.conf_ub);
console.log(sp)
const sp = getShadedPoints(pfB[0], pfB[1]);

curveStyle.lineColour = headerHex;
curveStyle.fillColour = headerHex;
shadeStyle.lineColour = shadedHex;
shadeStyle.fillColour = shadedHex;

clearCanv()
drawPoints([0, CANVAS_WIDTH], [H_GAUSS, H_GAUSS], xAxisStyle)
drawPoints([CANVAS_WIDTH / 2, CANVAS_WIDTH / 2], [0, H_GAUSS], yAxisStyle)
drawPoints(indices, yPoints, curveStyle);
drawPoints(sp.xPoints, sp.yPoints, shadeStyle);
drawText([params.conf_lb, params.mu, params.conf_ub], [sp.xPoints[0], CANVAS_WIDTH / 2, sp.xPoints[sp.xPoints.length - 1]]);
}, [])
drawText([pfB[0], params.mu, pfB[1]!], [sp.xPoints[0], CANVAS_WIDTH / 2, sp.xPoints[sp.xPoints.length - 1]]);
}

const setConf = (e: any) => {
setSelectedConf(Number(e.target!.value));
};

useEffect(() => {
// generate, draw
redraw()
}, [params])

useEffect(() => {
const result = getPredictionInterval(selectedConf / 100, analysisInfo?.pf!, analysisInfo?.cumSumSum!)
const [lbData, ubData] = [result[0], result[1]]
const sigma = 0.5 * (ubData - lbData) // sigma should be fixed as ub and lb changes - this should be reflected in results as well
console.log(analysisInfo?.stdModel!)
const newMaxY = normalDist(phaseFrac, phaseFrac, analysisInfo?.stdModel!)
const newStartPf = 0.5 * phaseFrac
const newEndPf = 1.5 * phaseFrac
const newParams: NormalParams = { mu: phaseFrac, sigma: sigma, start_pf: newStartPf, end_pf: newEndPf, max_y: newMaxY }
setParams(newParams)
}, [analysisInfo])

useEffect(() => {
redraw()
}, [pfB])

useEffect(() => {
const result = getPredictionInterval(selectedConf / 100, analysisInfo?.pf!, analysisInfo?.cumSumSum!)
setPfB(result)
}, [selectedConf])

return (
<div>
<canvas width={CANVAS_WIDTH} height={CANVAS_HEIGHT} ref={canvasRef} />
<canvas width={CANVAS_WIDTH} height={CANVAS_HEIGHT} ref={canvasRef} style={{ marginBottom: '0.5em' }} />
<InputGroup>
<InputGroup.Text>Confidence in Phase Fraction Bounds (%):</InputGroup.Text>

<Form.Control type="number" min={0} max={100} value={selectedConf} step={0.5} onChange={(e) => setConf(e)} width={1} size="sm"></Form.Control>
<Form.Range min={80} max={100} value={selectedConf} step={0.1} onChange={(e) => setConf(e)} />
</InputGroup>
</div>)
}

Expand Down
2 changes: 2 additions & 0 deletions frontend/src/components/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const AppContextProvider = (props: {
const [selectedConf, setSelectedConf] = useState<number>(95);
const [errVF, setErrVF] = useState<number>(5);
const [targetL, setTargetL] = useState<number | null>(null);
const [pfB, setPfB] = useState<number[] | null>(null);
// server data
const [accurateFractions, setAccurateFractions] = useState<{ [val: number]: number } | null>(null);
const [analysisInfo, setAnalysisInfo] = useState<AnalysisInfo | null>(null);
Expand All @@ -32,6 +33,7 @@ const AppContextProvider = (props: {
selectedConf: [selectedConf, setSelectedConf],
errVF: [errVF, setErrVF],
targetL: [targetL, setTargetL],
pfB: [pfB, setPfB],
accurateFractions: [accurateFractions, setAccurateFractions],
analysisInfo: [analysisInfo, setAnalysisInfo],
menuState: [menuState, setMenuState],
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/components/interfaces.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ interface contextProps {
targetL: number | null,
setTargetL: (e: number | null) => void,
];
pfB: [
pfB: number[] | null,
setpfB: (e: number[] | null) => void,
];
accurateFractions: [
accurateFractions: { [val: number]: number } | null,
setAccurateFractions: (e: { [val: number]: number } | null) => void,
Expand Down Expand Up @@ -100,10 +104,13 @@ export interface ImageLoadInfo {
export interface AnalysisInfo {
integralRange: number,
z: number,
stdModel: number
percentageErr: number,
absError: number,
lForDefaultErr: number,
vf: number
vf: number,
pf: Array<number>,
cumSumSum: Array<number>
}

export interface ErrorMessage {
Expand Down
1 change: 1 addition & 0 deletions representativity/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,7 @@ def make_error_prediction(
result = {
"integral_range": integral_range,
"z": z,
"std_model": std_model,
"percent_err": percentage_err_for_img,
"abs_err": abs_err_for_img,
"l": l_for_err_targ,
Expand Down
1 change: 1 addition & 0 deletions representativity/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ def representativity(request) -> Response:
out = {
"abs_err": result["abs_err"],
"percent_err": result["percent_err"] * 100,
"std_model": result["std_model"],
"l": result["l"],
"cls": result["integral_range"],
"pf_1d": result["pf_1d"],
Expand Down

0 comments on commit 564f5e8

Please sign in to comment.