Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat pims multi channels colors #104

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
1824439
feat: Use webp thumbnails
urubens May 21, 2021
522d773
fix: Change tile urls
urubens May 21, 2021
d945e7e
fix: Do not display unavailable image filters
urubens Jun 25, 2021
a8f9cb6
fix: annotation drawing with no outline with pims
urubens Jun 29, 2021
1464e3c
fix: Find vendor from format instead of mime
urubens Jul 1, 2021
44c1479
feat: Add new uploaded file status (to be moved to JS client)
urubens Sep 24, 2021
614c76a
Fix: tile parameter for inverse colormap, filter and intensities
urubens Sep 24, 2021
3de8e2b
feat: Add tile parameters for z-slice and timepoint
urubens Sep 24, 2021
8a8ad27
feat: Extract thumbnail display into component [REQUIRES JS CLIENT WI…
urubens Oct 25, 2021
643e4e4
fix: Improve thumb/crop URL building when used as background image
urubens Oct 25, 2021
529a05f
Support extra parameters for image thumb url
urubens Oct 26, 2021
0177bc6
Fix thumb url when no extra parameters
urubens Oct 26, 2021
03e707b
feat: Show dynamic list of supported formats from backend image servers
urubens Nov 5, 2021
5ede248
fix: Update attributes of ImageInstance/AbstractImage:extrinsicChanne…
urubens Mar 31, 2022
ba494e2
fix: mess with channels and apparentChannels
urubens Apr 7, 2022
61c177b
feat: Add nb of concrete channels and nb of samples in image details
urubens Apr 21, 2022
f12ebb4
fix: slow thumbnail in image selector + improve detection of best ima…
urubens Apr 22, 2022
4bdf629
Extract image controls shift buttons to a component
urubens Jun 29, 2020
ba9c9e4
feat: Add step selector in image controls
urubens Jun 29, 2020
c6f6f8b
Simplify image controls buttons
urubens Jun 29, 2020
ddb10e4
refactor: Each image dimension has its own step value in image controls
urubens Jun 29, 2020
18fdd63
chore: update license year for controls.js
urubens May 16, 2022
3d8f939
Add support for non-standard tile size
urubens Sep 29, 2020
a801586
Clean tile URL building in viewer
urubens Oct 26, 2021
46decba
feat: Allow to have multiple active slices at same time in a viewer. …
urubens Oct 29, 2021
8f187c3
feat: Disable draw when multiple slices (channels) are displayed
urubens Nov 3, 2021
e632dd4
feat: Show annotation channel name in current selection if multiple c…
urubens Nov 3, 2021
5193ff9
Merge branch 'uliege-refactor-image-controls' into uliege-feat-pims-m…
urubens May 19, 2022
5a18f07
Speed up fetchSliceInstancesAround for images with many slices
urubens Dec 22, 2021
08c41ff
refactor: Comment old color manipulation methods
urubens Apr 7, 2022
1532c24
refactor: Start rewrite Color Manipulation panel: allow to show/hide …
urubens Apr 7, 2022
ef7e9fb
feat: Color manipulation: adjustable LUT + auto adjust + reset
urubens Apr 20, 2022
ad3b740
Color manipulation: channel name is clickable (and toggles histogram)
urubens Apr 20, 2022
6202963
fix: histogram background color update
urubens Apr 20, 2022
ff9670f
Clean histogram chart
urubens Apr 20, 2022
f2f5b8b
histogram chart: remove highest value
urubens Apr 20, 2022
de03bf0
Improve color manipulation adjust/reset button UX
urubens Apr 20, 2022
15872e9
color manipulation: disable adjust/reset button in per-channel adjust…
urubens Apr 21, 2022
cb42ee6
Debounce color selector for channel color manipulation
urubens Apr 21, 2022
b55bc39
feat: Synchronize channel image controls select with merged channels …
urubens Apr 21, 2022
9e8ef2c
Re-use max mergeable channels with new color manipulation panel, a li…
urubens Apr 21, 2022
5dddee7
Fix sorting of channels thanks to the beauty of JS
urubens Apr 21, 2022
25b8e70
Fix selection of "mergd-channels" in image controls
urubens Apr 21, 2022
ee95a90
Color manipulation: add settings dropdown with histogram scale param
urubens Apr 21, 2022
a235abe
Color manipulation: add setting to choose adjustment of intensities: …
urubens Apr 21, 2022
3ffd308
Color manipulation: do not show filter selector if there is none
urubens Apr 22, 2022
a10b9f6
Fix watchers for brightness/contrast computation
urubens Apr 21, 2022
0d7fd01
feat: set default max mergeable channels to 36
urubens Mar 31, 2022
c32d352
fix: color manipulation panel display when more than one image in a v…
urubens Mar 5, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/components/annotations/AnnotationDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@
</td>
</tr>

<tr v-if="showChannelInfo">
<td><strong>{{$t('channel')}}</strong></td>
<td>
<channel-name :channel="sliceChannel" />
</td>
</tr>

<template v-if="isPropDisplayed('geometry-info')">
<tr v-if="annotation.area > 0">
<td><strong>{{$t('area')}}</strong></td>
Expand Down Expand Up @@ -235,10 +242,12 @@ import TrackTree from '@/components/track/TrackTree';
import CytomineTrack from '@/components/track/CytomineTrack';
import AnnotationCommentsModal from './AnnotationCommentsModal';
import ProfileModal from '@/components/viewer/ProfileModal';
import ChannelName from '@/components/viewer/ChannelName';

export default {
name: 'annotations-details',
components: {
ChannelName,
ImageName,
CytomineDescription,
CytomineTerm,
Expand All @@ -256,8 +265,10 @@ export default {
tracks: {type: Array},
users: {type: Array},
images: {type: Array},
slices: {type: Array, default: () => []},
profiles: {type: Array, default: () => []},
showImageInfo: {type: Boolean, default: true},
showChannelInfo: {type: Boolean, default: false},
showComments: {type: Boolean, default: false}
},
data() {
Expand Down Expand Up @@ -298,6 +309,9 @@ export default {
return this.images.find(image => image.id === this.annotation.image) ||
{'id': this.annotation.image, 'instanceFilename': this.annotation.instanceFilename};
},
sliceChannel() {
return this.slices.find(slice => slice.id === this.annotation.slice) || {};
},
maxRank() {
return this.image.depth * this.image.duration * this.image.channels;
},
Expand Down
29 changes: 25 additions & 4 deletions src/components/annotations/AnnotationPreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,33 @@ export default {
};
},
computed: {
styleAnnotDetails() {
let outlineParams = this.color ? '&draw=true&color=0x' + this.color : '';
let url = `${this.annot.url}?maxSize=${this.size}&square=true&complete=false&thickness=2&increaseArea=1.25${outlineParams}`;
cropParameters() {
let params = {
square: true,
complete: true,
thickness: 2,
increaseArea: 1.25,
rev: this.revisionCrop,
};

if (this.color || this.color === '') {
params.draw = true;
}
if (this.color) {
params.color = `0x${this.color}`;
}
if (this.annot.updated) {
params.updated = this.annot.updated;
}

return params;
},
cropUrl() {
return this.annot.annotationCropURL(this.size, 'jpg', this.cropParameters);
},
styleAnnotDetails() {
return {
backgroundImage: `url(${url})`,
backgroundImage: `url(${this.cropUrl})`,
backgroundRepeat: 'no-repeat',
width: this.size + 'px',
height: this.size + 'px'
Expand Down
2 changes: 1 addition & 1 deletion src/components/annotations/ListAnnotations.vue
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ export default {

colors() {
let colors = defaultColors.map(color => ({label: this.$t(color.name), ...color}));
colors.push({label: this.$t('no-outline')});
colors.push({label: this.$t('no-outline'), hexaCode: ''});
return colors;
},

Expand Down
19 changes: 13 additions & 6 deletions src/components/annotations/ListAnnotationsBy.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<template v-else>
<annotation-preview
v-for="annot in annotations" :key="((isInViewer) ? index : '') + title + annot.id"
:class="{active: isInViewer && annot.slice === imageWrapper.activeSlice.id}"
:class="{active: isInViewer && activeSlicesIds.includes(annot.slice)}"
:annot="annot"
:size="size"
:color="color"
Expand Down Expand Up @@ -69,6 +69,7 @@ import AnnotationPreview from './AnnotationPreview';

import {AnnotationCollection} from 'cytomine-client';
import constants from '@/utils/constants';
import _ from 'lodash';

export default {
name: 'list-annotations-by',
Expand Down Expand Up @@ -262,9 +263,15 @@ export default {
this.$store.commit(this.projectModule + 'listAnnotations/setCurrentPage', {prop: this.prop.id, page});
}
},
activeSlice() {
return (this.imageWrapper) ? this.imageWrapper.activeSlice : null;
}
activeSlices() {
return (this.imageWrapper) ? this.imageWrapper.activeSlices : null;
},
activeSlicesIds() {
return (this.activeSlices) ? this.activeSlices.map(slice => slice.id) : [];
},
activeSliceWithSmallestRank() {
return (this.activeSlices) ? _.orderBy(this.activeSlices, ['rank'])[0] : null;
},
},
watch: {
currentPage() {
Expand All @@ -283,7 +290,7 @@ export default {
this.fetchPage();
}
},
activeSlice() {
activeSlices() {
this.findPage();
}
},
Expand All @@ -295,7 +302,7 @@ export default {
async findPage() {
if (this.isInViewer) {
let countCollection = this.collection.clone();
countCollection.beforeSlice = this.activeSlice.id;
countCollection.beforeSlice = this.activeSliceWithSmallestRank;
countCollection.max = 1;
this.currentPage = Math.ceil(((await countCollection.fetchPage()).totalNbItems + 1)/ this.nbPerPage);
}
Expand Down
243 changes: 243 additions & 0 deletions src/components/charts/HistogramChart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
/*
* Copyright (c) 2009-2022. Authors: see NOTICE file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {Line} from 'vue-chartjs';
import _ from 'lodash';

export default {
name: 'histogram-chart',
extends: Line,
props: {
logScale: Boolean,
color: String,

histogram: Array,
nBins: Number,
firstBin: Number,
lastBin: Number,

defaultBounds: Object,
imageBounds: Object,
currentBounds: Object,
gamma: Number,
inverted: Boolean,
},
data() {
return {
chartData: null,
};
},
computed: {
extendedHistogram() {
const missingLeft = new Array(this.firstBin).fill(0);
const missingRight = new Array(this.nBins - this.lastBin - 1).fill(0);
return missingLeft.concat(this.histogram).concat(missingRight);
},
logHistogram() {
return this.extendedHistogram.map(v => Math.log(v));
},
scaledHistogram() {
return this.logScale ? this.logHistogram : this.extendedHistogram;
},

binSize() {
return (this.defaultBounds.max + 1) / this.nBins;
},
integerBinSize() {
return Math.round(this.binSize);
},
labels() {
return this.extendedHistogram.map((_, idx) => Math.round(idx * this.binSize));
},
imageBoundsLabel() {
return {
min: this.findLabel(this.imageBounds.min),
max: this.findLabel(this.imageBounds.max)
};
},
currentBoundsLabel() {
return {
min: Math.max(this.defaultBounds.min, this.findLabel(this.currentBounds.min)),
max: Math.min(this.defaultBounds.max, this.findLabel(this.currentBounds.max))
};
},
boundsLabel() {
return {
min: Math.min(this.imageBoundsLabel.min, this.currentBoundsLabel.min),
max: Math.max(this.imageBoundsLabel.max, this.currentBoundsLabel.max)
};
},

currentLabels() {
return this.labels.filter(label =>
label >= this.currentBoundsLabel.min && label <= this.currentBoundsLabel.max
);
},

systemResponse() {
if (this.currentLabels.length === 1) {
return [
{x: this.currentLabels[0], y: 0},
{x: this.currentLabels[0], y: 255}
];
}

let nbPoints = 100;
let step = (this.currentLabels.length - 1) / nbPoints;
let range = _.range(0, this.currentLabels.length - 1 + step, step);

let ymin = (this.inverted) ? 1 : 0;
let ymax = (this.inverted) ? 0 : 1;
let m = (ymin - ymax) / (this.currentBoundsLabel.min - this.currentBoundsLabel.max);
let p = ymin - m * this.currentBoundsLabel.min;
let gamma = (this.inverted) ? 1/this.gamma : this.gamma;
let response = range.map(idx => {
let label = this.currentLabels[Math.round(idx)];
return {
x: label,
y: Math.pow((m * label + p), gamma) * 255.0
};
});
return _.uniqBy(response, 'x');
},

backgroundColor() {
if (this.color !== null) {
return this.color;
}
return '#fff';
},
datasets() {
return [
{
data: this.systemResponse,
fill: false,
pointRadius: 0,
pointHoverRadius: 0,
borderColor: '#333',
borderWidth: 1,
type: 'line',
order: 2,
cubicInterpolationMode: 'monotone',
yAxisID: 'yResponse'
},
{
data: this.scaledHistogram,
backgroundColor: this.backgroundColor,
pointRadius: 1,
order: 1,
yAxisID: 'yHistogram'
},
];
}
},
watch: {
currentBoundsLabel() {
this.doRenderChart();
},
logScale() {
this.doRenderChart();
},
systemResponse() {
this.doRenderChart();
},
backgroundColor() {
this.doRenderChart();
}
},
methods: {
findBin(value) {
return Math.floor(value / this.binSize);
},
findLabel(value) {
return Math.floor(this.findBin(value) * this.binSize);
},
doRenderChart() {
try {
this.renderChart({
labels: this.labels,
datasets: this.datasets,

// Additional data for tooltips
logScale: this.logScale,
binSize: this.integerBinSize,
}, {
tooltips: {
filter: function(tooltipItem) {
return tooltipItem.datasetIndex !== 0;
},
callbacks: {
label: function(tooltipItem, data) {
if (data.logScale) {
return Math.round(Math.exp(tooltipItem.value));
}
return tooltipItem.value;
},
title: function(tooltipItems, data) {
if (tooltipItems.length > 0) {
let left = Number(tooltipItems[0].label);
if (data.binSize === 1) {
return left;
}
let right = left + data.binSize;
return `[${left} - ${right}[`;
}
}
}
},
maintainAspectRatio: false,
responsive: true,
title: {
display: false
},
legend: {
display: false
},
animation: {
duration: 0,
},
scales: {
xAxes: [{
display: true,
scaleLabel: {
display: false
},
ticks: {
...this.boundsLabel,
fontSize: 10,
}
}],
yAxes: [{
id: 'yHistogram',
display: false,
}, {
id: 'yResponse',
display: false
}
]
},
});
}
catch (error) {
console.log(error);
this.$emit('error', true);
}
}
},
async mounted () {
this.doRenderChart();
}
};
Loading