Skip to content

Commit

Permalink
Uniform and non-uniform scalar bars for deepssm heatmaps
Browse files Browse the repository at this point in the history
  • Loading branch information
JakeWags committed Apr 12, 2024
1 parent 61462b2 commit 81f1a08
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 91 deletions.
7 changes: 3 additions & 4 deletions shapeworks_cloud/core/deepssm_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,10 +426,9 @@ def post_command_function(project, download_dir, result_data, project_filename):
filename = file1.split('.')[0]

# filename here represents the SUBJECT INDEX OF THE TEST SPLIT
subject_name = (
result_data['testing']['test_split_subjects'][int(filename)]
.get_display_name()
)
subject_name = result_data['testing']['test_split_subjects'][
int(filename)
].get_display_name()

test_pair = models.DeepSSMTestingData.objects.create(
project=project,
Expand Down
12 changes: 7 additions & 5 deletions web/shapeworks/src/api/rest.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AnalysisParams, DataObject, Dataset, LandmarkInfo, Constraints, Project, Subject } from "@/types";
import { apiClient } from "./auth";
import { loadGroomedShapeForObject, loadParticlesForObject } from "@/store";
import { deepSSMDataTab, loadGroomedShapeForObject, loadParticlesForObject } from "@/store";


export async function getDatasets(search: string | undefined): Promise<Dataset[]>{
Expand Down Expand Up @@ -101,10 +101,12 @@ export async function getOptimizedParticlesForDataObject(
export async function getGroomedShapeForDataObject(
type: string, id: number, projectId: number|undefined
) {
const plural = `${type}${type == 'mesh' ?'es' :'s'}`
return (await apiClient.get(`/groomed-${plural}`, {
params: {[type]: id, project: projectId}
})).data.results
if (type !== 'image') {
const plural = `${type}${type == 'mesh' ?'es' :'s'}`
return (await apiClient.get(`/groomed-${plural}`, {
params: {[type]: id, project: projectId}
})).data.results
}
}

export async function getReconstructedSamplesForProject(
Expand Down
2 changes: 1 addition & 1 deletion web/shapeworks/src/components/Analysis/PCA.vue
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ import { groupBy } from '../../helper';
currentTasks.value[selectedProject.value.id] = {}
}
const taskIds = await spawnJob("analyze", {"analysis": {range, steps: numSteps}}); // Record<string, any>
const taskIds = await spawnJob("analyze", {"range": range, "steps": numSteps}); // Record<string, any>
if(!taskIds || taskIds.length === 0) {
message.value = `Failed to submit analysis job.`
Expand Down
133 changes: 95 additions & 38 deletions web/shapeworks/src/components/ShapeViewer/methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,15 +251,12 @@ export default {
if ([1, 2].includes(deepSSMDataTab.value)) {
const data = shapeData.getPointData().getArrayByName('deepssm_error').getData()
let normalizeRange;
console.log('uniform scale?', uniformScale.value)
if (uniformScale.value) {
normalizeRange = deepSSMErrorGlobalRange.value
} else {
normalizeRange = [Math.min(...data), Math.max(...data)]
}
console.log('normalize range', normalizeRange)
const normalizedData = data.map((v) => v / (normalizeRange[1] - normalizeRange[0]))
console.log('normalized data', normalizedData)
const normalizedArray = vtkDataArray.newInstance({
name: 'deepssm_error_normalized',
values: normalizedData,
Expand Down Expand Up @@ -378,51 +375,107 @@ export default {
if (showDifferenceFromMeanMode.value) {
this.lookupTable.setMappingRange(0, 1)
this.lookupTable.updateRange();
this.prepareColorScale()
this.prepareColorScales()
}

this.render()
},
prepareColorScale() {
const canvas = this.$refs.colors
const labelDiv = this.$refs.colorLabels;
let dataRange = this.lookupTable.getMappingRange()

if (uniformScale.value) {
dataRange = deepSSMErrorGlobalRange.value
prepareColorScales() {
const canvasDiv = this.$refs.colors
const titleDiv = this.$refs.colorsTitle
const labelsDiv = this.$refs.colorsLabels
canvasDiv.innerHTML = ""
titleDiv.innerHTML = ""
labelsDiv.innerHTML = ""
if (showDifferenceFromMeanMode.value || uniformScale.value) {
const canvas = document.createElement('canvas')
canvas.style.right = "10px"
canvas.style.height = "100%"
canvas.style.width = "20px"
canvasDiv.appendChild(canvas)

const labels = document.createElement('div')
labels.style.right = "35px"
labels.style.height = "100%"
labelsDiv.appendChild(labels)

let range = [-5, 5]
if (showDifferenceFromMeanMode.value) {
titleDiv.innerHTML = "Distance from particle on mean shape"
} else if (uniformScale.value) {
titleDiv.innerHTML = "DeepSSMError"
range = deepSSMErrorGlobalRange.value
}
this.prepareColorScale(canvas, labels, range)
} else {
const { width, height } = this.$refs.vtk.getBoundingClientRect()
Object.entries(this.data).forEach(([label, data], i) => {
const [x1, y1, x2, y2] = this.grid[i]
const canvas = document.createElement('canvas')
canvas.style.top = `calc(${(1 - y2) * 100}%)`
canvas.style.right = `calc(${(1 - x2) * 100}% + 10px)`
canvas.style.height = `${(y2 - y1) * 96}%`
canvas.style.width = "10px"
canvasDiv.appendChild(canvas)

const labels = document.createElement('div')
labels.style.top = `calc(${(1 - y2) * 100}%)`
labels.style.right = `calc(${(1 - x2) * 100}% + 35px)`
labels.style.height = `${(y2 - y1) * 96}%`
labelsDiv.appendChild(labels)

let range = [-5, 5]
data[0].shape.forEach((shape) => {
if (shape.getClassName() === 'vtkPolyData') {
const arr = shape.getPointData().getArrayByName('deepssm_error').getData()
if (arr) range = [Math.min(...arr), Math.max(...arr)]
}
})
this.prepareColorScale(canvas, labels, range)
})
}

if (canvas && labelDiv) {
},
prepareColorScale(canvas, labels, range) {
if (canvas && labels) {
canvas.style.position = "absolute"
canvas.style.zIndex = "1"
const { width, height } = canvas
const context = canvas.getContext('2d', { willReadFrequently: true });
const pixelsArea = context.getImageData(0, 0, width, height);
const colorsData = this.lookupTable.getUint8Table(
...dataRange,
...this.lookupTable.getMappingRange(),
height * width,
true
)

pixelsArea.data.set(colorsData)
context.putImageData(pixelsArea, 0, 0)

const labelProportions = [1, 0.75, 0.5, 0.25, 0];
labels.innerHTML = ''
labels.style.position = "absolute"
labels.style.display = "flex"
labels.style.flexDirection = "column"
labels.style.justifyContent = "space-between"
labels.style.alignItems = "flex-end"
labels.style.textAlign = "right"
labelProportions.forEach((p) => {
const child = document.createElement('span');
child.innerHTML = Math.round(p * (range[1] - range[0]) + range[0]);
labels.appendChild(child);
})
}

const labels = [1, 0.75, 0.5, 0.25, 0];
labelDiv.innerHTML = ''
labels.forEach((l) => {
const child = document.createElement('span');
child.innerHTML = l * (dataRange[1] - dataRange[0]) + dataRange[0];
labelDiv.appendChild(child);
})
},
prepareLabelCanvas() {
const labelCanvas = this.$refs.labels
const labelCanvasContext = labelCanvas.getContext('2d')
const { clientWidth, clientHeight } = this.$refs.vtk;
// increase the resolution of the canvas so text isn't blurry
this.labelCanvas.width = clientWidth;
this.labelCanvas.height = clientHeight;
labelCanvas.width = clientWidth;
labelCanvas.height = clientHeight;

this.labelCanvasContext.clearRect(0, 0, this.labelCanvas.width, this.labelCanvas.height)
this.labelCanvasContext.font = "16px Arial";
this.labelCanvasContext.fillStyle = "white";
labelCanvasContext.clearRect(0, 0, labelCanvas.width, labelCanvas.height)
labelCanvasContext.font = "16px Arial";
labelCanvasContext.fillStyle = "white";
},
populateRenderer(renderer, shapes) {
this.addShapes(renderer, shapes.map(({ shape }) => shape));
Expand Down Expand Up @@ -463,10 +516,12 @@ export default {
const newRenderer = vtkRenderer.newInstance({ background: [0.115, 0.115, 0.115] });
const bounds = this.grid[i];

this.labelCanvasContext.fillText(
const labelCanvas = this.$refs.labels
const labelCanvasContext = labelCanvas.getContext('2d')
labelCanvasContext.fillText(
label,
this.labelCanvas.width * bounds[0],
this.labelCanvas.height * (1 - bounds[1]) - 20
labelCanvas.width * bounds[0],
labelCanvas.height * (1 - bounds[1]) - 20
);
newRenderer.setViewport.apply(newRenderer, bounds);
this.vtk.renderers[label] = newRenderer;
Expand All @@ -489,16 +544,18 @@ export default {
if (imageViewIntersectMode.value) this.resetIntersections()

else if ([1, 2].includes(deepSSMDataTab.value)) {
this.prepareColorScale()
this.prepareColorScales()
}

const targetRenderer = Object.values(this.vtk.renderers)[this.columns - 1]
this.vtk.orientationCube = this.newOrientationCube(this.vtk.interactor)
if (targetRenderer) {
this.vtk.orientationCube.setParentRenderer(targetRenderer)
this.vtk.orientationCube.setEnabled(true);
this.vtk.interactor.enable()
if (!this.showColorScale) {
const targetRenderer = Object.values(this.vtk.renderers)[this.columns - 1]
this.vtk.orientationCube = this.newOrientationCube(this.vtk.interactor)
if (targetRenderer) {
this.vtk.orientationCube.setParentRenderer(targetRenderer)
this.vtk.orientationCube.setEnabled(true);
}
}
this.vtk.interactor.enable()
this.render();
renderLoading.value = false;
setTimeout(
Expand Down
6 changes: 0 additions & 6 deletions web/shapeworks/src/components/ShapeViewer/scripting.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,6 @@ export default {
}
return grid;
},
labelCanvas() {
return this.$refs.labels;
},
labelCanvasContext() {
return this.labelCanvas.getContext("2d");
},
showDifferenceFromMeanMode() {
return showDifferenceFromMeanMode.value
},
Expand Down
32 changes: 11 additions & 21 deletions web/shapeworks/src/components/ShapeViewer/viewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@
class="render-area"
>
<canvas class="labels-canvas" ref="labels"/>
<canvas class="color-scale-canvas" ref="colors" v-if="showColorScale"/>
<div class ="color-scale-title-text" v-if="showColorScale">
<span v-if="showDifferenceFromMeanMode">Distance from particle on mean shape</span>
<span v-if="[1, 2].includes(deepSSMDataTab)">DeepSSM Error</span>
</div>
<div class="color-scale-labels-canvas" ref="colorLabels" v-if="showColorScale"/>
<div class="color-scales" ref="colors" v-if="showColorScale" />
<div class ="color-scale-title" ref="colorsTitle" v-if="showColorScale" />
<div class="color-scale-labels" ref="colorsLabels" v-if="showColorScale" />
</div>
</template>

Expand All @@ -25,28 +22,21 @@
width: 100%;
height: 100%;
}
.color-scale-canvas {
.color-scales {
position: absolute;
right: 10px;
width: 20px;
width: 100%;
height: 100%;
z-index: 1;
}
.color-scale-labels-canvas {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: flex-end;
.color-scale-title {
position: absolute;
right: 35px;
width: 50px;
writing-mode: vertical-rl;
right: -10px;
text-align: center;
height: 100%;
}
.color-scale-title-text {
.color-scale-labels {
position: absolute;
writing-mode: vertical-rl;
right: -10px;
text-align: center;;
width: 100%;
height: 100%;
}
</style>
Expand Down
6 changes: 1 addition & 5 deletions web/shapeworks/src/reader/image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ async function readDeepSSMScalars(url: string | undefined) {
const data = reader.getOutputData();

const content = (await axios.get(url)).data;
let values: number[] = []
const values: number[] = []
content.split('\r\n\r').forEach((section) => {
if (section.includes('deepssm_error')) {
const [header, data] = section.split('deepssm_error')
Expand All @@ -38,18 +38,14 @@ async function readDeepSSMScalars(url: string | undefined) {
}
}
})
console.log(values)
const dataRange = [
Math.min(...values),
Math.max(...values),
]
console.log('range', dataRange)
console.log('global 1', deepSSMErrorGlobalRange.value)
deepSSMErrorGlobalRange.value = [
Math.min(deepSSMErrorGlobalRange.value[0], dataRange[0]),
Math.max(deepSSMErrorGlobalRange.value[1], dataRange[1]),
]
console.log(deepSSMErrorGlobalRange.value)

const arr = vtkDataArray.newInstance({
name: 'deepssm_error',
Expand Down
24 changes: 13 additions & 11 deletions web/shapeworks/src/views/Main.vue
Original file line number Diff line number Diff line change
Expand Up @@ -442,17 +442,19 @@ export default {
)
}
if(layersShown.value.includes("Groomed")){
const shapeURL = groomedShapesForOriginalDataObjects.value[
dataObject.type
][dataObject.id].file
shapePromises.push(
imageReader(
shapeURL,
shortFileName(shapeURL),
"Groomed",
{ domain: dataObject.anatomy_type.replace('anatomy_', '') }
)
)
if (groomedShapesForOriginalDataObjects.value[dataObject.type]) {
const shapeURL = groomedShapesForOriginalDataObjects.value[
dataObject.type
][dataObject.id].file
shapePromises.push(
imageReader(
shapeURL,
shortFileName(shapeURL),
"Groomed",
{ domain: dataObject.anatomy_type.replace('anatomy_', '') }
)
)
}
}
if(layersShown.value.includes("Reconstructed")){
const targetReconstruction = reconstructionsForOriginalDataObjects.value.find(
Expand Down

0 comments on commit 81f1a08

Please sign in to comment.