Skip to content

Commit

Permalink
Merge pull request #1306 from girder/max-merge
Browse files Browse the repository at this point in the history
Max Merge option in Frame Selector
  • Loading branch information
annehaley authored Oct 4, 2023
2 parents ec79c12 + 97c22d7 commit a469851
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
import {restRequest} from '@girder/core/rest';
import {Chrome} from 'vue-color';
import {CHANNEL_COLORS, OTHER_COLORS} from '../utils/colors';
import {OTHER_COLORS, getChannelColor} from '../utils/colors';
import HistogramEditor from './HistogramEditor.vue';
export default {
components: {
'color-picker': Chrome,
HistogramEditor
},
props: ['itemId', 'currentFrame', 'currentStyle', 'layers', 'layerMap', 'active'],
props: ['itemId', 'currentFrame', 'currentStyle', 'histogramParamStyle', 'layers', 'layerMap', 'active'],
emits: ['updateStyle'],
data() {
return {
Expand All @@ -20,17 +20,21 @@ export default {
compositeLayerInfo: {},
expandedRows: [],
autoRangeForAll: undefined,
histogramParams: {
showKeyboardShortcuts: false
};
},
computed: {
histogramParams() {
return {
frame: this.currentFrame,
width: 1024,
height: 1024,
bins: 512,
resample: false,
style: '{}',
style: this.histogramParamStyle,
roundRange: true
},
showKeyboardShortcuts: false
};
};
}
},
watch: {
active() {
Expand All @@ -44,6 +48,9 @@ export default {
if (this.currentStyle.preset) {
this.initializeStateFromStyle();
}
},
histogramParams() {
this.fetchCurrentFrameHistogram();
}
},
mounted() {
Expand Down Expand Up @@ -114,13 +121,11 @@ export default {
// Assign colors
this.layers.forEach((layerName) => {
if (!this.compositeLayerInfo[layerName].palette) {
// Search for case-insensitive regex match among known channel-colors
Object.entries(CHANNEL_COLORS).forEach(([channelPattern, color]) => {
if (layerName.match(new RegExp(channelPattern, 'i')) && !usedColors.includes(color)) {
this.compositeLayerInfo[layerName].palette = color;
usedColors.push(color);
}
});
const channelColor = getChannelColor(layerName);
if (channelColor) {
this.compositeLayerInfo[layerName].palette = channelColor;
usedColors.push(channelColor);
}
}
});
this.layers.forEach((layerName) => {
Expand Down
30 changes: 26 additions & 4 deletions girder/girder_large_image/web_client/vue/components/DualInput.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<script>
export default {
props: ['label', 'currentValue', 'valueMax', 'sliderLabels'],
emits: ['updateValue'],
props: ['label', 'currentValue', 'valueMax', 'sliderLabels', 'maxMerge'],
emits: ['updateValue', 'updateMaxMerge'],
data() {
return {
value: this.currentValue
value: this.currentValue,
merge: this.maxMerge
};
},
watch: {
Expand All @@ -13,6 +14,12 @@ export default {
},
value(v) {
this.$emit('updateValue', v);
},
maxMerge(v) {
this.merge = v;
},
merge(v) {
this.$emit('updateMaxMerge', v);
}
}
};
Expand All @@ -28,15 +35,17 @@ export default {
name="numberControl"
min="0"
:max="valueMax"
:disabled="merge"
>
</td>
<td style="width:90%">
<td style="width: 100%">
<input
v-model="value"
type="range"
name="sliderControl"
min="0"
:max="valueMax"
:disabled="merge"
>
<div class="bubble-wrap">
<output
Expand All @@ -53,12 +62,25 @@ export default {
/>
</div>
</td>
<td
v-show="merge !== undefined"
style="min-width: 150px; text-align: right;"
>
<input
:id="'maxMerge'+label"
v-model="merge"
type="checkbox"
style="margin: 0px 5px 0px 10px"
>
<label :for="'maxMerge'+label">Max Merge</label>
</td>
</tr>
</template>

<style scoped>
.dual-controls > * > * {
margin-right: 15px;
white-space: nowrap;
}
.dual-controls.tall {
height: 40px;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<script>
import Vue from 'vue';
import {getChannelColor} from '../utils/colors';
import CompositeLayers from './CompositeLayers.vue';
import DualInput from './DualInput.vue';
import PresetsMenu from './PresetsMenu.vue';
Expand All @@ -18,7 +20,8 @@ export default Vue.extend({
indices: [],
indexInfo: {},
style: {},
modesShown: {1: true}
modesShown: {1: true},
histogramParamStyles: {}
};
},
computed: {
Expand Down Expand Up @@ -63,14 +66,39 @@ export default Vue.extend({
})
);
},
updateHistogramParamStyles() {
this.histogramParamStyles = {};
Array.from([2, 3]).forEach((modeID) => {
const mergedStyle = this.maxMergeStyle();
if (mergedStyle.bands.length) {
const simpleMergedStyleString = JSON.stringify({
dtype: 'source',
bands: mergedStyle.bands.map((b) => ({
framedelta: b.framedelta,
band: b.band
// including min, max, and palette gives strange results
}))
});
this.histogramParamStyles[modeID] = simpleMergedStyleString;
} else {
this.histogramParamStyles[modeID] = '{}';
}
});
},
updateStyle(idx, style) {
this.$set(this.style, idx, style);
this.updateHistogramParamStyles();
this.update();
},
updateAxisSlider(event) {
this.indexInfo[event.index].current = event.frame;
this.update();
},
updateMaxMergeAxis(event) {
this.indexInfo[event.index].maxMerge = event.maxMerge;
this.update();
this.updateHistogramParamStyles();
},
updateFrameSlider(frame) {
this.currentFrame = frame;
this.frameUpdate(frame, undefined);
Expand All @@ -80,14 +108,73 @@ export default Vue.extend({
this.indices.forEach((index) => {
if (this.sliderIndices.includes(index)) {
const info = this.indexInfo[index];
frame += info.current * info.stride;
if (!info.maxMerge) {
frame += info.current * info.stride;
}
}
});
this.currentFrame = frame;
const style = this.currentModeId > 1 ? Object.assign({}, this.style[this.currentModeId]) : undefined;
let style = this.currentModeId > 1 ? Object.assign({}, this.style[this.currentModeId]) : undefined;
if (style && style.preset) delete style.preset;
style = this.maxMergeStyle(style);
this.frameUpdate(frame, style);
},
maxMergeStyle(style) {
const bandsArray = (style ? style.bands : []) || [];
let newBandsArray = [];
let frameDeltas = [];
Object.entries(this.indexInfo).forEach(([indexName, {range, stride, maxMerge}]) => {
if (this.currentModeId === 2 && indexName === 'IndexC') {
// channel compositing is already in bandsArray
// skip permutations for this axis
} else if (maxMerge) {
const axisFrameDeltas = [...Array(range + 1).keys()].map((i) => i * stride);
if (frameDeltas.length) {
const newFrameDeltas = [];
frameDeltas.forEach((d) => {
axisFrameDeltas.forEach((a) => {
newFrameDeltas.push(d + a);
});
});
frameDeltas = newFrameDeltas;
} else {
frameDeltas = axisFrameDeltas;
}
}
});
if (frameDeltas.length) {
if (bandsArray.length) {
// some style already applied, add permutations
bandsArray.forEach((b) => {
frameDeltas.forEach((framedelta) => {
newBandsArray.push(
Object.assign({}, b, {
framedelta: b.framedelta ? b.framedelta + framedelta : framedelta
})
);
});
});
} else {
// no style applied yet, create new permutations list
const {bands} = this.metadata;
bands.forEach((b, i) => {
const bandPalette = getChannelColor(b);
frameDeltas.forEach((framedelta) => {
newBandsArray.push({
band: i + 1,
framedelta,
palette: bandPalette
});
});
});
}
} else {
// no max merge permutations to apply, keep old bandsArray
newBandsArray = bandsArray;
}
return {bands: newBandsArray};
},
fillMetadata() {
if (!this.metadata.frames) {
this.metadata.frames = [{
Expand Down Expand Up @@ -242,6 +329,8 @@ export default Vue.extend({
:value-max="indexInfo[index].range"
:label="index.replace('Index', '')"
:slider-labels="index === 'IndexC' ? imageMetadata.channels : []"
:max-merge="indexInfo[index].maxMerge || false"
@updateMaxMerge="(v) => updateMaxMergeAxis({index, maxMerge: v})"
@updateValue="(v) => updateAxisSlider({index, frame: v})"
/>
</table>
Expand All @@ -255,6 +344,7 @@ export default Vue.extend({
:item-id="itemId"
:current-frame="currentFrame"
:current-style="style[2]"
:histogram-param-style="histogramParamStyles[2]"
:layers="metadata.channels"
:layer-map="metadata.channelmap"
:active="currentModeId === 2"
Expand All @@ -267,6 +357,7 @@ export default Vue.extend({
:item-id="itemId"
:current-frame="currentFrame"
:current-style="style[3]"
:histogram-param-style="histogramParamStyles[3]"
:layers="metadata.bands"
:layer-map="undefined"
:active="currentModeId === 3"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ export default {
currentFrame() {
this.fetchHistogram();
},
currentFrameHistogram() {
this.fetchHistogram();
},
histogram() {
// allow rerender to occur first
Vue.nextTick().then(() => {
Expand Down Expand Up @@ -290,10 +293,11 @@ export default {
class="handles-svg"
>
<text
v-if="vRange[0] !== undefined"
x="5"
y="43"
class="small"
>{{ vRange[0] }}</text>
>{{ +vRange[0].toFixed(2) || 0 }}</text>
<rect
ref="minExclusionBox"
x="5"
Expand All @@ -314,10 +318,11 @@ export default {
y2="30"
/>
<text
v-if="vRange[1] !== undefined"
:x="xRange[1] && vRange[1] ? xRange[1] - (`${vRange[1]}`.length * 6): 0"
y="43"
class="small"
>{{ vRange[1] }}</text>
>{{ +vRange[1].toFixed(2) || 1 }}</text>
<rect
ref="maxExclusionBox"
x="5"
Expand Down
9 changes: 9 additions & 0 deletions girder/girder_large_image/web_client/vue/utils/colors.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ export const CHANNEL_COLORS = {
'^gr[ae]y(|scale)$': '#FFFFFF'
};

export function getChannelColor(name) {
// Search for case-insensitive regex match among known channel-colors
for (const [channelPattern, color] of Object.entries(CHANNEL_COLORS)) {
if (name.match(new RegExp(channelPattern, 'i'))) {
return color;
}
}
}

export const OTHER_COLORS = [
'#FF0000',
'#00FF00',
Expand Down

0 comments on commit a469851

Please sign in to comment.