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

Image outline #578

Open
wants to merge 39 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
2485530
Add outline icon and outline image ROI (full image)
Rdornier Jul 9, 2024
a606cde
update gitignore
Rdornier Jul 17, 2024
125060c
adapt rectangle to the viewport
Rdornier Jul 19, 2024
5c16973
fix outline area
Rdornier Jul 19, 2024
25ffe0e
add space to remove useless changes
Rdornier Jul 22, 2024
822e758
Update stockeWidth after creating the Rectangle
Rdornier Aug 9, 2024
7652e92
Merge branch 'ome:master' into image-outline
Rdornier Aug 12, 2024
90eec45
Merge branch 'image-outline' of https://github.com/Rdornier/omero-fig…
Rdornier Aug 12, 2024
4aa3ee6
Merge branch 'ome:master' into image-outline
Rdornier Aug 21, 2024
97d2638
Merge branch 'image-outline' of https://github.com/Rdornier/omero-fig…
Rdornier Aug 21, 2024
c9da69a
Merge branch 'ome:master' into image-outline
Rdornier Aug 23, 2024
47122cd
Merge branch 'image-outline' of https://github.com/Rdornier/omero-fig…
Rdornier Aug 23, 2024
09243c1
Add css border to images
Rdornier Aug 27, 2024
a8341cd
handle dynamic change of color and stroke width
Rdornier Aug 27, 2024
0a71133
remove last implementation
Rdornier Aug 27, 2024
e7925cc
add border ouside panel and replace outline by border
Rdornier Aug 29, 2024
c0c20fc
remove extra space and commented code
Rdornier Aug 29, 2024
0f5fa39
adjust label margin according to borders
Rdornier Aug 29, 2024
c57f8a8
Fix line-width drop-down thanks to Will
Rdornier Aug 29, 2024
8413745
Toggle show/hide with one button
Rdornier Aug 29, 2024
830d988
Fixing showBorder logic
Rdornier Aug 30, 2024
f2013b1
Update figure_to_pdf
Rdornier Aug 30, 2024
a368b28
fix bug on showBorder
Rdornier Aug 30, 2024
08bc8d5
Add tiff export border support
Rdornier Aug 30, 2024
c069e16
Merge branch 'ome:master' into image-outline
Rdornier Sep 3, 2024
11e92dc
Fix flake8 errors
Rdornier Sep 3, 2024
46dbf91
Merge branch 'image-outline' of https://github.com/Rdornier/omero-fig…
Rdornier Sep 3, 2024
24fe033
Merge branch 'master' of https://github.com/Rdornier/omero-figure int…
Rdornier Sep 9, 2024
ec73abb
Fixing margin-top
Rdornier Sep 9, 2024
d538c6d
Fix method name
Rdornier Sep 9, 2024
e5a5963
Fixing tiff export
Rdornier Sep 9, 2024
526f3b6
Fix flake8 errors
Rdornier Sep 9, 2024
5d9e438
Fix flake8 errors
Rdornier Sep 9, 2024
88de839
fix rotation issue on pdf
Rdornier Sep 9, 2024
5a5063c
Fix rotation issue
Rdornier Sep 11, 2024
cfcbf35
Fix border width rounding
Rdornier Sep 11, 2024
243ea0c
try fixing padding
Rdornier Sep 13, 2024
b59d9ee
Merge branch 'ome:master' into image-outline
Rdornier Oct 1, 2024
3e0427f
Merge branch 'ome:master' into image-outline
Rdornier Oct 10, 2024
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ dist
omero_figure.egg-info
omero_figure/templates/
omero_figure/static/
.vscode
.idea
53 changes: 46 additions & 7 deletions omero_figure/scripts/omero/figure_scripts/Figure_To_Pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,10 @@ def apply_transform(tf, point):
point[0] * tf['A10'] + point[1] * tf['A11'] + tf['A12'],
] if tf else point

def draw_rectangle(self, shape):
def draw_outline(self, shape):
self.draw_rectangle(shape, False)

def draw_rectangle(self, shape, in_panel_check=True):
# to support rotation/transforms, convert rectangle to a simple
# four point polygon and draw that instead
s = deepcopy(shape)
Expand All @@ -229,7 +232,7 @@ def draw_rectangle(self, shape):
]
s['points'] = ' '.join(','.join(
map(str, self.apply_transform(t, point))) for point in points)
self.draw_polygon(s)
self.draw_polygon(s, closed=True, in_panel_check=in_panel_check)

def draw_point(self, shape):
s = deepcopy(shape)
Expand Down Expand Up @@ -407,8 +410,8 @@ def draw_arrow(self, shape):

self.draw_shape_label(shape, Bounds((x1, y1), (x2, y2)))

def draw_polygon(self, shape, closed=True):
polygon_in_viewport = False
def draw_polygon(self, shape, closed=True, in_panel_check=True):
polygon_in_viewport = not in_panel_check
points = []
for point in shape['points'].split(" "):
# Older polygons/polylines may be 'x,y,'
Expand Down Expand Up @@ -451,7 +454,7 @@ def draw_polygon(self, shape, closed=True):
self.draw_shape_label(shape, Bounds(*points))

def draw_polyline(self, shape):
self.draw_polygon(shape, False)
self.draw_polygon(shape, closed=False)

def draw_ellipse(self, shape):
stroke_width = float(shape.get('strokeWidth', 1))
Expand Down Expand Up @@ -520,6 +523,24 @@ def __init__(self, pil_img, panel, crop):
self.scale = pil_img.size[0] / crop['width']
self.draw = ImageDraw.Draw(pil_img)

if 'border' in panel and panel['border'].get('showBorder'):
sw = panel['border'].get('strokeWidth')
shift_pos = sw / (float(panel['zoom'])/100)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand the need for diving by the zoom here?
When I was testing on a big image, the zoom was very large, so the shift_pos was effectively zero and the border wasn't getting drawn around the outside of the panel. When I simply tried shift_pos = sw it seemed to work OK?


shape = {}
shape['strokeColor'] = panel['border'].get('color')
shape['strokeWidth'] = sw
shape['x'] = crop['x'] - shift_pos
shape['y'] = crop['y'] - shift_pos
shape['width'] = crop['width'] + 2*shift_pos
shape['height'] = crop['height'] + 2*shift_pos
shape['type'] = "outline"

if "shapes" not in panel:
panel['shapes'] = [shape]
else:
panel['shapes'].append(shape)

super(ShapeToPilExport, self).__init__(panel)

def get_panel_coords(self, shape_x, shape_y):
Expand Down Expand Up @@ -625,7 +646,7 @@ def draw_arrow(self, shape):

# Override to not just call draw_polygon, because we want square corners
# for rectangles and not the rounded corners draw_polygon creates
def draw_rectangle(self, shape):
def draw_rectangle(self, shape, in_panel_check=True):
points = [
(shape['x'], shape['y']),
(shape['x'] + shape['width'], shape['y']),
Expand All @@ -643,7 +664,6 @@ def draw_rectangle(self, shape):

stroke_width = scale_to_export_dpi(float(shape.get('strokeWidth', 2)))
buffer = int(ceil(stroke_width) * 1.5)

# if fill, draw filled polygon without outline, then add line later
# with correct stroke width
rgba = self.get_rgba_int(shape.get('fillColor', '#00000000'))
Expand Down Expand Up @@ -1126,6 +1146,25 @@ def add_rois(self, panel, page):
"""
Add any Shapes
"""
if 'border' in panel and panel['border'].get('showBorder'):
crop = self.get_crop_region(panel)
sw = panel['border'].get('strokeWidth')
shift_pos = sw / (float(panel['zoom'])/100)

shape = {}
shape['strokeColor'] = panel['border'].get('color')
shape['strokeWidth'] = sw
shape['x'] = crop['x'] - shift_pos
shape['y'] = crop['y'] - shift_pos
shape['width'] = crop['width'] + 2*shift_pos
shape['height'] = crop['height'] + 2*shift_pos
shape['type'] = "outline"

if "shapes" not in panel:
panel['shapes'] = [shape]
else:
panel['shapes'].append(shape)

if "shapes" not in panel:
return

Expand Down
9 changes: 9 additions & 0 deletions src/css/figure.css
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@
font-size: 1.0rem;
}

/* e.g. Inset form */
.form-inline h5 {
display: inline-flex;
margin-right: 10px;
vertical-align: middle;
margin-bottom: 0;
}

header {
background: gray;
height: 30px;
Expand Down Expand Up @@ -1000,6 +1008,7 @@

.colorpicker span,
.label-color span:first-child,
.border-color span:first-child,
.shape-color span:first-child {
border: solid 1px #bbb;
}
Expand Down
31 changes: 31 additions & 0 deletions src/js/models/panel_model.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,37 @@
this.save('scalebar', sb);
},

show_border: function(color, strokeWidth){
var border = {
'color': '#'+color,
'strokeWidth': strokeWidth,
'showBorder':true
}
this.save('border', border);
},

remove_border: function(){
this.setBorderAttr('showBorder', false)
},

setBorderColor: function(color){
this.setBorderAttr('color', '#'+color)
},

setBorderStrokeWidth: function(strokeWidth){
this.setBorderAttr('strokeWidth', strokeWidth)
},

setBorderAttr: function(attr, value){
var border = this.get('border');
if(border != undefined){
var xtra = {};
xtra[attr] = value;
var new_border = $.extend(true, {}, border, xtra);
this.save('border', new_border);
}
},

// Simple checking whether shape is in viewport (x, y, width, height)
// Return true if any of the points in shape are within viewport.
is_shape_in_viewport: function(shape, viewport) {
Expand Down
53 changes: 48 additions & 5 deletions src/js/views/panel_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
'change:channels change:zoom change:dx change:dy change:width change:height change:rotation change:labels change:theT change:deltaT change:theZ change:deltaZ change:z_projection change:z_start change:z_end',
this.render_labels);
this.listenTo(this.model, 'change:shapes', this.render_shapes);

this.listenTo(this.model, 'change:border', this.render_layout);
// During drag or resize, model isn't updated, but we trigger 'drag'
this.model.on('drag_resize', this.drag_resize, this);

Expand All @@ -63,6 +63,12 @@
h = xywh[3];
if (w == this.model.get('width') && h == this.model.get('height')) {
// If we're only dragging - simply update position
var border = this.model.get('border');
if (border?.showBorder) {
let sw = border.strokeWidth;
x = x - sw
y = y - sw
}
this.$el.css({'top': y +'px', 'left': x +'px'});
} else {
this.update_resize(x, y, w, h);
Expand All @@ -78,20 +84,37 @@
h = this.model.get('height');

this.update_resize(x, y, w, h);
this.render_labels();
this.$el.removeClass('dragging');
},

update_resize: function(x, y, w, h) {

// If we have a panel border, need to adjust x,y,w,h on the page
// but NOT the w & h we use for img_css below.
var border = this.model.get('border');
var page_w = w;
var page_h = h;
if (border?.showBorder) {
let sw = border.strokeWidth;
this.$el.css({'border': `solid ${sw}px ${border.color}`})
x = x - sw;
y = y - sw;
page_w = w + (sw * 2);
page_h = h + (sw * 2);
} else {
this.$el.css({'border': ''})
}

// update layout of panel on the canvas
this.$el.css({'top': y +'px',
'left': x +'px',
'width': w +'px',
'height': h +'px'});
'width': page_w +'px',
'height': page_h +'px'});

// container needs to be square for rotation to vertical
$('.left_vlabels', this.$el).css('width', 3 * h + 'px');
$('.right_vlabels', this.$el).css('width', 3 * h + 'px');
$('.left_vlabels', this.$el).css('width', 3 * page_h + 'px');
$('.right_vlabels', this.$el).css('width', 3 * page_h + 'px');

// update the img within the panel
var zoom = this.model.get('zoom'),
Expand Down Expand Up @@ -282,6 +305,26 @@
$('.left_vlabels', self.$el).css('width', 3 * self.$el.height() + 'px');
$('.right_vlabels', self.$el).css('width', 3 * self.$el.height() + 'px');

var border = this.model.get('border')
if(border != undefined){
var margin = 5 + border.strokeWidth
$('.left_vlabels>div', self.$el).css('margin-bottom', margin + 'px');
$('.right_vlabels>div', self.$el).css('margin-bottom', margin + 'px');
margin = 3 + border.strokeWidth
$('.label_top', self.$el).css('margin-bottom', margin + 'px');
margin = border.strokeWidth
$('.label_bottom', self.$el).css('margin-top', margin + 'px');
$('.label_left', self.$el).css('margin-right', margin + 'px');
$('.label_right', self.$el).css('margin-left', margin + 'px');
}else{
$('.left_vlabels>div', self.$el).css('mSargin-bottom', '5px');
$('.right_vlabels>div', self.$el).css('margin-bottom', '5px');
$('.label_top', self.$el).css('margin-bottom', '3px');
$('.label_bottom', self.$el).css('margin-top', '');
$('.label_left', self.$el).css('margin-right', '');
$('.label_right', self.$el).css('margin-left', '');
}

return this;
},

Expand Down
56 changes: 55 additions & 1 deletion src/js/views/right_panel_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,16 @@
var self = this;
this.model.getSelected().forEach(function(m){
self.listenTo(m, 'change:shapes', self.render);
self.listenTo(m, 'change:border', self.render);
});
},

events: {
"click .edit_rois": "editRois",
"click .show_border": "showBorder",
"click .remove_border": "removeBorder",
"change .border-color": "changeBorderColor",
"change .border-width": "changeBorderStrokeWidth",
"click .copyROIs": "copyROIs",
"click .pasteROIs": "pasteROIs",
"click .deleteROIs": "deleteROIs",
Expand Down Expand Up @@ -135,6 +140,25 @@
});
},

changeBorderColor: function() {
var color = $('button.border-color span:first', this.$el).attr('data-color'),
sel = this.model.getSelected();

sel.forEach(function(panel){
panel.setBorderColor(color);
});
},

changeBorderStrokeWidth: function() {
var width = $('button.border-width span:first', this.$el).attr('data-line-width'),
sel = this.model.getSelected();
width = parseFloat(width, 10);

sel.forEach(function(panel){
panel.setBorderStrokeWidth(width);
});
},

copyROIs: function(event) {
event.preventDefault();
var sel = this.model.getSelected(),
Expand All @@ -154,6 +178,24 @@
this.render();
},

showBorder: function(event){
var width = $('button.border-width span:first', this.$el).attr('data-line-width');
var color = $('button.border-color span:first', this.$el).attr('data-color');
width = parseFloat(width, 10);

this.model.getSelected().forEach(panel => {
panel.show_border(color, width)
})
event.preventDefault();
},

removeBorder: function(event){
this.model.getSelected().forEach(panel => {
panel.remove_border()
})
event.preventDefault();
},

rectToPolygon: function(rect, rotation) {
// rotate Rect around centre point - return points "x,y, x,y, x,y, x,y"
let cx = rect.x + (rect.width / 2);
Expand Down Expand Up @@ -245,7 +287,9 @@
clipboard_data = this.model.get('clipboard'),
canPaste = clipboard_data && ('SHAPES' in clipboard_data || 'CROP' in clipboard_data),
color,
width;
width,
border,
show_btn_state = false;

sel.forEach(function(panel){
var rois = panel.get('shapes');
Expand All @@ -269,6 +313,13 @@
}
});
}

border = panel.get("border")
if(border?.showBorder){
panel.show_border(border.color.replace('#',''), parseFloat(border.strokeWidth, 10))
}else{
show_btn_state = true;
}
});

var json = {
Expand All @@ -277,6 +328,9 @@
'lineWidth': width || 2,
'roiCount': roiCount,
'canPaste': canPaste,
'borderWidth': border ? border.strokeWidth : 2,
'borderColor': border ? border.color.replace('#', '') : 'FFFFFF',
'showState': show_btn_state,
}
$('#edit_rois_form').html(this.roisTemplate(json));
},
Expand Down
1 change: 0 additions & 1 deletion src/js/views/roi_modal_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ export const RoiModalView = Backbone.View.extend({

// Here we handle init of the dialog when it's shown...
document.getElementById('roiModal').addEventListener('shown.bs.modal', () => {
console.log("ROI modal shown...")
// Clone the 'first' selected panel as our reference for everything
self.m = self.model.getSelected().head().clone();

Expand Down
Loading